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() */
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 greater 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." },
349 /* Channel note list */
350 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
351 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
352 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
353 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
354 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
355 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
356 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
357 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
358 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
359 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
360 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
361 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
362 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
363 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
364 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
366 /* Channel [un]suspension */
367 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
368 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
369 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
370 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
371 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
372 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
373 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
375 /* Access information */
376 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
377 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
378 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
379 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
380 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
381 { "CSMSG_USER_HAS_ACCESS", "%s has access $b%d$b in %s." },
382 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
383 { "CSMSG_HELPER_HAS_ACCESS", "%s has access $b%d$b in %s and has $bsecurity override$b enabled." },
384 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
385 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
386 { "CSMSG_OPERATOR_TITLE", "IRC operator" },
387 { "CSMSG_UC_H_TITLE", "network helper" },
388 { "CSMSG_LC_H_TITLE", "support helper" },
389 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
390 { "CSMSG_MYACCESS_COUNT", "%s has access in $b%d$b channels and is owner of $b%d$b channel(s)." },
391 { "CSMSG_MYACCESS_COUNT_1", "%s has access in $b%d$b channel and is owner of $b%d$b channel(s)." },
394 /* Seen information */
395 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
396 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
397 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
398 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
400 /* Names information */
401 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
402 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
404 /* Channel information */
405 { "CSMSG_CHANNEL_INFO", "$b%s$b Information:" },
406 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
407 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
408 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
409 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
410 { "CSMSG_CHANNEL_MAX_TIME", "$bRecord Visitors: $b%i (%s ago.)" },
411 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
412 { "CSMSG_CHANNEL_BANS", "$bBan Count: $b%i" },
413 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
414 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
415 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
416 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
417 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
418 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
419 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
420 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
421 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
422 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
423 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
424 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
425 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
426 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
428 { "CSMSG_PEEK_INFO", "$b%s$b Status:" },
429 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
430 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
431 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d (%d ops, %d voices, %d regulars)" },
432 { "CSMSG_PEEK_OPS", "$bOps:$b" },
433 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
435 /* Network information */
436 { "CSMSG_NETWORK_INFO", "Network Information:" },
437 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
438 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
439 { "CSMSG_NETWORK_BANS", "$bTotal Ban Count: $b%i" },
440 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
441 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
442 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
443 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
444 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
447 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
448 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
449 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
451 /* Channel searches */
452 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
453 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
454 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
455 { "CSMSG_CHANNEL_SEARCH_RESULTS", "The following channels were found:" },
457 /* Channel configuration */
458 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
459 { "CSMSG_INVALID_CFLAG", "$b%s$b is not a recognized channel flag." },
460 { "CSMSG_CHANNEL_OPTIONS", "Channel Options:" },
461 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
464 { "CSMSG_USER_OPTIONS", "User Options:" },
465 { "CSMSG_USER_PROTECTED_2", "That user is protected." },
468 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
469 { "CSMSG_PING_RESPONSE", "Pong!" },
470 { "CSMSG_WUT_RESPONSE", "wut" },
471 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
472 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
473 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
474 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
475 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
476 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
477 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
480 { "CSMSG_ADDVOTE_DONE", "Vote added. Use $baddoption$b to add at least 2 vote options and then $bstartvote$b to start the voting." },
481 { "CSMSG_ADDVOTE_FULL", "There is already a vote in this channel. Use $bdelvote$b to delete it." },
482 { "CSMSG_DELVOTE_DONE", "Vote deleted." },
483 { "CSMSG_NO_VOTE", "There is no vote in this channel." },
484 { "CSMSG_ADDOPTION_DONE", "Vote option added with id %i (%i - %i)." },
485 { "CSMSG_DELOPTION_DONE", "Vote option deleted." },
486 { "CSMSG_DELOPTION_NONE", "Vote option does not exist." },
487 { "CSMSG_VOTE_NEED_OPTIONS", "There must be at least 2 options in a vote." },
488 { "CSMSG_VOTE_NOT_STARTED", "The vote is not started. Use $bstartvote$b to allow voting." },
489 { "CSMSG_VOTE_QUESTION", "Question: %s" },
490 { "CSMSG_VOTE_OPTION", "$b%i$b: %s ($b%i$b votes)" },
491 { "CSMSG_VOTERES_QUESTION", "Question: %s" },
492 { "CSMSG_VOTERES_OPTION", "
\ 2%i
\ 2: %s (
\ 2%i
\ 2 votes)" },
493 { "CSMSG_STARTVOTE_TOP", "
\ 2%s
\ 2 has started a voting:" },
494 { "CSMSG_STARTVOTE_QUESTION", "
\ 2Question:
\ 2 %s" },
495 { "CSMSG_STARTVOTE_OPTION", "
\ 2%i:
\ 2 %s" },
496 { "CSMSG_STARTVOTE_ACCESS", "All channel users with at least
\ 2%i
\ 2 access can vote." },
497 { "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." },
498 { "CSMSG_STARTVOTE_RUNNING", "The vote is already running." },
499 { "CSMSG_VOTE_VOTED", "You have already voted." },
500 { "CSMSG_VOTE_INVALID", "Vote option does not exist." },
501 { "CSMSG_VOTE_DONE", "You voted for $b%s$b." },
502 { "CSMSG_ENDVOTE_DONE", "The vote has been finished." },
503 { "CSMSG_ENDVOTE_STOPPED", "The vote is not running." },
506 { "CSMSG_EVENT_SEARCH_RESULTS", "The following channel events were found:" },
510 /* eject_user and unban_user flags */
511 #define ACTION_KICK 0x0001
512 #define ACTION_BAN 0x0002
513 #define ACTION_ADD_BAN 0x0004
514 #define ACTION_ADD_TIMED_BAN 0x0008
515 #define ACTION_UNBAN 0x0010
516 #define ACTION_DEL_BAN 0x0020
518 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
519 #define MODELEN 40 + KEYLEN
523 #define CSFUNC_ARGS user, channel, argc, argv, cmd
525 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
526 #define CHANSERV_SYNTAX() svccmd_send_help(user, chanserv, cmd)
527 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
528 reply("MSG_MISSING_PARAMS", argv[0]); \
532 DECLARE_LIST(dnrList, struct do_not_register *);
533 DEFINE_LIST(dnrList, struct do_not_register *)
535 static int eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action);
537 struct userNode *chanserv;
540 static dict_t plain_dnrs, mask_dnrs, handle_dnrs;
541 static struct log_type *CS_LOG;
545 struct channelList support_channels;
546 struct mod_chanmode default_modes;
548 unsigned long db_backup_frequency;
549 unsigned long channel_expire_frequency;
550 unsigned long dnr_expire_frequency;
552 unsigned long invited_timeout;
554 unsigned long info_delay;
555 unsigned long adjust_delay;
556 unsigned long channel_expire_delay;
557 unsigned int nodelete_level;
559 unsigned int adjust_threshold;
560 int join_flood_threshold;
562 unsigned int greeting_length;
563 unsigned int refresh_period;
564 unsigned int giveownership_period;
566 unsigned int max_owned;
567 unsigned int max_chan_users;
568 unsigned int max_chan_bans;
569 unsigned int max_userinfo_length;
571 struct string_list *set_shows;
572 struct string_list *eightball;
573 struct string_list *old_ban_names;
575 const char *ctcp_short_ban_duration;
576 const char *ctcp_long_ban_duration;
578 const char *irc_operator_epithet;
579 const char *network_helper_epithet;
580 const char *support_helper_epithet;
582 const char *new_channel_authed;
583 const char *new_channel_unauthed;
584 const char *new_channel_msg;
589 struct userNode *user;
590 struct userNode *bot;
591 struct chanNode *channel;
593 unsigned short lowest;
594 unsigned short highest;
595 struct userData **users;
596 struct helpfile_table table;
601 struct userNode *user;
602 struct chanNode *chan;
605 enum note_access_type
607 NOTE_SET_CHANNEL_ACCESS,
608 NOTE_SET_CHANNEL_SETTER,
612 enum note_visible_type
615 NOTE_VIS_CHANNEL_USERS,
621 enum note_access_type set_access_type;
623 unsigned int min_opserv;
624 unsigned short min_ulevel;
626 enum note_visible_type visible_type;
627 unsigned int max_length;
634 struct note_type *type;
635 char setter[NICKSERV_HANDLE_LEN+1];
639 static unsigned int registered_channels;
640 static unsigned int banCount;
642 static const struct {
645 unsigned short level;
648 { "peon", "Peon", UL_PEON, '+' },
649 { "op", "Op", UL_OP, '@' },
650 { "master", "Master", UL_MASTER, '%' },
651 { "coowner", "Coowner", UL_COOWNER, '*' },
652 { "owner", "Owner", UL_OWNER, '!' },
653 { "helper", "BUG:", UL_HELPER, 'X' }
656 static const struct {
659 unsigned short default_value;
660 unsigned int old_idx;
661 unsigned int old_flag;
662 unsigned short flag_value;
664 { "CSMSG_SET_GIVE_VOICE", "givevoice", 100, ~0, CHANNEL_VOICE_ALL, 0 },
665 { "CSMSG_SET_GIVE_OPS", "giveops", 200, 2, 0, 0 },
666 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
667 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
668 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
669 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
670 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
671 { "CSMSG_SET_CTCPUSERS", "ctcpusers", 0, 9, 0, 0 },
672 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES, 1 },
673 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE, 200 },
674 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF, 1 },
675 { "CSMSG_SET_VOTE", "vote", 100, ~0, 0, 0 }
678 struct charOptionValues {
681 } protectValues[] = {
682 { 'a', "CSMSG_PROTECT_ALL" },
683 { 'e', "CSMSG_PROTECT_EQUAL" },
684 { 'l', "CSMSG_PROTECT_LOWER" },
685 { 'n', "CSMSG_PROTECT_NONE" }
687 { 'd', "CSMSG_TOYS_DISABLED" },
688 { 'n', "CSMSG_TOYS_PRIVATE" },
689 { 'p', "CSMSG_TOYS_PUBLIC" }
690 }, topicRefreshValues[] = {
691 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
692 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
693 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
694 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
695 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
696 }, ctcpReactionValues[] = {
697 { 'k', "CSMSG_CTCPREACTION_KICK" },
698 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
699 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
700 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
703 static const struct {
707 unsigned int old_idx;
709 struct charOptionValues *values;
711 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues), protectValues },
712 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues), toysValues },
713 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues), topicRefreshValues },
714 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 't', 10, ArrayLength(ctcpReactionValues), ctcpReactionValues }
717 struct userData *helperList;
718 struct chanData *channelList;
719 static struct module *chanserv_module;
720 static unsigned int userCount;
722 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
723 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
724 static void unregister_channel(struct chanData *channel, const char *reason);
727 user_level_from_name(const char *name, unsigned short clamp_level)
729 unsigned int level = 0, ii;
731 level = strtoul(name, NULL, 10);
732 else for(ii = 0; (ii < ArrayLength(accessLevels)) && !level; ++ii)
733 if(!irccasecmp(name, accessLevels[ii].name))
734 level = accessLevels[ii].level;
735 if(level > clamp_level)
741 parse_level_range(unsigned short *minl, unsigned short *maxl, const char *arg)
744 *minl = strtoul(arg, &sep, 10);
752 *maxl = strtoul(sep+1, &sep, 10);
760 _GetChannelUser(struct chanData *channel, struct handle_info *handle, int override, int allow_suspended)
762 struct userData *uData, **head;
764 if(!channel || !handle)
767 if(override && HANDLE_FLAGGED(handle, HELPING)
768 && ((handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel)))
770 for(uData = helperList;
771 uData && uData->handle != handle;
772 uData = uData->next);
776 uData = calloc(1, sizeof(struct userData));
777 uData->handle = handle;
779 uData->access = UL_HELPER;
785 uData->next = helperList;
787 helperList->prev = uData;
795 for(uData = channel->users; uData; uData = uData->next)
796 if((uData->handle == handle) && (allow_suspended || !IsUserSuspended(uData)))
799 head = &(channel->users);
802 if(uData && (uData != *head))
804 /* Shuffle the user to the head of whatever list he was in. */
806 uData->next->prev = uData->prev;
808 uData->prev->next = uData->next;
814 (**head).prev = uData;
821 /* Returns non-zero if user has at least the minimum access.
822 * exempt_owner is set when handling !set, so the owner can set things
825 int check_user_level(struct chanNode *channel, struct userNode *user, enum levelOption opt, int allow_override, int exempt_owner)
827 struct userData *uData;
828 struct chanData *cData = channel->channel_info;
829 unsigned short minimum = cData->lvlOpts[opt];
832 uData = _GetChannelUser(cData, user->handle_info, allow_override, 0);
835 if(minimum <= uData->access)
837 if((minimum > UL_OWNER) && (uData->access == UL_OWNER) && exempt_owner)
842 /* Scan for other users authenticated to the same handle
843 still in the channel. If so, keep them listed as present.
845 user is optional, if not null, it skips checking that userNode
846 (for the handle_part function) */
848 scan_user_presence(struct userData *uData, struct userNode *user)
852 if(IsSuspended(uData->channel)
853 || IsUserSuspended(uData)
854 || !(mn = find_handle_in_channel(uData->channel->channel, uData->handle, user)))
866 chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, const char *text, UNUSED_ARG(struct userNode *bot), UNUSED_ARG(unsigned int is_notice))
868 unsigned int eflags, argc;
870 static char *bad_ctcp_reason = "CTCPs to this channel are forbidden.";
872 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
873 if(!channel->channel_info
874 || IsSuspended(channel->channel_info)
876 || !ircncasecmp(text, "ACTION ", 7))
878 /* Figure out the minimum level needed to CTCP the channel */
879 if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
881 /* We need to enforce against them; do so. */
883 argv[0] = (char*)text;
884 argv[1] = user->nick;
886 if(GetUserMode(channel, user))
887 eflags |= ACTION_KICK;
888 switch(channel->channel_info->chOpts[chCTCPReaction]) {
889 default: case 'k': /* just do the kick */ break;
891 eflags |= ACTION_BAN;
894 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
895 argv[argc++] = (char*)chanserv_conf.ctcp_short_ban_duration;
898 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
899 argv[argc++] = (char*)chanserv_conf.ctcp_long_ban_duration;
902 argv[argc++] = bad_ctcp_reason;
903 eject_user(chanserv, channel, argc, argv, NULL, eflags);
907 chanserv_create_note_type(const char *name)
909 struct note_type *ntype = calloc(1, sizeof(*ntype) + strlen(name));
910 strcpy(ntype->name, name);
912 dict_insert(note_types, ntype->name, ntype);
917 free_vote_options(void *data)
919 struct vote_option *vOpt = data;
921 free(vOpt->option_str);
926 chanserv_deref_note_type(void *data)
928 struct note_type *ntype = data;
930 if(--ntype->refs > 0)
936 chanserv_flush_note_type(struct note_type *ntype)
938 struct chanData *cData;
939 for(cData = channelList; cData; cData = cData->next)
940 dict_remove(cData->notes, ntype->name);
944 chanserv_truncate_notes(struct note_type *ntype)
946 struct chanData *cData;
948 unsigned int size = sizeof(*note) + ntype->max_length;
950 for(cData = channelList; cData; cData = cData->next) {
951 note = dict_find(cData->notes, ntype->name, NULL);
954 if(strlen(note->note) <= ntype->max_length)
956 dict_remove2(cData->notes, ntype->name, 1);
957 note = realloc(note, size);
958 note->note[ntype->max_length] = 0;
959 dict_insert(cData->notes, ntype->name, note);
963 static int note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user);
966 chanserv_add_channel_note(struct chanData *channel, struct note_type *type, const char *setter, const char *text)
969 unsigned int len = strlen(text);
971 if(len > type->max_length) len = type->max_length;
972 note = calloc(1, sizeof(*note) + len);
974 strncpy(note->setter, setter, sizeof(note->setter)-1);
975 memcpy(note->note, text, len);
977 dict_insert(channel->notes, type->name, note);
983 chanserv_free_note(void *data)
985 struct note *note = data;
987 chanserv_deref_note_type(note->type);
988 assert(note->type->refs > 0); /* must use delnote to remove the type */
992 static MODCMD_FUNC(cmd_createnote) {
993 struct note_type *ntype;
994 unsigned int arg = 1, existed = 0, max_length;
996 if((ntype = dict_find(note_types, argv[1], NULL)))
999 ntype = chanserv_create_note_type(argv[arg]);
1000 if(!irccasecmp(argv[++arg], "privileged"))
1003 ntype->set_access_type = NOTE_SET_PRIVILEGED;
1004 ntype->set_access.min_opserv = strtoul(argv[arg], NULL, 0);
1006 else if(!irccasecmp(argv[arg], "channel"))
1008 unsigned short ulvl = user_level_from_name(argv[++arg], UL_OWNER);
1011 reply("CSMSG_INVALID_ACCESS", argv[arg]);
1014 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
1015 ntype->set_access.min_ulevel = ulvl;
1017 else if(!irccasecmp(argv[arg], "setter"))
1019 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
1023 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
1027 if(!irccasecmp(argv[++arg], "privileged"))
1028 ntype->visible_type = NOTE_VIS_PRIVILEGED;
1029 else if(!irccasecmp(argv[arg], "channel_users"))
1030 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
1031 else if(!irccasecmp(argv[arg], "all"))
1032 ntype->visible_type = NOTE_VIS_ALL;
1034 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
1038 if((arg+1) >= argc) {
1039 reply("MSG_MISSING_PARAMS", argv[0]);
1042 max_length = strtoul(argv[++arg], NULL, 0);
1043 if(max_length < 20 || max_length > 450)
1045 reply("CSMSG_BAD_MAX_LENGTH", argv[arg]);
1048 if(existed && (max_length < ntype->max_length))
1050 ntype->max_length = max_length;
1051 chanserv_truncate_notes(ntype);
1053 ntype->max_length = max_length;
1056 reply("CSMSG_NOTE_MODIFIED", ntype->name);
1058 reply("CSMSG_NOTE_CREATED", ntype->name);
1063 dict_remove(note_types, ntype->name);
1067 static MODCMD_FUNC(cmd_removenote) {
1068 struct note_type *ntype;
1071 ntype = dict_find(note_types, argv[1], NULL);
1072 force = (argc > 2) && !irccasecmp(argv[2], "force");
1075 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
1082 reply("CSMSG_NOTE_TYPE_USED", ntype->name);
1085 chanserv_flush_note_type(ntype);
1087 dict_remove(note_types, argv[1]);
1088 reply("CSMSG_NOTE_DELETED", argv[1]);
1093 chanserv_expire_channel(void *data)
1095 struct chanData *channel = data;
1096 char reason[MAXLEN];
1097 sprintf(reason, "channel expired.");
1098 channel->expiry = 0;
1099 spamserv_cs_unregister(NULL, channel->channel, expire, NULL);
1100 unregister_channel(channel, reason);
1103 static MODCMD_FUNC(chan_opt_expire)
1105 struct chanData *cData = channel->channel_info;
1106 unsigned long value = cData->expiry;
1110 if((!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
1112 reply("MSG_SETTING_PRIVILEGED", argv[0]);
1115 unsigned long expiry,duration;
1117 /* The two directions can have different ACLs. */
1118 if(!strcmp(argv[1], "0"))
1120 else if((duration = ParseInterval(argv[1])))
1121 expiry = now + duration;
1124 reply("MSG_INVALID_DURATION", argv[1]);
1128 if (expiry != value)
1132 timeq_del(value, chanserv_expire_channel, cData, 0);
1135 cData->expiry = value;
1138 timeq_add(expiry, chanserv_expire_channel, cData);
1143 if(cData->expiry > now) {
1144 char expirestr[INTERVALLEN];
1145 reply("CSMSG_SET_EXPIRE", intervalString(expirestr, cData->expiry - now, user->handle_info));
1147 reply("CSMSG_SET_EXPIRE_OFF");
1152 mode_lock_violated(const struct mod_chanmode *orig, const struct mod_chanmode *change)
1156 if(orig->modes_set & change->modes_clear)
1158 if(orig->modes_clear & change->modes_set)
1160 if((orig->modes_set & MODE_KEY) && (change->modes_set & MODE_KEY)
1161 && strcmp(orig->new_key, change->new_key))
1163 if((orig->modes_set & MODE_LIMIT) && (change->modes_set & MODE_LIMIT)
1164 && (orig->new_limit != change->new_limit))
1169 static char max_length_text[MAXLEN+1][16];
1171 static struct helpfile_expansion
1172 chanserv_expand_variable(const char *variable)
1174 struct helpfile_expansion exp;
1176 if(!irccasecmp(variable, "notes"))
1179 exp.type = HF_TABLE;
1180 exp.value.table.length = 1;
1181 exp.value.table.width = 3;
1182 exp.value.table.flags = 0;
1183 exp.value.table.contents = calloc(dict_size(note_types)+1, sizeof(char**));
1184 exp.value.table.contents[0] = calloc(exp.value.table.width, sizeof(char*));
1185 exp.value.table.contents[0][0] = "Note Type";
1186 exp.value.table.contents[0][1] = "Visibility";
1187 exp.value.table.contents[0][2] = "Max Length";
1188 for(it=dict_first(note_types); it; it=iter_next(it))
1190 struct note_type *ntype = iter_data(it);
1193 if(!note_type_visible_to_user(NULL, ntype, message_dest)) continue;
1194 row = exp.value.table.length++;
1195 exp.value.table.contents[row] = calloc(exp.value.table.width, sizeof(char*));
1196 exp.value.table.contents[row][0] = ntype->name;
1197 exp.value.table.contents[row][1] = (ntype->visible_type == NOTE_VIS_ALL) ? "all" :
1198 (ntype->visible_type == NOTE_VIS_CHANNEL_USERS) ? "chan users" :
1200 if(!max_length_text[ntype->max_length][0])
1201 snprintf(max_length_text[ntype->max_length], sizeof(max_length_text[ntype->max_length]), "%u", ntype->max_length);
1202 exp.value.table.contents[row][2] = max_length_text[ntype->max_length];
1207 exp.type = HF_STRING;
1208 exp.value.str = NULL;
1212 static struct chanData*
1213 register_channel(struct chanNode *cNode, char *registrar)
1215 struct chanData *channel;
1216 enum levelOption lvlOpt;
1217 enum charOption chOpt;
1219 channel = calloc(1, sizeof(struct chanData));
1221 channel->notes = dict_new();
1222 dict_set_free_data(channel->notes, chanserv_free_note);
1224 channel->registrar = strdup(registrar);
1225 channel->registered = now;
1226 channel->visited = now;
1227 channel->limitAdjusted = now;
1228 channel->ownerTransfer = now;
1229 channel->flags = CHANNEL_DEFAULT_FLAGS;
1230 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
1231 channel->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
1232 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
1233 channel->chOpts[chOpt] = charOptions[chOpt].default_value;
1235 channel->prev = NULL;
1236 channel->next = channelList;
1239 channelList->prev = channel;
1240 channelList = channel;
1241 registered_channels++;
1243 channel->channel = cNode;
1245 cNode->channel_info = channel;
1247 channel->vote = NULL;
1252 static struct userData*
1253 add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned short access_level, unsigned long seen, const char *info)
1255 struct userData *ud;
1257 if(access_level > UL_OWNER)
1260 ud = calloc(1, sizeof(*ud));
1261 ud->channel = channel;
1262 ud->handle = handle;
1264 ud->access = access_level;
1265 ud->info = info ? strdup(info) : NULL;
1268 ud->next = channel->users;
1270 channel->users->prev = ud;
1271 channel->users = ud;
1273 channel->userCount++;
1277 ud->u_next = ud->handle->channels;
1279 ud->u_next->u_prev = ud;
1280 ud->handle->channels = ud;
1286 del_channel_user(struct userData *user, int do_gc)
1288 struct chanData *channel = user->channel;
1290 channel->userCount--;
1294 user->prev->next = user->next;
1296 channel->users = user->next;
1298 user->next->prev = user->prev;
1301 user->u_prev->u_next = user->u_next;
1303 user->handle->channels = user->u_next;
1305 user->u_next->u_prev = user->u_prev;
1309 if(do_gc && !channel->users && !IsProtected(channel)) {
1310 spamserv_cs_unregister(NULL, channel->channel, lost_all_users, NULL);
1311 unregister_channel(channel, "lost all users.");
1315 static void expire_ban(void *data);
1318 add_channel_ban(struct chanData *channel, const char *mask, char *owner, unsigned long set, unsigned long triggered, unsigned long expires, char *reason)
1321 unsigned int ii, l1, l2;
1326 bd = malloc(sizeof(struct banData));
1328 bd->channel = channel;
1330 bd->triggered = triggered;
1331 bd->expires = expires;
1333 for(ii = 0; ii < chanserv_conf.old_ban_names->used; ++ii)
1335 extern const char *hidden_host_suffix;
1336 const char *old_name = chanserv_conf.old_ban_names->list[ii];
1340 l2 = strlen(old_name);
1343 if(irccasecmp(mask + l1 - l2, old_name))
1345 new_mask = alloca(MAXLEN);
1346 sprintf(new_mask, "%.*s%s", (int)(l1-l2), mask, hidden_host_suffix);
1349 safestrncpy(bd->mask, mask, sizeof(bd->mask));
1351 safestrncpy(bd->owner, owner, sizeof(bd->owner));
1352 bd->reason = strdup(reason);
1355 timeq_add(expires, expire_ban, bd);
1358 bd->next = channel->bans;
1360 channel->bans->prev = bd;
1362 channel->banCount++;
1369 del_channel_ban(struct banData *ban)
1371 ban->channel->banCount--;
1375 ban->prev->next = ban->next;
1377 ban->channel->bans = ban->next;
1380 ban->next->prev = ban->prev;
1383 timeq_del(0, expire_ban, ban, TIMEQ_IGNORE_WHEN);
1392 expire_ban(void *data)
1394 struct banData *bd = data;
1395 if(!IsSuspended(bd->channel))
1397 struct banList bans;
1398 struct mod_chanmode change;
1400 bans = bd->channel->channel->banlist;
1401 mod_chanmode_init(&change);
1402 for(ii=0; ii<bans.used; ii++)
1404 if(!strcmp(bans.list[ii]->ban, bd->mask))
1407 change.args[0].mode = MODE_REMOVE|MODE_BAN;
1408 change.args[0].u.hostmask = bd->mask;
1409 mod_chanmode_announce(chanserv, bd->channel->channel, &change);
1415 del_channel_ban(bd);
1418 static void chanserv_expire_suspension(void *data);
1421 unregister_channel(struct chanData *channel, const char *reason)
1423 struct mod_chanmode change;
1424 char msgbuf[MAXLEN];
1426 /* After channel unregistration, the following must be cleaned
1428 - Channel information.
1431 - Channel suspension data.
1432 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1438 timeq_del(0, NULL, channel, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
1442 mod_chanmode_init(&change);
1443 change.modes_clear |= MODE_REGISTERED;
1444 mod_chanmode_announce(chanserv, channel->channel, &change);
1447 while(channel->users)
1448 del_channel_user(channel->users, 0);
1450 while(channel->bans)
1451 del_channel_ban(channel->bans);
1453 free(channel->topic);
1454 free(channel->registrar);
1455 free(channel->greeting);
1456 free(channel->user_greeting);
1457 free(channel->topic_mask);
1460 channel->prev->next = channel->next;
1462 channelList = channel->next;
1465 channel->next->prev = channel->prev;
1467 if(channel->suspended)
1469 struct chanNode *cNode = channel->channel;
1470 struct suspended *suspended, *next_suspended;
1472 for(suspended = channel->suspended; suspended; suspended = next_suspended)
1474 next_suspended = suspended->previous;
1475 free(suspended->suspender);
1476 free(suspended->reason);
1477 if(suspended->expires)
1478 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
1483 cNode->channel_info = NULL;
1486 timeq_del(channel->expiry, chanserv_expire_channel, channel, 0);
1487 channel->channel->channel_info = NULL;
1489 dict_delete(channel->notes);
1490 sprintf(msgbuf, "%s %s", channel->channel->name, reason);
1491 if(!IsSuspended(channel))
1492 DelChannelUser(chanserv, channel->channel, msgbuf, 0);
1493 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, msgbuf);
1494 UnlockChannel(channel->channel);
1496 registered_channels--;
1500 expire_channels(UNUSED_ARG(void *data))
1502 struct chanData *channel, *next;
1503 struct userData *user;
1504 char delay[INTERVALLEN], reason[INTERVALLEN + 64];
1506 intervalString(delay, chanserv_conf.channel_expire_delay, NULL);
1507 sprintf(reason, "Channel registration automatically expired after %s of disuse.", delay);
1509 for(channel = channelList; channel; channel = next)
1511 next = channel->next;
1513 /* See if the channel can be expired. */
1514 if(((now - channel->visited) <= chanserv_conf.channel_expire_delay)
1515 || IsProtected(channel))
1518 /* Make sure there are no high-ranking users still in the channel. */
1519 for(user=channel->users; user; user=user->next)
1520 if(user->present && (user->access >= UL_PRESENT) && !HANDLE_FLAGGED(user->handle, BOT))
1525 /* Unregister the channel */
1526 log_module(CS_LOG, LOG_INFO, "(%s) Channel registration expired.", channel->channel->name);
1527 spamserv_cs_unregister(NULL, channel->channel, expire, NULL);
1528 unregister_channel(channel, "registration expired.");
1531 if(chanserv_conf.channel_expire_frequency)
1532 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
1536 expire_dnrs(UNUSED_ARG(void *data))
1538 dict_iterator_t it, next;
1539 struct do_not_register *dnr;
1541 for(it = dict_first(handle_dnrs); it; it = next)
1543 dnr = iter_data(it);
1544 next = iter_next(it);
1545 if(dnr->expires && dnr->expires <= now)
1546 dict_remove(handle_dnrs, dnr->chan_name + 1);
1548 for(it = dict_first(plain_dnrs); it; it = next)
1550 dnr = iter_data(it);
1551 next = iter_next(it);
1552 if(dnr->expires && dnr->expires <= now)
1553 dict_remove(plain_dnrs, dnr->chan_name + 1);
1555 for(it = dict_first(mask_dnrs); it; it = next)
1557 dnr = iter_data(it);
1558 next = iter_next(it);
1559 if(dnr->expires && dnr->expires <= now)
1560 dict_remove(mask_dnrs, dnr->chan_name + 1);
1563 if(chanserv_conf.dnr_expire_frequency)
1564 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
1568 protect_user(const struct userNode *victim, const struct userNode *aggressor, struct chanData *channel)
1570 char protect = channel->chOpts[chProtect];
1571 struct userData *cs_victim, *cs_aggressor;
1573 /* Don't protect if no one is to be protected, someone is attacking
1574 himself, or if the aggressor is an IRC Operator. */
1575 if(protect == 'n' || victim == aggressor || IsOper(aggressor))
1578 /* Don't protect if the victim isn't authenticated (because they
1579 can't be a channel user), unless we are to protect non-users
1581 cs_victim = GetChannelAccess(channel, victim->handle_info);
1582 if(protect != 'a' && !cs_victim)
1585 /* Protect if the aggressor isn't a user because at this point,
1586 the aggressor can only be less than or equal to the victim. */
1587 cs_aggressor = GetChannelAccess(channel, aggressor->handle_info);
1591 /* If the aggressor was a user, then the victim can't be helped. */
1598 if(cs_victim->access > cs_aggressor->access)
1603 if(cs_victim->access >= cs_aggressor->access)
1612 validate_op(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1614 struct chanData *cData = channel->channel_info;
1615 struct userData *cs_victim;
1617 if((!(cs_victim = GetChannelUser(cData, victim->handle_info))
1618 || (cs_victim->access < cData->lvlOpts[lvlGiveOps]))
1619 && !check_user_level(channel, user, lvlEnfOps, 0, 0))
1621 send_message(user, chanserv, "CSMSG_OPBY_LOCKED");
1629 validate_deop(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1631 if(IsService(victim))
1633 send_message(user, chanserv, "MSG_SERVICE_IMMUNE", victim->nick);
1637 if(protect_user(victim, user, channel->channel_info))
1639 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
1646 static struct do_not_register *
1647 chanserv_add_dnr(const char *chan_name, const char *setter, unsigned long expires, const char *reason)
1649 struct do_not_register *dnr = calloc(1, sizeof(*dnr)+strlen(reason));
1650 safestrncpy(dnr->chan_name, chan_name, sizeof(dnr->chan_name));
1651 safestrncpy(dnr->setter, setter, sizeof(dnr->setter));
1652 strcpy(dnr->reason, reason);
1654 dnr->expires = expires;
1655 if(dnr->chan_name[0] == '*')
1656 dict_insert(handle_dnrs, dnr->chan_name+1, dnr);
1657 else if(strpbrk(dnr->chan_name, "*?"))
1658 dict_insert(mask_dnrs, dnr->chan_name, dnr);
1660 dict_insert(plain_dnrs, dnr->chan_name, dnr);
1664 static struct dnrList
1665 chanserv_find_dnrs(const char *chan_name, const char *handle, unsigned int max)
1667 struct dnrList list;
1668 dict_iterator_t it, next;
1669 struct do_not_register *dnr;
1671 dnrList_init(&list);
1673 if(handle && (dnr = dict_find(handle_dnrs, handle, NULL)))
1675 if(dnr->expires && dnr->expires <= now)
1676 dict_remove(handle_dnrs, handle);
1677 else if(list.used < max)
1678 dnrList_append(&list, dnr);
1681 if(chan_name && (dnr = dict_find(plain_dnrs, chan_name, NULL)))
1683 if(dnr->expires && dnr->expires <= now)
1684 dict_remove(plain_dnrs, chan_name);
1685 else if(list.used < max)
1686 dnrList_append(&list, dnr);
1691 for(it = dict_first(mask_dnrs); it && list.used < max; it = next)
1693 next = iter_next(it);
1694 if(!match_ircglob(chan_name, iter_key(it)))
1696 dnr = iter_data(it);
1697 if(dnr->expires && dnr->expires <= now)
1698 dict_remove(mask_dnrs, iter_key(it));
1700 dnrList_append(&list, dnr);
1707 static int dnr_print_func(struct do_not_register *dnr, void *extra)
1709 struct userNode *user;
1710 char buf1[INTERVALLEN];
1711 char buf2[INTERVALLEN];
1718 strftime(buf1, sizeof(buf1), "%d %b %Y", localtime(&feh));
1723 strftime(buf2, sizeof(buf2), "%d %b %Y", localtime(&feh));
1724 send_message(user, chanserv, "CSMSG_DNR_INFO_SET_EXPIRES", dnr->chan_name, buf1, dnr->setter, buf2, dnr->reason);
1728 send_message(user, chanserv, "CSMSG_DNR_INFO_SET", dnr->chan_name, buf1, dnr->setter, dnr->reason);
1731 send_message(user, chanserv, "CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1736 chanserv_show_dnrs(struct userNode *user, struct svccmd *cmd, const char *chan_name, const char *handle)
1738 struct dnrList list;
1741 list = chanserv_find_dnrs(chan_name, handle, UINT_MAX);
1742 for(ii = 0; (ii < list.used) && (ii < 10); ++ii)
1743 dnr_print_func(list.list[ii], user);
1745 reply("CSMSG_MORE_DNRS", list.used - ii);
1750 struct do_not_register *
1751 chanserv_is_dnr(const char *chan_name, struct handle_info *handle)
1753 struct dnrList list;
1754 struct do_not_register *dnr;
1756 list = chanserv_find_dnrs(chan_name, handle ? handle->handle : NULL, 1);
1757 dnr = list.used ? list.list[0] : NULL;
1762 static unsigned int send_dnrs(struct userNode *user, dict_t dict)
1764 struct do_not_register *dnr;
1765 dict_iterator_t it, next;
1766 unsigned int matches = 0;
1768 for(it = dict_first(dict); it; it = next)
1770 dnr = iter_data(it);
1771 next = iter_next(it);
1772 if(dnr->expires && dnr->expires <= now)
1774 dict_remove(dict, iter_key(it));
1777 dnr_print_func(dnr, user);
1784 static CHANSERV_FUNC(cmd_noregister)
1788 unsigned long expiry, duration;
1789 unsigned int matches;
1793 reply("CSMSG_DNR_SEARCH_RESULTS");
1794 matches = send_dnrs(user, handle_dnrs);
1795 matches += send_dnrs(user, plain_dnrs);
1796 matches += send_dnrs(user, mask_dnrs);
1798 reply("MSG_MATCH_COUNT", matches);
1800 reply("MSG_NO_MATCHES");
1806 if(!IsChannelName(target) && (*target != '*'))
1808 reply("CSMSG_NOT_DNR", target);
1816 reply("MSG_INVALID_DURATION", argv[2]);
1820 if(!strcmp(argv[2], "0"))
1822 else if((duration = ParseInterval(argv[2])))
1823 expiry = now + duration;
1826 reply("MSG_INVALID_DURATION", argv[2]);
1830 reason = unsplit_string(argv + 3, argc - 3, NULL);
1831 if((*target == '*') && !get_handle_info(target + 1))
1833 reply("MSG_HANDLE_UNKNOWN", target + 1);
1836 chanserv_add_dnr(target, user->handle_info->handle, expiry, reason);
1837 reply("CSMSG_NOREGISTER_CHANNEL", target);
1841 reply("CSMSG_DNR_SEARCH_RESULTS");
1843 matches = chanserv_show_dnrs(user, cmd, NULL, target + 1);
1845 matches = chanserv_show_dnrs(user, cmd, target, NULL);
1847 reply("MSG_NO_MATCHES");
1851 static CHANSERV_FUNC(cmd_allowregister)
1853 const char *chan_name = argv[1];
1855 if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
1856 || dict_remove(plain_dnrs, chan_name)
1857 || dict_remove(mask_dnrs, chan_name))
1859 reply("CSMSG_DNR_REMOVED", chan_name);
1862 reply("CSMSG_NO_SUCH_DNR", chan_name);
1867 struct userNode *source;
1871 unsigned long min_set, max_set;
1872 unsigned long min_expires, max_expires;
1877 dnr_search_matches(const struct do_not_register *dnr, const struct dnr_search *search)
1879 return !((dnr->set < search->min_set)
1880 || (dnr->set > search->max_set)
1881 || (dnr->expires < search->min_expires)
1882 || (search->max_expires
1883 && ((dnr->expires == 0)
1884 || (dnr->expires > search->max_expires)))
1885 || (search->chan_mask
1886 && !match_ircglob(dnr->chan_name, search->chan_mask))
1887 || (search->setter_mask
1888 && !match_ircglob(dnr->setter, search->setter_mask))
1889 || (search->reason_mask
1890 && !match_ircglob(dnr->reason, search->reason_mask)));
1893 static struct dnr_search *
1894 dnr_search_create(struct userNode *user, struct svccmd *cmd, unsigned int argc, char *argv[])
1896 struct dnr_search *discrim;
1899 discrim = calloc(1, sizeof(*discrim));
1900 discrim->source = user;
1901 discrim->chan_mask = NULL;
1902 discrim->setter_mask = NULL;
1903 discrim->reason_mask = NULL;
1904 discrim->max_set = INT_MAX;
1905 discrim->limit = 50;
1907 for(ii=0; ii<argc; ++ii)
1911 reply("MSG_MISSING_PARAMS", argv[ii]);
1914 else if(0 == irccasecmp(argv[ii], "channel"))
1916 discrim->chan_mask = argv[++ii];
1918 else if(0 == irccasecmp(argv[ii], "setter"))
1920 discrim->setter_mask = argv[++ii];
1922 else if(0 == irccasecmp(argv[ii], "reason"))
1924 discrim->reason_mask = argv[++ii];
1926 else if(0 == irccasecmp(argv[ii], "limit"))
1928 discrim->limit = strtoul(argv[++ii], NULL, 0);
1930 else if(0 == irccasecmp(argv[ii], "set"))
1932 const char *cmp = argv[++ii];
1935 discrim->min_set = now - ParseInterval(cmp + 2);
1937 discrim->min_set = now - (ParseInterval(cmp + 1) - 1);
1938 } else if(cmp[0] == '=') {
1939 discrim->min_set = discrim->max_set = now - ParseInterval(cmp + 1);
1940 } else if(cmp[0] == '>') {
1942 discrim->max_set = now - ParseInterval(cmp + 2);
1944 discrim->max_set = now - (ParseInterval(cmp + 1) - 1);
1946 discrim->max_set = now - (ParseInterval(cmp) - 1);
1949 else if(0 == irccasecmp(argv[ii], "expires"))
1951 const char *cmp = argv[++ii];
1954 discrim->max_expires = now + ParseInterval(cmp + 2);
1956 discrim->max_expires = now + (ParseInterval(cmp + 1) - 1);
1957 } else if(cmp[0] == '=') {
1958 discrim->min_expires = discrim->max_expires = now + ParseInterval(cmp + 1);
1959 } else if(cmp[0] == '>') {
1961 discrim->min_expires = now + ParseInterval(cmp + 2);
1963 discrim->min_expires = now + (ParseInterval(cmp + 1) - 1);
1965 discrim->min_expires = now + (ParseInterval(cmp) - 1);
1970 reply("MSG_INVALID_CRITERIA", argv[ii]);
1981 typedef int (*dnr_search_func)(struct do_not_register *match, void *extra);
1984 dnr_search(struct dnr_search *discrim, dnr_search_func dsf, void *data)
1986 struct do_not_register *dnr;
1987 dict_iterator_t next;
1992 /* Initialize local variables. */
1995 if(discrim->chan_mask)
1997 int shift = (discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*') ? 2 : 0;
1998 if('\0' == discrim->chan_mask[shift + strcspn(discrim->chan_mask+shift, "*?")])
2002 if(target_fixed && discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*')
2004 /* Check against account-based DNRs. */
2005 dnr = dict_find(handle_dnrs, discrim->chan_mask + 2, NULL);
2006 if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
2009 else if(target_fixed)
2011 /* Check against channel-based DNRs. */
2012 dnr = dict_find(plain_dnrs, discrim->chan_mask, NULL);
2013 if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
2018 /* Exhaustively search account DNRs. */
2019 for(it = dict_first(handle_dnrs); it; it = next)
2021 next = iter_next(it);
2022 dnr = iter_data(it);
2023 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
2027 /* Do the same for channel DNRs. */
2028 for(it = dict_first(plain_dnrs); it; it = next)
2030 next = iter_next(it);
2031 dnr = iter_data(it);
2032 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
2036 /* Do the same for wildcarded channel DNRs. */
2037 for(it = dict_first(mask_dnrs); it; it = next)
2039 next = iter_next(it);
2040 dnr = iter_data(it);
2041 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
2049 dnr_remove_func(struct do_not_register *match, void *extra)
2051 struct userNode *user;
2054 chan_name = alloca(strlen(match->chan_name) + 1);
2055 strcpy(chan_name, match->chan_name);
2057 if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
2058 || dict_remove(plain_dnrs, chan_name)
2059 || dict_remove(mask_dnrs, chan_name))
2061 send_message(user, chanserv, "CSMSG_DNR_REMOVED", chan_name);
2067 dnr_count_func(struct do_not_register *match, void *extra)
2069 return 0; (void)match; (void)extra;
2072 static MODCMD_FUNC(cmd_dnrsearch)
2074 struct dnr_search *discrim;
2075 dnr_search_func action;
2076 struct svccmd *subcmd;
2077 unsigned int matches;
2080 sprintf(buf, "dnrsearch %s", argv[1]);
2081 subcmd = dict_find(cmd->parent->commands, buf, NULL);
2084 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
2087 if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
2089 if(!irccasecmp(argv[1], "print"))
2090 action = dnr_print_func;
2091 else if(!irccasecmp(argv[1], "remove"))
2092 action = dnr_remove_func;
2093 else if(!irccasecmp(argv[1], "count"))
2094 action = dnr_count_func;
2097 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
2101 discrim = dnr_search_create(user, cmd, argc-2, argv+2);
2105 if(action == dnr_print_func)
2106 reply("CSMSG_DNR_SEARCH_RESULTS");
2107 matches = dnr_search(discrim, action, user);
2109 reply("MSG_MATCH_COUNT", matches);
2111 reply("MSG_NO_MATCHES");
2117 chanserv_get_owned_count(struct handle_info *hi)
2119 struct userData *cList;
2122 for(owned=0, cList=hi->channels; cList; cList=cList->u_next)
2123 if(cList->access == UL_OWNER)
2128 static CHANSERV_FUNC(cmd_register)
2130 struct handle_info *handle;
2131 struct chanData *cData;
2132 struct modeNode *mn;
2133 char reason[MAXLEN];
2135 unsigned int new_channel, force=0;
2136 struct do_not_register *dnr;
2140 if(channel->channel_info)
2142 reply("CSMSG_ALREADY_REGGED", channel->name);
2146 if(channel->bad_channel)
2148 reply("CSMSG_ILLEGAL_CHANNEL", channel->name);
2153 && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
2155 reply("CSMSG_MUST_BE_OPPED", channel->name);
2160 chan_name = channel->name;
2164 if((argc < 2) || !IsChannelName(argv[1]))
2166 reply("MSG_NOT_CHANNEL_NAME");
2170 if(opserv_bad_channel(argv[1]))
2172 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2177 chan_name = argv[1];
2180 if(argc >= (new_channel+2))
2182 if(!IsHelping(user))
2184 reply("CSMSG_PROXY_FORBIDDEN");
2188 if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
2190 force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
2191 dnr = chanserv_is_dnr(chan_name, handle);
2195 handle = user->handle_info;
2196 dnr = chanserv_is_dnr(chan_name, handle);
2200 if(!IsHelping(user))
2201 reply("CSMSG_DNR_CHANNEL", chan_name);
2203 chanserv_show_dnrs(user, cmd, chan_name, handle->handle);
2207 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2209 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
2214 channel = AddChannel(argv[1], now, NULL, NULL);
2216 cData = register_channel(channel, user->handle_info->handle);
2217 scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL), NULL);
2218 cData->modes = chanserv_conf.default_modes;
2220 cData->modes.modes_set |= MODE_REGISTERED;
2221 if (IsOffChannel(cData))
2223 mod_chanmode_announce(chanserv, channel, &cData->modes);
2227 struct mod_chanmode *change = mod_chanmode_dup(&cData->modes, 1);
2228 change->args[change->argc].mode = MODE_CHANOP;
2229 change->args[change->argc].u.member = AddChannelUser(chanserv, channel);
2231 mod_chanmode_announce(chanserv, channel, change);
2232 mod_chanmode_free(change);
2235 /* Initialize the channel's max user record. */
2236 cData->max = channel->members.used;
2237 cData->max_time = 0;
2239 if(handle != user->handle_info)
2240 reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
2242 reply("CSMSG_REG_SUCCESS", channel->name);
2244 sprintf(reason, "%s registered to %s by %s.", channel->name, handle->handle, user->handle_info->handle);
2245 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2250 make_confirmation_string(struct userData *uData)
2252 static char strbuf[16];
2257 for(src = uData->handle->handle; *src; )
2258 accum = accum * 31 + toupper(*src++);
2260 for(src = uData->channel->channel->name; *src; )
2261 accum = accum * 31 + toupper(*src++);
2262 sprintf(strbuf, "%08x", accum);
2266 static CHANSERV_FUNC(cmd_unregister)
2269 char reason[MAXLEN];
2270 struct chanData *cData;
2271 struct userData *uData;
2273 cData = channel->channel_info;
2276 reply("CSMSG_NOT_REGISTERED", channel->name);
2280 uData = GetChannelUser(cData, user->handle_info);
2281 if(!uData || (uData->access < UL_OWNER))
2283 reply("CSMSG_NO_ACCESS");
2287 if(IsProtected(cData))
2289 reply("CSMSG_UNREG_NODELETE", channel->name);
2293 if(!IsHelping(user))
2295 const char *confirm_string;
2296 if(IsSuspended(cData))
2298 reply("CSMSG_CHAN_SUSPENDED", channel->name, cData->suspended->reason);
2301 confirm_string = make_confirmation_string(uData);
2302 if((argc < 2) || strcmp(argv[1], confirm_string))
2304 reply("CSMSG_CONFIRM_UNREG", confirm_string);
2309 sprintf(reason, "unregistered by %s.", user->handle_info->handle);
2310 name = strdup(channel->name);
2311 unregister_channel(cData, reason);
2312 spamserv_cs_unregister(user, channel, manually, "unregistered");
2313 reply("CSMSG_UNREG_SUCCESS", name);
2319 ss_cs_join_channel(struct chanNode *channel, int spamserv_join)
2321 extern struct userNode *spamserv;
2322 struct mod_chanmode *change;
2324 if(spamserv && spamserv_join && get_chanInfo(channel->name))
2326 change = mod_chanmode_alloc(2);
2328 change->args[0].mode = MODE_CHANOP;
2329 change->args[0].u.member = AddChannelUser(chanserv, channel);
2330 change->args[1].mode = MODE_CHANOP;
2331 change->args[1].u.member = AddChannelUser(spamserv, channel);
2335 change = mod_chanmode_alloc(1);
2337 change->args[0].mode = MODE_CHANOP;
2338 change->args[0].u.member = AddChannelUser(chanserv, channel);
2341 mod_chanmode_announce(chanserv, channel, change);
2342 mod_chanmode_free(change);
2345 static CHANSERV_FUNC(cmd_move)
2347 struct mod_chanmode change;
2348 struct chanNode *target;
2349 struct modeNode *mn;
2350 struct userData *uData;
2351 char reason[MAXLEN];
2352 struct do_not_register *dnr;
2353 int chanserv_join = 0, spamserv_join;
2357 if(IsProtected(channel->channel_info))
2359 reply("CSMSG_MOVE_NODELETE", channel->name);
2363 if(!IsChannelName(argv[1]))
2365 reply("MSG_NOT_CHANNEL_NAME");
2369 if(opserv_bad_channel(argv[1]))
2371 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2375 if(!IsHelping(user) || (argc < 3) || irccasecmp(argv[2], "force"))
2377 for(uData = channel->channel_info->users; uData; uData = uData->next)
2379 if((uData->access == UL_OWNER) && (dnr = chanserv_is_dnr(argv[1], uData->handle)))
2381 if(!IsHelping(user))
2382 reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]);
2384 chanserv_show_dnrs(user, cmd, argv[1], uData->handle->handle);
2390 mod_chanmode_init(&change);
2391 if(!(target = GetChannel(argv[1])))
2393 target = AddChannel(argv[1], now, NULL, NULL);
2394 if(!IsSuspended(channel->channel_info))
2397 else if(target->channel_info)
2399 reply("CSMSG_ALREADY_REGGED", target->name);
2402 else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
2403 && !IsHelping(user))
2405 reply("CSMSG_MUST_BE_OPPED", target->name);
2408 else if(!IsSuspended(channel->channel_info))
2413 /* Clear MODE_REGISTERED from old channel, add it to new. */
2415 change.modes_clear = MODE_REGISTERED;
2416 mod_chanmode_announce(chanserv, channel, &change);
2417 change.modes_clear = 0;
2418 change.modes_set = MODE_REGISTERED;
2419 mod_chanmode_announce(chanserv, target, &change);
2422 /* Move the channel_info to the target channel; it
2423 shouldn't be necessary to clear timeq callbacks
2424 for the old channel. */
2425 target->channel_info = channel->channel_info;
2426 target->channel_info->channel = target;
2427 channel->channel_info = NULL;
2429 /* Check whether users are present in the new channel. */
2430 for(uData = target->channel_info->users; uData; uData = uData->next)
2431 scan_user_presence(uData, NULL);
2433 spamserv_join = spamserv_cs_move_merge(user, channel, target, 1);
2436 ss_cs_join_channel(target, spamserv_join);
2438 sprintf(reason, "%s moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
2439 if(!IsSuspended(target->channel_info))
2441 char reason2[MAXLEN];
2442 sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
2443 DelChannelUser(chanserv, channel, reason2, 0);
2445 UnlockChannel(channel);
2446 LockChannel(target);
2447 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2448 reply("CSMSG_MOVE_SUCCESS", target->name);
2453 merge_users(struct chanData *source, struct chanData *target)
2455 struct userData *suData, *tuData, *next;
2461 /* Insert the source's users into the scratch area. */
2462 for(suData = source->users; suData; suData = suData->next)
2463 dict_insert(merge, suData->handle->handle, suData);
2465 /* Iterate through the target's users, looking for
2466 users common to both channels. The lower access is
2467 removed from either the scratch area or target user
2469 for(tuData = target->users; tuData; tuData = next)
2471 struct userData *choice;
2473 next = tuData->next;
2475 /* If a source user exists with the same handle as a target
2476 channel's user, resolve the conflict by removing one. */
2477 suData = dict_find(merge, tuData->handle->handle, NULL);
2481 /* Pick the data we want to keep. */
2482 /* If the access is the same, use the later seen time. */
2483 if(suData->access == tuData->access)
2484 choice = (suData->seen > tuData->seen) ? suData : tuData;
2485 else /* Otherwise, keep the higher access level. */
2486 choice = (suData->access > tuData->access) ? suData : tuData;
2487 /* Use the later seen time. */
2488 if(suData->seen < tuData->seen)
2489 suData->seen = tuData->seen;
2491 tuData->seen = suData->seen;
2493 /* Remove the user that wasn't picked. */
2494 if(choice == tuData)
2496 dict_remove(merge, suData->handle->handle);
2497 del_channel_user(suData, 0);
2500 del_channel_user(tuData, 0);
2503 /* Move the remaining users to the target channel. */
2504 for(it = dict_first(merge); it; it = iter_next(it))
2506 suData = iter_data(it);
2508 /* Insert the user into the target channel's linked list. */
2509 suData->prev = NULL;
2510 suData->next = target->users;
2511 suData->channel = target;
2514 target->users->prev = suData;
2515 target->users = suData;
2517 /* Update the user counts for the target channel; the
2518 source counts are left alone. */
2519 target->userCount++;
2521 /* Check whether the user is in the target channel. */
2522 scan_user_presence(suData, NULL);
2525 /* Possible to assert (source->users == NULL) here. */
2526 source->users = NULL;
2531 merge_bans(struct chanData *source, struct chanData *target)
2533 struct banData *sbData, *tbData, *sNext, *tNext, *tFront;
2535 /* Hold on to the original head of the target ban list
2536 to avoid comparing source bans with source bans. */
2537 tFront = target->bans;
2539 /* Perform a totally expensive O(n*m) merge, ick. */
2540 for(sbData = source->bans; sbData; sbData = sNext)
2542 /* Flag to track whether the ban's been moved
2543 to the destination yet. */
2546 /* Possible to assert (sbData->prev == NULL) here. */
2547 sNext = sbData->next;
2549 for(tbData = tFront; tbData; tbData = tNext)
2551 tNext = tbData->next;
2553 /* Perform two comparisons between each source
2554 and target ban, conflicts are resolved by
2555 keeping the broader ban and copying the later
2556 expiration and triggered time. */
2557 if(match_ircglobs(tbData->mask, sbData->mask))
2559 /* There is a broader ban in the target channel that
2560 overrides one in the source channel; remove the
2561 source ban and break. */
2562 if(sbData->expires > tbData->expires)
2563 tbData->expires = sbData->expires;
2564 if(sbData->triggered > tbData->triggered)
2565 tbData->triggered = sbData->triggered;
2566 del_channel_ban(sbData);
2569 else if(match_ircglobs(sbData->mask, tbData->mask))
2571 /* There is a broader ban in the source channel that
2572 overrides one in the target channel; remove the
2573 target ban, fall through and move the source over. */
2574 if(tbData->expires > sbData->expires)
2575 sbData->expires = tbData->expires;
2576 if(tbData->triggered > sbData->triggered)
2577 sbData->triggered = tbData->triggered;
2578 if(tbData == tFront)
2580 del_channel_ban(tbData);
2583 /* Source bans can override multiple target bans, so
2584 we allow a source to run through this loop multiple
2585 times, but we can only move it once. */
2590 /* Remove the source ban from the source ban list. */
2592 sbData->next->prev = sbData->prev;
2594 /* Modify the source ban's associated channel. */
2595 sbData->channel = target;
2597 /* Insert the ban into the target channel's linked list. */
2598 sbData->prev = NULL;
2599 sbData->next = target->bans;
2602 target->bans->prev = sbData;
2603 target->bans = sbData;
2605 /* Update the user counts for the target channel. */
2610 /* Possible to assert (source->bans == NULL) here. */
2611 source->bans = NULL;
2615 merge_data(struct chanData *source, struct chanData *target)
2617 /* Use more recent visited and owner-transfer time; use older
2618 * registered time. Bitwise or may_opchan. Use higher max.
2619 * Do not touch last_refresh, ban count or user counts.
2621 if(source->visited > target->visited)
2622 target->visited = source->visited;
2623 if(source->registered < target->registered)
2624 target->registered = source->registered;
2625 if(source->ownerTransfer > target->ownerTransfer)
2626 target->ownerTransfer = source->ownerTransfer;
2627 if(source->may_opchan)
2628 target->may_opchan = 1;
2629 if(source->max > target->max) {
2630 target->max = source->max;
2631 target->max_time = source->max_time;
2636 merge_channel(struct chanData *source, struct chanData *target)
2638 merge_users(source, target);
2639 merge_bans(source, target);
2640 merge_data(source, target);
2643 static CHANSERV_FUNC(cmd_merge)
2645 struct userData *target_user;
2646 struct chanNode *target;
2647 char reason[MAXLEN];
2651 /* Make sure the target channel exists and is registered to the user
2652 performing the command. */
2653 if(!(target = GetChannel(argv[1])))
2655 reply("MSG_INVALID_CHANNEL");
2659 if(!target->channel_info)
2661 reply("CSMSG_NOT_REGISTERED", target->name);
2665 if(IsProtected(channel->channel_info))
2667 reply("CSMSG_MERGE_NODELETE");
2671 if(IsSuspended(target->channel_info))
2673 reply("CSMSG_MERGE_SUSPENDED");
2677 if(channel == target)
2679 reply("CSMSG_MERGE_SELF");
2683 target_user = GetChannelUser(target->channel_info, user->handle_info);
2684 if(!target_user || (target_user->access < UL_OWNER))
2686 reply("CSMSG_MERGE_NOT_OWNER");
2690 /* Merge the channel structures and associated data. */
2691 merge_channel(channel->channel_info, target->channel_info);
2692 spamserv_cs_move_merge(user, channel, target, 0);
2693 sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
2694 unregister_channel(channel->channel_info, reason);
2695 reply("CSMSG_MERGE_SUCCESS", target->name);
2699 static CHANSERV_FUNC(cmd_opchan)
2701 struct mod_chanmode change;
2702 if(!IsHelping(user) && !channel->channel_info->may_opchan)
2704 reply("CSMSG_ALREADY_OPCHANNED", channel->name);
2707 channel->channel_info->may_opchan = 0;
2708 mod_chanmode_init(&change);
2710 change.args[0].mode = MODE_CHANOP;
2711 change.args[0].u.member = GetUserMode(channel, chanserv);
2712 if(!change.args[0].u.member)
2714 reply("CSMSG_OUT_OF_CHANNEL", channel->name);
2717 mod_chanmode_announce(chanserv, channel, &change);
2718 reply("CSMSG_OPCHAN_DONE", channel->name);
2722 static CHANSERV_FUNC(cmd_adduser)
2724 struct userData *actee;
2725 struct userData *actor, *real_actor;
2726 struct handle_info *handle;
2727 unsigned short access_level, override = 0;
2731 if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
2733 reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
2737 access_level = user_level_from_name(argv[2], UL_OWNER);
2740 reply("CSMSG_INVALID_ACCESS", argv[2]);
2744 actor = GetChannelUser(channel->channel_info, user->handle_info);
2745 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2747 if(actor->access <= access_level)
2749 reply("CSMSG_NO_BUMP_ACCESS");
2753 /* Trying to add someone with equal/more access? */
2754 if (!real_actor || real_actor->access <= access_level)
2755 override = CMD_LOG_OVERRIDE;
2757 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2760 if((actee = GetTrueChannelAccess(channel->channel_info, handle)))
2762 reply("CSMSG_USER_EXISTS", handle->handle, channel->name, actee->access);
2766 actee = add_channel_user(channel->channel_info, handle, access_level, 0, NULL);
2767 scan_user_presence(actee, NULL);
2768 reply("CSMSG_ADDED_USER", handle->handle, channel->name, access_level);
2769 return 1 | override;
2772 static CHANSERV_FUNC(cmd_clvl)
2774 struct handle_info *handle;
2775 struct userData *victim;
2776 struct userData *actor, *real_actor;
2777 unsigned short new_access, override = 0;
2778 int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
2782 actor = GetChannelUser(channel->channel_info, user->handle_info);
2783 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2785 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2788 if(handle == user->handle_info && !privileged)
2790 reply("CSMSG_NO_SELF_CLVL");
2794 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2796 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2800 if(actor->access <= victim->access && !privileged)
2802 reply("MSG_USER_OUTRANKED", handle->handle);
2806 new_access = user_level_from_name(argv[2], UL_OWNER);
2810 reply("CSMSG_INVALID_ACCESS", argv[2]);
2814 if(new_access >= actor->access && !privileged)
2816 reply("CSMSG_NO_BUMP_ACCESS");
2820 /* Trying to clvl a equal/higher user? */
2821 if(!real_actor || (real_actor->access <= victim->access && handle != user->handle_info))
2822 override = CMD_LOG_OVERRIDE;
2823 /* Trying to clvl someone to equal/higher access? */
2824 if(!real_actor || new_access >= real_actor->access)
2825 override = CMD_LOG_OVERRIDE;
2826 /* Helpers clvling themselves get caught by the "clvl someone to equal/higher access" check.
2827 * If they lower their own access it's not a big problem.
2830 victim->access = new_access;
2831 reply("CSMSG_CHANGED_ACCESS", handle->handle, new_access, channel->name);
2832 return 1 | override;
2835 static CHANSERV_FUNC(cmd_deluser)
2837 struct handle_info *handle;
2838 struct userData *victim;
2839 struct userData *actor, *real_actor;
2840 unsigned short access_level, override = 0;
2845 actor = GetChannelUser(channel->channel_info, user->handle_info);
2846 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2848 if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
2851 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2853 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2859 access_level = user_level_from_name(argv[1], UL_OWNER);
2862 reply("CSMSG_INVALID_ACCESS", argv[1]);
2865 if(access_level != victim->access)
2867 reply("CSMSG_INCORRECT_ACCESS", handle->handle, victim->access, argv[1]);
2873 access_level = victim->access;
2876 if((actor->access <= victim->access) && !IsHelping(user))
2878 reply("MSG_USER_OUTRANKED", victim->handle->handle);
2882 /* If people delete themselves it is an override, but they
2883 * could've used deleteme so we don't log it as an override
2885 if(!real_actor || (real_actor->access <= victim->access && real_actor != victim))
2886 override = CMD_LOG_OVERRIDE;
2888 chan_name = strdup(channel->name);
2889 del_channel_user(victim, 1);
2890 reply("CSMSG_DELETED_USER", handle->handle, access_level, chan_name);
2892 return 1 | override;
2896 cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
2898 struct userData *actor, *real_actor, *uData, *next;
2899 unsigned int override = 0;
2901 actor = GetChannelUser(channel->channel_info, user->handle_info);
2902 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2904 if(min_access > max_access)
2906 reply("CSMSG_BAD_RANGE", min_access, max_access);
2910 if(actor->access <= max_access)
2912 reply("CSMSG_NO_ACCESS");
2916 if(!real_actor || real_actor->access <= max_access)
2917 override = CMD_LOG_OVERRIDE;
2919 for(uData = channel->channel_info->users; uData; uData = next)
2923 if((uData->access >= min_access)
2924 && (uData->access <= max_access)
2925 && match_ircglob(uData->handle->handle, mask))
2926 del_channel_user(uData, 1);
2929 reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
2930 return 1 | override;
2933 static CHANSERV_FUNC(cmd_mdelowner)
2935 return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
2938 static CHANSERV_FUNC(cmd_mdelcoowner)
2940 return cmd_mdel_user(user, channel, UL_COOWNER, UL_COOWNER, argv[1], cmd);
2943 static CHANSERV_FUNC(cmd_mdelmaster)
2945 return cmd_mdel_user(user, channel, UL_MASTER, UL_MASTER, argv[1], cmd);
2948 static CHANSERV_FUNC(cmd_mdelop)
2950 return cmd_mdel_user(user, channel, UL_OP, UL_OP, argv[1], cmd);
2953 static CHANSERV_FUNC(cmd_mdelpeon)
2955 return cmd_mdel_user(user, channel, UL_PEON, UL_PEON, argv[1], cmd);
2959 cmd_trim_bans(struct userNode *user, struct chanNode *channel, unsigned long duration)
2961 struct banData *bData, *next;
2962 char interval[INTERVALLEN];
2964 unsigned long limit;
2967 limit = now - duration;
2968 for(bData = channel->channel_info->bans; bData; bData = next)
2972 if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
2975 del_channel_ban(bData);
2979 intervalString(interval, duration, user->handle_info);
2980 send_message(user, chanserv, "CSMSG_TRIMMED_BANS", count, channel->name, interval);
2985 cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration, int vacation)
2987 struct userData *actor, *uData, *next;
2988 char interval[INTERVALLEN];
2990 unsigned long limit;
2992 actor = GetChannelAccess(channel->channel_info, user->handle_info);
2993 if(min_access > max_access)
2995 send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
2999 if(!actor || actor->access <= max_access)
3001 send_message(user, chanserv, "CSMSG_NO_ACCESS");
3006 limit = now - duration;
3007 for(uData = channel->channel_info->users; uData; uData = next)
3011 if((uData->seen > limit)
3013 || (HANDLE_FLAGGED(uData->handle, FROZEN) && !vacation))
3016 if(((uData->access >= min_access) && (uData->access <= max_access))
3017 || (!max_access && (uData->access < actor->access)))
3019 del_channel_user(uData, 1);
3027 max_access = (actor->access > UL_OWNER) ? UL_OWNER : (actor->access - 1);
3029 send_message(user, chanserv, "CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration, user->handle_info));
3033 static CHANSERV_FUNC(cmd_trim)
3035 unsigned long duration;
3036 unsigned short min_level, max_level;
3041 vacation = argc > 3 && !strcmp(argv[3], "vacation");
3042 duration = ParseInterval(argv[2]);
3045 reply("CSMSG_CANNOT_TRIM");
3049 if(!irccasecmp(argv[1], "bans"))
3051 cmd_trim_bans(user, channel, duration);
3054 else if(!irccasecmp(argv[1], "users"))
3056 cmd_trim_users(user, channel, 0, 0, duration, vacation);
3059 else if(parse_level_range(&min_level, &max_level, argv[1]))
3061 cmd_trim_users(user, channel, min_level, max_level, duration, vacation);
3064 else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
3066 cmd_trim_users(user, channel, min_level, min_level, duration, vacation);
3071 reply("CSMSG_INVALID_TRIM", argv[1]);
3076 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
3077 to the user. cmd_all takes advantage of this. */
3078 static CHANSERV_FUNC(cmd_up)
3080 struct mod_chanmode change;
3081 struct userData *uData;
3084 mod_chanmode_init(&change);
3086 change.args[0].u.member = GetUserMode(channel, user);
3087 if(!change.args[0].u.member)
3090 reply("MSG_CHANNEL_ABSENT", channel->name);
3094 uData = GetChannelAccess(channel->channel_info, user->handle_info);
3098 reply("CSMSG_GODMODE_UP", argv[0]);
3101 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveOps])
3103 change.args[0].mode = MODE_CHANOP;
3104 errmsg = "CSMSG_ALREADY_OPPED";
3106 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveVoice])
3108 change.args[0].mode = MODE_VOICE;
3109 errmsg = "CSMSG_ALREADY_VOICED";
3114 reply("CSMSG_NO_ACCESS");
3117 change.args[0].mode &= ~change.args[0].u.member->modes;
3118 if(!change.args[0].mode)
3121 reply(errmsg, channel->name);
3124 modcmd_chanmode_announce(&change);
3128 static CHANSERV_FUNC(cmd_down)
3130 struct mod_chanmode change;
3132 mod_chanmode_init(&change);
3134 change.args[0].u.member = GetUserMode(channel, user);
3135 if(!change.args[0].u.member)
3138 reply("MSG_CHANNEL_ABSENT", channel->name);
3142 if(!change.args[0].u.member->modes)
3145 reply("CSMSG_ALREADY_DOWN", channel->name);
3149 change.args[0].mode = MODE_REMOVE | change.args[0].u.member->modes;
3150 modcmd_chanmode_announce(&change);
3154 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)
3156 struct userData *cList;
3158 for(cList = user->handle_info->channels; cList; cList = cList->u_next)
3160 if(IsSuspended(cList->channel)
3161 || IsUserSuspended(cList)
3162 || !GetUserMode(cList->channel->channel, user))
3165 mcmd(user, cList->channel->channel, 0, NULL, cmd);
3171 static CHANSERV_FUNC(cmd_upall)
3173 return cmd_all(CSFUNC_ARGS, cmd_up);
3176 static CHANSERV_FUNC(cmd_downall)
3178 return cmd_all(CSFUNC_ARGS, cmd_down);
3181 typedef int validate_func_t(struct userNode *user, struct chanNode *channel, struct userNode *victim);
3182 typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
3185 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)
3187 unsigned int ii, valid;
3188 struct userNode *victim;
3189 struct mod_chanmode *change;
3191 change = mod_chanmode_alloc(argc - 1);
3193 for(ii=valid=0; ++ii < argc; )
3195 if(!(victim = GetUserH(argv[ii])))
3197 change->args[valid].mode = mode;
3198 change->args[valid].u.member = GetUserMode(channel, victim);
3199 if(!change->args[valid].u.member)
3201 if(validate && !validate(user, channel, victim))
3206 change->argc = valid;
3207 if(valid < (argc-1))
3208 reply("CSMSG_PROCESS_FAILED");
3211 modcmd_chanmode_announce(change);
3212 reply(action, channel->name);
3214 mod_chanmode_free(change);
3218 static CHANSERV_FUNC(cmd_op)
3220 return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
3223 static CHANSERV_FUNC(cmd_deop)
3225 return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
3228 static CHANSERV_FUNC(cmd_voice)
3230 return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
3233 static CHANSERV_FUNC(cmd_devoice)
3235 return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
3239 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, unsigned int *victimCount, struct modeNode **victims)
3245 for(ii=0; ii<channel->members.used; ii++)
3247 struct modeNode *mn = channel->members.list[ii];
3249 if(IsService(mn->user))
3252 if(!user_matches_glob(mn->user, ban, MATCH_USENICK | MATCH_VISIBLE))
3255 if(protect_user(mn->user, user, channel->channel_info))
3259 victims[(*victimCount)++] = mn;
3265 eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3267 struct userNode *victim;
3268 struct modeNode **victims;
3269 unsigned int offset, n, victimCount, duration = 0;
3270 char *reason = "Bye.", *ban, *name;
3271 char interval[INTERVALLEN];
3273 offset = (action & ACTION_ADD_TIMED_BAN) ? 3 : 2;
3274 REQUIRE_PARAMS(offset);
3275 if(argc > offset && IsNetServ(user))
3277 if(*argv[offset] == '$') {
3278 struct userNode *hib;
3279 const char *accountnameb = argv[offset] + 1;
3280 if(!(hib = GetUserH(accountnameb)))
3282 reply("MSG_HANDLE_UNKNOWN", accountnameb);
3291 reason = unsplit_string(argv + offset, argc - offset, NULL);
3292 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
3294 /* Truncate the reason to a length of TOPICLEN, as
3295 the ircd does; however, leave room for an ellipsis
3296 and the kicker's nick. */
3297 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
3301 if((victim = GetUserH(argv[1])))
3303 victims = alloca(sizeof(victims[0]));
3304 victims[0] = GetUserMode(channel, victim);
3305 /* XXX: The comparison with ACTION_KICK is just because all
3306 * other actions can work on users outside the channel, and we
3307 * want to allow those (e.g. unbans) in that case. If we add
3308 * some other ejection action for in-channel users, change
3310 victimCount = victims[0] ? 1 : 0;
3312 if(IsService(victim))
3314 reply("MSG_SERVICE_IMMUNE", victim->nick);
3318 if((action == ACTION_KICK) && !victimCount)
3320 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
3324 if(protect_user(victim, user, channel->channel_info))
3326 reply("CSMSG_USER_PROTECTED", victim->nick);
3330 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
3331 name = victim->nick;
3333 else if(!is_ircmask(argv[1]) && (*argv[1] == '*'))
3335 struct handle_info *hi;
3336 extern const char *titlehost_suffix;
3337 char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
3338 const char *accountname = argv[1] + 1;
3340 if(!(hi = get_handle_info(accountname)))
3342 reply("MSG_HANDLE_UNKNOWN", accountname);
3346 snprintf(banmask, sizeof(banmask), "*!*@%s.*.%s", hi->handle, titlehost_suffix);
3347 victims = alloca(sizeof(victims[0]) * channel->members.used);
3349 if(bad_channel_ban(channel, user, banmask, &victimCount, victims))
3351 reply("CSMSG_MASK_PROTECTED", banmask);
3355 if((action == ACTION_KICK) && (victimCount == 0))
3357 reply("CSMSG_NO_MATCHING_USERS", channel->name, banmask);
3361 name = ban = strdup(banmask);
3365 if(!is_ircmask(argv[1]))
3367 reply("MSG_NICK_UNKNOWN", argv[1]);
3371 victims = alloca(sizeof(victims[0]) * channel->members.used);
3373 if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
3375 reply("CSMSG_MASK_PROTECTED", argv[1]);
3379 if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
3381 reply("CSMSG_LAME_MASK", argv[1]);
3385 if((action == ACTION_KICK) && (victimCount == 0))
3387 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
3391 name = ban = strdup(argv[1]);
3394 /* Truncate the ban in place if necessary; we must ensure
3395 that 'ban' is a valid ban mask before sanitizing it. */
3396 sanitize_ircmask(ban);
3398 if(action & ACTION_ADD_BAN)
3400 struct banData *bData, *next;
3402 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans)
3404 reply("CSMSG_MAXIMUM_BANS", chanserv_conf.max_chan_bans);
3409 if(action & ACTION_ADD_TIMED_BAN)
3411 duration = ParseInterval(argv[2]);
3415 reply("CSMSG_DURATION_TOO_LOW");
3419 else if(duration > (86400 * 365 * 2))
3421 reply("CSMSG_DURATION_TOO_HIGH");
3427 for(bData = channel->channel_info->bans; bData; bData = next)
3429 if(match_ircglobs(bData->mask, ban))
3431 int exact = !irccasecmp(bData->mask, ban);
3433 /* The ban is redundant; there is already a ban
3434 with the same effect in place. */
3438 free(bData->reason);
3439 bData->reason = strdup(reason);
3440 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
3442 reply("CSMSG_REASON_CHANGE", ban);
3446 if(exact && bData->expires)
3450 /* If the ban matches an existing one exactly,
3451 extend the expiration time if the provided
3452 duration is longer. */
3453 if(duration && (now + duration > bData->expires))
3455 bData->expires = now + duration;
3466 /* Delete the expiration timeq entry and
3467 requeue if necessary. */
3468 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
3471 timeq_add(bData->expires, expire_ban, bData);
3475 /* automated kickban */
3478 reply("CSMSG_BAN_EXTENDED", ban, intervalString(interval, duration, user->handle_info));
3480 reply("CSMSG_BAN_ADDED", name, channel->name);
3486 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3493 if(match_ircglobs(ban, bData->mask))
3495 /* The ban we are adding makes previously existing
3496 bans redundant; silently remove them. */
3497 del_channel_ban(bData);
3501 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);
3503 name = ban = strdup(bData->mask);
3507 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
3509 extern const char *hidden_host_suffix;
3510 const char *old_name = chanserv_conf.old_ban_names->list[n];
3512 unsigned int l1, l2;
3515 l2 = strlen(old_name);
3518 if(irccasecmp(ban + l1 - l2, old_name))
3520 new_mask = malloc(MAXLEN);
3521 sprintf(new_mask, "%.*s%s", (int)(l1-l2), ban, hidden_host_suffix);
3523 name = ban = new_mask;
3528 if(action & ACTION_BAN)
3530 unsigned int exists;
3531 struct mod_chanmode *change;
3533 if(channel->banlist.used >= MAXBANS)
3536 reply("CSMSG_BANLIST_FULL", channel->name);
3541 exists = ChannelBanExists(channel, ban);
3542 change = mod_chanmode_alloc(victimCount + 1);
3543 for(n = 0; n < victimCount; ++n)
3545 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_VOICE;
3546 change->args[n].u.member = victims[n];
3550 change->args[n].mode = MODE_BAN;
3551 change->args[n++].u.hostmask = ban;
3555 modcmd_chanmode_announce(change);
3557 mod_chanmode_announce(chanserv, channel, change);
3558 mod_chanmode_free(change);
3560 if(exists && (action == ACTION_BAN))
3563 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3569 if(action & ACTION_KICK)
3571 char kick_reason[MAXLEN];
3572 sprintf(kick_reason, "(%s) %s", user->nick, reason);
3574 for(n = 0; n < victimCount; n++)
3575 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
3580 /* No response, since it was automated. */
3582 else if(action & ACTION_ADD_BAN)
3585 reply("CSMSG_TIMED_BAN_ADDED", name, channel->name, intervalString(interval, duration, user->handle_info));
3587 reply("CSMSG_BAN_ADDED", name, channel->name);
3589 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
3590 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
3591 else if(action & ACTION_BAN)
3592 reply("CSMSG_BAN_DONE", name, channel->name);
3593 else if(action & ACTION_KICK && victimCount)
3594 reply("CSMSG_KICK_DONE", name, channel->name);
3600 static CHANSERV_FUNC(cmd_kickban)
3602 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
3605 static CHANSERV_FUNC(cmd_kick)
3607 return eject_user(CSFUNC_ARGS, ACTION_KICK);
3610 static CHANSERV_FUNC(cmd_ban)
3612 return eject_user(CSFUNC_ARGS, ACTION_BAN);
3615 static CHANSERV_FUNC(cmd_addban)
3617 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN);
3620 static CHANSERV_FUNC(cmd_addtimedban)
3622 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN);
3625 struct mod_chanmode *
3626 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
3628 struct mod_chanmode *change;
3629 unsigned char *match;
3630 unsigned int ii, count;
3632 match = alloca(bans->used);
3635 for(ii = count = 0; ii < bans->used; ++ii)
3637 match[ii] = user_matches_glob(actee, bans->list[ii]->ban,
3638 MATCH_USENICK | MATCH_VISIBLE);
3645 for(ii = count = 0; ii < bans->used; ++ii)
3647 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
3654 change = mod_chanmode_alloc(count);
3655 for(ii = count = 0; ii < bans->used; ++ii)
3659 change->args[count].mode = MODE_REMOVE | MODE_BAN;
3660 change->args[count++].u.hostmask = strdup(bans->list[ii]->ban);
3662 assert(count == change->argc);
3667 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3669 struct userNode *actee;
3675 /* may want to allow a comma delimited list of users... */
3676 if(!(actee = GetUserH(argv[1])))
3678 if(!is_ircmask(argv[1]) && *argv[1] == '*')
3680 char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
3681 const char *accountname = argv[1] + 1;
3683 snprintf(banmask, sizeof(banmask), "*!*@%s.*", accountname);
3684 mask = strdup(banmask);
3686 else if(!is_ircmask(argv[1]))
3688 reply("MSG_NICK_UNKNOWN", argv[1]);
3693 mask = strdup(argv[1]);
3697 /* We don't sanitize the mask here because ircu
3699 if(action & ACTION_UNBAN)
3701 struct mod_chanmode *change;
3702 change = find_matching_bans(&channel->banlist, actee, mask);
3707 modcmd_chanmode_announce(change);
3708 for(ii = 0; ii < change->argc; ++ii)
3709 free((char*)change->args[ii].u.hostmask);
3710 mod_chanmode_free(change);
3715 if(action & ACTION_DEL_BAN)
3717 struct banData *ban, *next;
3719 ban = channel->channel_info->bans;
3723 for( ; ban && !user_matches_glob(actee, ban->mask,
3724 MATCH_USENICK | MATCH_VISIBLE);
3727 for( ; ban && !match_ircglobs(mask, ban->mask);
3732 del_channel_ban(ban);
3739 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3741 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3747 static CHANSERV_FUNC(cmd_unban)
3749 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3752 static CHANSERV_FUNC(cmd_delban)
3754 /* it doesn't necessarily have to remove the channel ban - may want
3755 to make that an option. */
3756 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_BAN);
3759 static CHANSERV_FUNC(cmd_unbanme)
3761 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3762 long flags = ACTION_UNBAN;
3764 /* remove permanent bans if the user has the proper access. */
3765 if(uData->access >= UL_MASTER)
3766 flags |= ACTION_DEL_BAN;
3768 argv[1] = user->nick;
3769 return unban_user(user, channel, 2, argv, cmd, flags);
3772 static CHANSERV_FUNC(cmd_unbanall)
3774 struct mod_chanmode *change;
3777 if(!channel->banlist.used)
3779 reply("CSMSG_NO_BANS", channel->name);
3783 change = mod_chanmode_alloc(channel->banlist.used);
3784 for(ii=0; ii<channel->banlist.used; ii++)
3786 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3787 change->args[ii].u.hostmask = strdup(channel->banlist.list[ii]->ban);
3789 modcmd_chanmode_announce(change);
3790 for(ii = 0; ii < change->argc; ++ii)
3791 free((char*)change->args[ii].u.hostmask);
3792 mod_chanmode_free(change);
3793 reply("CSMSG_BANS_REMOVED", channel->name);
3797 static CHANSERV_FUNC(cmd_open)
3799 struct mod_chanmode *change;
3802 change = find_matching_bans(&channel->banlist, user, NULL);
3804 change = mod_chanmode_alloc(0);
3805 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3806 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3807 && channel->channel_info->modes.modes_set)
3808 change->modes_clear &= ~channel->channel_info->modes.modes_set;
3809 modcmd_chanmode_announce(change);
3810 reply("CSMSG_CHANNEL_OPENED", channel->name);
3811 for(ii = 0; ii < change->argc; ++ii)
3812 free((char*)change->args[ii].u.hostmask);
3813 mod_chanmode_free(change);
3817 static CHANSERV_FUNC(cmd_myaccess)
3819 static struct string_buffer sbuf;
3820 struct handle_info *target_handle;
3821 struct userData *uData;
3826 target_handle = user->handle_info;
3827 else if(!IsStaff(user))
3829 reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
3832 else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3835 if(!oper_outranks(user, target_handle))
3838 if(!target_handle->channels)
3840 reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
3844 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3845 for(uData = target_handle->channels; uData; uData = uData->u_next)
3847 struct chanData *cData = uData->channel;
3850 if(uData->access > UL_OWNER)
3852 if(uData->access == UL_OWNER)
3855 if(IsProtected(cData)
3856 && (target_handle != user->handle_info)
3857 && !GetTrueChannelAccess(cData, user->handle_info))
3860 string_buffer_append_printf(&sbuf, "[%s (%d", cData->channel->name, uData->access);
3861 if(uData->flags != USER_AUTO_OP)
3862 string_buffer_append(&sbuf, ',');
3863 if(IsUserSuspended(uData))
3864 string_buffer_append(&sbuf, 's');
3865 if(IsUserAutoOp(uData))
3867 if(uData->access >= cData->lvlOpts[lvlGiveOps])
3868 string_buffer_append(&sbuf, 'o');
3869 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
3870 string_buffer_append(&sbuf, 'v');
3872 if(IsUserAutoInvite(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe]))
3873 string_buffer_append(&sbuf, 'i');
3875 string_buffer_append_printf(&sbuf, ")] %s", uData->info);
3877 string_buffer_append_string(&sbuf, ")]");
3878 string_buffer_append(&sbuf, '\0');
3879 send_message_type(4, user, cmd->parent->bot, "%s", sbuf.list);
3883 reply("CSMSG_MYACCESS_COUNT_1", target_handle->handle, ccount, ocount);
3885 reply("CSMSG_MYACCESS_COUNT", target_handle->handle, ccount, ocount);
3891 static CHANSERV_FUNC(cmd_access)
3893 struct userNode *target;
3894 struct handle_info *target_handle;
3895 struct userData *uData;
3897 char prefix[MAXLEN];
3902 target_handle = target->handle_info;
3904 else if((target = GetUserH(argv[1])))
3906 target_handle = target->handle_info;
3908 else if(argv[1][0] == '*')
3910 if(!(target_handle = get_handle_info(argv[1]+1)))
3912 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3918 reply("MSG_NICK_UNKNOWN", argv[1]);
3922 assert(target || target_handle);
3924 if(target == chanserv)
3926 reply("CSMSG_IS_CHANSERV");
3934 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
3939 reply("MSG_USER_AUTHENTICATE", target->nick);
3942 reply("MSG_AUTHENTICATE");
3948 const char *epithet = NULL, *type = NULL;
3951 epithet = chanserv_conf.irc_operator_epithet;
3952 type = user_find_message(user, "CSMSG_OPERATOR_TITLE");
3954 else if(IsNetworkHelper(target))
3956 epithet = chanserv_conf.network_helper_epithet;
3957 type = user_find_message(user, "CSMSG_UC_H_TITLE");
3959 else if(IsSupportHelper(target))
3961 epithet = chanserv_conf.support_helper_epithet;
3962 type = user_find_message(user, "CSMSG_LC_H_TITLE");
3966 if(target_handle->epithet)
3967 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
3969 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
3971 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
3975 sprintf(prefix, "%s", target_handle->handle);
3978 if(!channel->channel_info)
3980 reply("CSMSG_NOT_REGISTERED", channel->name);
3984 helping = HANDLE_FLAGGED(target_handle, HELPING)
3985 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
3986 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
3988 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
3989 /* To prevent possible information leaks, only show infolines
3990 * if the requestor is in the channel or it's their own
3992 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
3994 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
3996 /* Likewise, only say it's suspended if the user has active
3997 * access in that channel or it's their own entry. */
3998 if(IsUserSuspended(uData)
3999 && (GetChannelUser(channel->channel_info, user->handle_info)
4000 || (user->handle_info == uData->handle)))
4002 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
4007 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
4014 zoot_list(struct listData *list)
4016 struct userData *uData;
4017 unsigned int start, curr, highest, lowest;
4018 struct helpfile_table tmp_table;
4019 const char **temp, *msg;
4021 if(list->table.length == 1)
4024 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
4026 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
4027 msg = user_find_message(list->user, "MSG_NONE");
4028 send_message_type(4, list->user, list->bot, " %s", msg);
4030 tmp_table.width = list->table.width;
4031 tmp_table.flags = list->table.flags;
4032 list->table.contents[0][0] = " ";
4033 highest = list->highest;
4034 if(list->lowest != 0)
4035 lowest = list->lowest;
4036 else if(highest < 100)
4039 lowest = highest - 100;
4040 for(start = curr = 1; curr < list->table.length; )
4042 uData = list->users[curr-1];
4043 list->table.contents[curr++][0] = " ";
4044 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
4047 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, lowest, highest, list->search);
4049 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, lowest, highest);
4050 temp = list->table.contents[--start];
4051 list->table.contents[start] = list->table.contents[0];
4052 tmp_table.contents = list->table.contents + start;
4053 tmp_table.length = curr - start;
4054 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
4055 list->table.contents[start] = temp;
4057 highest = lowest - 1;
4058 lowest = (highest < 100) ? 0 : (highest - 99);
4064 def_list(struct listData *list)
4068 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
4070 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
4071 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4072 if(list->table.length == 1)
4074 msg = user_find_message(list->user, "MSG_NONE");
4075 send_message_type(4, list->user, list->bot, " %s", msg);
4080 userData_access_comp(const void *arg_a, const void *arg_b)
4082 const struct userData *a = *(struct userData**)arg_a;
4083 const struct userData *b = *(struct userData**)arg_b;
4085 if(a->access != b->access)
4086 res = b->access - a->access;
4088 res = irccasecmp(a->handle->handle, b->handle->handle);
4093 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
4095 void (*send_list)(struct listData *);
4096 struct userData *uData;
4097 struct listData lData;
4098 unsigned int matches;
4102 lData.bot = cmd->parent->bot;
4103 lData.channel = channel;
4104 lData.lowest = lowest;
4105 lData.highest = highest;
4106 lData.search = (argc > 1) ? argv[1] : NULL;
4107 send_list = def_list;
4108 (void)zoot_list; /* since it doesn't show user levels */
4110 if(user->handle_info)
4112 switch(user->handle_info->userlist_style)
4114 case HI_STYLE_DEF: send_list = def_list; break;
4115 case HI_STYLE_ZOOT: send_list = def_list; break;
4119 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
4121 for(uData = channel->channel_info->users; uData; uData = uData->next)
4123 if((uData->access < lowest)
4124 || (uData->access > highest)
4125 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
4127 lData.users[matches++] = uData;
4129 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
4131 lData.table.length = matches+1;
4132 lData.table.width = 4;
4133 lData.table.flags = TABLE_NO_FREE;
4134 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
4135 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
4136 lData.table.contents[0] = ary;
4139 ary[2] = "Last Seen";
4141 for(matches = 1; matches < lData.table.length; ++matches)
4143 char seen[INTERVALLEN];
4145 uData = lData.users[matches-1];
4146 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
4147 lData.table.contents[matches] = ary;
4148 ary[0] = strtab(uData->access);
4149 ary[1] = uData->handle->handle;
4152 else if(!uData->seen)
4155 ary[2] = intervalString(seen, now - uData->seen, user->handle_info);
4156 ary[2] = strdup(ary[2]);
4157 if(IsUserSuspended(uData))
4158 ary[3] = "Suspended";
4159 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
4160 ary[3] = "Vacation";
4161 else if(HANDLE_FLAGGED(uData->handle, BOT))
4167 for(matches = 1; matches < lData.table.length; ++matches)
4169 free((char*)lData.table.contents[matches][2]);
4170 free(lData.table.contents[matches]);
4172 free(lData.table.contents[0]);
4173 free(lData.table.contents);
4177 static CHANSERV_FUNC(cmd_users)
4179 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
4182 static CHANSERV_FUNC(cmd_wlist)
4184 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
4187 static CHANSERV_FUNC(cmd_clist)
4189 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
4192 static CHANSERV_FUNC(cmd_mlist)
4194 return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
4197 static CHANSERV_FUNC(cmd_olist)
4199 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
4202 static CHANSERV_FUNC(cmd_plist)
4204 return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
4207 static CHANSERV_FUNC(cmd_bans)
4209 struct userNode *search_u = NULL;
4210 struct helpfile_table tbl;
4211 unsigned int matches = 0, timed = 0, search_wilds = 0, ii;
4212 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
4213 const char *msg_never, *triggered, *expires;
4214 struct banData *ban, **bans;
4218 else if(strchr(search = argv[1], '!'))
4221 search_wilds = search[strcspn(search, "?*")];
4223 else if(!(search_u = GetUserH(search)))
4224 reply("MSG_NICK_UNKNOWN", search);
4226 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
4228 for(ban = channel->channel_info->bans; ban; ban = ban->next)
4232 if(!user_matches_glob(search_u, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
4237 if(search_wilds ? !match_ircglobs(search, ban->mask) : !match_ircglob(search, ban->mask))
4240 bans[matches++] = ban;
4245 tbl.length = matches + 1;
4246 tbl.width = 4 + timed;
4248 tbl.flags = TABLE_NO_FREE;
4249 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
4250 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4251 tbl.contents[0][0] = "Mask";
4252 tbl.contents[0][1] = "Set By";
4253 tbl.contents[0][2] = "Triggered";
4256 tbl.contents[0][3] = "Expires";
4257 tbl.contents[0][4] = "Reason";
4260 tbl.contents[0][3] = "Reason";
4263 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4265 free(tbl.contents[0]);
4270 msg_never = user_find_message(user, "MSG_NEVER");
4271 for(ii = 0; ii < matches; )
4277 else if(ban->expires)
4278 expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
4280 expires = msg_never;
4283 triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
4285 triggered = msg_never;
4287 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4288 tbl.contents[ii][0] = ban->mask;
4289 tbl.contents[ii][1] = ban->owner;
4290 tbl.contents[ii][2] = strdup(triggered);
4293 tbl.contents[ii][3] = strdup(expires);
4294 tbl.contents[ii][4] = ban->reason;
4297 tbl.contents[ii][3] = ban->reason;
4299 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4300 reply("MSG_MATCH_COUNT", matches);
4301 for(ii = 1; ii < tbl.length; ++ii)
4303 free((char*)tbl.contents[ii][2]);
4305 free((char*)tbl.contents[ii][3]);
4306 free(tbl.contents[ii]);
4308 free(tbl.contents[0]);
4314 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
4316 struct chanData *cData = channel->channel_info;
4317 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
4319 if(cData->topic_mask)
4320 return !match_ircglob(new_topic, cData->topic_mask);
4321 else if(cData->topic)
4322 return irccasecmp(new_topic, cData->topic);
4327 static CHANSERV_FUNC(cmd_topic)
4329 struct chanData *cData;
4332 cData = channel->channel_info;
4337 SetChannelTopic(channel, chanserv, cData->topic, 1);
4338 reply("CSMSG_TOPIC_SET", cData->topic);
4342 reply("CSMSG_NO_TOPIC", channel->name);
4346 topic = unsplit_string(argv + 1, argc - 1, NULL);
4347 /* If they say "!topic *", use an empty topic. */
4348 if((topic[0] == '*') && (topic[1] == 0))
4350 if(bad_topic(channel, user, topic))
4352 char *topic_mask = cData->topic_mask;
4355 char new_topic[TOPICLEN+1], tchar;
4356 int pos=0, starpos=-1, dpos=0, len;
4358 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
4365 len = strlen(topic);
4366 if((dpos + len) > TOPICLEN)
4367 len = TOPICLEN + 1 - dpos;
4368 memcpy(new_topic+dpos, topic, len);
4372 case '\\': tchar = topic_mask[pos++]; /* and fall through */
4373 default: new_topic[dpos++] = tchar; break;
4376 if((dpos > TOPICLEN) || tchar)
4379 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
4380 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
4383 new_topic[dpos] = 0;
4384 SetChannelTopic(channel, chanserv, new_topic, 1);
4386 reply("CSMSG_TOPIC_LOCKED", channel->name);
4391 SetChannelTopic(channel, chanserv, topic, 1);
4393 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
4395 /* Grab the topic and save it as the default topic. */
4397 cData->topic = strdup(channel->topic);
4403 static CHANSERV_FUNC(cmd_mode)
4405 struct userData *uData;
4406 struct mod_chanmode *change;
4412 change = &channel->channel_info->modes;
4413 if(change->modes_set || change->modes_clear) {
4414 modcmd_chanmode_announce(change);
4415 reply("CSMSG_DEFAULTED_MODES", channel->name);
4417 reply("CSMSG_NO_MODES", channel->name);
4421 uData = GetChannelUser(channel->channel_info, user->handle_info);
4423 base_oplevel = MAXOPLEVEL;
4424 else if (uData->access >= UL_OWNER)
4427 base_oplevel = 1 + UL_OWNER - uData->access;
4428 change = mod_chanmode_parse(channel, user, argv+1, argc-1, MCP_KEY_FREE|MCP_IGN_REGISTERED|MCP_NO_APASS, base_oplevel);
4431 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
4435 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
4436 && mode_lock_violated(&channel->channel_info->modes, change))
4439 mod_chanmode_format(&channel->channel_info->modes, modes);
4440 reply("CSMSG_MODE_LOCKED", modes, channel->name);
4444 modcmd_chanmode_announce(change);
4445 mod_chanmode_format(change, fmt);
4446 mod_chanmode_free(change);
4447 reply("CSMSG_MODES_SET", fmt);
4452 chanserv_del_invite_mark(void *data)
4454 struct ChanUser *chanuser = data;
4455 struct chanNode *channel = chanuser->chan;
4457 if(!channel) return;
4458 for(i = 0; i < channel->invited.used; i++)
4460 if(channel->invited.list[i] == chanuser->user) {
4461 userList_remove(&channel->invited, chanuser->user);
4467 static CHANSERV_FUNC(cmd_invite)
4469 struct userData *uData;
4470 struct userNode *invite;
4471 struct ChanUser *chanuser;
4474 uData = GetChannelUser(channel->channel_info, user->handle_info);
4478 if(!(invite = GetUserH(argv[1])))
4480 reply("MSG_NICK_UNKNOWN", argv[1]);
4487 if(GetUserMode(channel, invite))
4489 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
4493 for(i = 0; i < channel->invited.used; i++)
4495 if(channel->invited.list[i] == invite) {
4496 reply("CSMSG_ALREADY_INVITED", invite->nick, channel->name);
4505 char *reason = unsplit_string(argv + 2, argc - 2, NULL);
4506 send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
4509 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
4511 irc_invite(chanserv, invite, channel);
4513 reply("CSMSG_INVITED_USER", argv[1], channel->name);
4515 userList_append(&channel->invited, invite);
4516 chanuser = calloc(1, sizeof(*chanuser));
4517 chanuser->user=invite;
4518 chanuser->chan=channel;
4519 timeq_add(now + chanserv_conf.invited_timeout, chanserv_del_invite_mark, chanuser);
4524 static CHANSERV_FUNC(cmd_inviteme)
4526 if(GetUserMode(channel, user))
4528 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
4531 if(channel->channel_info
4532 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
4534 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
4537 irc_invite(cmd->parent->bot, user, channel);
4541 static CHANSERV_FUNC(cmd_invitemeall)
4543 struct handle_info *target = user->handle_info;
4544 struct userData *uData;
4546 if(!target->channels)
4548 reply("CSMSG_SQUAT_ACCESS", target->handle);
4552 for(uData = target->channels; uData; uData = uData->u_next)
4554 struct chanData *cData = uData->channel;
4555 if(uData->access >= cData->lvlOpts[lvlInviteMe])
4557 irc_invite(cmd->parent->bot, user, cData->channel);
4564 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
4567 char buf1[INTERVALLEN], buf2[INTERVALLEN];
4569 /* We display things based on two dimensions:
4570 * - Issue time: present or absent
4571 * - Expiration: revoked, expired, expires in future, or indefinite expiration
4572 * (in order of precedence, so something both expired and revoked
4573 * only counts as revoked)
4575 combo = (suspended->issued ? 4 : 0)
4576 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
4578 case 0: /* no issue time, indefinite expiration */
4579 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
4581 case 1: /* no issue time, expires in future */
4582 intervalString(buf1, suspended->expires-now, user->handle_info);
4583 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
4585 case 2: /* no issue time, expired */
4586 intervalString(buf1, now-suspended->expires, user->handle_info);
4587 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
4589 case 3: /* no issue time, revoked */
4590 intervalString(buf1, now-suspended->revoked, user->handle_info);
4591 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
4593 case 4: /* issue time set, indefinite expiration */
4594 intervalString(buf1, now-suspended->issued, user->handle_info);
4595 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
4597 case 5: /* issue time set, expires in future */
4598 intervalString(buf1, now-suspended->issued, user->handle_info);
4599 intervalString(buf2, suspended->expires-now, user->handle_info);
4600 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
4602 case 6: /* issue time set, expired */
4603 intervalString(buf1, now-suspended->issued, user->handle_info);
4604 intervalString(buf2, now-suspended->expires, user->handle_info);
4605 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
4607 case 7: /* issue time set, revoked */
4608 intervalString(buf1, now-suspended->issued, user->handle_info);
4609 intervalString(buf2, now-suspended->revoked, user->handle_info);
4610 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
4613 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
4618 static CHANSERV_FUNC(cmd_info)
4620 char modes[MAXLEN], buffer[INTERVALLEN];
4621 struct userData *uData, *owner;
4622 struct chanData *cData;
4623 struct do_not_register *dnr;
4628 cData = channel->channel_info;
4629 reply("CSMSG_CHANNEL_INFO", channel->name);
4631 uData = GetChannelUser(cData, user->handle_info);
4632 if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
4634 mod_chanmode_format(&cData->modes, modes);
4635 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
4636 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
4639 for(it = dict_first(cData->notes); it; it = iter_next(it))
4643 note = iter_data(it);
4644 if(!note_type_visible_to_user(cData, note->type, user))
4647 padding = PADLEN - 1 - strlen(iter_key(it));
4648 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
4651 if(cData->max_time) {
4652 reply("CSMSG_CHANNEL_MAX_TIME", cData->max, intervalString(buffer, now - cData->max_time, user->handle_info));
4654 reply("CSMSG_CHANNEL_MAX", cData->max);
4656 for(owner = cData->users; owner; owner = owner->next)
4657 if(owner->access == UL_OWNER)
4658 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
4659 reply("CSMSG_CHANNEL_USERS", cData->userCount);
4660 reply("CSMSG_CHANNEL_BANS", cData->banCount);
4661 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
4663 privileged = IsStaff(user);
4665 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
4666 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
4667 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
4669 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
4670 chanserv_show_dnrs(user, cmd, channel->name, NULL);
4672 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
4674 struct suspended *suspended;
4675 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
4676 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
4677 show_suspension_info(cmd, user, suspended);
4679 else if(IsSuspended(cData))
4681 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
4682 show_suspension_info(cmd, user, cData->suspended);
4687 static CHANSERV_FUNC(cmd_netinfo)
4689 extern unsigned long boot_time;
4690 extern unsigned long burst_length;
4691 char interval[INTERVALLEN];
4693 reply("CSMSG_NETWORK_INFO");
4694 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
4695 reply("CSMSG_NETWORK_USERS", dict_size(clients));
4696 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
4697 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
4698 reply("CSMSG_NETWORK_BANS", banCount);
4699 reply("CSMSG_NETWORK_CHANUSERS", userCount);
4700 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
4701 reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
4706 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
4708 struct helpfile_table table;
4710 struct userNode *user;
4715 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4716 table.contents = alloca(list->used*sizeof(*table.contents));
4717 for(nn=0; nn<list->used; nn++)
4719 user = list->list[nn];
4720 if(user->modes & skip_flags)
4726 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
4729 nick = alloca(strlen(user->nick)+3);
4730 sprintf(nick, "(%s)", user->nick);
4734 table.contents[table.length][0] = nick;
4737 table_send(chanserv, to->nick, 0, NULL, table);
4740 static CHANSERV_FUNC(cmd_ircops)
4742 reply("CSMSG_STAFF_OPERS");
4743 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
4747 static CHANSERV_FUNC(cmd_helpers)
4749 reply("CSMSG_STAFF_HELPERS");
4750 send_staff_list(user, &curr_helpers, FLAGS_OPER);
4754 static CHANSERV_FUNC(cmd_staff)
4756 reply("CSMSG_NETWORK_STAFF");
4757 cmd_ircops(CSFUNC_ARGS);
4758 cmd_helpers(CSFUNC_ARGS);
4762 static CHANSERV_FUNC(cmd_peek)
4764 struct modeNode *mn;
4765 char modes[MODELEN];
4767 struct helpfile_table table;
4768 int opcount = 0, voicecount = 0, srvcount = 0;
4770 irc_make_chanmode(channel, modes);
4772 reply("CSMSG_PEEK_INFO", channel->name);
4773 reply("CSMSG_PEEK_TOPIC", channel->topic);
4774 reply("CSMSG_PEEK_MODES", modes);
4778 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4779 table.contents = alloca(channel->members.used*sizeof(*table.contents));
4780 for(n = 0; n < channel->members.used; n++)
4782 mn = channel->members.list[n];
4783 if(IsLocal(mn->user))
4785 else if(mn->modes & MODE_CHANOP)
4787 else if(mn->modes & MODE_VOICE)
4790 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
4792 table.contents[table.length] = alloca(sizeof(**table.contents));
4793 table.contents[table.length][0] = mn->user->nick;
4797 reply("CSMSG_PEEK_USERS", channel->members.used, opcount, voicecount,
4798 (channel->members.used - opcount - voicecount - srvcount));
4802 reply("CSMSG_PEEK_OPS");
4803 table_send(chanserv, user->nick, 0, NULL, table);
4806 reply("CSMSG_PEEK_NO_OPS");
4810 static MODCMD_FUNC(cmd_wipeinfo)
4812 struct handle_info *victim;
4813 struct userData *ud, *actor, *real_actor;
4814 unsigned int override = 0;
4817 actor = GetChannelUser(channel->channel_info, user->handle_info);
4818 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
4819 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4821 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4823 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4826 if((ud->access >= actor->access) && (ud != actor))
4828 reply("MSG_USER_OUTRANKED", victim->handle);
4831 if((ud != real_actor) && (!real_actor || (ud->access >= real_actor->access)))
4832 override = CMD_LOG_OVERRIDE;
4836 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4837 return 1 | override;
4840 static CHANSERV_FUNC(cmd_resync)
4842 struct mod_chanmode *changes;
4843 struct chanData *cData = channel->channel_info;
4844 unsigned int ii, used;
4846 changes = mod_chanmode_alloc(channel->members.used * 2);
4847 for(ii = used = 0; ii < channel->members.used; ++ii)
4849 struct modeNode *mn = channel->members.list[ii];
4850 struct userData *uData;
4852 if(IsService(mn->user))
4855 uData = GetChannelAccess(cData, mn->user->handle_info);
4856 if(!cData->lvlOpts[lvlGiveOps]
4857 || (uData && uData->access >= cData->lvlOpts[lvlGiveOps]))
4859 if(!(mn->modes & MODE_CHANOP))
4861 if(!uData || IsUserAutoOp(uData))
4863 changes->args[used].mode = MODE_CHANOP;
4864 changes->args[used++].u.member = mn;
4865 if(!(mn->modes & MODE_VOICE))
4867 changes->args[used].mode = MODE_VOICE;
4868 changes->args[used++].u.member = mn;
4873 else if(!cData->lvlOpts[lvlGiveVoice]
4874 || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice]))
4876 if(mn->modes & MODE_CHANOP)
4878 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4879 changes->args[used++].u.member = mn;
4881 if(!(mn->modes & MODE_VOICE) && (!uData || IsUserAutoOp(uData)))
4883 changes->args[used].mode = MODE_VOICE;
4884 changes->args[used++].u.member = mn;
4891 changes->args[used].mode = MODE_REMOVE | mn->modes;
4892 changes->args[used++].u.member = mn;
4896 changes->argc = used;
4897 modcmd_chanmode_announce(changes);
4898 mod_chanmode_free(changes);
4899 reply("CSMSG_RESYNCED_USERS", channel->name);
4903 static CHANSERV_FUNC(cmd_seen)
4905 struct userData *uData;
4906 struct handle_info *handle;
4907 char seen[INTERVALLEN];
4911 if(!irccasecmp(argv[1], chanserv->nick))
4913 reply("CSMSG_IS_CHANSERV");
4917 if(!(handle = get_handle_info(argv[1])))
4919 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4923 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4925 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4930 reply("CSMSG_USER_PRESENT", handle->handle);
4931 else if(uData->seen)
4932 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
4934 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4936 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4937 reply("CSMSG_USER_VACATION", handle->handle);
4942 static MODCMD_FUNC(cmd_names)
4944 struct userNode *targ;
4945 struct userData *targData;
4946 unsigned int ii, pos;
4949 for(ii=pos=0; ii<channel->members.used; ++ii)
4951 targ = channel->members.list[ii]->user;
4952 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4955 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
4958 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4962 if(IsUserSuspended(targData))
4964 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4967 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4968 reply("CSMSG_END_NAMES", channel->name);
4973 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
4975 switch(ntype->visible_type)
4977 case NOTE_VIS_ALL: return 1;
4978 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
4979 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
4984 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
4986 struct userData *uData;
4988 switch(ntype->set_access_type)
4990 case NOTE_SET_CHANNEL_ACCESS:
4991 if(!user->handle_info)
4993 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
4995 return uData->access >= ntype->set_access.min_ulevel;
4996 case NOTE_SET_CHANNEL_SETTER:
4997 return check_user_level(channel, user, lvlSetters, 1, 0);
4998 case NOTE_SET_PRIVILEGED: default:
4999 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
5003 static CHANSERV_FUNC(cmd_note)
5005 struct chanData *cData;
5007 struct note_type *ntype;
5009 cData = channel->channel_info;
5012 reply("CSMSG_NOT_REGISTERED", channel->name);
5016 /* If no arguments, show all visible notes for the channel. */
5022 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
5024 note = iter_data(it);
5025 if(!note_type_visible_to_user(cData, note->type, user))
5028 reply("CSMSG_NOTELIST_HEADER", channel->name);
5029 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
5032 reply("CSMSG_NOTELIST_END", channel->name);
5034 reply("CSMSG_NOTELIST_EMPTY", channel->name);
5036 /* If one argument, show the named note. */
5039 if((note = dict_find(cData->notes, argv[1], NULL))
5040 && note_type_visible_to_user(cData, note->type, user))
5042 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
5044 else if((ntype = dict_find(note_types, argv[1], NULL))
5045 && note_type_visible_to_user(NULL, ntype, user))
5047 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
5052 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
5056 /* Assume they're trying to set a note. */
5060 ntype = dict_find(note_types, argv[1], NULL);
5063 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
5066 else if(note_type_settable_by_user(channel, ntype, user))
5068 note_text = unsplit_string(argv+2, argc-2, NULL);
5069 if((note = dict_find(cData->notes, argv[1], NULL)))
5070 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
5071 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
5072 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
5074 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
5076 /* The note is viewable to staff only, so return 0
5077 to keep the invocation from getting logged (or
5078 regular users can see it in !events). */
5084 reply("CSMSG_NO_ACCESS");
5091 static CHANSERV_FUNC(cmd_delnote)
5096 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
5097 || !note_type_settable_by_user(channel, note->type, user))
5099 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
5102 dict_remove(channel->channel_info->notes, note->type->name);
5103 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
5107 static CHANSERV_FUNC(cmd_events)
5109 struct logSearch discrim;
5110 struct logReport report;
5111 unsigned int matches, limit;
5113 limit = (argc > 1) ? atoi(argv[1]) : 10;
5114 if(limit < 1 || limit > 200)
5117 memset(&discrim, 0, sizeof(discrim));
5118 discrim.masks.bot = chanserv;
5119 discrim.masks.channel_name = channel->name;
5121 discrim.masks.command = argv[2];
5122 discrim.limit = limit;
5123 discrim.max_time = INT_MAX;
5124 discrim.severities = 1 << LOG_COMMAND;
5125 report.reporter = chanserv;
5127 reply("CSMSG_EVENT_SEARCH_RESULTS");
5128 matches = log_entry_search(&discrim, log_report_entry, &report);
5130 reply("MSG_MATCH_COUNT", matches);
5132 reply("MSG_NO_MATCHES");
5136 static CHANSERV_FUNC(cmd_say)
5142 msg = unsplit_string(argv + 1, argc - 1, NULL);
5143 send_channel_message(channel, cmd->parent->bot, "%s", msg);
5145 else if(*argv[1] == '*' && argv[1][1] != '\0')
5147 struct handle_info *hi;
5148 struct userNode *authed;
5151 msg = unsplit_string(argv + 2, argc - 2, NULL);
5153 if (!(hi = get_handle_info(argv[1] + 1)))
5155 reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
5159 for (authed = hi->users; authed; authed = authed->next_authed)
5160 send_target_message(5, authed->nick, cmd->parent->bot, "%s", msg);
5162 else if(GetUserH(argv[1]))
5165 msg = unsplit_string(argv + 2, argc - 2, NULL);
5166 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
5170 reply("MSG_NOT_TARGET_NAME");
5176 static CHANSERV_FUNC(cmd_emote)
5182 /* CTCP is so annoying. */
5183 msg = unsplit_string(argv + 1, argc - 1, NULL);
5184 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
5186 else if(*argv[1] == '*' && argv[1][1] != '\0')
5188 struct handle_info *hi;
5189 struct userNode *authed;
5192 msg = unsplit_string(argv + 2, argc - 2, NULL);
5194 if (!(hi = get_handle_info(argv[1] + 1)))
5196 reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
5200 for (authed = hi->users; authed; authed = authed->next_authed)
5201 send_target_message(5, authed->nick, cmd->parent->bot, "\001ACTION %s\001", msg);
5203 else if(GetUserH(argv[1]))
5205 msg = unsplit_string(argv + 2, argc - 2, NULL);
5206 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
5210 reply("MSG_NOT_TARGET_NAME");
5216 struct channelList *
5217 chanserv_support_channels(void)
5219 return &chanserv_conf.support_channels;
5222 static CHANSERV_FUNC(cmd_expire)
5224 int channel_count = registered_channels;
5225 expire_channels(NULL);
5226 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
5231 chanserv_expire_suspension(void *data)
5233 struct suspended *suspended = data;
5234 struct chanNode *channel;
5237 /* Update the channel registration data structure. */
5238 if(!suspended->expires || (now < suspended->expires))
5239 suspended->revoked = now;
5240 channel = suspended->cData->channel;
5241 suspended->cData->channel = channel;
5242 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
5244 /* If appropriate, re-join ChanServ to the channel. */
5245 if(!IsOffChannel(suspended->cData))
5247 spamserv_cs_suspend(channel, 0, 0, NULL);
5248 ss_cs_join_channel(channel, 1);
5251 /* Mark everyone currently in the channel as present. */
5252 for(ii = 0; ii < channel->members.used; ++ii)
5254 struct userData *uData = GetChannelAccess(suspended->cData, channel->members.list[ii]->user->handle_info);
5263 static CHANSERV_FUNC(cmd_csuspend)
5265 struct suspended *suspended;
5266 char reason[MAXLEN];
5267 unsigned long expiry, duration;
5268 struct userData *uData;
5272 if(IsProtected(channel->channel_info))
5274 reply("CSMSG_SUSPEND_NODELETE", channel->name);
5278 if(argv[1][0] == '!')
5280 else if(IsSuspended(channel->channel_info))
5282 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
5283 show_suspension_info(cmd, user, channel->channel_info->suspended);
5287 if(!strcmp(argv[1], "0"))
5289 else if((duration = ParseInterval(argv[1])))
5290 expiry = now + duration;
5293 reply("MSG_INVALID_DURATION", argv[1]);
5297 unsplit_string(argv + 2, argc - 2, reason);
5299 suspended = calloc(1, sizeof(*suspended));
5300 suspended->revoked = 0;
5301 suspended->issued = now;
5302 suspended->suspender = strdup(user->handle_info->handle);
5303 suspended->expires = expiry;
5304 suspended->reason = strdup(reason);
5305 suspended->cData = channel->channel_info;
5306 suspended->previous = suspended->cData->suspended;
5307 suspended->cData->suspended = suspended;
5309 if(suspended->expires)
5310 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
5312 if(IsSuspended(channel->channel_info))
5314 suspended->previous->revoked = now;
5315 if(suspended->previous->expires)
5316 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
5317 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
5318 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5322 /* Mark all users in channel as absent. */
5323 for(uData = channel->channel_info->users; uData; uData = uData->next)
5332 /* Mark the channel as suspended, then part. */
5333 channel->channel_info->flags |= CHANNEL_SUSPENDED;
5334 spamserv_cs_suspend(channel, expiry, 1, suspended->reason);
5335 DelChannelUser(chanserv, channel, suspended->reason, 0);
5336 reply("CSMSG_SUSPENDED", channel->name);
5337 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
5338 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5343 static CHANSERV_FUNC(cmd_cunsuspend)
5345 struct suspended *suspended;
5346 char message[MAXLEN];
5348 if(!IsSuspended(channel->channel_info))
5350 reply("CSMSG_NOT_SUSPENDED", channel->name);
5354 suspended = channel->channel_info->suspended;
5356 /* Expire the suspension and join ChanServ to the channel. */
5357 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
5358 chanserv_expire_suspension(suspended);
5359 reply("CSMSG_UNSUSPENDED", channel->name);
5360 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
5361 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
5365 typedef struct chanservSearch
5370 unsigned long unvisited;
5371 unsigned long registered;
5373 unsigned long flags;
5377 typedef void (*channel_search_func)(struct chanData *channel, void *data);
5380 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
5385 search = malloc(sizeof(struct chanservSearch));
5386 memset(search, 0, sizeof(*search));
5389 for(i = 0; i < argc; i++)
5391 /* Assume all criteria require arguments. */
5394 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
5398 if(!irccasecmp(argv[i], "name"))
5399 search->name = argv[++i];
5400 else if(!irccasecmp(argv[i], "registrar"))
5401 search->registrar = argv[++i];
5402 else if(!irccasecmp(argv[i], "unvisited"))
5403 search->unvisited = ParseInterval(argv[++i]);
5404 else if(!irccasecmp(argv[i], "registered"))
5405 search->registered = ParseInterval(argv[++i]);
5406 else if(!irccasecmp(argv[i], "flags"))
5409 if(!irccasecmp(argv[i], "nodelete"))
5410 search->flags |= CHANNEL_NODELETE;
5411 else if(!irccasecmp(argv[i], "suspended"))
5412 search->flags |= CHANNEL_SUSPENDED;
5413 else if(!irccasecmp(argv[i], "unreviewed"))
5414 search->flags |= CHANNEL_UNREVIEWED;
5417 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
5421 else if(!irccasecmp(argv[i], "limit"))
5422 search->limit = strtoul(argv[++i], NULL, 10);
5425 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
5430 if(search->name && !strcmp(search->name, "*"))
5432 if(search->registrar && !strcmp(search->registrar, "*"))
5433 search->registrar = 0;
5442 chanserv_channel_match(struct chanData *channel, search_t search)
5444 const char *name = channel->channel->name;
5445 if((search->name && !match_ircglob(name, search->name)) ||
5446 (search->registrar && !channel->registrar) ||
5447 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
5448 (search->unvisited && (now - channel->visited) < search->unvisited) ||
5449 (search->registered && (now - channel->registered) > search->registered) ||
5450 (search->flags && ((search->flags & channel->flags) != search->flags)))
5457 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
5459 struct chanData *channel;
5460 unsigned int matches = 0;
5462 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
5464 if(!chanserv_channel_match(channel, search))
5474 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
5479 search_print(struct chanData *channel, void *data)
5481 send_message_type(4, data, chanserv, "%s", channel->channel->name);
5484 static CHANSERV_FUNC(cmd_search)
5487 unsigned int matches;
5488 channel_search_func action;
5492 if(!irccasecmp(argv[1], "count"))
5493 action = search_count;
5494 else if(!irccasecmp(argv[1], "print"))
5495 action = search_print;
5498 reply("CSMSG_ACTION_INVALID", argv[1]);
5502 search = chanserv_search_create(user, argc - 2, argv + 2);
5506 if(action == search_count)
5507 search->limit = INT_MAX;
5509 if(action == search_print)
5510 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
5512 matches = chanserv_channel_search(search, action, user);
5515 reply("MSG_MATCH_COUNT", matches);
5517 reply("MSG_NO_MATCHES");
5523 static CHANSERV_FUNC(cmd_unvisited)
5525 struct chanData *cData;
5526 unsigned long interval = chanserv_conf.channel_expire_delay;
5527 char buffer[INTERVALLEN];
5528 unsigned int limit = 25, matches = 0;
5532 interval = ParseInterval(argv[1]);
5534 limit = atoi(argv[2]);
5537 intervalString(buffer, interval, user->handle_info);
5538 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
5540 for(cData = channelList; cData && matches < limit; cData = cData->next)
5542 if((now - cData->visited) < interval)
5545 intervalString(buffer, now - cData->visited, user->handle_info);
5546 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
5553 static MODCMD_FUNC(chan_opt_defaulttopic)
5559 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5561 reply("CSMSG_TOPIC_LOCKED", channel->name);
5565 topic = unsplit_string(argv+1, argc-1, NULL);
5567 free(channel->channel_info->topic);
5568 if(topic[0] == '*' && topic[1] == 0)
5570 topic = channel->channel_info->topic = NULL;
5574 topic = channel->channel_info->topic = strdup(topic);
5575 if(channel->channel_info->topic_mask
5576 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
5577 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5579 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
5582 if(channel->channel_info->topic)
5583 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
5585 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
5589 static MODCMD_FUNC(chan_opt_topicmask)
5593 struct chanData *cData = channel->channel_info;
5596 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5598 reply("CSMSG_TOPIC_LOCKED", channel->name);
5602 mask = unsplit_string(argv+1, argc-1, NULL);
5604 if(cData->topic_mask)
5605 free(cData->topic_mask);
5606 if(mask[0] == '*' && mask[1] == 0)
5608 cData->topic_mask = 0;
5612 cData->topic_mask = strdup(mask);
5614 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
5615 else if(!match_ircglob(cData->topic, cData->topic_mask))
5616 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5620 if(channel->channel_info->topic_mask)
5621 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
5623 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
5627 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
5631 char *greeting = unsplit_string(argv+1, argc-1, NULL);
5635 if(greeting[0] == '*' && greeting[1] == 0)
5639 unsigned int length = strlen(greeting);
5640 if(length > chanserv_conf.greeting_length)
5642 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
5645 *data = strdup(greeting);
5654 reply(name, user_find_message(user, "MSG_NONE"));
5658 static MODCMD_FUNC(chan_opt_greeting)
5660 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
5663 static MODCMD_FUNC(chan_opt_usergreeting)
5665 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
5668 static MODCMD_FUNC(chan_opt_modes)
5670 struct mod_chanmode *new_modes;
5675 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
5677 reply("CSMSG_NO_ACCESS");
5680 if(argv[1][0] == '*' && argv[1][1] == 0)
5682 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
5684 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)))
5686 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5689 else if(new_modes->argc > 1)
5691 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5692 mod_chanmode_free(new_modes);
5697 channel->channel_info->modes = *new_modes;
5698 modcmd_chanmode_announce(new_modes);
5699 mod_chanmode_free(new_modes);
5703 mod_chanmode_format(&channel->channel_info->modes, modes);
5705 reply("CSMSG_SET_MODES", modes);
5707 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
5711 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5713 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5715 struct chanData *cData = channel->channel_info;
5720 /* Set flag according to value. */
5721 if(enabled_string(argv[1]))
5723 cData->flags |= mask;
5726 else if(disabled_string(argv[1]))
5728 cData->flags &= ~mask;
5733 reply("MSG_INVALID_BINARY", argv[1]);
5739 /* Find current option value. */
5740 value = (cData->flags & mask) ? 1 : 0;
5744 reply(name, user_find_message(user, "MSG_ON"));
5746 reply(name, user_find_message(user, "MSG_OFF"));
5750 static MODCMD_FUNC(chan_opt_nodelete)
5752 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
5754 reply("MSG_SETTING_PRIVILEGED", argv[0]);
5758 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
5761 static MODCMD_FUNC(chan_opt_dynlimit)
5763 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
5766 static MODCMD_FUNC(chan_opt_offchannel)
5768 struct chanData *cData = channel->channel_info;
5773 /* Set flag according to value. */
5774 if(enabled_string(argv[1]))
5776 if(!IsOffChannel(cData))
5777 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
5778 cData->flags |= CHANNEL_OFFCHANNEL;
5781 else if(disabled_string(argv[1]))
5783 if(IsOffChannel(cData))
5785 struct mod_chanmode change;
5786 mod_chanmode_init(&change);
5788 change.args[0].mode = MODE_CHANOP;
5789 change.args[0].u.member = AddChannelUser(chanserv, channel);
5790 mod_chanmode_announce(chanserv, channel, &change);
5792 cData->flags &= ~CHANNEL_OFFCHANNEL;
5797 reply("MSG_INVALID_BINARY", argv[1]);
5803 /* Find current option value. */
5804 value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
5808 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
5810 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
5814 static MODCMD_FUNC(chan_opt_unreviewed)
5816 struct chanData *cData = channel->channel_info;
5817 int value = (cData->flags & CHANNEL_UNREVIEWED) ? 1 : 0;
5823 /* The two directions can have different ACLs. */
5824 if(enabled_string(argv[1]))
5826 else if(disabled_string(argv[1]))
5830 reply("MSG_INVALID_BINARY", argv[1]);
5834 if (new_value != value)
5836 struct svccmd *subcmd;
5837 char subcmd_name[32];
5839 snprintf(subcmd_name, sizeof(subcmd_name), "%s %s", argv[0], (new_value ? "on" : "off"));
5840 subcmd = dict_find(cmd->parent->commands, subcmd_name, NULL);
5843 reply("MSG_COMMAND_DISABLED", subcmd_name);
5846 else if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
5850 cData->flags |= CHANNEL_UNREVIEWED;
5853 free(cData->registrar);
5854 cData->registrar = strdup(user->handle_info->handle);
5855 cData->flags &= ~CHANNEL_UNREVIEWED;
5862 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_ON"));
5864 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_OFF"));
5868 static MODCMD_FUNC(chan_opt_defaults)
5870 struct userData *uData;
5871 struct chanData *cData;
5872 const char *confirm;
5873 enum levelOption lvlOpt;
5874 enum charOption chOpt;
5876 cData = channel->channel_info;
5877 uData = GetChannelUser(cData, user->handle_info);
5878 if(!uData || (uData->access < UL_OWNER))
5880 reply("CSMSG_OWNER_DEFAULTS", channel->name);
5883 confirm = make_confirmation_string(uData);
5884 if((argc < 2) || strcmp(argv[1], confirm))
5886 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
5889 cData->flags = (CHANNEL_DEFAULT_FLAGS & ~CHANNEL_PRESERVED_FLAGS)
5890 | (cData->flags & CHANNEL_PRESERVED_FLAGS);
5891 cData->modes = chanserv_conf.default_modes;
5892 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
5893 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
5894 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
5895 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
5896 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
5901 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5903 struct chanData *cData = channel->channel_info;
5904 struct userData *uData;
5905 unsigned short value;
5909 if(!check_user_level(channel, user, option, 1, 1))
5911 reply("CSMSG_CANNOT_SET");
5914 value = user_level_from_name(argv[1], UL_OWNER+1);
5915 if(!value && strcmp(argv[1], "0"))
5917 reply("CSMSG_INVALID_ACCESS", argv[1]);
5920 uData = GetChannelUser(cData, user->handle_info);
5921 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
5923 reply("CSMSG_BAD_SETLEVEL");
5929 if(value > cData->lvlOpts[lvlGiveOps])
5931 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
5936 if(value < cData->lvlOpts[lvlGiveVoice])
5938 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
5943 /* This test only applies to owners, since non-owners
5944 * trying to set an option to above their level get caught
5945 * by the CSMSG_BAD_SETLEVEL test above.
5947 if(value > uData->access)
5949 reply("CSMSG_BAD_SETTERS");
5956 cData->lvlOpts[option] = value;
5958 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
5962 static MODCMD_FUNC(chan_opt_enfops)
5964 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
5967 static MODCMD_FUNC(chan_opt_giveops)
5969 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
5972 static MODCMD_FUNC(chan_opt_enfmodes)
5974 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
5977 static MODCMD_FUNC(chan_opt_enftopic)
5979 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
5982 static MODCMD_FUNC(chan_opt_pubcmd)
5984 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
5987 static MODCMD_FUNC(chan_opt_setters)
5989 return channel_level_option(lvlSetters, CSFUNC_ARGS);
5992 static MODCMD_FUNC(chan_opt_ctcpusers)
5994 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
5997 static MODCMD_FUNC(chan_opt_userinfo)
5999 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
6002 static MODCMD_FUNC(chan_opt_givevoice)
6004 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
6007 static MODCMD_FUNC(chan_opt_topicsnarf)
6009 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
6012 static MODCMD_FUNC(chan_opt_vote)
6014 return channel_level_option(lvlVote, CSFUNC_ARGS);
6017 static MODCMD_FUNC(chan_opt_inviteme)
6019 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
6023 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
6025 struct chanData *cData = channel->channel_info;
6026 int count = charOptions[option].count, idx;
6030 idx = atoi(argv[1]);
6032 if(!isdigit(argv[1][0]) || (idx < 0) || (idx >= count))
6034 reply("CSMSG_INVALID_NUMERIC", idx);
6035 /* Show possible values. */
6036 for(idx = 0; idx < count; idx++)
6037 reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
6041 cData->chOpts[option] = charOptions[option].values[idx].value;
6045 /* Find current option value. */
6048 (idx < count) && (cData->chOpts[option] != charOptions[option].values[idx].value);
6052 /* Somehow, the option value is corrupt; reset it to the default. */
6053 cData->chOpts[option] = charOptions[option].default_value;
6058 reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
6062 static MODCMD_FUNC(chan_opt_protect)
6064 return channel_multiple_option(chProtect, CSFUNC_ARGS);
6067 static MODCMD_FUNC(chan_opt_toys)
6069 return channel_multiple_option(chToys, CSFUNC_ARGS);
6072 static MODCMD_FUNC(chan_opt_ctcpreaction)
6074 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
6077 static MODCMD_FUNC(chan_opt_topicrefresh)
6079 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
6082 static struct svccmd_list set_shows_list;
6085 handle_svccmd_unbind(struct svccmd *target) {
6087 for(ii=0; ii<set_shows_list.used; ++ii)
6088 if(target == set_shows_list.list[ii])
6089 set_shows_list.used = 0;
6092 static CHANSERV_FUNC(cmd_set)
6094 struct svccmd *subcmd;
6098 /* Check if we need to (re-)initialize set_shows_list. */
6099 if(!set_shows_list.used)
6101 if(!set_shows_list.size)
6103 set_shows_list.size = chanserv_conf.set_shows->used;
6104 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
6106 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
6108 const char *name = chanserv_conf.set_shows->list[ii];
6109 sprintf(buf, "%s %s", argv[0], name);
6110 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6113 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
6116 svccmd_list_append(&set_shows_list, subcmd);
6122 reply("CSMSG_CHANNEL_OPTIONS");
6123 for(ii = 0; ii < set_shows_list.used; ii++)
6125 subcmd = set_shows_list.list[ii];
6126 subcmd->command->func(user, channel, 1, argv+1, subcmd);
6131 sprintf(buf, "%s %s", argv[0], argv[1]);
6132 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6135 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
6138 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
6140 reply("CSMSG_NO_ACCESS");
6146 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
6150 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
6152 struct userData *uData;
6154 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6157 reply("CSMSG_NOT_USER", channel->name);
6163 /* Just show current option value. */
6165 else if(enabled_string(argv[1]))
6167 uData->flags |= mask;
6169 else if(disabled_string(argv[1]))
6171 uData->flags &= ~mask;
6175 reply("MSG_INVALID_BINARY", argv[1]);
6179 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
6183 static MODCMD_FUNC(user_opt_noautoop)
6185 struct userData *uData;
6187 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6190 reply("CSMSG_NOT_USER", channel->name);
6193 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
6194 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
6196 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
6199 static MODCMD_FUNC(user_opt_autoinvite)
6201 if((argc > 1) && !check_user_level(channel, user, lvlInviteMe, 1, 0))
6203 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
6205 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
6208 static MODCMD_FUNC(user_opt_info)
6210 struct userData *uData;
6213 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6217 /* If they got past the command restrictions (which require access)
6218 * but fail this test, we have some fool with security override on.
6220 reply("CSMSG_NOT_USER", channel->name);
6227 infoline = unsplit_string(argv + 1, argc - 1, NULL);
6228 if(strlen(infoline) > chanserv_conf.max_userinfo_length)
6230 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
6233 bp = strcspn(infoline, "\001");
6236 reply("CSMSG_BAD_INFOLINE", infoline[bp]);
6241 if(infoline[0] == '*' && infoline[1] == 0)
6244 uData->info = strdup(infoline);
6247 reply("CSMSG_USET_INFO", uData->info);
6249 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
6253 struct svccmd_list uset_shows_list;
6255 static CHANSERV_FUNC(cmd_uset)
6257 struct svccmd *subcmd;
6261 /* Check if we need to (re-)initialize uset_shows_list. */
6262 if(!uset_shows_list.used)
6266 "NoAutoOp", "AutoInvite", "Info"
6269 if(!uset_shows_list.size)
6271 uset_shows_list.size = ArrayLength(options);
6272 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
6274 for(ii = 0; ii < ArrayLength(options); ii++)
6276 const char *name = options[ii];
6277 sprintf(buf, "%s %s", argv[0], name);
6278 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6281 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
6284 svccmd_list_append(&uset_shows_list, subcmd);
6290 /* Do this so options are presented in a consistent order. */
6291 reply("CSMSG_USER_OPTIONS");
6292 for(ii = 0; ii < uset_shows_list.used; ii++)
6293 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
6297 sprintf(buf, "%s %s", argv[0], argv[1]);
6298 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6301 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
6305 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
6308 static CHANSERV_FUNC(cmd_giveownership)
6310 struct handle_info *new_owner_hi;
6311 struct userData *new_owner;
6312 struct userData *curr_user;
6313 struct userData *invoker;
6314 struct chanData *cData = channel->channel_info;
6315 struct do_not_register *dnr;
6316 const char *confirm;
6318 unsigned short co_access;
6319 char reason[MAXLEN];
6322 curr_user = GetChannelAccess(cData, user->handle_info);
6323 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
6324 if(!curr_user || (curr_user->access != UL_OWNER))
6326 struct userData *owner = NULL;
6327 for(curr_user = channel->channel_info->users;
6329 curr_user = curr_user->next)
6331 if(curr_user->access != UL_OWNER)
6335 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
6342 else if(!force && (now < cData->ownerTransfer + chanserv_conf.giveownership_period))
6344 char delay[INTERVALLEN];
6345 intervalString(delay, cData->ownerTransfer + chanserv_conf.giveownership_period - now, user->handle_info);
6346 reply("CSMSG_TRANSFER_WAIT", delay, channel->name);
6349 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
6351 if(new_owner_hi == user->handle_info)
6353 reply("CSMSG_NO_TRANSFER_SELF");
6356 new_owner = GetChannelAccess(cData, new_owner_hi);
6361 new_owner = add_channel_user(cData, new_owner_hi, UL_OWNER - 1, 0, NULL);
6365 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
6369 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
6371 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
6374 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
6375 if(!IsHelping(user))
6376 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
6378 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi->handle);
6381 invoker = GetChannelUser(cData, user->handle_info);
6382 if(invoker->access <= UL_OWNER)
6384 confirm = make_confirmation_string(curr_user);
6385 if((argc < 3) || strcmp(argv[2], confirm))
6387 reply("CSMSG_CONFIRM_GIVEOWNERSHIP", new_owner_hi->handle, confirm);
6391 if(new_owner->access >= UL_COOWNER)
6392 co_access = new_owner->access;
6394 co_access = UL_COOWNER;
6395 new_owner->access = UL_OWNER;
6397 curr_user->access = co_access;
6398 cData->ownerTransfer = now;
6399 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
6400 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
6401 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
6405 static CHANSERV_FUNC(cmd_suspend)
6407 struct handle_info *hi;
6408 struct userData *actor, *real_actor, *target;
6409 unsigned int override = 0;
6412 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6413 actor = GetChannelUser(channel->channel_info, user->handle_info);
6414 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
6415 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6417 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6420 if(target->access >= actor->access)
6422 reply("MSG_USER_OUTRANKED", hi->handle);
6425 if(target->flags & USER_SUSPENDED)
6427 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
6432 target->present = 0;
6435 if(!real_actor || target->access >= real_actor->access)
6436 override = CMD_LOG_OVERRIDE;
6437 target->flags |= USER_SUSPENDED;
6438 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
6439 return 1 | override;
6442 static CHANSERV_FUNC(cmd_unsuspend)
6444 struct handle_info *hi;
6445 struct userData *actor, *real_actor, *target;
6446 unsigned int override = 0;
6449 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6450 actor = GetChannelUser(channel->channel_info, user->handle_info);
6451 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
6452 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6454 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6457 if(target->access >= actor->access)
6459 reply("MSG_USER_OUTRANKED", hi->handle);
6462 if(!(target->flags & USER_SUSPENDED))
6464 reply("CSMSG_NOT_SUSPENDED", hi->handle);
6467 if(!real_actor || target->access >= real_actor->access)
6468 override = CMD_LOG_OVERRIDE;
6469 target->flags &= ~USER_SUSPENDED;
6470 scan_user_presence(target, NULL);
6471 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
6472 return 1 | override;
6475 static MODCMD_FUNC(cmd_deleteme)
6477 struct handle_info *hi;
6478 struct userData *target;
6479 const char *confirm_string;
6480 unsigned short access_level;
6483 hi = user->handle_info;
6484 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6486 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6489 if(target->access == UL_OWNER)
6491 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
6494 confirm_string = make_confirmation_string(target);
6495 if((argc < 2) || strcmp(argv[1], confirm_string))
6497 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
6500 access_level = target->access;
6501 channel_name = strdup(channel->name);
6502 del_channel_user(target, 1);
6503 reply("CSMSG_DELETED_YOU", access_level, channel_name);
6508 static CHANSERV_FUNC(cmd_addvote)
6510 struct chanData *cData = channel->channel_info;
6511 struct userData *uData, *target;
6512 struct handle_info *hi;
6513 if (!cData) return 0;
6515 hi = user->handle_info;
6516 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6518 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6521 if(target->access < 300) {
6522 reply("CSMSG_NO_ACCESS");
6526 reply("CSMSG_ADDVOTE_FULL");
6530 msg = unsplit_string(argv + 1, argc - 1, NULL);
6531 cData->vote = strdup(msg);
6532 cData->vote_start=0;
6533 dict_delete(cData->vote_options);
6534 cData->vote_options = dict_new();
6535 dict_set_free_data(cData->vote_options, free_vote_options);
6536 for(uData = channel->channel_info->users; uData; uData = uData->next)
6541 reply("CSMSG_ADDVOTE_DONE");
6545 static CHANSERV_FUNC(cmd_delvote)
6547 struct chanData *cData = channel->channel_info;
6548 struct userData *target;
6549 struct handle_info *hi;
6550 if (!cData) return 0;
6551 hi = user->handle_info;
6552 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6554 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6557 if(target->access < 300) {
6558 reply("CSMSG_NO_ACCESS");
6562 reply("CSMSG_NO_VOTE");
6567 reply("CSMSG_DELVOTE_DONE");
6571 static CHANSERV_FUNC(cmd_addoption)
6573 struct chanData *cData = channel->channel_info;
6574 struct userData *target;
6575 struct handle_info *hi;
6576 if (!cData) return 0;
6578 hi = user->handle_info;
6579 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6581 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6584 if(target->access < 300) {
6585 reply("CSMSG_NO_ACCESS");
6589 reply("CSMSG_NO_VOTE");
6595 msg = unsplit_string(argv + 1, argc - 1, NULL);
6598 unsigned int lastid = 1;
6599 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6600 struct vote_option *cvOpt = iter_data(it);
6601 if(cvOpt->option_id > lastid)
6602 lastid = cvOpt->option_id;
6604 struct vote_option *vOpt;
6605 vOpt = calloc(1, sizeof(*vOpt));
6606 vOpt->name = strdup(msg);
6607 vOpt->option_id = (lastid + 1);
6609 sprintf(str,"%i",(lastid + 1));
6610 vOpt->option_str = strdup(str);
6612 dict_insert(cData->vote_options,vOpt->option_str,vOpt);
6614 reply("CSMSG_ADDOPTION_DONE",dict_size(cData->vote_options),lastid,(lastid + 1));
6618 static CHANSERV_FUNC(cmd_deloption)
6620 struct chanData *cData = channel->channel_info;
6621 struct userData *uData, *target;
6622 struct handle_info *hi;
6623 if (!cData) return 0;
6625 hi = user->handle_info;
6626 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6628 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6631 if(target->access < 300) {
6632 reply("CSMSG_NO_ACCESS");
6636 reply("CSMSG_NO_VOTE");
6639 if(cData->vote_start) {
6640 if(dict_size(cData->vote_options) < 3) {
6641 reply("CSMSG_VOTE_NEED_OPTIONS");
6646 int find_id = atoi(argv[1]);
6648 unsigned int found = 0;
6651 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6653 if (find_id == ii) {
6654 struct vote_option *vOpt = iter_data(it);
6655 found = vOpt->option_id;
6657 sprintf(str,"%i",vOpt->option_id);
6658 dict_remove(cData->vote_options, str);
6663 for(uData = channel->channel_info->users; uData; uData = uData->next) {
6664 if(uData->votefor == found) {
6669 reply("CSMSG_DELOPTION_DONE");
6672 reply("CSMSG_DELOPTION_NONE");
6677 static CHANSERV_FUNC(cmd_vote)
6679 struct chanData *cData = channel->channel_info;
6680 struct userData *target;
6681 struct handle_info *hi;
6682 unsigned int votedfor = 0;
6683 char *votedfor_str = NULL;
6685 if (!cData || !cData->vote) {
6686 reply("CSMSG_NO_VOTE");
6689 if(argc > 1 && cData->vote_start) {
6690 hi = user->handle_info;
6691 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6693 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6696 if(!check_user_level(channel, user, lvlVote, 1, 0)) {
6697 reply("CSMSG_NO_ACCESS");
6701 reply("CSMSG_VOTE_VOTED");
6704 int find_id = atoi(argv[1]);
6707 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6709 if (find_id == ii) {
6710 struct vote_option *vOpt = iter_data(it);
6713 target->votefor = vOpt->option_id;
6714 votedfor = vOpt->option_id;
6715 votedfor_str = vOpt->name;
6719 reply("CSMSG_VOTE_INVALID");
6723 if (!cData->vote_start) {
6724 reply("CSMSG_VOTE_NOT_STARTED");
6726 reply("CSMSG_VOTE_QUESTION",cData->vote);
6728 unsigned int voteid = 0;
6731 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6732 struct vote_option *vOpt = iter_data(it);
6734 reply("CSMSG_VOTE_OPTION",voteid,vOpt->name,vOpt->voted);
6736 if(argc > 1 && cData->vote_start && votedfor_str) {
6737 reply("CSMSG_VOTE_DONE",votedfor_str);
6742 static CHANSERV_FUNC(cmd_startvote)
6744 struct chanData *cData = channel->channel_info;
6745 struct userData *target;
6746 struct handle_info *hi;
6747 if (!cData) return 0;
6748 hi = user->handle_info;
6749 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6751 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6754 if(target->access < 300) {
6755 reply("CSMSG_NO_ACCESS");
6759 reply("CSMSG_NO_VOTE");
6762 if(cData->vote_start) {
6763 reply("CSMSG_STARTVOTE_RUNNING");
6766 if(dict_size(cData->vote_options) < 2) {
6767 reply("CSMSG_VOTE_NEED_OPTIONS");
6770 cData->vote_start = 1;
6771 char response[MAXLEN];
6772 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_TOP"), user->nick);
6773 irc_privmsg(cmd->parent->bot, channel->name, response);
6774 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_QUESTION"), cData->vote);
6775 irc_privmsg(cmd->parent->bot, channel->name, response);
6776 unsigned int voteid = 0;
6778 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6779 struct vote_option *vOpt = iter_data(it);
6781 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_OPTION"), voteid, vOpt->name);
6782 irc_privmsg(cmd->parent->bot, channel->name, response);
6784 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_ACCESS"), cData->lvlOpts[lvlVote]); //Todo
6785 irc_privmsg(cmd->parent->bot, channel->name, response);
6786 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_HOWTO")); //Todo
6787 irc_privmsg(cmd->parent->bot, channel->name, response);
6791 static CHANSERV_FUNC(cmd_endvote)
6793 struct chanData *cData = channel->channel_info;
6794 struct userData *target;
6795 struct handle_info *hi;
6796 if (!cData) return 0;
6797 hi = user->handle_info;
6798 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6800 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6803 if(target->access < 300) {
6804 reply("CSMSG_NO_ACCESS");
6808 reply("CSMSG_NO_VOTE");
6811 if(!cData->vote_start) {
6812 reply("CSMSG_ENDVOTE_STOPPED");
6815 cData->vote_start = 0;
6816 reply("CSMSG_ENDVOTE_DONE");
6820 static CHANSERV_FUNC(cmd_voteresults)
6822 struct chanData *cData = channel->channel_info;
6823 struct userData *target;
6824 struct handle_info *hi;
6825 if (!cData) return 0;
6827 reply("CSMSG_NO_VOTE");
6830 if (argc > 1 && !irccasecmp(argv[1], "*")) {
6831 hi = user->handle_info;
6832 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6834 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6837 if(target->access < 300) {
6838 reply("CSMSG_NO_ACCESS");
6841 char response[MAXLEN];
6842 sprintf(response, user_find_message(user, "CSMSG_VOTERES_QUESTION"), cData->vote);
6843 irc_privmsg(cmd->parent->bot, channel->name, response);
6844 unsigned int voteid = 0;
6846 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6847 struct vote_option *vOpt = iter_data(it);
6849 sprintf(response, user_find_message(user, "CSMSG_VOTERES_OPTION"), voteid, vOpt->name, vOpt->voted);
6850 irc_privmsg(cmd->parent->bot, channel->name, response);
6853 reply("CSMSG_VOTE_QUESTION",cData->vote);
6854 unsigned int voteid = 0;
6856 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6857 struct vote_option *vOpt = iter_data(it);
6859 reply("CSMSG_VOTE_OPTION",voteid,vOpt->name,vOpt->voted);
6866 chanserv_refresh_topics(UNUSED_ARG(void *data))
6868 unsigned int refresh_num = (now - self->link_time) / chanserv_conf.refresh_period;
6869 struct chanData *cData;
6872 for(cData = channelList; cData; cData = cData->next)
6874 if(IsSuspended(cData))
6876 opt = cData->chOpts[chTopicRefresh];
6879 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
6882 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
6883 cData->last_refresh = refresh_num;
6885 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
6888 static CHANSERV_FUNC(cmd_unf)
6892 char response[MAXLEN];
6893 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
6894 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6895 irc_privmsg(cmd->parent->bot, channel->name, response);
6898 reply("CSMSG_UNF_RESPONSE");
6902 static CHANSERV_FUNC(cmd_ping)
6906 char response[MAXLEN];
6907 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
6908 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6909 irc_privmsg(cmd->parent->bot, channel->name, response);
6912 reply("CSMSG_PING_RESPONSE");
6916 static CHANSERV_FUNC(cmd_wut)
6920 char response[MAXLEN];
6921 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
6922 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6923 irc_privmsg(cmd->parent->bot, channel->name, response);
6926 reply("CSMSG_WUT_RESPONSE");
6930 static CHANSERV_FUNC(cmd_8ball)
6932 unsigned int i, j, accum;
6937 for(i=1; i<argc; i++)
6938 for(j=0; argv[i][j]; j++)
6939 accum = (accum << 5) - accum + toupper(argv[i][j]);
6940 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
6943 char response[MAXLEN];
6944 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
6945 irc_privmsg(cmd->parent->bot, channel->name, response);
6948 send_message_type(4, user, cmd->parent->bot, "%s", resp);
6952 static CHANSERV_FUNC(cmd_d)
6954 unsigned long sides, count, modifier, ii, total;
6955 char response[MAXLEN], *sep;
6959 if((count = strtoul(argv[1], &sep, 10)) < 1)
6969 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
6970 && (sides = strtoul(sep+1, &sep, 10)) > 1)
6974 else if((sep[0] == '-') && isdigit(sep[1]))
6975 modifier = strtoul(sep, NULL, 10);
6976 else if((sep[0] == '+') && isdigit(sep[1]))
6977 modifier = strtoul(sep+1, NULL, 10);
6984 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
6989 reply("CSMSG_BAD_DICE_COUNT", count, 10);
6992 for(total = ii = 0; ii < count; ++ii)
6993 total += (rand() % sides) + 1;
6996 if((count > 1) || modifier)
6998 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
6999 sprintf(response, fmt, total, count, sides, modifier);
7003 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
7004 sprintf(response, fmt, total, sides);
7007 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
7009 send_message_type(4, user, cmd->parent->bot, "%s", response);
7013 static CHANSERV_FUNC(cmd_huggle)
7015 /* CTCP must be via PRIVMSG, never notice */
7017 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
7019 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
7024 chanserv_adjust_limit(void *data)
7026 struct mod_chanmode change;
7027 struct chanData *cData = data;
7028 struct chanNode *channel = cData->channel;
7031 if(IsSuspended(cData))
7034 cData->limitAdjusted = now;
7035 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
7036 if(cData->modes.modes_set & MODE_LIMIT)
7038 if(limit > cData->modes.new_limit)
7039 limit = cData->modes.new_limit;
7040 else if(limit == cData->modes.new_limit)
7044 mod_chanmode_init(&change);
7045 change.modes_set = MODE_LIMIT;
7046 change.new_limit = limit;
7047 mod_chanmode_announce(chanserv, channel, &change);
7051 handle_new_channel(struct chanNode *channel)
7053 struct chanData *cData;
7055 if(!(cData = channel->channel_info))
7058 if(cData->modes.modes_set || cData->modes.modes_clear)
7059 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
7061 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
7062 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
7065 void handle_new_channel_created(char *chan, struct userNode *user) {
7066 if(user->handle_info && chanserv_conf.new_channel_authed) {
7067 send_target_message(5, chan, chanserv, "%s", chanserv_conf.new_channel_authed);
7068 } else if(!user->handle_info && chanserv_conf.new_channel_unauthed) {
7069 send_target_message(5, chan, chanserv, "%s", chanserv_conf.new_channel_unauthed);
7071 if(chanserv_conf.new_channel_msg)
7072 send_target_message(5, chan, chanserv, "%s", chanserv_conf.new_channel_msg);
7075 /* Welcome to my worst nightmare. Warning: Read (or modify)
7076 the code below at your own risk. */
7078 handle_join(struct modeNode *mNode)
7080 struct mod_chanmode change;
7081 struct userNode *user = mNode->user;
7082 struct chanNode *channel = mNode->channel;
7083 struct chanData *cData;
7084 struct userData *uData = NULL;
7085 struct banData *bData;
7086 struct handle_info *handle;
7087 unsigned int modes = 0, info = 0;
7091 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
7094 cData = channel->channel_info;
7095 if(channel->members.used > cData->max) {
7096 cData->max = channel->members.used;
7097 cData->max_time = now;
7100 for(i = 0; i < channel->invited.used; i++)
7102 if(channel->invited.list[i] == user) {
7103 userList_remove(&channel->invited, user);
7107 /* Check for bans. If they're joining through a ban, one of two
7109 * 1: Join during a netburst, by riding the break. Kick them
7110 * unless they have ops or voice in the channel.
7111 * 2: They're allowed to join through the ban (an invite in
7112 * ircu2.10, or a +e on Hybrid, or something).
7113 * If they're not joining through a ban, and the banlist is not
7114 * full, see if they're on the banlist for the channel. If so,
7117 if(user->uplink->burst && !mNode->modes)
7120 for(ii = 0; ii < channel->banlist.used; ii++)
7122 if(user_matches_glob(user, channel->banlist.list[ii]->ban, MATCH_USENICK))
7124 /* Riding a netburst. Naughty. */
7125 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
7131 mod_chanmode_init(&change);
7133 if(channel->banlist.used < MAXBANS)
7135 /* Not joining through a ban. */
7136 for(bData = cData->bans;
7137 bData && !user_matches_glob(user, bData->mask, MATCH_USENICK);
7138 bData = bData->next);
7142 char kick_reason[MAXLEN];
7143 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
7145 bData->triggered = now;
7146 if(bData != cData->bans)
7148 /* Shuffle the ban to the head of the list. */
7150 bData->next->prev = bData->prev;
7152 bData->prev->next = bData->next;
7155 bData->next = cData->bans;
7158 cData->bans->prev = bData;
7159 cData->bans = bData;
7162 change.args[0].mode = MODE_BAN;
7163 change.args[0].u.hostmask = bData->mask;
7164 mod_chanmode_announce(chanserv, channel, &change);
7165 KickChannelUser(user, channel, chanserv, kick_reason);
7170 /* ChanServ will not modify the limits in join-flooded channels,
7171 or when there are enough slots left below the limit. */
7172 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
7173 && !channel->join_flooded
7174 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
7176 /* The user count has begun "bumping" into the channel limit,
7177 so set a timer to raise the limit a bit. Any previous
7178 timers are removed so three incoming users within the delay
7179 results in one limit change, not three. */
7181 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7182 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7185 if(channel->join_flooded)
7187 /* don't automatically give ops or voice during a join flood */
7189 else if(cData->lvlOpts[lvlGiveOps] == 0)
7190 modes |= MODE_CHANOP;
7191 else if(cData->lvlOpts[lvlGiveVoice] == 0)
7192 modes |= MODE_VOICE;
7194 greeting = cData->greeting;
7195 if(user->handle_info)
7197 handle = user->handle_info;
7199 if(IsHelper(user) && !IsHelping(user))
7202 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7204 if(channel == chanserv_conf.support_channels.list[ii])
7206 HANDLE_SET_FLAG(user->handle_info, HELPING);
7212 uData = GetTrueChannelAccess(cData, handle);
7213 if(uData && !IsUserSuspended(uData))
7215 /* Ops and above were handled by the above case. */
7216 if(IsUserAutoOp(uData))
7218 if(uData->access >= cData->lvlOpts[lvlGiveOps])
7219 modes |= MODE_CHANOP;
7220 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
7221 modes |= MODE_VOICE;
7223 if(uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
7224 cData->visited = now;
7225 if(cData->user_greeting)
7226 greeting = cData->user_greeting;
7228 && (uData->access >= cData->lvlOpts[lvlUserInfo])
7229 && ((now - uData->seen) >= chanserv_conf.info_delay)
7237 /* If user joining normally (not during burst), apply op or voice,
7238 * and send greeting/userinfo as appropriate.
7240 if(!user->uplink->burst)
7244 if(modes & MODE_CHANOP)
7245 modes &= ~MODE_VOICE;
7246 change.args[0].mode = modes;
7247 change.args[0].u.member = mNode;
7248 mod_chanmode_announce(chanserv, channel, &change);
7251 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
7252 if(uData && info && (modes || !(channel->modes & MODE_DELAYJOINS)))
7253 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
7259 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
7261 struct mod_chanmode change;
7262 struct userData *channel;
7263 unsigned int ii, jj;
7265 if(!user->handle_info)
7268 mod_chanmode_init(&change);
7270 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
7272 struct chanNode *cn;
7273 struct modeNode *mn;
7274 if(IsUserSuspended(channel)
7275 || IsSuspended(channel->channel)
7276 || !(cn = channel->channel->channel))
7279 mn = GetUserMode(cn, user);
7282 if(!IsUserSuspended(channel)
7283 && IsUserAutoInvite(channel)
7284 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
7286 && !user->uplink->burst)
7287 irc_invite(chanserv, user, cn);
7291 if(channel->access >= UL_PRESENT && !HANDLE_FLAGGED(channel->handle, BOT))
7292 channel->channel->visited = now;
7294 if(IsUserAutoOp(channel))
7296 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
7297 change.args[0].mode = MODE_CHANOP;
7298 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
7299 change.args[0].mode = MODE_VOICE;
7301 change.args[0].mode = 0;
7302 change.args[0].u.member = mn;
7303 if(change.args[0].mode)
7304 mod_chanmode_announce(chanserv, cn, &change);
7307 channel->seen = now;
7308 channel->present = 1;
7311 for(ii = 0; ii < user->channels.used; ++ii)
7313 struct chanNode *chan = user->channels.list[ii]->channel;
7314 struct banData *ban;
7316 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
7317 || !chan->channel_info
7318 || IsSuspended(chan->channel_info))
7320 for(jj = 0; jj < chan->banlist.used; ++jj)
7321 if(user_matches_glob(user, chan->banlist.list[jj]->ban, MATCH_USENICK))
7323 if(jj < chan->banlist.used)
7325 for(ban = chan->channel_info->bans; ban; ban = ban->next)
7327 char kick_reason[MAXLEN];
7328 if(!user_matches_glob(user, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
7330 change.args[0].mode = MODE_BAN;
7331 change.args[0].u.hostmask = ban->mask;
7332 mod_chanmode_announce(chanserv, chan, &change);
7333 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
7334 KickChannelUser(user, chan, chanserv, kick_reason);
7335 ban->triggered = now;
7340 if(IsSupportHelper(user))
7342 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7344 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
7346 HANDLE_SET_FLAG(user->handle_info, HELPING);
7354 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
7356 struct chanData *cData;
7357 struct userData *uData;
7359 cData = mn->channel->channel_info;
7360 if(!cData || IsSuspended(cData) || IsLocal(mn->user))
7363 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
7365 /* Allow for a bit of padding so that the limit doesn't
7366 track the user count exactly, which could get annoying. */
7367 if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
7369 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7370 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7374 if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
7376 scan_user_presence(uData, mn->user);
7378 if (uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
7379 cData->visited = now;
7382 if(IsHelping(mn->user) && IsSupportHelper(mn->user))
7385 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii) {
7386 struct chanNode *channel;
7387 struct userNode *exclude;
7388 /* When looking at the channel that is being /part'ed, we
7389 * have to skip over the client that is leaving. For
7390 * other channels, we must not do that.
7392 channel = chanserv_conf.support_channels.list[ii];
7393 exclude = (channel == mn->channel) ? mn->user : NULL;
7394 if(find_handle_in_channel(channel, mn->user->handle_info, exclude))
7397 if(ii == chanserv_conf.support_channels.used)
7398 HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
7403 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
7405 struct userData *uData;
7407 if(!channel->channel_info || !kicker || IsService(kicker)
7408 || (kicker == victim) || IsSuspended(channel->channel_info)
7409 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
7412 if(protect_user(victim, kicker, channel->channel_info))
7414 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED_2");
7415 KickChannelUser(kicker, channel, chanserv, reason);
7418 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
7423 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
7425 struct chanData *cData;
7427 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
7430 cData = channel->channel_info;
7431 if(bad_topic(channel, user, channel->topic))
7433 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
7434 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
7435 SetChannelTopic(channel, chanserv, old_topic, 1);
7436 else if(cData->topic)
7437 SetChannelTopic(channel, chanserv, cData->topic, 1);
7440 /* With topicsnarf, grab the topic and save it as the default topic. */
7441 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
7444 cData->topic = strdup(channel->topic);
7450 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
7452 struct mod_chanmode *bounce = NULL;
7453 unsigned int bnc, ii;
7456 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
7459 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
7460 && mode_lock_violated(&channel->channel_info->modes, change))
7462 char correct[MAXLEN];
7463 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
7464 mod_chanmode_format(&channel->channel_info->modes, correct);
7465 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
7467 for(ii = bnc = 0; ii < change->argc; ++ii)
7469 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
7471 const struct userNode *victim = change->args[ii].u.member->user;
7472 if(!protect_user(victim, user, channel->channel_info))
7475 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7478 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
7479 bounce->args[bnc].u.member = GetUserMode(channel, user);
7480 if(bounce->args[bnc].u.member)
7484 bounce->args[bnc].mode = MODE_CHANOP;
7485 bounce->args[bnc].u.member = change->args[ii].u.member;
7487 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
7489 else if(change->args[ii].mode & MODE_CHANOP)
7491 const struct userNode *victim = change->args[ii].u.member->user;
7492 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
7495 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7496 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
7497 bounce->args[bnc].u.member = change->args[ii].u.member;
7500 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
7502 const char *ban = change->args[ii].u.hostmask;
7503 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
7506 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7507 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
7508 bounce->args[bnc].u.hostmask = strdup(ban);
7510 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
7515 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
7516 mod_chanmode_announce(chanserv, channel, bounce);
7517 for(ii = 0; ii < change->argc; ++ii)
7518 if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
7519 free((char*)bounce->args[ii].u.hostmask);
7520 mod_chanmode_free(bounce);
7525 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
7527 struct chanNode *channel;
7528 struct banData *bData;
7529 struct mod_chanmode change;
7530 unsigned int ii, jj;
7531 char kick_reason[MAXLEN];
7533 mod_chanmode_init(&change);
7535 change.args[0].mode = MODE_BAN;
7536 for(ii = 0; ii < user->channels.used; ++ii)
7538 channel = user->channels.list[ii]->channel;
7539 /* Need not check for bans if they're opped or voiced. */
7540 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
7542 /* Need not check for bans unless channel registration is active. */
7543 if(!channel->channel_info || IsSuspended(channel->channel_info))
7545 /* Look for a matching ban already on the channel. */
7546 for(jj = 0; jj < channel->banlist.used; ++jj)
7547 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
7549 /* Need not act if we found one. */
7550 if(jj < channel->banlist.used)
7552 /* Look for a matching ban in this channel. */
7553 for(bData = channel->channel_info->bans; bData; bData = bData->next)
7555 if(!user_matches_glob(user, bData->mask, MATCH_USENICK | MATCH_VISIBLE))
7557 change.args[0].u.hostmask = bData->mask;
7558 mod_chanmode_announce(chanserv, channel, &change);
7559 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
7560 KickChannelUser(user, channel, chanserv, kick_reason);
7561 bData->triggered = now;
7562 break; /* we don't need to check any more bans in the channel */
7567 static void handle_rename(struct handle_info *handle, const char *old_handle)
7569 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
7573 dict_remove2(handle_dnrs, old_handle, 1);
7574 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
7575 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
7580 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
7582 struct userNode *h_user;
7584 if(handle->channels)
7586 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
7587 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
7589 while(handle->channels)
7590 del_channel_user(handle->channels, 1);
7595 handle_server_link(UNUSED_ARG(struct server *server))
7597 struct chanData *cData;
7599 for(cData = channelList; cData; cData = cData->next)
7601 if(!IsSuspended(cData))
7602 cData->may_opchan = 1;
7603 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
7604 && !cData->channel->join_flooded
7605 && ((cData->channel->limit - cData->channel->members.used)
7606 < chanserv_conf.adjust_threshold))
7608 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7609 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7615 chanserv_conf_read(void)
7619 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
7620 struct mod_chanmode *change;
7621 struct string_list *strlist;
7622 struct chanNode *chan;
7625 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
7627 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
7630 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7631 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7632 chanserv_conf.support_channels.used = 0;
7633 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
7635 for(ii = 0; ii < strlist->used; ++ii)
7637 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7640 chan = AddChannel(strlist->list[ii], now, str2, NULL);
7642 channelList_append(&chanserv_conf.support_channels, chan);
7645 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
7648 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7651 chan = AddChannel(str, now, str2, NULL);
7653 channelList_append(&chanserv_conf.support_channels, chan);
7655 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
7656 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
7657 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
7658 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
7659 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
7660 chanserv_conf.greeting_length = str ? atoi(str) : 200;
7661 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
7662 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
7663 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
7664 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
7665 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
7666 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
7667 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
7668 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
7669 str = database_get_data(conf_node, KEY_DNR_EXPIRE_FREQ, RECDB_QSTRING);
7670 chanserv_conf.dnr_expire_frequency = str ? ParseInterval(str) : 3600;
7671 str = database_get_data(conf_node, KEY_INVITED_INTERVAL, RECDB_QSTRING);
7672 chanserv_conf.invited_timeout = str ? ParseInterval(str) : 600*2;
7673 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
7674 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
7675 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
7676 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
7677 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
7678 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
7679 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
7680 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
7681 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
7683 NickChange(chanserv, str, 0);
7684 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
7685 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
7686 str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
7687 chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
7688 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
7689 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
7690 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
7691 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
7692 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
7693 chanserv_conf.max_owned = str ? atoi(str) : 5;
7694 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
7695 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
7696 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
7697 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
7698 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
7699 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
7700 str = database_get_data(conf_node, KEY_NEW_CHANNEL_AUTHED, RECDB_QSTRING);
7701 chanserv_conf.new_channel_authed = str ? str : NULL;
7702 str = database_get_data(conf_node, KEY_NEW_CHANNEL_UNAUTHED, RECDB_QSTRING);
7703 chanserv_conf.new_channel_unauthed = str ? str : NULL;
7704 str = database_get_data(conf_node, KEY_NEW_CHANNEL_MSG, RECDB_QSTRING);
7705 chanserv_conf.new_channel_msg = str ? str : NULL;
7706 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
7709 safestrncpy(mode_line, str, sizeof(mode_line));
7710 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
7711 if((change = mod_chanmode_parse(NULL, NULL, modes, ii, MCP_KEY_FREE|MCP_NO_APASS, 0))
7712 && (change->argc < 2))
7714 chanserv_conf.default_modes = *change;
7715 mod_chanmode_free(change);
7717 free_string_list(chanserv_conf.set_shows);
7718 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
7720 strlist = string_list_copy(strlist);
7723 static const char *list[] = {
7724 /* free form text */
7725 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
7726 /* options based on user level */
7727 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
7728 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
7729 /* multiple choice options */
7730 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
7731 /* binary options */
7732 "DynLimit", "NoDelete", "expire", "Vote",
7736 strlist = alloc_string_list(ArrayLength(list)-1);
7737 for(ii=0; list[ii]; ii++)
7738 string_list_append(strlist, strdup(list[ii]));
7740 chanserv_conf.set_shows = strlist;
7741 /* We don't look things up now, in case the list refers to options
7742 * defined by modules initialized after this point. Just mark the
7743 * function list as invalid, so it will be initialized.
7745 set_shows_list.used = 0;
7746 free_string_list(chanserv_conf.eightball);
7747 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
7750 strlist = string_list_copy(strlist);
7754 strlist = alloc_string_list(4);
7755 string_list_append(strlist, strdup("Yes."));
7756 string_list_append(strlist, strdup("No."));
7757 string_list_append(strlist, strdup("Maybe so."));
7759 chanserv_conf.eightball = strlist;
7760 free_string_list(chanserv_conf.old_ban_names);
7761 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
7763 strlist = string_list_copy(strlist);
7765 strlist = alloc_string_list(2);
7766 chanserv_conf.old_ban_names = strlist;
7767 str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
7768 off_channel = str ? atoi(str) : 0;
7772 chanserv_note_type_read(const char *key, struct record_data *rd)
7775 struct note_type *ntype;
7778 if(!(obj = GET_RECORD_OBJECT(rd)))
7780 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
7783 if(!(ntype = chanserv_create_note_type(key)))
7785 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
7789 /* Figure out set access */
7790 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
7792 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7793 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
7795 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
7797 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
7798 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
7800 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
7802 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
7806 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
7807 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7808 ntype->set_access.min_opserv = 0;
7811 /* Figure out visibility */
7812 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
7813 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7814 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
7815 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7816 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
7817 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
7818 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
7819 ntype->visible_type = NOTE_VIS_ALL;
7821 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7823 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
7824 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
7828 vote_option_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7830 struct vote_option *vOpt;
7833 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7835 log_module(CS_LOG, LOG_ERROR, "Invalid vote option in %s.", chan->channel->name);
7839 vOpt = calloc(1, sizeof(*vOpt));
7840 vOpt->name = strdup(database_get_data(rd->d.object, KEY_VOTE_OPTION_NAME, RECDB_QSTRING));
7841 str = database_get_data(rd->d.object, KEY_VOTE_OPTION_VOTED, RECDB_QSTRING);
7842 vOpt->voted = str ? atoi(str) : 0;
7843 vOpt->option_id = str ? atoi(key) : 0;
7844 vOpt->option_str = strdup(key);
7845 dict_insert(chan->vote_options,vOpt->option_str,vOpt);
7849 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7851 struct handle_info *handle;
7852 struct userData *uData;
7853 char *seen, *inf, *flags, *voted, *votefor;
7854 unsigned long last_seen;
7855 unsigned short access_level;
7857 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7859 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
7863 access_level = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
7864 if(access_level > UL_OWNER)
7866 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
7870 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
7871 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
7872 last_seen = seen ? strtoul(seen, NULL, 0) : now;
7873 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
7874 voted = database_get_data(rd->d.object, KEY_VOTE_VOTED, RECDB_QSTRING);
7875 votefor = database_get_data(rd->d.object, KEY_VOTE_VOTEDFOR, RECDB_QSTRING);
7876 handle = get_handle_info(key);
7879 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
7883 uData = add_channel_user(chan, handle, access_level, last_seen, inf);
7884 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
7886 uData->voted = voted ? strtoul(voted, NULL, 0) : 0;
7887 uData->votefor = votefor ? strtoul(votefor, NULL, 0) : 0;
7895 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7897 struct banData *bData;
7898 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
7899 unsigned long set_time, triggered_time, expires_time;
7901 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7903 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
7907 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
7908 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
7909 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
7910 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
7911 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
7912 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
7913 if (!reason || !owner)
7916 set_time = set ? strtoul(set, NULL, 0) : now;
7917 triggered_time = triggered ? strtoul(triggered, NULL, 0) : 0;
7919 expires_time = strtoul(s_expires, NULL, 0);
7921 expires_time = set_time + atoi(s_duration);
7925 if(!reason || (expires_time && (expires_time < now)))
7928 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
7931 static struct suspended *
7932 chanserv_read_suspended(dict_t obj)
7934 struct suspended *suspended = calloc(1, sizeof(*suspended));
7938 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
7939 suspended->expires = str ? strtoul(str, NULL, 0) : 0;
7940 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
7941 suspended->revoked = str ? strtoul(str, NULL, 0) : 0;
7942 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
7943 suspended->issued = str ? strtoul(str, NULL, 0) : 0;
7944 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
7945 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
7946 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
7947 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
7952 chanserv_channel_read(const char *key, struct record_data *hir)
7954 struct suspended *suspended;
7955 struct mod_chanmode *modes;
7956 struct chanNode *cNode;
7957 struct chanData *cData;
7958 struct dict *channel, *obj;
7959 char *str, *argv[10];
7963 channel = hir->d.object;
7965 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
7968 cNode = AddChannel(key, now, NULL, NULL);
7971 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
7974 cData = register_channel(cNode, str);
7977 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
7981 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
7983 enum levelOption lvlOpt;
7984 enum charOption chOpt;
7986 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
7987 cData->flags = atoi(str);
7989 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7991 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
7993 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
7994 else if(levelOptions[lvlOpt].old_flag)
7996 if(cData->flags & levelOptions[lvlOpt].old_flag)
7997 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
7999 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
8003 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8005 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
8007 cData->chOpts[chOpt] = str[0];
8010 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
8012 enum levelOption lvlOpt;
8013 enum charOption chOpt;
8016 cData->flags = base64toint(str, 5);
8017 count = strlen(str += 5);
8018 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8021 if(levelOptions[lvlOpt].old_flag)
8023 if(cData->flags & levelOptions[lvlOpt].old_flag)
8024 lvl = levelOptions[lvlOpt].flag_value;
8026 lvl = levelOptions[lvlOpt].default_value;
8028 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
8030 case 'c': lvl = UL_COOWNER; break;
8031 case 'm': lvl = UL_MASTER; break;
8032 case 'n': lvl = UL_OWNER+1; break;
8033 case 'o': lvl = UL_OP; break;
8034 case 'p': lvl = UL_PEON; break;
8035 case 'w': lvl = UL_OWNER; break;
8036 default: lvl = 0; break;
8038 cData->lvlOpts[lvlOpt] = lvl;
8040 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8041 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
8044 if((str = database_get_data(hir->d.object, KEY_EXPIRE, RECDB_QSTRING)))
8046 cData->expiry = atoi(str);
8047 if(cData->expiry > 0) {
8048 if(cData->expiry > now) {
8049 timeq_add(cData->expiry, chanserv_expire_channel, cData);
8051 timeq_add(1, chanserv_expire_channel, cData);
8058 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
8060 suspended = chanserv_read_suspended(obj);
8061 cData->suspended = suspended;
8062 suspended->cData = cData;
8063 /* We could use suspended->expires and suspended->revoked to
8064 * set the CHANNEL_SUSPENDED flag, but we don't. */
8066 else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
8068 suspended = calloc(1, sizeof(*suspended));
8069 suspended->issued = 0;
8070 suspended->revoked = 0;
8071 suspended->suspender = strdup(str);
8072 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
8073 suspended->expires = str ? atoi(str) : 0;
8074 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
8075 suspended->reason = strdup(str ? str : "No reason");
8076 suspended->previous = NULL;
8077 cData->suspended = suspended;
8078 suspended->cData = cData;
8082 cData->flags &= ~CHANNEL_SUSPENDED;
8083 suspended = NULL; /* to squelch a warning */
8086 if(IsSuspended(cData)) {
8087 if(suspended->expires > now)
8088 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
8089 else if(suspended->expires)
8090 cData->flags &= ~CHANNEL_SUSPENDED;
8093 if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
8094 struct mod_chanmode change;
8095 mod_chanmode_init(&change);
8097 change.args[0].mode = MODE_CHANOP;
8098 change.args[0].u.member = AddChannelUser(chanserv, cNode);
8099 mod_chanmode_announce(chanserv, cNode, &change);
8102 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
8103 cData->registered = str ? strtoul(str, NULL, 0) : now;
8104 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
8105 cData->visited = str ? strtoul(str, NULL, 0) : now;
8106 str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
8107 cData->ownerTransfer = str ? strtoul(str, NULL, 0) : 0;
8108 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
8109 cData->max = str ? atoi(str) : 0;
8110 str = database_get_data(channel, KEY_MAX_TIME, RECDB_QSTRING);
8111 cData->max_time = str ? atoi(str) : 0;
8112 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
8113 cData->greeting = str ? strdup(str) : NULL;
8114 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
8115 cData->user_greeting = str ? strdup(str) : NULL;
8116 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
8117 cData->topic_mask = str ? strdup(str) : NULL;
8118 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
8119 cData->topic = str ? strdup(str) : NULL;
8121 str = database_get_data(channel, KEY_VOTE, RECDB_QSTRING);
8123 cData->vote = str ? strdup(str) : NULL;
8124 dict_delete(cData->vote_options);
8125 cData->vote_options = dict_new();
8126 dict_set_free_data(cData->vote_options, free_vote_options);
8127 str = database_get_data(channel, KEY_VOTE_START, RECDB_QSTRING);
8128 cData->vote_start = str ? atoi(str) : 0;
8129 obj = database_get_data(channel, KEY_VOTE_OPTIONS, RECDB_OBJECT);
8130 for(it = dict_first(obj); it; it = iter_next(it)) {
8131 vote_option_read_helper(iter_key(it), iter_data(it), cData);
8135 if(!IsSuspended(cData)
8136 && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
8137 && (argc = split_line(str, 0, ArrayLength(argv), argv))
8138 && (modes = mod_chanmode_parse(cNode, NULL, argv, argc, MCP_KEY_FREE|MCP_NO_APASS, 0))) {
8139 cData->modes = *modes;
8141 cData->modes.modes_set |= MODE_REGISTERED;
8142 if(cData->modes.argc > 1)
8143 cData->modes.argc = 1;
8144 mod_chanmode_announce(chanserv, cNode, &cData->modes);
8145 mod_chanmode_free(modes);
8148 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
8149 for(it = dict_first(obj); it; it = iter_next(it))
8150 user_read_helper(iter_key(it), iter_data(it), cData);
8152 if(!cData->users && !IsProtected(cData))
8154 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
8155 unregister_channel(cData, "has empty user list.");
8159 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
8160 for(it = dict_first(obj); it; it = iter_next(it))
8161 ban_read_helper(iter_key(it), iter_data(it), cData);
8163 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
8164 for(it = dict_first(obj); it; it = iter_next(it))
8166 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
8167 struct record_data *rd = iter_data(it);
8168 const char *note, *setter;
8170 if(rd->type != RECDB_OBJECT)
8172 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
8176 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
8178 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
8180 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
8184 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
8185 if(!setter) setter = "<unknown>";
8186 chanserv_add_channel_note(cData, ntype, setter, note);
8194 chanserv_dnr_read(const char *key, struct record_data *hir)
8196 const char *setter, *reason, *str;
8197 struct do_not_register *dnr;
8198 unsigned long expiry;
8200 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
8203 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
8206 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
8209 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
8212 str = database_get_data(hir->d.object, KEY_EXPIRES, RECDB_QSTRING);
8213 expiry = str ? strtoul(str, NULL, 0) : 0;
8214 if(expiry && expiry <= now)
8216 dnr = chanserv_add_dnr(key, setter, expiry, reason);
8219 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
8221 dnr->set = atoi(str);
8227 chanserv_saxdb_read(struct dict *database)
8229 struct dict *section;
8232 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
8233 for(it = dict_first(section); it; it = iter_next(it))
8234 chanserv_note_type_read(iter_key(it), iter_data(it));
8236 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
8237 for(it = dict_first(section); it; it = iter_next(it))
8238 chanserv_channel_read(iter_key(it), iter_data(it));
8240 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
8241 for(it = dict_first(section); it; it = iter_next(it))
8242 chanserv_dnr_read(iter_key(it), iter_data(it));
8248 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
8250 int high_present = 0;
8251 saxdb_start_record(ctx, KEY_USERS, 1);
8252 for(; uData; uData = uData->next)
8254 if((uData->access >= UL_PRESENT) && uData->present && !HANDLE_FLAGGED(uData->handle, BOT))
8256 saxdb_start_record(ctx, uData->handle->handle, 0);
8257 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
8258 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
8260 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
8261 if(uData->channel->vote && uData->voted)
8262 saxdb_write_int(ctx, KEY_VOTE_VOTED, uData->voted);
8263 if(uData->channel->vote && uData->votefor)
8264 saxdb_write_int(ctx, KEY_VOTE_VOTEDFOR, uData->votefor);
8266 saxdb_write_string(ctx, KEY_INFO, uData->info);
8267 saxdb_end_record(ctx);
8269 saxdb_end_record(ctx);
8270 return high_present;
8274 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
8278 saxdb_start_record(ctx, KEY_BANS, 1);
8279 for(; bData; bData = bData->next)
8281 saxdb_start_record(ctx, bData->mask, 0);
8282 saxdb_write_int(ctx, KEY_SET, bData->set);
8283 if(bData->triggered)
8284 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
8286 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
8288 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
8290 saxdb_write_string(ctx, KEY_REASON, bData->reason);
8291 saxdb_end_record(ctx);
8293 saxdb_end_record(ctx);
8297 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
8299 saxdb_start_record(ctx, name, 0);
8300 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
8301 saxdb_write_string(ctx, KEY_REASON, susp->reason);
8303 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
8305 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
8307 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
8309 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
8310 saxdb_end_record(ctx);
8314 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
8318 enum levelOption lvlOpt;
8319 enum charOption chOpt;
8322 saxdb_start_record(ctx, channel->channel->name, 1);
8324 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
8325 saxdb_write_int(ctx, KEY_MAX, channel->max);
8326 saxdb_write_int(ctx, KEY_MAX_TIME, channel->max_time);
8328 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
8329 if(channel->registrar)
8330 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
8331 if(channel->greeting)
8332 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
8333 if(channel->user_greeting)
8334 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
8335 if(channel->topic_mask)
8336 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
8337 if(channel->suspended)
8338 chanserv_write_suspended(ctx, "suspended", channel->suspended);
8340 saxdb_write_int(ctx, KEY_EXPIRE, channel->expiry);
8343 saxdb_write_string(ctx, KEY_VOTE, channel->vote);
8344 if(channel->vote_start)
8345 saxdb_write_int(ctx, KEY_VOTE_START, channel->vote_start);
8346 if (dict_size(channel->vote_options)) {
8347 saxdb_start_record(ctx, KEY_VOTE_OPTIONS, 1);
8348 for (it = dict_first(channel->vote_options); it; it = iter_next(it)) {
8349 struct vote_option *vOpt = iter_data(it);
8351 sprintf(str,"%i",vOpt->option_id);
8352 saxdb_start_record(ctx, str, 0);
8354 saxdb_write_int(ctx, KEY_VOTE_OPTION_VOTED, vOpt->voted);
8356 saxdb_write_string(ctx, KEY_VOTE_OPTION_NAME, vOpt->name);
8357 saxdb_end_record(ctx);
8359 saxdb_end_record(ctx);
8363 saxdb_start_record(ctx, KEY_OPTIONS, 0);
8364 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
8365 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8366 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
8367 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8369 buf[0] = channel->chOpts[chOpt];
8371 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
8373 saxdb_end_record(ctx);
8375 if(channel->modes.modes_set || channel->modes.modes_clear)
8377 mod_chanmode_format(&channel->modes, buf);
8378 saxdb_write_string(ctx, KEY_MODES, buf);
8381 high_present = chanserv_write_users(ctx, channel->users);
8382 chanserv_write_bans(ctx, channel->bans);
8384 if(dict_size(channel->notes))
8388 saxdb_start_record(ctx, KEY_NOTES, 1);
8389 for(it = dict_first(channel->notes); it; it = iter_next(it))
8391 struct note *note = iter_data(it);
8392 saxdb_start_record(ctx, iter_key(it), 0);
8393 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
8394 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
8395 saxdb_end_record(ctx);
8397 saxdb_end_record(ctx);
8400 if(channel->ownerTransfer)
8401 saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
8402 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
8403 saxdb_end_record(ctx);
8407 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
8411 saxdb_start_record(ctx, ntype->name, 0);
8412 switch(ntype->set_access_type)
8414 case NOTE_SET_CHANNEL_ACCESS:
8415 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
8417 case NOTE_SET_CHANNEL_SETTER:
8418 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
8420 case NOTE_SET_PRIVILEGED: default:
8421 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
8424 switch(ntype->visible_type)
8426 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
8427 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
8428 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
8430 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
8431 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
8432 saxdb_end_record(ctx);
8436 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
8438 struct do_not_register *dnr;
8439 dict_iterator_t it, next;
8441 for(it = dict_first(dnrs); it; it = next)
8443 next = iter_next(it);
8444 dnr = iter_data(it);
8445 if(dnr->expires && dnr->expires <= now)
8447 dict_remove(dnrs, iter_key(it));
8450 saxdb_start_record(ctx, dnr->chan_name, 0);
8452 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
8454 saxdb_write_int(ctx, KEY_EXPIRES, dnr->expires);
8455 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
8456 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
8457 saxdb_end_record(ctx);
8462 chanserv_saxdb_write(struct saxdb_context *ctx)
8465 struct chanData *channel;
8468 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
8469 for(it = dict_first(note_types); it; it = iter_next(it))
8470 chanserv_write_note_type(ctx, iter_data(it));
8471 saxdb_end_record(ctx);
8474 saxdb_start_record(ctx, KEY_DNR, 1);
8475 write_dnrs_helper(ctx, handle_dnrs);
8476 write_dnrs_helper(ctx, plain_dnrs);
8477 write_dnrs_helper(ctx, mask_dnrs);
8478 saxdb_end_record(ctx);
8481 saxdb_start_record(ctx, KEY_CHANNELS, 1);
8482 for(channel = channelList; channel; channel = channel->next)
8483 chanserv_write_channel(ctx, channel);
8484 saxdb_end_record(ctx);
8490 chanserv_db_cleanup(void) {
8492 unreg_part_func(handle_part);
8494 unregister_channel(channelList, "terminating.");
8495 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
8496 UnlockChannel(chanserv_conf.support_channels.list[ii]);
8497 free(chanserv_conf.support_channels.list);
8498 dict_delete(handle_dnrs);
8499 dict_delete(plain_dnrs);
8500 dict_delete(mask_dnrs);
8501 dict_delete(note_types);
8502 free_string_list(chanserv_conf.eightball);
8503 free_string_list(chanserv_conf.old_ban_names);
8504 free_string_list(chanserv_conf.set_shows);
8505 free(set_shows_list.list);
8506 free(uset_shows_list.list);
8509 struct userData *helper = helperList;
8510 helperList = helperList->next;
8515 #if defined(GCC_VARMACROS)
8516 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ARGS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ARGS)
8517 #elif defined(C99_VARMACROS)
8518 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, __VA_ARGS__)
8520 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
8521 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
8524 init_chanserv(const char *nick)
8526 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
8527 conf_register_reload(chanserv_conf_read);
8531 reg_server_link_func(handle_server_link);
8532 reg_new_channel_func(handle_new_channel);
8533 reg_join_func(handle_join);
8534 reg_part_func(handle_part);
8535 reg_kick_func(handle_kick);
8536 reg_topic_func(handle_topic);
8537 reg_mode_change_func(handle_mode);
8538 reg_nick_change_func(handle_nick_change);
8539 reg_auth_func(handle_auth);
8542 reg_handle_rename_func(handle_rename);
8543 reg_unreg_func(handle_unreg);
8545 handle_dnrs = dict_new();
8546 dict_set_free_data(handle_dnrs, free);
8547 plain_dnrs = dict_new();
8548 dict_set_free_data(plain_dnrs, free);
8549 mask_dnrs = dict_new();
8550 dict_set_free_data(mask_dnrs, free);
8552 reg_svccmd_unbind_func(handle_svccmd_unbind);
8553 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
8554 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
8555 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
8556 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
8557 DEFINE_COMMAND(dnrsearch, 3, 0, "template", "noregister", NULL);
8558 modcmd_register(chanserv_module, "dnrsearch print", NULL, 0, 0, NULL);
8559 modcmd_register(chanserv_module, "dnrsearch remove", NULL, 0, 0, NULL);
8560 modcmd_register(chanserv_module, "dnrsearch count", NULL, 0, 0, NULL);
8561 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
8562 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
8563 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
8564 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
8565 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
8567 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
8568 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
8570 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8571 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8572 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8573 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8574 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
8576 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
8577 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
8578 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
8579 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8580 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8582 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8583 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
8584 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8585 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
8587 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
8588 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
8589 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8590 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8591 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
8592 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8593 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8594 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8596 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8597 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8598 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8599 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
8600 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
8601 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
8602 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
8603 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
8604 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8605 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
8606 DEFINE_COMMAND(invitemeall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8607 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
8608 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
8609 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8610 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8612 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
8613 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8614 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8615 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8616 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
8618 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
8619 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
8621 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
8622 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8623 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8624 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8625 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8626 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8627 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8628 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8629 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8630 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8631 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8633 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
8634 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
8636 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
8637 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
8638 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
8639 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
8641 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
8642 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
8643 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
8644 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
8645 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
8647 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8648 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8649 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8650 DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8651 DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8652 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8654 DEFINE_COMMAND(addvote, 1, MODCMD_REQUIRE_AUTHED, NULL);
8655 DEFINE_COMMAND(delvote, 1, MODCMD_REQUIRE_AUTHED, NULL);
8656 DEFINE_COMMAND(addoption, 1, MODCMD_REQUIRE_AUTHED, NULL);
8657 DEFINE_COMMAND(deloption, 1, MODCMD_REQUIRE_AUTHED, NULL);
8658 DEFINE_COMMAND(vote, 1, MODCMD_REQUIRE_AUTHED, NULL);
8659 DEFINE_COMMAND(startvote, 1, MODCMD_REQUIRE_AUTHED, NULL);
8660 DEFINE_COMMAND(endvote, 1, MODCMD_REQUIRE_AUTHED, NULL);
8661 DEFINE_COMMAND(voteresults, 1, MODCMD_REQUIRE_AUTHED, NULL);
8663 /* Channel options */
8664 DEFINE_CHANNEL_OPTION(defaulttopic);
8665 DEFINE_CHANNEL_OPTION(topicmask);
8666 DEFINE_CHANNEL_OPTION(greeting);
8667 DEFINE_CHANNEL_OPTION(usergreeting);
8668 DEFINE_CHANNEL_OPTION(modes);
8669 DEFINE_CHANNEL_OPTION(enfops);
8670 DEFINE_CHANNEL_OPTION(giveops);
8671 DEFINE_CHANNEL_OPTION(protect);
8672 DEFINE_CHANNEL_OPTION(enfmodes);
8673 DEFINE_CHANNEL_OPTION(enftopic);
8674 DEFINE_CHANNEL_OPTION(pubcmd);
8675 DEFINE_CHANNEL_OPTION(givevoice);
8676 DEFINE_CHANNEL_OPTION(userinfo);
8677 DEFINE_CHANNEL_OPTION(dynlimit);
8678 DEFINE_CHANNEL_OPTION(topicsnarf);
8679 DEFINE_CHANNEL_OPTION(vote);
8680 DEFINE_CHANNEL_OPTION(nodelete);
8681 DEFINE_CHANNEL_OPTION(toys);
8682 DEFINE_CHANNEL_OPTION(setters);
8683 DEFINE_CHANNEL_OPTION(topicrefresh);
8684 DEFINE_CHANNEL_OPTION(ctcpusers);
8685 DEFINE_CHANNEL_OPTION(ctcpreaction);
8686 DEFINE_CHANNEL_OPTION(inviteme);
8687 DEFINE_CHANNEL_OPTION(unreviewed);
8688 modcmd_register(chanserv_module, "set expire", chan_opt_expire, 1, 0, "flags", "+helping", NULL);
8689 modcmd_register(chanserv_module, "set unreviewed on", NULL, 0, 0, "flags", "+helping", NULL);
8690 modcmd_register(chanserv_module, "set unreviewed off", NULL, 0, 0, "flags", "+oper", NULL);
8692 DEFINE_CHANNEL_OPTION(offchannel);
8693 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
8695 /* Alias set topic to set defaulttopic for compatibility. */
8696 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
8699 DEFINE_USER_OPTION(noautoop);
8700 DEFINE_USER_OPTION(autoinvite);
8701 DEFINE_USER_OPTION(info);
8703 /* Alias uset autovoice to uset autoop. */
8704 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
8706 note_types = dict_new();
8707 dict_set_free_data(note_types, chanserv_deref_note_type);
8710 const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
8711 chanserv = AddLocalUser(nick, nick, NULL, "Channel Services", modes);
8712 service_register(chanserv)->trigger = '!';
8713 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
8715 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
8717 if(chanserv_conf.channel_expire_frequency)
8718 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
8720 if(chanserv_conf.dnr_expire_frequency)
8721 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
8723 if(chanserv_conf.refresh_period)
8725 unsigned long next_refresh;
8726 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
8727 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
8730 reg_exit_func(chanserv_db_cleanup);
8731 message_register_table(msgtab);