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." },
391 { "CSMSG_MYACCESS_COUNT_1", "%s has access in $b%d$b channel." },
393 /* Seen information */
394 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
395 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
396 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
397 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
399 /* Names information */
400 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
401 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
403 /* Channel information */
404 { "CSMSG_CHANNEL_INFO", "$b%s$b Information:" },
405 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
406 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
407 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
408 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
409 { "CSMSG_CHANNEL_MAX_TIME", "$bRecord Visitors: $b%i (%s ago.)" },
410 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
411 { "CSMSG_CHANNEL_BANS", "$bBan Count: $b%i" },
412 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
413 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
414 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
415 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
416 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
417 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
418 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
419 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
420 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
421 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
422 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
423 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
424 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
425 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
427 { "CSMSG_PEEK_INFO", "$b%s$b Status:" },
428 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
429 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
430 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d (%d ops, %d voices, %d regulars)" },
431 { "CSMSG_PEEK_OPS", "$bOps:$b" },
432 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
434 /* Network information */
435 { "CSMSG_NETWORK_INFO", "Network Information:" },
436 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
437 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
438 { "CSMSG_NETWORK_BANS", "$bTotal Ban Count: $b%i" },
439 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
440 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
441 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
442 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
443 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
446 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
447 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
448 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
450 /* Channel searches */
451 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
452 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
453 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
454 { "CSMSG_CHANNEL_SEARCH_RESULTS", "The following channels were found:" },
456 /* Channel configuration */
457 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
458 { "CSMSG_INVALID_CFLAG", "$b%s$b is not a recognized channel flag." },
459 { "CSMSG_CHANNEL_OPTIONS", "Channel Options:" },
460 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
463 { "CSMSG_USER_OPTIONS", "User Options:" },
464 { "CSMSG_USER_PROTECTED_2", "That user is protected." },
467 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
468 { "CSMSG_PING_RESPONSE", "Pong!" },
469 { "CSMSG_WUT_RESPONSE", "wut" },
470 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
471 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
472 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
473 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
474 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
475 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
476 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
479 { "CSMSG_ADDVOTE_DONE", "Vote added. Use $baddoption$b to add at least 2 vote options and then $bstartvote$b to start the voting." },
480 { "CSMSG_ADDVOTE_FULL", "There is already a vote in this channel. Use $bdelvote$b to delete it." },
481 { "CSMSG_DELVOTE_DONE", "Vote deleted." },
482 { "CSMSG_NO_VOTE", "There is no vote in this channel." },
483 { "CSMSG_ADDOPTION_DONE", "Vote option added with id %i (%i - %i)." },
484 { "CSMSG_DELOPTION_DONE", "Vote option deleted." },
485 { "CSMSG_DELOPTION_NONE", "Vote option does not exist." },
486 { "CSMSG_VOTE_NEED_OPTIONS", "There must be at least 2 options in a vote." },
487 { "CSMSG_VOTE_NOT_STARTED", "The vote is not started. Use $bstartvote$b to allow voting." },
488 { "CSMSG_VOTE_QUESTION", "Question: %s" },
489 { "CSMSG_VOTE_OPTION", "$b%i$b: %s ($b%i$b votes)" },
490 { "CSMSG_VOTERES_QUESTION", "Question: %s" },
491 { "CSMSG_VOTERES_OPTION", "
\ 2%i
\ 2: %s (
\ 2%i
\ 2 votes)" },
492 { "CSMSG_STARTVOTE_TOP", "
\ 2%s
\ 2 has started a voting:" },
493 { "CSMSG_STARTVOTE_QUESTION", "
\ 2Question:
\ 2 %s" },
494 { "CSMSG_STARTVOTE_OPTION", "
\ 2%i:
\ 2 %s" },
495 { "CSMSG_STARTVOTE_ACCESS", "All channel users with at least
\ 2%i
\ 2 access can vote." },
496 { "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." },
497 { "CSMSG_STARTVOTE_RUNNING", "The vote is already running." },
498 { "CSMSG_VOTE_VOTED", "You have already voted." },
499 { "CSMSG_VOTE_INVALID", "Vote option does not exist." },
500 { "CSMSG_VOTE_DONE", "You voted for $b%s$b." },
501 { "CSMSG_ENDVOTE_DONE", "The vote has been finished." },
502 { "CSMSG_ENDVOTE_STOPPED", "The vote is not running." },
505 { "CSMSG_EVENT_SEARCH_RESULTS", "The following channel events were found:" },
509 /* eject_user and unban_user flags */
510 #define ACTION_KICK 0x0001
511 #define ACTION_BAN 0x0002
512 #define ACTION_ADD_BAN 0x0004
513 #define ACTION_ADD_TIMED_BAN 0x0008
514 #define ACTION_UNBAN 0x0010
515 #define ACTION_DEL_BAN 0x0020
517 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
518 #define MODELEN 40 + KEYLEN
522 #define CSFUNC_ARGS user, channel, argc, argv, cmd
524 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
525 #define CHANSERV_SYNTAX() svccmd_send_help(user, chanserv, cmd)
526 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
527 reply("MSG_MISSING_PARAMS", argv[0]); \
531 DECLARE_LIST(dnrList, struct do_not_register *);
532 DEFINE_LIST(dnrList, struct do_not_register *)
534 static int eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action);
536 struct userNode *chanserv;
539 static dict_t plain_dnrs, mask_dnrs, handle_dnrs;
540 static struct log_type *CS_LOG;
544 struct channelList support_channels;
545 struct mod_chanmode default_modes;
547 unsigned long db_backup_frequency;
548 unsigned long channel_expire_frequency;
549 unsigned long dnr_expire_frequency;
551 unsigned long invited_timeout;
553 unsigned long info_delay;
554 unsigned long adjust_delay;
555 unsigned long channel_expire_delay;
556 unsigned int nodelete_level;
558 unsigned int adjust_threshold;
559 int join_flood_threshold;
561 unsigned int greeting_length;
562 unsigned int refresh_period;
563 unsigned int giveownership_period;
565 unsigned int max_owned;
566 unsigned int max_chan_users;
567 unsigned int max_chan_bans;
568 unsigned int max_userinfo_length;
570 struct string_list *set_shows;
571 struct string_list *eightball;
572 struct string_list *old_ban_names;
574 const char *ctcp_short_ban_duration;
575 const char *ctcp_long_ban_duration;
577 const char *irc_operator_epithet;
578 const char *network_helper_epithet;
579 const char *support_helper_epithet;
581 const char *new_channel_authed;
582 const char *new_channel_unauthed;
583 const char *new_channel_msg;
588 struct userNode *user;
589 struct userNode *bot;
590 struct chanNode *channel;
592 unsigned short lowest;
593 unsigned short highest;
594 struct userData **users;
595 struct helpfile_table table;
600 struct userNode *user;
601 struct chanNode *chan;
604 enum note_access_type
606 NOTE_SET_CHANNEL_ACCESS,
607 NOTE_SET_CHANNEL_SETTER,
611 enum note_visible_type
614 NOTE_VIS_CHANNEL_USERS,
620 enum note_access_type set_access_type;
622 unsigned int min_opserv;
623 unsigned short min_ulevel;
625 enum note_visible_type visible_type;
626 unsigned int max_length;
633 struct note_type *type;
634 char setter[NICKSERV_HANDLE_LEN+1];
638 static unsigned int registered_channels;
639 static unsigned int banCount;
641 static const struct {
644 unsigned short level;
647 { "peon", "Peon", UL_PEON, '+' },
648 { "op", "Op", UL_OP, '@' },
649 { "master", "Master", UL_MASTER, '%' },
650 { "coowner", "Coowner", UL_COOWNER, '*' },
651 { "owner", "Owner", UL_OWNER, '!' },
652 { "helper", "BUG:", UL_HELPER, 'X' }
655 static const struct {
658 unsigned short default_value;
659 unsigned int old_idx;
660 unsigned int old_flag;
661 unsigned short flag_value;
663 { "CSMSG_SET_GIVE_VOICE", "givevoice", 100, ~0, CHANNEL_VOICE_ALL, 0 },
664 { "CSMSG_SET_GIVE_OPS", "giveops", 200, 2, 0, 0 },
665 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
666 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
667 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
668 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
669 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
670 { "CSMSG_SET_CTCPUSERS", "ctcpusers", 0, 9, 0, 0 },
671 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES, 1 },
672 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE, 200 },
673 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF, 1 },
674 { "CSMSG_SET_VOTE", "vote", 100, ~0, 0, 0 }
677 struct charOptionValues {
680 } protectValues[] = {
681 { 'a', "CSMSG_PROTECT_ALL" },
682 { 'e', "CSMSG_PROTECT_EQUAL" },
683 { 'l', "CSMSG_PROTECT_LOWER" },
684 { 'n', "CSMSG_PROTECT_NONE" }
686 { 'd', "CSMSG_TOYS_DISABLED" },
687 { 'n', "CSMSG_TOYS_PRIVATE" },
688 { 'p', "CSMSG_TOYS_PUBLIC" }
689 }, topicRefreshValues[] = {
690 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
691 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
692 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
693 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
694 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
695 }, ctcpReactionValues[] = {
696 { 'k', "CSMSG_CTCPREACTION_KICK" },
697 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
698 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
699 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
702 static const struct {
706 unsigned int old_idx;
708 struct charOptionValues *values;
710 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues), protectValues },
711 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues), toysValues },
712 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues), topicRefreshValues },
713 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 't', 10, ArrayLength(ctcpReactionValues), ctcpReactionValues }
716 struct userData *helperList;
717 struct chanData *channelList;
718 static struct module *chanserv_module;
719 static unsigned int userCount;
721 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
722 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
723 static void unregister_channel(struct chanData *channel, const char *reason);
726 user_level_from_name(const char *name, unsigned short clamp_level)
728 unsigned int level = 0, ii;
730 level = strtoul(name, NULL, 10);
731 else for(ii = 0; (ii < ArrayLength(accessLevels)) && !level; ++ii)
732 if(!irccasecmp(name, accessLevels[ii].name))
733 level = accessLevels[ii].level;
734 if(level > clamp_level)
740 parse_level_range(unsigned short *minl, unsigned short *maxl, const char *arg)
743 *minl = strtoul(arg, &sep, 10);
751 *maxl = strtoul(sep+1, &sep, 10);
759 _GetChannelUser(struct chanData *channel, struct handle_info *handle, int override, int allow_suspended)
761 struct userData *uData, **head;
763 if(!channel || !handle)
766 if(override && HANDLE_FLAGGED(handle, HELPING)
767 && ((handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel)))
769 for(uData = helperList;
770 uData && uData->handle != handle;
771 uData = uData->next);
775 uData = calloc(1, sizeof(struct userData));
776 uData->handle = handle;
778 uData->access = UL_HELPER;
784 uData->next = helperList;
786 helperList->prev = uData;
794 for(uData = channel->users; uData; uData = uData->next)
795 if((uData->handle == handle) && (allow_suspended || !IsUserSuspended(uData)))
798 head = &(channel->users);
801 if(uData && (uData != *head))
803 /* Shuffle the user to the head of whatever list he was in. */
805 uData->next->prev = uData->prev;
807 uData->prev->next = uData->next;
813 (**head).prev = uData;
820 /* Returns non-zero if user has at least the minimum access.
821 * exempt_owner is set when handling !set, so the owner can set things
824 int check_user_level(struct chanNode *channel, struct userNode *user, enum levelOption opt, int allow_override, int exempt_owner)
826 struct userData *uData;
827 struct chanData *cData = channel->channel_info;
828 unsigned short minimum = cData->lvlOpts[opt];
831 uData = _GetChannelUser(cData, user->handle_info, allow_override, 0);
834 if(minimum <= uData->access)
836 if((minimum > UL_OWNER) && (uData->access == UL_OWNER) && exempt_owner)
841 /* Scan for other users authenticated to the same handle
842 still in the channel. If so, keep them listed as present.
844 user is optional, if not null, it skips checking that userNode
845 (for the handle_part function) */
847 scan_user_presence(struct userData *uData, struct userNode *user)
851 if(IsSuspended(uData->channel)
852 || IsUserSuspended(uData)
853 || !(mn = find_handle_in_channel(uData->channel->channel, uData->handle, user)))
865 chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, const char *text, UNUSED_ARG(struct userNode *bot), UNUSED_ARG(unsigned int is_notice))
867 unsigned int eflags, argc;
869 static char *bad_ctcp_reason = "CTCPs to this channel are forbidden.";
871 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
872 if(!channel->channel_info
873 || IsSuspended(channel->channel_info)
875 || !ircncasecmp(text, "ACTION ", 7))
877 /* Figure out the minimum level needed to CTCP the channel */
878 if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
880 /* We need to enforce against them; do so. */
882 argv[0] = (char*)text;
883 argv[1] = user->nick;
885 if(GetUserMode(channel, user))
886 eflags |= ACTION_KICK;
887 switch(channel->channel_info->chOpts[chCTCPReaction]) {
888 default: case 'k': /* just do the kick */ break;
890 eflags |= ACTION_BAN;
893 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
894 argv[argc++] = (char*)chanserv_conf.ctcp_short_ban_duration;
897 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
898 argv[argc++] = (char*)chanserv_conf.ctcp_long_ban_duration;
901 argv[argc++] = bad_ctcp_reason;
902 eject_user(chanserv, channel, argc, argv, NULL, eflags);
906 chanserv_create_note_type(const char *name)
908 struct note_type *ntype = calloc(1, sizeof(*ntype) + strlen(name));
909 strcpy(ntype->name, name);
911 dict_insert(note_types, ntype->name, ntype);
916 free_vote_options(void *data)
918 struct vote_option *vOpt = data;
920 free(vOpt->option_str);
925 chanserv_deref_note_type(void *data)
927 struct note_type *ntype = data;
929 if(--ntype->refs > 0)
935 chanserv_flush_note_type(struct note_type *ntype)
937 struct chanData *cData;
938 for(cData = channelList; cData; cData = cData->next)
939 dict_remove(cData->notes, ntype->name);
943 chanserv_truncate_notes(struct note_type *ntype)
945 struct chanData *cData;
947 unsigned int size = sizeof(*note) + ntype->max_length;
949 for(cData = channelList; cData; cData = cData->next) {
950 note = dict_find(cData->notes, ntype->name, NULL);
953 if(strlen(note->note) <= ntype->max_length)
955 dict_remove2(cData->notes, ntype->name, 1);
956 note = realloc(note, size);
957 note->note[ntype->max_length] = 0;
958 dict_insert(cData->notes, ntype->name, note);
962 static int note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user);
965 chanserv_add_channel_note(struct chanData *channel, struct note_type *type, const char *setter, const char *text)
968 unsigned int len = strlen(text);
970 if(len > type->max_length) len = type->max_length;
971 note = calloc(1, sizeof(*note) + len);
973 strncpy(note->setter, setter, sizeof(note->setter)-1);
974 memcpy(note->note, text, len);
976 dict_insert(channel->notes, type->name, note);
982 chanserv_free_note(void *data)
984 struct note *note = data;
986 chanserv_deref_note_type(note->type);
987 assert(note->type->refs > 0); /* must use delnote to remove the type */
991 static MODCMD_FUNC(cmd_createnote) {
992 struct note_type *ntype;
993 unsigned int arg = 1, existed = 0, max_length;
995 if((ntype = dict_find(note_types, argv[1], NULL)))
998 ntype = chanserv_create_note_type(argv[arg]);
999 if(!irccasecmp(argv[++arg], "privileged"))
1002 ntype->set_access_type = NOTE_SET_PRIVILEGED;
1003 ntype->set_access.min_opserv = strtoul(argv[arg], NULL, 0);
1005 else if(!irccasecmp(argv[arg], "channel"))
1007 unsigned short ulvl = user_level_from_name(argv[++arg], UL_OWNER);
1010 reply("CSMSG_INVALID_ACCESS", argv[arg]);
1013 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
1014 ntype->set_access.min_ulevel = ulvl;
1016 else if(!irccasecmp(argv[arg], "setter"))
1018 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
1022 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
1026 if(!irccasecmp(argv[++arg], "privileged"))
1027 ntype->visible_type = NOTE_VIS_PRIVILEGED;
1028 else if(!irccasecmp(argv[arg], "channel_users"))
1029 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
1030 else if(!irccasecmp(argv[arg], "all"))
1031 ntype->visible_type = NOTE_VIS_ALL;
1033 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
1037 if((arg+1) >= argc) {
1038 reply("MSG_MISSING_PARAMS", argv[0]);
1041 max_length = strtoul(argv[++arg], NULL, 0);
1042 if(max_length < 20 || max_length > 450)
1044 reply("CSMSG_BAD_MAX_LENGTH", argv[arg]);
1047 if(existed && (max_length < ntype->max_length))
1049 ntype->max_length = max_length;
1050 chanserv_truncate_notes(ntype);
1052 ntype->max_length = max_length;
1055 reply("CSMSG_NOTE_MODIFIED", ntype->name);
1057 reply("CSMSG_NOTE_CREATED", ntype->name);
1062 dict_remove(note_types, ntype->name);
1066 static MODCMD_FUNC(cmd_removenote) {
1067 struct note_type *ntype;
1070 ntype = dict_find(note_types, argv[1], NULL);
1071 force = (argc > 2) && !irccasecmp(argv[2], "force");
1074 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
1081 reply("CSMSG_NOTE_TYPE_USED", ntype->name);
1084 chanserv_flush_note_type(ntype);
1086 dict_remove(note_types, argv[1]);
1087 reply("CSMSG_NOTE_DELETED", argv[1]);
1092 chanserv_expire_channel(void *data)
1094 struct chanData *channel = data;
1095 char reason[MAXLEN];
1096 sprintf(reason, "channel expired.");
1097 channel->expiry = 0;
1098 spamserv_cs_unregister(NULL, channel->channel, expire, NULL);
1099 unregister_channel(channel, reason);
1102 static MODCMD_FUNC(chan_opt_expire)
1104 struct chanData *cData = channel->channel_info;
1105 unsigned long value = cData->expiry;
1109 if((!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
1111 reply("MSG_SETTING_PRIVILEGED", argv[0]);
1114 unsigned long expiry,duration;
1116 /* The two directions can have different ACLs. */
1117 if(!strcmp(argv[1], "0"))
1119 else if((duration = ParseInterval(argv[1])))
1120 expiry = now + duration;
1123 reply("MSG_INVALID_DURATION", argv[1]);
1127 if (expiry != value)
1131 timeq_del(value, chanserv_expire_channel, cData, 0);
1134 cData->expiry = value;
1137 timeq_add(expiry, chanserv_expire_channel, cData);
1142 if(cData->expiry > now) {
1143 char expirestr[INTERVALLEN];
1144 reply("CSMSG_SET_EXPIRE", intervalString(expirestr, cData->expiry - now, user->handle_info));
1146 reply("CSMSG_SET_EXPIRE_OFF");
1151 mode_lock_violated(const struct mod_chanmode *orig, const struct mod_chanmode *change)
1155 if(orig->modes_set & change->modes_clear)
1157 if(orig->modes_clear & change->modes_set)
1159 if((orig->modes_set & MODE_KEY) && (change->modes_set & MODE_KEY)
1160 && strcmp(orig->new_key, change->new_key))
1162 if((orig->modes_set & MODE_LIMIT) && (change->modes_set & MODE_LIMIT)
1163 && (orig->new_limit != change->new_limit))
1168 static char max_length_text[MAXLEN+1][16];
1170 static struct helpfile_expansion
1171 chanserv_expand_variable(const char *variable)
1173 struct helpfile_expansion exp;
1175 if(!irccasecmp(variable, "notes"))
1178 exp.type = HF_TABLE;
1179 exp.value.table.length = 1;
1180 exp.value.table.width = 3;
1181 exp.value.table.flags = 0;
1182 exp.value.table.contents = calloc(dict_size(note_types)+1, sizeof(char**));
1183 exp.value.table.contents[0] = calloc(exp.value.table.width, sizeof(char*));
1184 exp.value.table.contents[0][0] = "Note Type";
1185 exp.value.table.contents[0][1] = "Visibility";
1186 exp.value.table.contents[0][2] = "Max Length";
1187 for(it=dict_first(note_types); it; it=iter_next(it))
1189 struct note_type *ntype = iter_data(it);
1192 if(!note_type_visible_to_user(NULL, ntype, message_dest)) continue;
1193 row = exp.value.table.length++;
1194 exp.value.table.contents[row] = calloc(exp.value.table.width, sizeof(char*));
1195 exp.value.table.contents[row][0] = ntype->name;
1196 exp.value.table.contents[row][1] = (ntype->visible_type == NOTE_VIS_ALL) ? "all" :
1197 (ntype->visible_type == NOTE_VIS_CHANNEL_USERS) ? "chan users" :
1199 if(!max_length_text[ntype->max_length][0])
1200 snprintf(max_length_text[ntype->max_length], sizeof(max_length_text[ntype->max_length]), "%u", ntype->max_length);
1201 exp.value.table.contents[row][2] = max_length_text[ntype->max_length];
1206 exp.type = HF_STRING;
1207 exp.value.str = NULL;
1211 static struct chanData*
1212 register_channel(struct chanNode *cNode, char *registrar)
1214 struct chanData *channel;
1215 enum levelOption lvlOpt;
1216 enum charOption chOpt;
1218 channel = calloc(1, sizeof(struct chanData));
1220 channel->notes = dict_new();
1221 dict_set_free_data(channel->notes, chanserv_free_note);
1223 channel->registrar = strdup(registrar);
1224 channel->registered = now;
1225 channel->visited = now;
1226 channel->limitAdjusted = now;
1227 channel->ownerTransfer = now;
1228 channel->flags = CHANNEL_DEFAULT_FLAGS;
1229 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
1230 channel->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
1231 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
1232 channel->chOpts[chOpt] = charOptions[chOpt].default_value;
1234 channel->prev = NULL;
1235 channel->next = channelList;
1238 channelList->prev = channel;
1239 channelList = channel;
1240 registered_channels++;
1242 channel->channel = cNode;
1244 cNode->channel_info = channel;
1246 channel->vote = NULL;
1251 static struct userData*
1252 add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned short access_level, unsigned long seen, const char *info)
1254 struct userData *ud;
1256 if(access_level > UL_OWNER)
1259 ud = calloc(1, sizeof(*ud));
1260 ud->channel = channel;
1261 ud->handle = handle;
1263 ud->access = access_level;
1264 ud->info = info ? strdup(info) : NULL;
1267 ud->next = channel->users;
1269 channel->users->prev = ud;
1270 channel->users = ud;
1272 channel->userCount++;
1276 ud->u_next = ud->handle->channels;
1278 ud->u_next->u_prev = ud;
1279 ud->handle->channels = ud;
1285 del_channel_user(struct userData *user, int do_gc)
1287 struct chanData *channel = user->channel;
1289 channel->userCount--;
1293 user->prev->next = user->next;
1295 channel->users = user->next;
1297 user->next->prev = user->prev;
1300 user->u_prev->u_next = user->u_next;
1302 user->handle->channels = user->u_next;
1304 user->u_next->u_prev = user->u_prev;
1308 if(do_gc && !channel->users && !IsProtected(channel)) {
1309 spamserv_cs_unregister(NULL, channel->channel, lost_all_users, NULL);
1310 unregister_channel(channel, "lost all users.");
1314 static void expire_ban(void *data);
1317 add_channel_ban(struct chanData *channel, const char *mask, char *owner, unsigned long set, unsigned long triggered, unsigned long expires, char *reason)
1320 unsigned int ii, l1, l2;
1325 bd = malloc(sizeof(struct banData));
1327 bd->channel = channel;
1329 bd->triggered = triggered;
1330 bd->expires = expires;
1332 for(ii = 0; ii < chanserv_conf.old_ban_names->used; ++ii)
1334 extern const char *hidden_host_suffix;
1335 const char *old_name = chanserv_conf.old_ban_names->list[ii];
1339 l2 = strlen(old_name);
1342 if(irccasecmp(mask + l1 - l2, old_name))
1344 new_mask = alloca(MAXLEN);
1345 sprintf(new_mask, "%.*s%s", (int)(l1-l2), mask, hidden_host_suffix);
1348 safestrncpy(bd->mask, mask, sizeof(bd->mask));
1350 safestrncpy(bd->owner, owner, sizeof(bd->owner));
1351 bd->reason = strdup(reason);
1354 timeq_add(expires, expire_ban, bd);
1357 bd->next = channel->bans;
1359 channel->bans->prev = bd;
1361 channel->banCount++;
1368 del_channel_ban(struct banData *ban)
1370 ban->channel->banCount--;
1374 ban->prev->next = ban->next;
1376 ban->channel->bans = ban->next;
1379 ban->next->prev = ban->prev;
1382 timeq_del(0, expire_ban, ban, TIMEQ_IGNORE_WHEN);
1391 expire_ban(void *data)
1393 struct banData *bd = data;
1394 if(!IsSuspended(bd->channel))
1396 struct banList bans;
1397 struct mod_chanmode change;
1399 bans = bd->channel->channel->banlist;
1400 mod_chanmode_init(&change);
1401 for(ii=0; ii<bans.used; ii++)
1403 if(!strcmp(bans.list[ii]->ban, bd->mask))
1406 change.args[0].mode = MODE_REMOVE|MODE_BAN;
1407 change.args[0].u.hostmask = bd->mask;
1408 mod_chanmode_announce(chanserv, bd->channel->channel, &change);
1414 del_channel_ban(bd);
1417 static void chanserv_expire_suspension(void *data);
1420 unregister_channel(struct chanData *channel, const char *reason)
1422 struct mod_chanmode change;
1423 char msgbuf[MAXLEN];
1425 /* After channel unregistration, the following must be cleaned
1427 - Channel information.
1430 - Channel suspension data.
1431 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1437 timeq_del(0, NULL, channel, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
1441 mod_chanmode_init(&change);
1442 change.modes_clear |= MODE_REGISTERED;
1443 mod_chanmode_announce(chanserv, channel->channel, &change);
1446 while(channel->users)
1447 del_channel_user(channel->users, 0);
1449 while(channel->bans)
1450 del_channel_ban(channel->bans);
1452 free(channel->topic);
1453 free(channel->registrar);
1454 free(channel->greeting);
1455 free(channel->user_greeting);
1456 free(channel->topic_mask);
1459 channel->prev->next = channel->next;
1461 channelList = channel->next;
1464 channel->next->prev = channel->prev;
1466 if(channel->suspended)
1468 struct chanNode *cNode = channel->channel;
1469 struct suspended *suspended, *next_suspended;
1471 for(suspended = channel->suspended; suspended; suspended = next_suspended)
1473 next_suspended = suspended->previous;
1474 free(suspended->suspender);
1475 free(suspended->reason);
1476 if(suspended->expires)
1477 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
1482 cNode->channel_info = NULL;
1485 timeq_del(channel->expiry, chanserv_expire_channel, channel, 0);
1486 channel->channel->channel_info = NULL;
1488 dict_delete(channel->notes);
1489 sprintf(msgbuf, "%s %s", channel->channel->name, reason);
1490 if(!IsSuspended(channel))
1491 DelChannelUser(chanserv, channel->channel, msgbuf, 0);
1492 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, msgbuf);
1493 UnlockChannel(channel->channel);
1495 registered_channels--;
1499 expire_channels(UNUSED_ARG(void *data))
1501 struct chanData *channel, *next;
1502 struct userData *user;
1503 char delay[INTERVALLEN], reason[INTERVALLEN + 64];
1505 intervalString(delay, chanserv_conf.channel_expire_delay, NULL);
1506 sprintf(reason, "Channel registration automatically expired after %s of disuse.", delay);
1508 for(channel = channelList; channel; channel = next)
1510 next = channel->next;
1512 /* See if the channel can be expired. */
1513 if(((now - channel->visited) <= chanserv_conf.channel_expire_delay)
1514 || IsProtected(channel))
1517 /* Make sure there are no high-ranking users still in the channel. */
1518 for(user=channel->users; user; user=user->next)
1519 if(user->present && (user->access >= UL_PRESENT) && !HANDLE_FLAGGED(user->handle, BOT))
1524 /* Unregister the channel */
1525 log_module(CS_LOG, LOG_INFO, "(%s) Channel registration expired.", channel->channel->name);
1526 spamserv_cs_unregister(NULL, channel->channel, expire, NULL);
1527 unregister_channel(channel, "registration expired.");
1530 if(chanserv_conf.channel_expire_frequency)
1531 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
1535 expire_dnrs(UNUSED_ARG(void *data))
1537 dict_iterator_t it, next;
1538 struct do_not_register *dnr;
1540 for(it = dict_first(handle_dnrs); it; it = next)
1542 dnr = iter_data(it);
1543 next = iter_next(it);
1544 if(dnr->expires && dnr->expires <= now)
1545 dict_remove(handle_dnrs, dnr->chan_name + 1);
1547 for(it = dict_first(plain_dnrs); it; it = next)
1549 dnr = iter_data(it);
1550 next = iter_next(it);
1551 if(dnr->expires && dnr->expires <= now)
1552 dict_remove(plain_dnrs, dnr->chan_name + 1);
1554 for(it = dict_first(mask_dnrs); it; it = next)
1556 dnr = iter_data(it);
1557 next = iter_next(it);
1558 if(dnr->expires && dnr->expires <= now)
1559 dict_remove(mask_dnrs, dnr->chan_name + 1);
1562 if(chanserv_conf.dnr_expire_frequency)
1563 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
1567 protect_user(const struct userNode *victim, const struct userNode *aggressor, struct chanData *channel)
1569 char protect = channel->chOpts[chProtect];
1570 struct userData *cs_victim, *cs_aggressor;
1572 /* Don't protect if no one is to be protected, someone is attacking
1573 himself, or if the aggressor is an IRC Operator. */
1574 if(protect == 'n' || victim == aggressor || IsOper(aggressor))
1577 /* Don't protect if the victim isn't authenticated (because they
1578 can't be a channel user), unless we are to protect non-users
1580 cs_victim = GetChannelAccess(channel, victim->handle_info);
1581 if(protect != 'a' && !cs_victim)
1584 /* Protect if the aggressor isn't a user because at this point,
1585 the aggressor can only be less than or equal to the victim. */
1586 cs_aggressor = GetChannelAccess(channel, aggressor->handle_info);
1590 /* If the aggressor was a user, then the victim can't be helped. */
1597 if(cs_victim->access > cs_aggressor->access)
1602 if(cs_victim->access >= cs_aggressor->access)
1611 validate_op(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1613 struct chanData *cData = channel->channel_info;
1614 struct userData *cs_victim;
1616 if((!(cs_victim = GetChannelUser(cData, victim->handle_info))
1617 || (cs_victim->access < cData->lvlOpts[lvlGiveOps]))
1618 && !check_user_level(channel, user, lvlEnfOps, 0, 0))
1620 send_message(user, chanserv, "CSMSG_OPBY_LOCKED");
1628 validate_deop(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1630 if(IsService(victim))
1632 send_message(user, chanserv, "MSG_SERVICE_IMMUNE", victim->nick);
1636 if(protect_user(victim, user, channel->channel_info))
1638 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
1645 static struct do_not_register *
1646 chanserv_add_dnr(const char *chan_name, const char *setter, unsigned long expires, const char *reason)
1648 struct do_not_register *dnr = calloc(1, sizeof(*dnr)+strlen(reason));
1649 safestrncpy(dnr->chan_name, chan_name, sizeof(dnr->chan_name));
1650 safestrncpy(dnr->setter, setter, sizeof(dnr->setter));
1651 strcpy(dnr->reason, reason);
1653 dnr->expires = expires;
1654 if(dnr->chan_name[0] == '*')
1655 dict_insert(handle_dnrs, dnr->chan_name+1, dnr);
1656 else if(strpbrk(dnr->chan_name, "*?"))
1657 dict_insert(mask_dnrs, dnr->chan_name, dnr);
1659 dict_insert(plain_dnrs, dnr->chan_name, dnr);
1663 static struct dnrList
1664 chanserv_find_dnrs(const char *chan_name, const char *handle, unsigned int max)
1666 struct dnrList list;
1667 dict_iterator_t it, next;
1668 struct do_not_register *dnr;
1670 dnrList_init(&list);
1672 if(handle && (dnr = dict_find(handle_dnrs, handle, NULL)))
1674 if(dnr->expires && dnr->expires <= now)
1675 dict_remove(handle_dnrs, handle);
1676 else if(list.used < max)
1677 dnrList_append(&list, dnr);
1680 if(chan_name && (dnr = dict_find(plain_dnrs, chan_name, NULL)))
1682 if(dnr->expires && dnr->expires <= now)
1683 dict_remove(plain_dnrs, chan_name);
1684 else if(list.used < max)
1685 dnrList_append(&list, dnr);
1690 for(it = dict_first(mask_dnrs); it && list.used < max; it = next)
1692 next = iter_next(it);
1693 if(!match_ircglob(chan_name, iter_key(it)))
1695 dnr = iter_data(it);
1696 if(dnr->expires && dnr->expires <= now)
1697 dict_remove(mask_dnrs, iter_key(it));
1699 dnrList_append(&list, dnr);
1706 static int dnr_print_func(struct do_not_register *dnr, void *extra)
1708 struct userNode *user;
1709 char buf1[INTERVALLEN];
1710 char buf2[INTERVALLEN];
1717 strftime(buf1, sizeof(buf1), "%d %b %Y", localtime(&feh));
1722 strftime(buf2, sizeof(buf2), "%d %b %Y", localtime(&feh));
1723 send_message(user, chanserv, "CSMSG_DNR_INFO_SET_EXPIRES", dnr->chan_name, buf1, dnr->setter, buf2, dnr->reason);
1727 send_message(user, chanserv, "CSMSG_DNR_INFO_SET", dnr->chan_name, buf1, dnr->setter, dnr->reason);
1730 send_message(user, chanserv, "CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1735 chanserv_show_dnrs(struct userNode *user, struct svccmd *cmd, const char *chan_name, const char *handle)
1737 struct dnrList list;
1740 list = chanserv_find_dnrs(chan_name, handle, UINT_MAX);
1741 for(ii = 0; (ii < list.used) && (ii < 10); ++ii)
1742 dnr_print_func(list.list[ii], user);
1744 reply("CSMSG_MORE_DNRS", list.used - ii);
1749 struct do_not_register *
1750 chanserv_is_dnr(const char *chan_name, struct handle_info *handle)
1752 struct dnrList list;
1753 struct do_not_register *dnr;
1755 list = chanserv_find_dnrs(chan_name, handle ? handle->handle : NULL, 1);
1756 dnr = list.used ? list.list[0] : NULL;
1761 static unsigned int send_dnrs(struct userNode *user, dict_t dict)
1763 struct do_not_register *dnr;
1764 dict_iterator_t it, next;
1765 unsigned int matches = 0;
1767 for(it = dict_first(dict); it; it = next)
1769 dnr = iter_data(it);
1770 next = iter_next(it);
1771 if(dnr->expires && dnr->expires <= now)
1773 dict_remove(dict, iter_key(it));
1776 dnr_print_func(dnr, user);
1783 static CHANSERV_FUNC(cmd_noregister)
1787 unsigned long expiry, duration;
1788 unsigned int matches;
1792 reply("CSMSG_DNR_SEARCH_RESULTS");
1793 matches = send_dnrs(user, handle_dnrs);
1794 matches += send_dnrs(user, plain_dnrs);
1795 matches += send_dnrs(user, mask_dnrs);
1797 reply("MSG_MATCH_COUNT", matches);
1799 reply("MSG_NO_MATCHES");
1805 if(!IsChannelName(target) && (*target != '*'))
1807 reply("CSMSG_NOT_DNR", target);
1815 reply("MSG_INVALID_DURATION", argv[2]);
1819 if(!strcmp(argv[2], "0"))
1821 else if((duration = ParseInterval(argv[2])))
1822 expiry = now + duration;
1825 reply("MSG_INVALID_DURATION", argv[2]);
1829 reason = unsplit_string(argv + 3, argc - 3, NULL);
1830 if((*target == '*') && !get_handle_info(target + 1))
1832 reply("MSG_HANDLE_UNKNOWN", target + 1);
1835 chanserv_add_dnr(target, user->handle_info->handle, expiry, reason);
1836 reply("CSMSG_NOREGISTER_CHANNEL", target);
1840 reply("CSMSG_DNR_SEARCH_RESULTS");
1842 matches = chanserv_show_dnrs(user, cmd, NULL, target + 1);
1844 matches = chanserv_show_dnrs(user, cmd, target, NULL);
1846 reply("MSG_NO_MATCHES");
1850 static CHANSERV_FUNC(cmd_allowregister)
1852 const char *chan_name = argv[1];
1854 if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
1855 || dict_remove(plain_dnrs, chan_name)
1856 || dict_remove(mask_dnrs, chan_name))
1858 reply("CSMSG_DNR_REMOVED", chan_name);
1861 reply("CSMSG_NO_SUCH_DNR", chan_name);
1866 struct userNode *source;
1870 unsigned long min_set, max_set;
1871 unsigned long min_expires, max_expires;
1876 dnr_search_matches(const struct do_not_register *dnr, const struct dnr_search *search)
1878 return !((dnr->set < search->min_set)
1879 || (dnr->set > search->max_set)
1880 || (dnr->expires < search->min_expires)
1881 || (search->max_expires
1882 && ((dnr->expires == 0)
1883 || (dnr->expires > search->max_expires)))
1884 || (search->chan_mask
1885 && !match_ircglob(dnr->chan_name, search->chan_mask))
1886 || (search->setter_mask
1887 && !match_ircglob(dnr->setter, search->setter_mask))
1888 || (search->reason_mask
1889 && !match_ircglob(dnr->reason, search->reason_mask)));
1892 static struct dnr_search *
1893 dnr_search_create(struct userNode *user, struct svccmd *cmd, unsigned int argc, char *argv[])
1895 struct dnr_search *discrim;
1898 discrim = calloc(1, sizeof(*discrim));
1899 discrim->source = user;
1900 discrim->chan_mask = NULL;
1901 discrim->setter_mask = NULL;
1902 discrim->reason_mask = NULL;
1903 discrim->max_set = INT_MAX;
1904 discrim->limit = 50;
1906 for(ii=0; ii<argc; ++ii)
1910 reply("MSG_MISSING_PARAMS", argv[ii]);
1913 else if(0 == irccasecmp(argv[ii], "channel"))
1915 discrim->chan_mask = argv[++ii];
1917 else if(0 == irccasecmp(argv[ii], "setter"))
1919 discrim->setter_mask = argv[++ii];
1921 else if(0 == irccasecmp(argv[ii], "reason"))
1923 discrim->reason_mask = argv[++ii];
1925 else if(0 == irccasecmp(argv[ii], "limit"))
1927 discrim->limit = strtoul(argv[++ii], NULL, 0);
1929 else if(0 == irccasecmp(argv[ii], "set"))
1931 const char *cmp = argv[++ii];
1934 discrim->min_set = now - ParseInterval(cmp + 2);
1936 discrim->min_set = now - (ParseInterval(cmp + 1) - 1);
1937 } else if(cmp[0] == '=') {
1938 discrim->min_set = discrim->max_set = now - ParseInterval(cmp + 1);
1939 } else if(cmp[0] == '>') {
1941 discrim->max_set = now - ParseInterval(cmp + 2);
1943 discrim->max_set = now - (ParseInterval(cmp + 1) - 1);
1945 discrim->max_set = now - (ParseInterval(cmp) - 1);
1948 else if(0 == irccasecmp(argv[ii], "expires"))
1950 const char *cmp = argv[++ii];
1953 discrim->max_expires = now + ParseInterval(cmp + 2);
1955 discrim->max_expires = now + (ParseInterval(cmp + 1) - 1);
1956 } else if(cmp[0] == '=') {
1957 discrim->min_expires = discrim->max_expires = now + ParseInterval(cmp + 1);
1958 } else if(cmp[0] == '>') {
1960 discrim->min_expires = now + ParseInterval(cmp + 2);
1962 discrim->min_expires = now + (ParseInterval(cmp + 1) - 1);
1964 discrim->min_expires = now + (ParseInterval(cmp) - 1);
1969 reply("MSG_INVALID_CRITERIA", argv[ii]);
1980 typedef int (*dnr_search_func)(struct do_not_register *match, void *extra);
1983 dnr_search(struct dnr_search *discrim, dnr_search_func dsf, void *data)
1985 struct do_not_register *dnr;
1986 dict_iterator_t next;
1991 /* Initialize local variables. */
1994 if(discrim->chan_mask)
1996 int shift = (discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*') ? 2 : 0;
1997 if('\0' == discrim->chan_mask[shift + strcspn(discrim->chan_mask+shift, "*?")])
2001 if(target_fixed && discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*')
2003 /* Check against account-based DNRs. */
2004 dnr = dict_find(handle_dnrs, discrim->chan_mask + 2, NULL);
2005 if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
2008 else if(target_fixed)
2010 /* Check against channel-based DNRs. */
2011 dnr = dict_find(plain_dnrs, discrim->chan_mask, NULL);
2012 if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
2017 /* Exhaustively search account DNRs. */
2018 for(it = dict_first(handle_dnrs); it; it = next)
2020 next = iter_next(it);
2021 dnr = iter_data(it);
2022 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
2026 /* Do the same for channel DNRs. */
2027 for(it = dict_first(plain_dnrs); it; it = next)
2029 next = iter_next(it);
2030 dnr = iter_data(it);
2031 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
2035 /* Do the same for wildcarded channel DNRs. */
2036 for(it = dict_first(mask_dnrs); it; it = next)
2038 next = iter_next(it);
2039 dnr = iter_data(it);
2040 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
2048 dnr_remove_func(struct do_not_register *match, void *extra)
2050 struct userNode *user;
2053 chan_name = alloca(strlen(match->chan_name) + 1);
2054 strcpy(chan_name, match->chan_name);
2056 if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
2057 || dict_remove(plain_dnrs, chan_name)
2058 || dict_remove(mask_dnrs, chan_name))
2060 send_message(user, chanserv, "CSMSG_DNR_REMOVED", chan_name);
2066 dnr_count_func(struct do_not_register *match, void *extra)
2068 return 0; (void)match; (void)extra;
2071 static MODCMD_FUNC(cmd_dnrsearch)
2073 struct dnr_search *discrim;
2074 dnr_search_func action;
2075 struct svccmd *subcmd;
2076 unsigned int matches;
2079 sprintf(buf, "dnrsearch %s", argv[1]);
2080 subcmd = dict_find(cmd->parent->commands, buf, NULL);
2083 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
2086 if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
2088 if(!irccasecmp(argv[1], "print"))
2089 action = dnr_print_func;
2090 else if(!irccasecmp(argv[1], "remove"))
2091 action = dnr_remove_func;
2092 else if(!irccasecmp(argv[1], "count"))
2093 action = dnr_count_func;
2096 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
2100 discrim = dnr_search_create(user, cmd, argc-2, argv+2);
2104 if(action == dnr_print_func)
2105 reply("CSMSG_DNR_SEARCH_RESULTS");
2106 matches = dnr_search(discrim, action, user);
2108 reply("MSG_MATCH_COUNT", matches);
2110 reply("MSG_NO_MATCHES");
2116 chanserv_get_owned_count(struct handle_info *hi)
2118 struct userData *cList;
2121 for(owned=0, cList=hi->channels; cList; cList=cList->u_next)
2122 if(cList->access == UL_OWNER)
2127 static CHANSERV_FUNC(cmd_register)
2129 struct handle_info *handle;
2130 struct chanData *cData;
2131 struct modeNode *mn;
2132 char reason[MAXLEN];
2134 unsigned int new_channel, force=0;
2135 struct do_not_register *dnr;
2139 if(channel->channel_info)
2141 reply("CSMSG_ALREADY_REGGED", channel->name);
2145 if(channel->bad_channel)
2147 reply("CSMSG_ILLEGAL_CHANNEL", channel->name);
2152 && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
2154 reply("CSMSG_MUST_BE_OPPED", channel->name);
2159 chan_name = channel->name;
2163 if((argc < 2) || !IsChannelName(argv[1]))
2165 reply("MSG_NOT_CHANNEL_NAME");
2169 if(opserv_bad_channel(argv[1]))
2171 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2176 chan_name = argv[1];
2179 if(argc >= (new_channel+2))
2181 if(!IsHelping(user))
2183 reply("CSMSG_PROXY_FORBIDDEN");
2187 if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
2189 force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
2190 dnr = chanserv_is_dnr(chan_name, handle);
2194 handle = user->handle_info;
2195 dnr = chanserv_is_dnr(chan_name, handle);
2199 if(!IsHelping(user))
2200 reply("CSMSG_DNR_CHANNEL", chan_name);
2202 chanserv_show_dnrs(user, cmd, chan_name, handle->handle);
2206 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2208 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
2213 channel = AddChannel(argv[1], now, NULL, NULL);
2215 cData = register_channel(channel, user->handle_info->handle);
2216 scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL), NULL);
2217 cData->modes = chanserv_conf.default_modes;
2219 cData->modes.modes_set |= MODE_REGISTERED;
2220 if (IsOffChannel(cData))
2222 mod_chanmode_announce(chanserv, channel, &cData->modes);
2226 struct mod_chanmode *change = mod_chanmode_dup(&cData->modes, 1);
2227 change->args[change->argc].mode = MODE_CHANOP;
2228 change->args[change->argc].u.member = AddChannelUser(chanserv, channel);
2230 mod_chanmode_announce(chanserv, channel, change);
2231 mod_chanmode_free(change);
2234 /* Initialize the channel's max user record. */
2235 cData->max = channel->members.used;
2236 cData->max_time = 0;
2238 if(handle != user->handle_info)
2239 reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
2241 reply("CSMSG_REG_SUCCESS", channel->name);
2243 sprintf(reason, "%s registered to %s by %s.", channel->name, handle->handle, user->handle_info->handle);
2244 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2249 make_confirmation_string(struct userData *uData)
2251 static char strbuf[16];
2256 for(src = uData->handle->handle; *src; )
2257 accum = accum * 31 + toupper(*src++);
2259 for(src = uData->channel->channel->name; *src; )
2260 accum = accum * 31 + toupper(*src++);
2261 sprintf(strbuf, "%08x", accum);
2265 static CHANSERV_FUNC(cmd_unregister)
2268 char reason[MAXLEN];
2269 struct chanData *cData;
2270 struct userData *uData;
2272 cData = channel->channel_info;
2275 reply("CSMSG_NOT_REGISTERED", channel->name);
2279 uData = GetChannelUser(cData, user->handle_info);
2280 if(!uData || (uData->access < UL_OWNER))
2282 reply("CSMSG_NO_ACCESS");
2286 if(IsProtected(cData))
2288 reply("CSMSG_UNREG_NODELETE", channel->name);
2292 if(!IsHelping(user))
2294 const char *confirm_string;
2295 if(IsSuspended(cData))
2297 reply("CSMSG_CHAN_SUSPENDED", channel->name, cData->suspended->reason);
2300 confirm_string = make_confirmation_string(uData);
2301 if((argc < 2) || strcmp(argv[1], confirm_string))
2303 reply("CSMSG_CONFIRM_UNREG", confirm_string);
2308 sprintf(reason, "unregistered by %s.", user->handle_info->handle);
2309 name = strdup(channel->name);
2310 unregister_channel(cData, reason);
2311 spamserv_cs_unregister(user, channel, manually, "unregistered");
2312 reply("CSMSG_UNREG_SUCCESS", name);
2318 ss_cs_join_channel(struct chanNode *channel, int spamserv_join)
2320 extern struct userNode *spamserv;
2321 struct mod_chanmode *change;
2323 if(spamserv && spamserv_join && get_chanInfo(channel->name))
2325 change = mod_chanmode_alloc(2);
2327 change->args[0].mode = MODE_CHANOP;
2328 change->args[0].u.member = AddChannelUser(chanserv, channel);
2329 change->args[1].mode = MODE_CHANOP;
2330 change->args[1].u.member = AddChannelUser(spamserv, channel);
2334 change = mod_chanmode_alloc(1);
2336 change->args[0].mode = MODE_CHANOP;
2337 change->args[0].u.member = AddChannelUser(chanserv, channel);
2340 mod_chanmode_announce(chanserv, channel, change);
2341 mod_chanmode_free(change);
2344 static CHANSERV_FUNC(cmd_move)
2346 struct mod_chanmode change;
2347 struct chanNode *target;
2348 struct modeNode *mn;
2349 struct userData *uData;
2350 char reason[MAXLEN];
2351 struct do_not_register *dnr;
2352 int chanserv_join = 0, spamserv_join;
2356 if(IsProtected(channel->channel_info))
2358 reply("CSMSG_MOVE_NODELETE", channel->name);
2362 if(!IsChannelName(argv[1]))
2364 reply("MSG_NOT_CHANNEL_NAME");
2368 if(opserv_bad_channel(argv[1]))
2370 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2374 if(!IsHelping(user) || (argc < 3) || irccasecmp(argv[2], "force"))
2376 for(uData = channel->channel_info->users; uData; uData = uData->next)
2378 if((uData->access == UL_OWNER) && (dnr = chanserv_is_dnr(argv[1], uData->handle)))
2380 if(!IsHelping(user))
2381 reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]);
2383 chanserv_show_dnrs(user, cmd, argv[1], uData->handle->handle);
2389 mod_chanmode_init(&change);
2390 if(!(target = GetChannel(argv[1])))
2392 target = AddChannel(argv[1], now, NULL, NULL);
2393 if(!IsSuspended(channel->channel_info))
2396 else if(target->channel_info)
2398 reply("CSMSG_ALREADY_REGGED", target->name);
2401 else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
2402 && !IsHelping(user))
2404 reply("CSMSG_MUST_BE_OPPED", target->name);
2407 else if(!IsSuspended(channel->channel_info))
2412 /* Clear MODE_REGISTERED from old channel, add it to new. */
2414 change.modes_clear = MODE_REGISTERED;
2415 mod_chanmode_announce(chanserv, channel, &change);
2416 change.modes_clear = 0;
2417 change.modes_set = MODE_REGISTERED;
2418 mod_chanmode_announce(chanserv, target, &change);
2421 /* Move the channel_info to the target channel; it
2422 shouldn't be necessary to clear timeq callbacks
2423 for the old channel. */
2424 target->channel_info = channel->channel_info;
2425 target->channel_info->channel = target;
2426 channel->channel_info = NULL;
2428 /* Check whether users are present in the new channel. */
2429 for(uData = target->channel_info->users; uData; uData = uData->next)
2430 scan_user_presence(uData, NULL);
2432 spamserv_join = spamserv_cs_move_merge(user, channel, target, 1);
2435 ss_cs_join_channel(target, spamserv_join);
2437 sprintf(reason, "%s moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
2438 if(!IsSuspended(target->channel_info))
2440 char reason2[MAXLEN];
2441 sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
2442 DelChannelUser(chanserv, channel, reason2, 0);
2444 UnlockChannel(channel);
2445 LockChannel(target);
2446 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2447 reply("CSMSG_MOVE_SUCCESS", target->name);
2452 merge_users(struct chanData *source, struct chanData *target)
2454 struct userData *suData, *tuData, *next;
2460 /* Insert the source's users into the scratch area. */
2461 for(suData = source->users; suData; suData = suData->next)
2462 dict_insert(merge, suData->handle->handle, suData);
2464 /* Iterate through the target's users, looking for
2465 users common to both channels. The lower access is
2466 removed from either the scratch area or target user
2468 for(tuData = target->users; tuData; tuData = next)
2470 struct userData *choice;
2472 next = tuData->next;
2474 /* If a source user exists with the same handle as a target
2475 channel's user, resolve the conflict by removing one. */
2476 suData = dict_find(merge, tuData->handle->handle, NULL);
2480 /* Pick the data we want to keep. */
2481 /* If the access is the same, use the later seen time. */
2482 if(suData->access == tuData->access)
2483 choice = (suData->seen > tuData->seen) ? suData : tuData;
2484 else /* Otherwise, keep the higher access level. */
2485 choice = (suData->access > tuData->access) ? suData : tuData;
2486 /* Use the later seen time. */
2487 if(suData->seen < tuData->seen)
2488 suData->seen = tuData->seen;
2490 tuData->seen = suData->seen;
2492 /* Remove the user that wasn't picked. */
2493 if(choice == tuData)
2495 dict_remove(merge, suData->handle->handle);
2496 del_channel_user(suData, 0);
2499 del_channel_user(tuData, 0);
2502 /* Move the remaining users to the target channel. */
2503 for(it = dict_first(merge); it; it = iter_next(it))
2505 suData = iter_data(it);
2507 /* Insert the user into the target channel's linked list. */
2508 suData->prev = NULL;
2509 suData->next = target->users;
2510 suData->channel = target;
2513 target->users->prev = suData;
2514 target->users = suData;
2516 /* Update the user counts for the target channel; the
2517 source counts are left alone. */
2518 target->userCount++;
2520 /* Check whether the user is in the target channel. */
2521 scan_user_presence(suData, NULL);
2524 /* Possible to assert (source->users == NULL) here. */
2525 source->users = NULL;
2530 merge_bans(struct chanData *source, struct chanData *target)
2532 struct banData *sbData, *tbData, *sNext, *tNext, *tFront;
2534 /* Hold on to the original head of the target ban list
2535 to avoid comparing source bans with source bans. */
2536 tFront = target->bans;
2538 /* Perform a totally expensive O(n*m) merge, ick. */
2539 for(sbData = source->bans; sbData; sbData = sNext)
2541 /* Flag to track whether the ban's been moved
2542 to the destination yet. */
2545 /* Possible to assert (sbData->prev == NULL) here. */
2546 sNext = sbData->next;
2548 for(tbData = tFront; tbData; tbData = tNext)
2550 tNext = tbData->next;
2552 /* Perform two comparisons between each source
2553 and target ban, conflicts are resolved by
2554 keeping the broader ban and copying the later
2555 expiration and triggered time. */
2556 if(match_ircglobs(tbData->mask, sbData->mask))
2558 /* There is a broader ban in the target channel that
2559 overrides one in the source channel; remove the
2560 source ban and break. */
2561 if(sbData->expires > tbData->expires)
2562 tbData->expires = sbData->expires;
2563 if(sbData->triggered > tbData->triggered)
2564 tbData->triggered = sbData->triggered;
2565 del_channel_ban(sbData);
2568 else if(match_ircglobs(sbData->mask, tbData->mask))
2570 /* There is a broader ban in the source channel that
2571 overrides one in the target channel; remove the
2572 target ban, fall through and move the source over. */
2573 if(tbData->expires > sbData->expires)
2574 sbData->expires = tbData->expires;
2575 if(tbData->triggered > sbData->triggered)
2576 sbData->triggered = tbData->triggered;
2577 if(tbData == tFront)
2579 del_channel_ban(tbData);
2582 /* Source bans can override multiple target bans, so
2583 we allow a source to run through this loop multiple
2584 times, but we can only move it once. */
2589 /* Remove the source ban from the source ban list. */
2591 sbData->next->prev = sbData->prev;
2593 /* Modify the source ban's associated channel. */
2594 sbData->channel = target;
2596 /* Insert the ban into the target channel's linked list. */
2597 sbData->prev = NULL;
2598 sbData->next = target->bans;
2601 target->bans->prev = sbData;
2602 target->bans = sbData;
2604 /* Update the user counts for the target channel. */
2609 /* Possible to assert (source->bans == NULL) here. */
2610 source->bans = NULL;
2614 merge_data(struct chanData *source, struct chanData *target)
2616 /* Use more recent visited and owner-transfer time; use older
2617 * registered time. Bitwise or may_opchan. Use higher max.
2618 * Do not touch last_refresh, ban count or user counts.
2620 if(source->visited > target->visited)
2621 target->visited = source->visited;
2622 if(source->registered < target->registered)
2623 target->registered = source->registered;
2624 if(source->ownerTransfer > target->ownerTransfer)
2625 target->ownerTransfer = source->ownerTransfer;
2626 if(source->may_opchan)
2627 target->may_opchan = 1;
2628 if(source->max > target->max) {
2629 target->max = source->max;
2630 target->max_time = source->max_time;
2635 merge_channel(struct chanData *source, struct chanData *target)
2637 merge_users(source, target);
2638 merge_bans(source, target);
2639 merge_data(source, target);
2642 static CHANSERV_FUNC(cmd_merge)
2644 struct userData *target_user;
2645 struct chanNode *target;
2646 char reason[MAXLEN];
2650 /* Make sure the target channel exists and is registered to the user
2651 performing the command. */
2652 if(!(target = GetChannel(argv[1])))
2654 reply("MSG_INVALID_CHANNEL");
2658 if(!target->channel_info)
2660 reply("CSMSG_NOT_REGISTERED", target->name);
2664 if(IsProtected(channel->channel_info))
2666 reply("CSMSG_MERGE_NODELETE");
2670 if(IsSuspended(target->channel_info))
2672 reply("CSMSG_MERGE_SUSPENDED");
2676 if(channel == target)
2678 reply("CSMSG_MERGE_SELF");
2682 target_user = GetChannelUser(target->channel_info, user->handle_info);
2683 if(!target_user || (target_user->access < UL_OWNER))
2685 reply("CSMSG_MERGE_NOT_OWNER");
2689 /* Merge the channel structures and associated data. */
2690 merge_channel(channel->channel_info, target->channel_info);
2691 spamserv_cs_move_merge(user, channel, target, 0);
2692 sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
2693 unregister_channel(channel->channel_info, reason);
2694 reply("CSMSG_MERGE_SUCCESS", target->name);
2698 static CHANSERV_FUNC(cmd_opchan)
2700 struct mod_chanmode change;
2701 if(!IsHelping(user) && !channel->channel_info->may_opchan)
2703 reply("CSMSG_ALREADY_OPCHANNED", channel->name);
2706 channel->channel_info->may_opchan = 0;
2707 mod_chanmode_init(&change);
2709 change.args[0].mode = MODE_CHANOP;
2710 change.args[0].u.member = GetUserMode(channel, chanserv);
2711 if(!change.args[0].u.member)
2713 reply("CSMSG_OUT_OF_CHANNEL", channel->name);
2716 mod_chanmode_announce(chanserv, channel, &change);
2717 reply("CSMSG_OPCHAN_DONE", channel->name);
2721 static CHANSERV_FUNC(cmd_adduser)
2723 struct userData *actee;
2724 struct userData *actor, *real_actor;
2725 struct handle_info *handle;
2726 unsigned short access_level, override = 0;
2730 if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
2732 reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
2736 access_level = user_level_from_name(argv[2], UL_OWNER);
2739 reply("CSMSG_INVALID_ACCESS", argv[2]);
2743 actor = GetChannelUser(channel->channel_info, user->handle_info);
2744 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2746 if(actor->access <= access_level)
2748 reply("CSMSG_NO_BUMP_ACCESS");
2752 /* Trying to add someone with equal/more access? */
2753 if (!real_actor || real_actor->access <= access_level)
2754 override = CMD_LOG_OVERRIDE;
2756 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2759 if((actee = GetTrueChannelAccess(channel->channel_info, handle)))
2761 reply("CSMSG_USER_EXISTS", handle->handle, channel->name, actee->access);
2765 actee = add_channel_user(channel->channel_info, handle, access_level, 0, NULL);
2766 scan_user_presence(actee, NULL);
2767 reply("CSMSG_ADDED_USER", handle->handle, channel->name, access_level);
2768 return 1 | override;
2771 static CHANSERV_FUNC(cmd_clvl)
2773 struct handle_info *handle;
2774 struct userData *victim;
2775 struct userData *actor, *real_actor;
2776 unsigned short new_access, override = 0;
2777 int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
2781 actor = GetChannelUser(channel->channel_info, user->handle_info);
2782 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2784 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2787 if(handle == user->handle_info && !privileged)
2789 reply("CSMSG_NO_SELF_CLVL");
2793 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2795 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2799 if(actor->access <= victim->access && !privileged)
2801 reply("MSG_USER_OUTRANKED", handle->handle);
2805 new_access = user_level_from_name(argv[2], UL_OWNER);
2809 reply("CSMSG_INVALID_ACCESS", argv[2]);
2813 if(new_access >= actor->access && !privileged)
2815 reply("CSMSG_NO_BUMP_ACCESS");
2819 /* Trying to clvl a equal/higher user? */
2820 if(!real_actor || (real_actor->access <= victim->access && handle != user->handle_info))
2821 override = CMD_LOG_OVERRIDE;
2822 /* Trying to clvl someone to equal/higher access? */
2823 if(!real_actor || new_access >= real_actor->access)
2824 override = CMD_LOG_OVERRIDE;
2825 /* Helpers clvling themselves get caught by the "clvl someone to equal/higher access" check.
2826 * If they lower their own access it's not a big problem.
2829 victim->access = new_access;
2830 reply("CSMSG_CHANGED_ACCESS", handle->handle, new_access, channel->name);
2831 return 1 | override;
2834 static CHANSERV_FUNC(cmd_deluser)
2836 struct handle_info *handle;
2837 struct userData *victim;
2838 struct userData *actor, *real_actor;
2839 unsigned short access_level, override = 0;
2844 actor = GetChannelUser(channel->channel_info, user->handle_info);
2845 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2847 if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
2850 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2852 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2858 access_level = user_level_from_name(argv[1], UL_OWNER);
2861 reply("CSMSG_INVALID_ACCESS", argv[1]);
2864 if(access_level != victim->access)
2866 reply("CSMSG_INCORRECT_ACCESS", handle->handle, victim->access, argv[1]);
2872 access_level = victim->access;
2875 if((actor->access <= victim->access) && !IsHelping(user))
2877 reply("MSG_USER_OUTRANKED", victim->handle->handle);
2881 /* If people delete themselves it is an override, but they
2882 * could've used deleteme so we don't log it as an override
2884 if(!real_actor || (real_actor->access <= victim->access && real_actor != victim))
2885 override = CMD_LOG_OVERRIDE;
2887 chan_name = strdup(channel->name);
2888 del_channel_user(victim, 1);
2889 reply("CSMSG_DELETED_USER", handle->handle, access_level, chan_name);
2891 return 1 | override;
2895 cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
2897 struct userData *actor, *real_actor, *uData, *next;
2898 unsigned int override = 0;
2900 actor = GetChannelUser(channel->channel_info, user->handle_info);
2901 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2903 if(min_access > max_access)
2905 reply("CSMSG_BAD_RANGE", min_access, max_access);
2909 if(actor->access <= max_access)
2911 reply("CSMSG_NO_ACCESS");
2915 if(!real_actor || real_actor->access <= max_access)
2916 override = CMD_LOG_OVERRIDE;
2918 for(uData = channel->channel_info->users; uData; uData = next)
2922 if((uData->access >= min_access)
2923 && (uData->access <= max_access)
2924 && match_ircglob(uData->handle->handle, mask))
2925 del_channel_user(uData, 1);
2928 reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
2929 return 1 | override;
2932 static CHANSERV_FUNC(cmd_mdelowner)
2934 return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
2937 static CHANSERV_FUNC(cmd_mdelcoowner)
2939 return cmd_mdel_user(user, channel, UL_COOWNER, UL_COOWNER, argv[1], cmd);
2942 static CHANSERV_FUNC(cmd_mdelmaster)
2944 return cmd_mdel_user(user, channel, UL_MASTER, UL_MASTER, argv[1], cmd);
2947 static CHANSERV_FUNC(cmd_mdelop)
2949 return cmd_mdel_user(user, channel, UL_OP, UL_OP, argv[1], cmd);
2952 static CHANSERV_FUNC(cmd_mdelpeon)
2954 return cmd_mdel_user(user, channel, UL_PEON, UL_PEON, argv[1], cmd);
2958 cmd_trim_bans(struct userNode *user, struct chanNode *channel, unsigned long duration)
2960 struct banData *bData, *next;
2961 char interval[INTERVALLEN];
2963 unsigned long limit;
2966 limit = now - duration;
2967 for(bData = channel->channel_info->bans; bData; bData = next)
2971 if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
2974 del_channel_ban(bData);
2978 intervalString(interval, duration, user->handle_info);
2979 send_message(user, chanserv, "CSMSG_TRIMMED_BANS", count, channel->name, interval);
2984 cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration, int vacation)
2986 struct userData *actor, *uData, *next;
2987 char interval[INTERVALLEN];
2989 unsigned long limit;
2991 actor = GetChannelAccess(channel->channel_info, user->handle_info);
2992 if(min_access > max_access)
2994 send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
2998 if(!actor || actor->access <= max_access)
3000 send_message(user, chanserv, "CSMSG_NO_ACCESS");
3005 limit = now - duration;
3006 for(uData = channel->channel_info->users; uData; uData = next)
3010 if((uData->seen > limit)
3012 || (HANDLE_FLAGGED(uData->handle, FROZEN) && !vacation))
3015 if(((uData->access >= min_access) && (uData->access <= max_access))
3016 || (!max_access && (uData->access < actor->access)))
3018 del_channel_user(uData, 1);
3026 max_access = (actor->access > UL_OWNER) ? UL_OWNER : (actor->access - 1);
3028 send_message(user, chanserv, "CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration, user->handle_info));
3032 static CHANSERV_FUNC(cmd_trim)
3034 unsigned long duration;
3035 unsigned short min_level, max_level;
3040 vacation = argc > 3 && !strcmp(argv[3], "vacation");
3041 duration = ParseInterval(argv[2]);
3044 reply("CSMSG_CANNOT_TRIM");
3048 if(!irccasecmp(argv[1], "bans"))
3050 cmd_trim_bans(user, channel, duration);
3053 else if(!irccasecmp(argv[1], "users"))
3055 cmd_trim_users(user, channel, 0, 0, duration, vacation);
3058 else if(parse_level_range(&min_level, &max_level, argv[1]))
3060 cmd_trim_users(user, channel, min_level, max_level, duration, vacation);
3063 else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
3065 cmd_trim_users(user, channel, min_level, min_level, duration, vacation);
3070 reply("CSMSG_INVALID_TRIM", argv[1]);
3075 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
3076 to the user. cmd_all takes advantage of this. */
3077 static CHANSERV_FUNC(cmd_up)
3079 struct mod_chanmode change;
3080 struct userData *uData;
3083 mod_chanmode_init(&change);
3085 change.args[0].u.member = GetUserMode(channel, user);
3086 if(!change.args[0].u.member)
3089 reply("MSG_CHANNEL_ABSENT", channel->name);
3093 uData = GetChannelAccess(channel->channel_info, user->handle_info);
3097 reply("CSMSG_GODMODE_UP", argv[0]);
3100 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveOps])
3102 change.args[0].mode = MODE_CHANOP;
3103 errmsg = "CSMSG_ALREADY_OPPED";
3105 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveVoice])
3107 change.args[0].mode = MODE_VOICE;
3108 errmsg = "CSMSG_ALREADY_VOICED";
3113 reply("CSMSG_NO_ACCESS");
3116 change.args[0].mode &= ~change.args[0].u.member->modes;
3117 if(!change.args[0].mode)
3120 reply(errmsg, channel->name);
3123 modcmd_chanmode_announce(&change);
3127 static CHANSERV_FUNC(cmd_down)
3129 struct mod_chanmode change;
3131 mod_chanmode_init(&change);
3133 change.args[0].u.member = GetUserMode(channel, user);
3134 if(!change.args[0].u.member)
3137 reply("MSG_CHANNEL_ABSENT", channel->name);
3141 if(!change.args[0].u.member->modes)
3144 reply("CSMSG_ALREADY_DOWN", channel->name);
3148 change.args[0].mode = MODE_REMOVE | change.args[0].u.member->modes;
3149 modcmd_chanmode_announce(&change);
3153 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)
3155 struct userData *cList;
3157 for(cList = user->handle_info->channels; cList; cList = cList->u_next)
3159 if(IsSuspended(cList->channel)
3160 || IsUserSuspended(cList)
3161 || !GetUserMode(cList->channel->channel, user))
3164 mcmd(user, cList->channel->channel, 0, NULL, cmd);
3170 static CHANSERV_FUNC(cmd_upall)
3172 return cmd_all(CSFUNC_ARGS, cmd_up);
3175 static CHANSERV_FUNC(cmd_downall)
3177 return cmd_all(CSFUNC_ARGS, cmd_down);
3180 typedef int validate_func_t(struct userNode *user, struct chanNode *channel, struct userNode *victim);
3181 typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
3184 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)
3186 unsigned int ii, valid;
3187 struct userNode *victim;
3188 struct mod_chanmode *change;
3190 change = mod_chanmode_alloc(argc - 1);
3192 for(ii=valid=0; ++ii < argc; )
3194 if(!(victim = GetUserH(argv[ii])))
3196 change->args[valid].mode = mode;
3197 change->args[valid].u.member = GetUserMode(channel, victim);
3198 if(!change->args[valid].u.member)
3200 if(validate && !validate(user, channel, victim))
3205 change->argc = valid;
3206 if(valid < (argc-1))
3207 reply("CSMSG_PROCESS_FAILED");
3210 modcmd_chanmode_announce(change);
3211 reply(action, channel->name);
3213 mod_chanmode_free(change);
3217 static CHANSERV_FUNC(cmd_op)
3219 return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
3222 static CHANSERV_FUNC(cmd_deop)
3224 return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
3227 static CHANSERV_FUNC(cmd_voice)
3229 return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
3232 static CHANSERV_FUNC(cmd_devoice)
3234 return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
3238 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, unsigned int *victimCount, struct modeNode **victims)
3244 for(ii=0; ii<channel->members.used; ii++)
3246 struct modeNode *mn = channel->members.list[ii];
3248 if(IsService(mn->user))
3251 if(!user_matches_glob(mn->user, ban, MATCH_USENICK | MATCH_VISIBLE))
3254 if(protect_user(mn->user, user, channel->channel_info))
3258 victims[(*victimCount)++] = mn;
3264 eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3266 struct userNode *victim;
3267 struct modeNode **victims;
3268 unsigned int offset, n, victimCount, duration = 0;
3269 char *reason = "Bye.", *ban, *name;
3270 char interval[INTERVALLEN];
3272 offset = (action & ACTION_ADD_TIMED_BAN) ? 3 : 2;
3273 REQUIRE_PARAMS(offset);
3274 if(argc > offset && IsNetServ(user))
3276 if(*argv[offset] == '$') {
3277 struct userNode *hib;
3278 const char *accountnameb = argv[offset] + 1;
3279 if(!(hib = GetUserH(accountnameb)))
3281 reply("MSG_HANDLE_UNKNOWN", accountnameb);
3290 reason = unsplit_string(argv + offset, argc - offset, NULL);
3291 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
3293 /* Truncate the reason to a length of TOPICLEN, as
3294 the ircd does; however, leave room for an ellipsis
3295 and the kicker's nick. */
3296 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
3300 if((victim = GetUserH(argv[1])))
3302 victims = alloca(sizeof(victims[0]));
3303 victims[0] = GetUserMode(channel, victim);
3304 /* XXX: The comparison with ACTION_KICK is just because all
3305 * other actions can work on users outside the channel, and we
3306 * want to allow those (e.g. unbans) in that case. If we add
3307 * some other ejection action for in-channel users, change
3309 victimCount = victims[0] ? 1 : 0;
3311 if(IsService(victim))
3313 reply("MSG_SERVICE_IMMUNE", victim->nick);
3317 if((action == ACTION_KICK) && !victimCount)
3319 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
3323 if(protect_user(victim, user, channel->channel_info))
3325 reply("CSMSG_USER_PROTECTED", victim->nick);
3329 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
3330 name = victim->nick;
3332 else if(!is_ircmask(argv[1]) && (*argv[1] == '*'))
3334 struct handle_info *hi;
3335 extern const char *titlehost_suffix;
3336 char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
3337 const char *accountname = argv[1] + 1;
3339 if(!(hi = get_handle_info(accountname)))
3341 reply("MSG_HANDLE_UNKNOWN", accountname);
3345 snprintf(banmask, sizeof(banmask), "*!*@%s.*.%s", hi->handle, titlehost_suffix);
3346 victims = alloca(sizeof(victims[0]) * channel->members.used);
3348 if(bad_channel_ban(channel, user, banmask, &victimCount, victims))
3350 reply("CSMSG_MASK_PROTECTED", banmask);
3354 if((action == ACTION_KICK) && (victimCount == 0))
3356 reply("CSMSG_NO_MATCHING_USERS", channel->name, banmask);
3360 name = ban = strdup(banmask);
3364 if(!is_ircmask(argv[1]))
3366 reply("MSG_NICK_UNKNOWN", argv[1]);
3370 victims = alloca(sizeof(victims[0]) * channel->members.used);
3372 if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
3374 reply("CSMSG_MASK_PROTECTED", argv[1]);
3378 if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
3380 reply("CSMSG_LAME_MASK", argv[1]);
3384 if((action == ACTION_KICK) && (victimCount == 0))
3386 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
3390 name = ban = strdup(argv[1]);
3393 /* Truncate the ban in place if necessary; we must ensure
3394 that 'ban' is a valid ban mask before sanitizing it. */
3395 sanitize_ircmask(ban);
3397 if(action & ACTION_ADD_BAN)
3399 struct banData *bData, *next;
3401 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans)
3403 reply("CSMSG_MAXIMUM_BANS", chanserv_conf.max_chan_bans);
3408 if(action & ACTION_ADD_TIMED_BAN)
3410 duration = ParseInterval(argv[2]);
3414 reply("CSMSG_DURATION_TOO_LOW");
3418 else if(duration > (86400 * 365 * 2))
3420 reply("CSMSG_DURATION_TOO_HIGH");
3426 for(bData = channel->channel_info->bans; bData; bData = next)
3428 if(match_ircglobs(bData->mask, ban))
3430 int exact = !irccasecmp(bData->mask, ban);
3432 /* The ban is redundant; there is already a ban
3433 with the same effect in place. */
3437 free(bData->reason);
3438 bData->reason = strdup(reason);
3439 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
3441 reply("CSMSG_REASON_CHANGE", ban);
3445 if(exact && bData->expires)
3449 /* If the ban matches an existing one exactly,
3450 extend the expiration time if the provided
3451 duration is longer. */
3452 if(duration && (now + duration > bData->expires))
3454 bData->expires = now + duration;
3465 /* Delete the expiration timeq entry and
3466 requeue if necessary. */
3467 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
3470 timeq_add(bData->expires, expire_ban, bData);
3474 /* automated kickban */
3477 reply("CSMSG_BAN_EXTENDED", ban, intervalString(interval, duration, user->handle_info));
3479 reply("CSMSG_BAN_ADDED", name, channel->name);
3485 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3492 if(match_ircglobs(ban, bData->mask))
3494 /* The ban we are adding makes previously existing
3495 bans redundant; silently remove them. */
3496 del_channel_ban(bData);
3500 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);
3502 name = ban = strdup(bData->mask);
3506 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
3508 extern const char *hidden_host_suffix;
3509 const char *old_name = chanserv_conf.old_ban_names->list[n];
3511 unsigned int l1, l2;
3514 l2 = strlen(old_name);
3517 if(irccasecmp(ban + l1 - l2, old_name))
3519 new_mask = malloc(MAXLEN);
3520 sprintf(new_mask, "%.*s%s", (int)(l1-l2), ban, hidden_host_suffix);
3522 name = ban = new_mask;
3527 if(action & ACTION_BAN)
3529 unsigned int exists;
3530 struct mod_chanmode *change;
3532 if(channel->banlist.used >= MAXBANS)
3535 reply("CSMSG_BANLIST_FULL", channel->name);
3540 exists = ChannelBanExists(channel, ban);
3541 change = mod_chanmode_alloc(victimCount + 1);
3542 for(n = 0; n < victimCount; ++n)
3544 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_VOICE;
3545 change->args[n].u.member = victims[n];
3549 change->args[n].mode = MODE_BAN;
3550 change->args[n++].u.hostmask = ban;
3554 modcmd_chanmode_announce(change);
3556 mod_chanmode_announce(chanserv, channel, change);
3557 mod_chanmode_free(change);
3559 if(exists && (action == ACTION_BAN))
3562 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3568 if(action & ACTION_KICK)
3570 char kick_reason[MAXLEN];
3571 sprintf(kick_reason, "(%s) %s", user->nick, reason);
3573 for(n = 0; n < victimCount; n++)
3574 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
3579 /* No response, since it was automated. */
3581 else if(action & ACTION_ADD_BAN)
3584 reply("CSMSG_TIMED_BAN_ADDED", name, channel->name, intervalString(interval, duration, user->handle_info));
3586 reply("CSMSG_BAN_ADDED", name, channel->name);
3588 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
3589 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
3590 else if(action & ACTION_BAN)
3591 reply("CSMSG_BAN_DONE", name, channel->name);
3592 else if(action & ACTION_KICK && victimCount)
3593 reply("CSMSG_KICK_DONE", name, channel->name);
3599 static CHANSERV_FUNC(cmd_kickban)
3601 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
3604 static CHANSERV_FUNC(cmd_kick)
3606 return eject_user(CSFUNC_ARGS, ACTION_KICK);
3609 static CHANSERV_FUNC(cmd_ban)
3611 return eject_user(CSFUNC_ARGS, ACTION_BAN);
3614 static CHANSERV_FUNC(cmd_addban)
3616 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN);
3619 static CHANSERV_FUNC(cmd_addtimedban)
3621 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN);
3624 struct mod_chanmode *
3625 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
3627 struct mod_chanmode *change;
3628 unsigned char *match;
3629 unsigned int ii, count;
3631 match = alloca(bans->used);
3634 for(ii = count = 0; ii < bans->used; ++ii)
3636 match[ii] = user_matches_glob(actee, bans->list[ii]->ban,
3637 MATCH_USENICK | MATCH_VISIBLE);
3644 for(ii = count = 0; ii < bans->used; ++ii)
3646 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
3653 change = mod_chanmode_alloc(count);
3654 for(ii = count = 0; ii < bans->used; ++ii)
3658 change->args[count].mode = MODE_REMOVE | MODE_BAN;
3659 change->args[count++].u.hostmask = strdup(bans->list[ii]->ban);
3661 assert(count == change->argc);
3666 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3668 struct userNode *actee;
3674 /* may want to allow a comma delimited list of users... */
3675 if(!(actee = GetUserH(argv[1])))
3677 if(!is_ircmask(argv[1]) && *argv[1] == '*')
3679 char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
3680 const char *accountname = argv[1] + 1;
3682 snprintf(banmask, sizeof(banmask), "*!*@%s.*", accountname);
3683 mask = strdup(banmask);
3685 else if(!is_ircmask(argv[1]))
3687 reply("MSG_NICK_UNKNOWN", argv[1]);
3692 mask = strdup(argv[1]);
3696 /* We don't sanitize the mask here because ircu
3698 if(action & ACTION_UNBAN)
3700 struct mod_chanmode *change;
3701 change = find_matching_bans(&channel->banlist, actee, mask);
3706 modcmd_chanmode_announce(change);
3707 for(ii = 0; ii < change->argc; ++ii)
3708 free((char*)change->args[ii].u.hostmask);
3709 mod_chanmode_free(change);
3714 if(action & ACTION_DEL_BAN)
3716 struct banData *ban, *next;
3718 ban = channel->channel_info->bans;
3722 for( ; ban && !user_matches_glob(actee, ban->mask,
3723 MATCH_USENICK | MATCH_VISIBLE);
3726 for( ; ban && !match_ircglobs(mask, ban->mask);
3731 del_channel_ban(ban);
3738 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3740 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3746 static CHANSERV_FUNC(cmd_unban)
3748 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3751 static CHANSERV_FUNC(cmd_delban)
3753 /* it doesn't necessarily have to remove the channel ban - may want
3754 to make that an option. */
3755 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_BAN);
3758 static CHANSERV_FUNC(cmd_unbanme)
3760 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3761 long flags = ACTION_UNBAN;
3763 /* remove permanent bans if the user has the proper access. */
3764 if(uData->access >= UL_MASTER)
3765 flags |= ACTION_DEL_BAN;
3767 argv[1] = user->nick;
3768 return unban_user(user, channel, 2, argv, cmd, flags);
3771 static CHANSERV_FUNC(cmd_unbanall)
3773 struct mod_chanmode *change;
3776 if(!channel->banlist.used)
3778 reply("CSMSG_NO_BANS", channel->name);
3782 change = mod_chanmode_alloc(channel->banlist.used);
3783 for(ii=0; ii<channel->banlist.used; ii++)
3785 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3786 change->args[ii].u.hostmask = strdup(channel->banlist.list[ii]->ban);
3788 modcmd_chanmode_announce(change);
3789 for(ii = 0; ii < change->argc; ++ii)
3790 free((char*)change->args[ii].u.hostmask);
3791 mod_chanmode_free(change);
3792 reply("CSMSG_BANS_REMOVED", channel->name);
3796 static CHANSERV_FUNC(cmd_open)
3798 struct mod_chanmode *change;
3801 change = find_matching_bans(&channel->banlist, user, NULL);
3803 change = mod_chanmode_alloc(0);
3804 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3805 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3806 && channel->channel_info->modes.modes_set)
3807 change->modes_clear &= ~channel->channel_info->modes.modes_set;
3808 modcmd_chanmode_announce(change);
3809 reply("CSMSG_CHANNEL_OPENED", channel->name);
3810 for(ii = 0; ii < change->argc; ++ii)
3811 free((char*)change->args[ii].u.hostmask);
3812 mod_chanmode_free(change);
3816 static CHANSERV_FUNC(cmd_myaccess)
3818 static struct string_buffer sbuf;
3819 struct handle_info *target_handle;
3820 struct userData *uData;
3824 target_handle = user->handle_info;
3825 else if(!IsStaff(user))
3827 reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
3830 else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3833 if(!oper_outranks(user, target_handle))
3836 if(!target_handle->channels)
3838 reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
3842 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3843 for(uData = target_handle->channels; uData; uData = uData->u_next)
3845 struct chanData *cData = uData->channel;
3848 if(uData->access > UL_OWNER)
3850 if(IsProtected(cData)
3851 && (target_handle != user->handle_info)
3852 && !GetTrueChannelAccess(cData, user->handle_info))
3855 string_buffer_append_printf(&sbuf, "[%s (%d", cData->channel->name, uData->access);
3856 if(uData->flags != USER_AUTO_OP)
3857 string_buffer_append(&sbuf, ',');
3858 if(IsUserSuspended(uData))
3859 string_buffer_append(&sbuf, 's');
3860 if(IsUserAutoOp(uData))
3862 if(uData->access >= cData->lvlOpts[lvlGiveOps])
3863 string_buffer_append(&sbuf, 'o');
3864 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
3865 string_buffer_append(&sbuf, 'v');
3867 if(IsUserAutoInvite(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe]))
3868 string_buffer_append(&sbuf, 'i');
3870 string_buffer_append_printf(&sbuf, ")] %s", uData->info);
3872 string_buffer_append_string(&sbuf, ")]");
3873 string_buffer_append(&sbuf, '\0');
3874 send_message_type(4, user, cmd->parent->bot, "%s", sbuf.list);
3878 reply("CSMSG_MYACCESS_COUNT_1", target_handle->handle, ccount);
3880 reply("CSMSG_MYACCESS_COUNT", target_handle->handle, ccount);
3886 static CHANSERV_FUNC(cmd_access)
3888 struct userNode *target;
3889 struct handle_info *target_handle;
3890 struct userData *uData;
3892 char prefix[MAXLEN];
3897 target_handle = target->handle_info;
3899 else if((target = GetUserH(argv[1])))
3901 target_handle = target->handle_info;
3903 else if(argv[1][0] == '*')
3905 if(!(target_handle = get_handle_info(argv[1]+1)))
3907 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3913 reply("MSG_NICK_UNKNOWN", argv[1]);
3917 assert(target || target_handle);
3919 if(target == chanserv)
3921 reply("CSMSG_IS_CHANSERV");
3929 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
3934 reply("MSG_USER_AUTHENTICATE", target->nick);
3937 reply("MSG_AUTHENTICATE");
3943 const char *epithet = NULL, *type = NULL;
3946 epithet = chanserv_conf.irc_operator_epithet;
3947 type = user_find_message(user, "CSMSG_OPERATOR_TITLE");
3949 else if(IsNetworkHelper(target))
3951 epithet = chanserv_conf.network_helper_epithet;
3952 type = user_find_message(user, "CSMSG_UC_H_TITLE");
3954 else if(IsSupportHelper(target))
3956 epithet = chanserv_conf.support_helper_epithet;
3957 type = user_find_message(user, "CSMSG_LC_H_TITLE");
3961 if(target_handle->epithet)
3962 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
3964 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
3966 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
3970 sprintf(prefix, "%s", target_handle->handle);
3973 if(!channel->channel_info)
3975 reply("CSMSG_NOT_REGISTERED", channel->name);
3979 helping = HANDLE_FLAGGED(target_handle, HELPING)
3980 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
3981 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
3983 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
3984 /* To prevent possible information leaks, only show infolines
3985 * if the requestor is in the channel or it's their own
3987 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
3989 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
3991 /* Likewise, only say it's suspended if the user has active
3992 * access in that channel or it's their own entry. */
3993 if(IsUserSuspended(uData)
3994 && (GetChannelUser(channel->channel_info, user->handle_info)
3995 || (user->handle_info == uData->handle)))
3997 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
4002 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
4009 zoot_list(struct listData *list)
4011 struct userData *uData;
4012 unsigned int start, curr, highest, lowest;
4013 struct helpfile_table tmp_table;
4014 const char **temp, *msg;
4016 if(list->table.length == 1)
4019 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
4021 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
4022 msg = user_find_message(list->user, "MSG_NONE");
4023 send_message_type(4, list->user, list->bot, " %s", msg);
4025 tmp_table.width = list->table.width;
4026 tmp_table.flags = list->table.flags;
4027 list->table.contents[0][0] = " ";
4028 highest = list->highest;
4029 if(list->lowest != 0)
4030 lowest = list->lowest;
4031 else if(highest < 100)
4034 lowest = highest - 100;
4035 for(start = curr = 1; curr < list->table.length; )
4037 uData = list->users[curr-1];
4038 list->table.contents[curr++][0] = " ";
4039 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
4042 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, lowest, highest, list->search);
4044 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, lowest, highest);
4045 temp = list->table.contents[--start];
4046 list->table.contents[start] = list->table.contents[0];
4047 tmp_table.contents = list->table.contents + start;
4048 tmp_table.length = curr - start;
4049 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
4050 list->table.contents[start] = temp;
4052 highest = lowest - 1;
4053 lowest = (highest < 100) ? 0 : (highest - 99);
4059 def_list(struct listData *list)
4063 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
4065 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
4066 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4067 if(list->table.length == 1)
4069 msg = user_find_message(list->user, "MSG_NONE");
4070 send_message_type(4, list->user, list->bot, " %s", msg);
4075 userData_access_comp(const void *arg_a, const void *arg_b)
4077 const struct userData *a = *(struct userData**)arg_a;
4078 const struct userData *b = *(struct userData**)arg_b;
4080 if(a->access != b->access)
4081 res = b->access - a->access;
4083 res = irccasecmp(a->handle->handle, b->handle->handle);
4088 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
4090 void (*send_list)(struct listData *);
4091 struct userData *uData;
4092 struct listData lData;
4093 unsigned int matches;
4097 lData.bot = cmd->parent->bot;
4098 lData.channel = channel;
4099 lData.lowest = lowest;
4100 lData.highest = highest;
4101 lData.search = (argc > 1) ? argv[1] : NULL;
4102 send_list = def_list;
4103 (void)zoot_list; /* since it doesn't show user levels */
4105 if(user->handle_info)
4107 switch(user->handle_info->userlist_style)
4109 case HI_STYLE_DEF: send_list = def_list; break;
4110 case HI_STYLE_ZOOT: send_list = def_list; break;
4114 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
4116 for(uData = channel->channel_info->users; uData; uData = uData->next)
4118 if((uData->access < lowest)
4119 || (uData->access > highest)
4120 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
4122 lData.users[matches++] = uData;
4124 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
4126 lData.table.length = matches+1;
4127 lData.table.width = 4;
4128 lData.table.flags = TABLE_NO_FREE;
4129 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
4130 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
4131 lData.table.contents[0] = ary;
4134 ary[2] = "Last Seen";
4136 for(matches = 1; matches < lData.table.length; ++matches)
4138 char seen[INTERVALLEN];
4140 uData = lData.users[matches-1];
4141 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
4142 lData.table.contents[matches] = ary;
4143 ary[0] = strtab(uData->access);
4144 ary[1] = uData->handle->handle;
4147 else if(!uData->seen)
4150 ary[2] = intervalString(seen, now - uData->seen, user->handle_info);
4151 ary[2] = strdup(ary[2]);
4152 if(IsUserSuspended(uData))
4153 ary[3] = "Suspended";
4154 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
4155 ary[3] = "Vacation";
4156 else if(HANDLE_FLAGGED(uData->handle, BOT))
4162 for(matches = 1; matches < lData.table.length; ++matches)
4164 free((char*)lData.table.contents[matches][2]);
4165 free(lData.table.contents[matches]);
4167 free(lData.table.contents[0]);
4168 free(lData.table.contents);
4172 static CHANSERV_FUNC(cmd_users)
4174 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
4177 static CHANSERV_FUNC(cmd_wlist)
4179 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
4182 static CHANSERV_FUNC(cmd_clist)
4184 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
4187 static CHANSERV_FUNC(cmd_mlist)
4189 return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
4192 static CHANSERV_FUNC(cmd_olist)
4194 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
4197 static CHANSERV_FUNC(cmd_plist)
4199 return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
4202 static CHANSERV_FUNC(cmd_bans)
4204 struct userNode *search_u = NULL;
4205 struct helpfile_table tbl;
4206 unsigned int matches = 0, timed = 0, search_wilds = 0, ii;
4207 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
4208 const char *msg_never, *triggered, *expires;
4209 struct banData *ban, **bans;
4213 else if(strchr(search = argv[1], '!'))
4216 search_wilds = search[strcspn(search, "?*")];
4218 else if(!(search_u = GetUserH(search)))
4219 reply("MSG_NICK_UNKNOWN", search);
4221 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
4223 for(ban = channel->channel_info->bans; ban; ban = ban->next)
4227 if(!user_matches_glob(search_u, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
4232 if(search_wilds ? !match_ircglobs(search, ban->mask) : !match_ircglob(search, ban->mask))
4235 bans[matches++] = ban;
4240 tbl.length = matches + 1;
4241 tbl.width = 4 + timed;
4243 tbl.flags = TABLE_NO_FREE;
4244 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
4245 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4246 tbl.contents[0][0] = "Mask";
4247 tbl.contents[0][1] = "Set By";
4248 tbl.contents[0][2] = "Triggered";
4251 tbl.contents[0][3] = "Expires";
4252 tbl.contents[0][4] = "Reason";
4255 tbl.contents[0][3] = "Reason";
4258 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4260 free(tbl.contents[0]);
4265 msg_never = user_find_message(user, "MSG_NEVER");
4266 for(ii = 0; ii < matches; )
4272 else if(ban->expires)
4273 expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
4275 expires = msg_never;
4278 triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
4280 triggered = msg_never;
4282 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4283 tbl.contents[ii][0] = ban->mask;
4284 tbl.contents[ii][1] = ban->owner;
4285 tbl.contents[ii][2] = strdup(triggered);
4288 tbl.contents[ii][3] = strdup(expires);
4289 tbl.contents[ii][4] = ban->reason;
4292 tbl.contents[ii][3] = ban->reason;
4294 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4295 reply("MSG_MATCH_COUNT", matches);
4296 for(ii = 1; ii < tbl.length; ++ii)
4298 free((char*)tbl.contents[ii][2]);
4300 free((char*)tbl.contents[ii][3]);
4301 free(tbl.contents[ii]);
4303 free(tbl.contents[0]);
4309 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
4311 struct chanData *cData = channel->channel_info;
4312 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
4314 if(cData->topic_mask)
4315 return !match_ircglob(new_topic, cData->topic_mask);
4316 else if(cData->topic)
4317 return irccasecmp(new_topic, cData->topic);
4322 static CHANSERV_FUNC(cmd_topic)
4324 struct chanData *cData;
4327 cData = channel->channel_info;
4332 SetChannelTopic(channel, chanserv, cData->topic, 1);
4333 reply("CSMSG_TOPIC_SET", cData->topic);
4337 reply("CSMSG_NO_TOPIC", channel->name);
4341 topic = unsplit_string(argv + 1, argc - 1, NULL);
4342 /* If they say "!topic *", use an empty topic. */
4343 if((topic[0] == '*') && (topic[1] == 0))
4345 if(bad_topic(channel, user, topic))
4347 char *topic_mask = cData->topic_mask;
4350 char new_topic[TOPICLEN+1], tchar;
4351 int pos=0, starpos=-1, dpos=0, len;
4353 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
4360 len = strlen(topic);
4361 if((dpos + len) > TOPICLEN)
4362 len = TOPICLEN + 1 - dpos;
4363 memcpy(new_topic+dpos, topic, len);
4367 case '\\': tchar = topic_mask[pos++]; /* and fall through */
4368 default: new_topic[dpos++] = tchar; break;
4371 if((dpos > TOPICLEN) || tchar)
4374 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
4375 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
4378 new_topic[dpos] = 0;
4379 SetChannelTopic(channel, chanserv, new_topic, 1);
4381 reply("CSMSG_TOPIC_LOCKED", channel->name);
4386 SetChannelTopic(channel, chanserv, topic, 1);
4388 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
4390 /* Grab the topic and save it as the default topic. */
4392 cData->topic = strdup(channel->topic);
4398 static CHANSERV_FUNC(cmd_mode)
4400 struct userData *uData;
4401 struct mod_chanmode *change;
4407 change = &channel->channel_info->modes;
4408 if(change->modes_set || change->modes_clear) {
4409 modcmd_chanmode_announce(change);
4410 reply("CSMSG_DEFAULTED_MODES", channel->name);
4412 reply("CSMSG_NO_MODES", channel->name);
4416 uData = GetChannelUser(channel->channel_info, user->handle_info);
4418 base_oplevel = MAXOPLEVEL;
4419 else if (uData->access >= UL_OWNER)
4422 base_oplevel = 1 + UL_OWNER - uData->access;
4423 change = mod_chanmode_parse(channel, user, argv+1, argc-1, MCP_KEY_FREE|MCP_IGN_REGISTERED|MCP_NO_APASS, base_oplevel);
4426 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
4430 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
4431 && mode_lock_violated(&channel->channel_info->modes, change))
4434 mod_chanmode_format(&channel->channel_info->modes, modes);
4435 reply("CSMSG_MODE_LOCKED", modes, channel->name);
4439 modcmd_chanmode_announce(change);
4440 mod_chanmode_format(change, fmt);
4441 mod_chanmode_free(change);
4442 reply("CSMSG_MODES_SET", fmt);
4447 chanserv_del_invite_mark(void *data)
4449 struct ChanUser *chanuser = data;
4450 struct chanNode *channel = chanuser->chan;
4452 if(!channel) return;
4453 for(i = 0; i < channel->invited.used; i++)
4455 if(channel->invited.list[i] == chanuser->user) {
4456 userList_remove(&channel->invited, chanuser->user);
4462 static CHANSERV_FUNC(cmd_invite)
4464 struct userData *uData;
4465 struct userNode *invite;
4466 struct ChanUser *chanuser;
4469 uData = GetChannelUser(channel->channel_info, user->handle_info);
4473 if(!(invite = GetUserH(argv[1])))
4475 reply("MSG_NICK_UNKNOWN", argv[1]);
4482 if(GetUserMode(channel, invite))
4484 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
4488 for(i = 0; i < channel->invited.used; i++)
4490 if(channel->invited.list[i] == invite) {
4491 reply("CSMSG_ALREADY_INVITED", invite->nick, channel->name);
4500 char *reason = unsplit_string(argv + 2, argc - 2, NULL);
4501 send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
4504 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
4506 irc_invite(chanserv, invite, channel);
4508 reply("CSMSG_INVITED_USER", argv[1], channel->name);
4510 userList_append(&channel->invited, invite);
4511 chanuser = calloc(1, sizeof(*chanuser));
4512 chanuser->user=invite;
4513 chanuser->chan=channel;
4514 timeq_add(now + chanserv_conf.invited_timeout, chanserv_del_invite_mark, chanuser);
4519 static CHANSERV_FUNC(cmd_inviteme)
4521 if(GetUserMode(channel, user))
4523 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
4526 if(channel->channel_info
4527 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
4529 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
4532 irc_invite(cmd->parent->bot, user, channel);
4536 static CHANSERV_FUNC(cmd_invitemeall)
4538 struct handle_info *target = user->handle_info;
4539 struct userData *uData;
4541 if(!target->channels)
4543 reply("CSMSG_SQUAT_ACCESS", target->handle);
4547 for(uData = target->channels; uData; uData = uData->u_next)
4549 struct chanData *cData = uData->channel;
4550 if(uData->access >= cData->lvlOpts[lvlInviteMe])
4552 irc_invite(cmd->parent->bot, user, cData->channel);
4559 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
4562 char buf1[INTERVALLEN], buf2[INTERVALLEN];
4564 /* We display things based on two dimensions:
4565 * - Issue time: present or absent
4566 * - Expiration: revoked, expired, expires in future, or indefinite expiration
4567 * (in order of precedence, so something both expired and revoked
4568 * only counts as revoked)
4570 combo = (suspended->issued ? 4 : 0)
4571 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
4573 case 0: /* no issue time, indefinite expiration */
4574 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
4576 case 1: /* no issue time, expires in future */
4577 intervalString(buf1, suspended->expires-now, user->handle_info);
4578 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
4580 case 2: /* no issue time, expired */
4581 intervalString(buf1, now-suspended->expires, user->handle_info);
4582 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
4584 case 3: /* no issue time, revoked */
4585 intervalString(buf1, now-suspended->revoked, user->handle_info);
4586 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
4588 case 4: /* issue time set, indefinite expiration */
4589 intervalString(buf1, now-suspended->issued, user->handle_info);
4590 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
4592 case 5: /* issue time set, expires in future */
4593 intervalString(buf1, now-suspended->issued, user->handle_info);
4594 intervalString(buf2, suspended->expires-now, user->handle_info);
4595 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
4597 case 6: /* issue time set, expired */
4598 intervalString(buf1, now-suspended->issued, user->handle_info);
4599 intervalString(buf2, now-suspended->expires, user->handle_info);
4600 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
4602 case 7: /* issue time set, revoked */
4603 intervalString(buf1, now-suspended->issued, user->handle_info);
4604 intervalString(buf2, now-suspended->revoked, user->handle_info);
4605 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
4608 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
4613 static CHANSERV_FUNC(cmd_info)
4615 char modes[MAXLEN], buffer[INTERVALLEN];
4616 struct userData *uData, *owner;
4617 struct chanData *cData;
4618 struct do_not_register *dnr;
4623 cData = channel->channel_info;
4624 reply("CSMSG_CHANNEL_INFO", channel->name);
4626 uData = GetChannelUser(cData, user->handle_info);
4627 if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
4629 mod_chanmode_format(&cData->modes, modes);
4630 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
4631 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
4634 for(it = dict_first(cData->notes); it; it = iter_next(it))
4638 note = iter_data(it);
4639 if(!note_type_visible_to_user(cData, note->type, user))
4642 padding = PADLEN - 1 - strlen(iter_key(it));
4643 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
4646 if(cData->max_time) {
4647 reply("CSMSG_CHANNEL_MAX_TIME", cData->max, intervalString(buffer, now - cData->max_time, user->handle_info));
4649 reply("CSMSG_CHANNEL_MAX", cData->max);
4651 for(owner = cData->users; owner; owner = owner->next)
4652 if(owner->access == UL_OWNER)
4653 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
4654 reply("CSMSG_CHANNEL_USERS", cData->userCount);
4655 reply("CSMSG_CHANNEL_BANS", cData->banCount);
4656 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
4658 privileged = IsStaff(user);
4660 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
4661 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
4662 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
4664 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
4665 chanserv_show_dnrs(user, cmd, channel->name, NULL);
4667 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
4669 struct suspended *suspended;
4670 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
4671 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
4672 show_suspension_info(cmd, user, suspended);
4674 else if(IsSuspended(cData))
4676 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
4677 show_suspension_info(cmd, user, cData->suspended);
4682 static CHANSERV_FUNC(cmd_netinfo)
4684 extern unsigned long boot_time;
4685 extern unsigned long burst_length;
4686 char interval[INTERVALLEN];
4688 reply("CSMSG_NETWORK_INFO");
4689 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
4690 reply("CSMSG_NETWORK_USERS", dict_size(clients));
4691 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
4692 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
4693 reply("CSMSG_NETWORK_BANS", banCount);
4694 reply("CSMSG_NETWORK_CHANUSERS", userCount);
4695 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
4696 reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
4701 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
4703 struct helpfile_table table;
4705 struct userNode *user;
4710 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4711 table.contents = alloca(list->used*sizeof(*table.contents));
4712 for(nn=0; nn<list->used; nn++)
4714 user = list->list[nn];
4715 if(user->modes & skip_flags)
4721 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
4724 nick = alloca(strlen(user->nick)+3);
4725 sprintf(nick, "(%s)", user->nick);
4729 table.contents[table.length][0] = nick;
4732 table_send(chanserv, to->nick, 0, NULL, table);
4735 static CHANSERV_FUNC(cmd_ircops)
4737 reply("CSMSG_STAFF_OPERS");
4738 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
4742 static CHANSERV_FUNC(cmd_helpers)
4744 reply("CSMSG_STAFF_HELPERS");
4745 send_staff_list(user, &curr_helpers, FLAGS_OPER);
4749 static CHANSERV_FUNC(cmd_staff)
4751 reply("CSMSG_NETWORK_STAFF");
4752 cmd_ircops(CSFUNC_ARGS);
4753 cmd_helpers(CSFUNC_ARGS);
4757 static CHANSERV_FUNC(cmd_peek)
4759 struct modeNode *mn;
4760 char modes[MODELEN];
4762 struct helpfile_table table;
4763 int opcount = 0, voicecount = 0, srvcount = 0;
4765 irc_make_chanmode(channel, modes);
4767 reply("CSMSG_PEEK_INFO", channel->name);
4768 reply("CSMSG_PEEK_TOPIC", channel->topic);
4769 reply("CSMSG_PEEK_MODES", modes);
4773 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4774 table.contents = alloca(channel->members.used*sizeof(*table.contents));
4775 for(n = 0; n < channel->members.used; n++)
4777 mn = channel->members.list[n];
4778 if(IsLocal(mn->user))
4780 else if(mn->modes & MODE_CHANOP)
4782 else if(mn->modes & MODE_VOICE)
4785 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
4787 table.contents[table.length] = alloca(sizeof(**table.contents));
4788 table.contents[table.length][0] = mn->user->nick;
4792 reply("CSMSG_PEEK_USERS", channel->members.used, opcount, voicecount,
4793 (channel->members.used - opcount - voicecount - srvcount));
4797 reply("CSMSG_PEEK_OPS");
4798 table_send(chanserv, user->nick, 0, NULL, table);
4801 reply("CSMSG_PEEK_NO_OPS");
4805 static MODCMD_FUNC(cmd_wipeinfo)
4807 struct handle_info *victim;
4808 struct userData *ud, *actor, *real_actor;
4809 unsigned int override = 0;
4812 actor = GetChannelUser(channel->channel_info, user->handle_info);
4813 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
4814 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4816 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4818 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4821 if((ud->access >= actor->access) && (ud != actor))
4823 reply("MSG_USER_OUTRANKED", victim->handle);
4826 if((ud != real_actor) && (!real_actor || (ud->access >= real_actor->access)))
4827 override = CMD_LOG_OVERRIDE;
4831 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4832 return 1 | override;
4835 static CHANSERV_FUNC(cmd_resync)
4837 struct mod_chanmode *changes;
4838 struct chanData *cData = channel->channel_info;
4839 unsigned int ii, used;
4841 changes = mod_chanmode_alloc(channel->members.used * 2);
4842 for(ii = used = 0; ii < channel->members.used; ++ii)
4844 struct modeNode *mn = channel->members.list[ii];
4845 struct userData *uData;
4847 if(IsService(mn->user))
4850 uData = GetChannelAccess(cData, mn->user->handle_info);
4851 if(!cData->lvlOpts[lvlGiveOps]
4852 || (uData && uData->access >= cData->lvlOpts[lvlGiveOps]))
4854 if(!(mn->modes & MODE_CHANOP))
4856 if(!uData || IsUserAutoOp(uData))
4858 changes->args[used].mode = MODE_CHANOP;
4859 changes->args[used++].u.member = mn;
4860 if(!(mn->modes & MODE_VOICE))
4862 changes->args[used].mode = MODE_VOICE;
4863 changes->args[used++].u.member = mn;
4868 else if(!cData->lvlOpts[lvlGiveVoice]
4869 || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice]))
4871 if(mn->modes & MODE_CHANOP)
4873 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4874 changes->args[used++].u.member = mn;
4876 if(!(mn->modes & MODE_VOICE) && (!uData || IsUserAutoOp(uData)))
4878 changes->args[used].mode = MODE_VOICE;
4879 changes->args[used++].u.member = mn;
4886 changes->args[used].mode = MODE_REMOVE | mn->modes;
4887 changes->args[used++].u.member = mn;
4891 changes->argc = used;
4892 modcmd_chanmode_announce(changes);
4893 mod_chanmode_free(changes);
4894 reply("CSMSG_RESYNCED_USERS", channel->name);
4898 static CHANSERV_FUNC(cmd_seen)
4900 struct userData *uData;
4901 struct handle_info *handle;
4902 char seen[INTERVALLEN];
4906 if(!irccasecmp(argv[1], chanserv->nick))
4908 reply("CSMSG_IS_CHANSERV");
4912 if(!(handle = get_handle_info(argv[1])))
4914 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4918 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4920 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4925 reply("CSMSG_USER_PRESENT", handle->handle);
4926 else if(uData->seen)
4927 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
4929 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4931 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4932 reply("CSMSG_USER_VACATION", handle->handle);
4937 static MODCMD_FUNC(cmd_names)
4939 struct userNode *targ;
4940 struct userData *targData;
4941 unsigned int ii, pos;
4944 for(ii=pos=0; ii<channel->members.used; ++ii)
4946 targ = channel->members.list[ii]->user;
4947 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4950 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
4953 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4957 if(IsUserSuspended(targData))
4959 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4962 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4963 reply("CSMSG_END_NAMES", channel->name);
4968 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
4970 switch(ntype->visible_type)
4972 case NOTE_VIS_ALL: return 1;
4973 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
4974 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
4979 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
4981 struct userData *uData;
4983 switch(ntype->set_access_type)
4985 case NOTE_SET_CHANNEL_ACCESS:
4986 if(!user->handle_info)
4988 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
4990 return uData->access >= ntype->set_access.min_ulevel;
4991 case NOTE_SET_CHANNEL_SETTER:
4992 return check_user_level(channel, user, lvlSetters, 1, 0);
4993 case NOTE_SET_PRIVILEGED: default:
4994 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
4998 static CHANSERV_FUNC(cmd_note)
5000 struct chanData *cData;
5002 struct note_type *ntype;
5004 cData = channel->channel_info;
5007 reply("CSMSG_NOT_REGISTERED", channel->name);
5011 /* If no arguments, show all visible notes for the channel. */
5017 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
5019 note = iter_data(it);
5020 if(!note_type_visible_to_user(cData, note->type, user))
5023 reply("CSMSG_NOTELIST_HEADER", channel->name);
5024 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
5027 reply("CSMSG_NOTELIST_END", channel->name);
5029 reply("CSMSG_NOTELIST_EMPTY", channel->name);
5031 /* If one argument, show the named note. */
5034 if((note = dict_find(cData->notes, argv[1], NULL))
5035 && note_type_visible_to_user(cData, note->type, user))
5037 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
5039 else if((ntype = dict_find(note_types, argv[1], NULL))
5040 && note_type_visible_to_user(NULL, ntype, user))
5042 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
5047 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
5051 /* Assume they're trying to set a note. */
5055 ntype = dict_find(note_types, argv[1], NULL);
5058 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
5061 else if(note_type_settable_by_user(channel, ntype, user))
5063 note_text = unsplit_string(argv+2, argc-2, NULL);
5064 if((note = dict_find(cData->notes, argv[1], NULL)))
5065 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
5066 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
5067 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
5069 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
5071 /* The note is viewable to staff only, so return 0
5072 to keep the invocation from getting logged (or
5073 regular users can see it in !events). */
5079 reply("CSMSG_NO_ACCESS");
5086 static CHANSERV_FUNC(cmd_delnote)
5091 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
5092 || !note_type_settable_by_user(channel, note->type, user))
5094 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
5097 dict_remove(channel->channel_info->notes, note->type->name);
5098 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
5102 static CHANSERV_FUNC(cmd_events)
5104 struct logSearch discrim;
5105 struct logReport report;
5106 unsigned int matches, limit;
5108 limit = (argc > 1) ? atoi(argv[1]) : 10;
5109 if(limit < 1 || limit > 200)
5112 memset(&discrim, 0, sizeof(discrim));
5113 discrim.masks.bot = chanserv;
5114 discrim.masks.channel_name = channel->name;
5116 discrim.masks.command = argv[2];
5117 discrim.limit = limit;
5118 discrim.max_time = INT_MAX;
5119 discrim.severities = 1 << LOG_COMMAND;
5120 report.reporter = chanserv;
5122 reply("CSMSG_EVENT_SEARCH_RESULTS");
5123 matches = log_entry_search(&discrim, log_report_entry, &report);
5125 reply("MSG_MATCH_COUNT", matches);
5127 reply("MSG_NO_MATCHES");
5131 static CHANSERV_FUNC(cmd_say)
5137 msg = unsplit_string(argv + 1, argc - 1, NULL);
5138 send_channel_message(channel, cmd->parent->bot, "%s", msg);
5140 else if(*argv[1] == '*' && argv[1][1] != '\0')
5142 struct handle_info *hi;
5143 struct userNode *authed;
5146 msg = unsplit_string(argv + 2, argc - 2, NULL);
5148 if (!(hi = get_handle_info(argv[1] + 1)))
5150 reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
5154 for (authed = hi->users; authed; authed = authed->next_authed)
5155 send_target_message(5, authed->nick, cmd->parent->bot, "%s", msg);
5157 else if(GetUserH(argv[1]))
5160 msg = unsplit_string(argv + 2, argc - 2, NULL);
5161 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
5165 reply("MSG_NOT_TARGET_NAME");
5171 static CHANSERV_FUNC(cmd_emote)
5177 /* CTCP is so annoying. */
5178 msg = unsplit_string(argv + 1, argc - 1, NULL);
5179 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
5181 else if(*argv[1] == '*' && argv[1][1] != '\0')
5183 struct handle_info *hi;
5184 struct userNode *authed;
5187 msg = unsplit_string(argv + 2, argc - 2, NULL);
5189 if (!(hi = get_handle_info(argv[1] + 1)))
5191 reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
5195 for (authed = hi->users; authed; authed = authed->next_authed)
5196 send_target_message(5, authed->nick, cmd->parent->bot, "\001ACTION %s\001", msg);
5198 else if(GetUserH(argv[1]))
5200 msg = unsplit_string(argv + 2, argc - 2, NULL);
5201 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
5205 reply("MSG_NOT_TARGET_NAME");
5211 struct channelList *
5212 chanserv_support_channels(void)
5214 return &chanserv_conf.support_channels;
5217 static CHANSERV_FUNC(cmd_expire)
5219 int channel_count = registered_channels;
5220 expire_channels(NULL);
5221 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
5226 chanserv_expire_suspension(void *data)
5228 struct suspended *suspended = data;
5229 struct chanNode *channel;
5232 /* Update the channel registration data structure. */
5233 if(!suspended->expires || (now < suspended->expires))
5234 suspended->revoked = now;
5235 channel = suspended->cData->channel;
5236 suspended->cData->channel = channel;
5237 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
5239 /* If appropriate, re-join ChanServ to the channel. */
5240 if(!IsOffChannel(suspended->cData))
5242 spamserv_cs_suspend(channel, 0, 0, NULL);
5243 ss_cs_join_channel(channel, 1);
5246 /* Mark everyone currently in the channel as present. */
5247 for(ii = 0; ii < channel->members.used; ++ii)
5249 struct userData *uData = GetChannelAccess(suspended->cData, channel->members.list[ii]->user->handle_info);
5258 static CHANSERV_FUNC(cmd_csuspend)
5260 struct suspended *suspended;
5261 char reason[MAXLEN];
5262 unsigned long expiry, duration;
5263 struct userData *uData;
5267 if(IsProtected(channel->channel_info))
5269 reply("CSMSG_SUSPEND_NODELETE", channel->name);
5273 if(argv[1][0] == '!')
5275 else if(IsSuspended(channel->channel_info))
5277 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
5278 show_suspension_info(cmd, user, channel->channel_info->suspended);
5282 if(!strcmp(argv[1], "0"))
5284 else if((duration = ParseInterval(argv[1])))
5285 expiry = now + duration;
5288 reply("MSG_INVALID_DURATION", argv[1]);
5292 unsplit_string(argv + 2, argc - 2, reason);
5294 suspended = calloc(1, sizeof(*suspended));
5295 suspended->revoked = 0;
5296 suspended->issued = now;
5297 suspended->suspender = strdup(user->handle_info->handle);
5298 suspended->expires = expiry;
5299 suspended->reason = strdup(reason);
5300 suspended->cData = channel->channel_info;
5301 suspended->previous = suspended->cData->suspended;
5302 suspended->cData->suspended = suspended;
5304 if(suspended->expires)
5305 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
5307 if(IsSuspended(channel->channel_info))
5309 suspended->previous->revoked = now;
5310 if(suspended->previous->expires)
5311 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
5312 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
5313 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5317 /* Mark all users in channel as absent. */
5318 for(uData = channel->channel_info->users; uData; uData = uData->next)
5327 /* Mark the channel as suspended, then part. */
5328 channel->channel_info->flags |= CHANNEL_SUSPENDED;
5329 spamserv_cs_suspend(channel, expiry, 1, suspended->reason);
5330 DelChannelUser(chanserv, channel, suspended->reason, 0);
5331 reply("CSMSG_SUSPENDED", channel->name);
5332 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
5333 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5338 static CHANSERV_FUNC(cmd_cunsuspend)
5340 struct suspended *suspended;
5341 char message[MAXLEN];
5343 if(!IsSuspended(channel->channel_info))
5345 reply("CSMSG_NOT_SUSPENDED", channel->name);
5349 suspended = channel->channel_info->suspended;
5351 /* Expire the suspension and join ChanServ to the channel. */
5352 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
5353 chanserv_expire_suspension(suspended);
5354 reply("CSMSG_UNSUSPENDED", channel->name);
5355 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
5356 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
5360 typedef struct chanservSearch
5365 unsigned long unvisited;
5366 unsigned long registered;
5368 unsigned long flags;
5372 typedef void (*channel_search_func)(struct chanData *channel, void *data);
5375 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
5380 search = malloc(sizeof(struct chanservSearch));
5381 memset(search, 0, sizeof(*search));
5384 for(i = 0; i < argc; i++)
5386 /* Assume all criteria require arguments. */
5389 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
5393 if(!irccasecmp(argv[i], "name"))
5394 search->name = argv[++i];
5395 else if(!irccasecmp(argv[i], "registrar"))
5396 search->registrar = argv[++i];
5397 else if(!irccasecmp(argv[i], "unvisited"))
5398 search->unvisited = ParseInterval(argv[++i]);
5399 else if(!irccasecmp(argv[i], "registered"))
5400 search->registered = ParseInterval(argv[++i]);
5401 else if(!irccasecmp(argv[i], "flags"))
5404 if(!irccasecmp(argv[i], "nodelete"))
5405 search->flags |= CHANNEL_NODELETE;
5406 else if(!irccasecmp(argv[i], "suspended"))
5407 search->flags |= CHANNEL_SUSPENDED;
5408 else if(!irccasecmp(argv[i], "unreviewed"))
5409 search->flags |= CHANNEL_UNREVIEWED;
5412 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
5416 else if(!irccasecmp(argv[i], "limit"))
5417 search->limit = strtoul(argv[++i], NULL, 10);
5420 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
5425 if(search->name && !strcmp(search->name, "*"))
5427 if(search->registrar && !strcmp(search->registrar, "*"))
5428 search->registrar = 0;
5437 chanserv_channel_match(struct chanData *channel, search_t search)
5439 const char *name = channel->channel->name;
5440 if((search->name && !match_ircglob(name, search->name)) ||
5441 (search->registrar && !channel->registrar) ||
5442 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
5443 (search->unvisited && (now - channel->visited) < search->unvisited) ||
5444 (search->registered && (now - channel->registered) > search->registered) ||
5445 (search->flags && ((search->flags & channel->flags) != search->flags)))
5452 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
5454 struct chanData *channel;
5455 unsigned int matches = 0;
5457 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
5459 if(!chanserv_channel_match(channel, search))
5469 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
5474 search_print(struct chanData *channel, void *data)
5476 send_message_type(4, data, chanserv, "%s", channel->channel->name);
5479 static CHANSERV_FUNC(cmd_search)
5482 unsigned int matches;
5483 channel_search_func action;
5487 if(!irccasecmp(argv[1], "count"))
5488 action = search_count;
5489 else if(!irccasecmp(argv[1], "print"))
5490 action = search_print;
5493 reply("CSMSG_ACTION_INVALID", argv[1]);
5497 search = chanserv_search_create(user, argc - 2, argv + 2);
5501 if(action == search_count)
5502 search->limit = INT_MAX;
5504 if(action == search_print)
5505 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
5507 matches = chanserv_channel_search(search, action, user);
5510 reply("MSG_MATCH_COUNT", matches);
5512 reply("MSG_NO_MATCHES");
5518 static CHANSERV_FUNC(cmd_unvisited)
5520 struct chanData *cData;
5521 unsigned long interval = chanserv_conf.channel_expire_delay;
5522 char buffer[INTERVALLEN];
5523 unsigned int limit = 25, matches = 0;
5527 interval = ParseInterval(argv[1]);
5529 limit = atoi(argv[2]);
5532 intervalString(buffer, interval, user->handle_info);
5533 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
5535 for(cData = channelList; cData && matches < limit; cData = cData->next)
5537 if((now - cData->visited) < interval)
5540 intervalString(buffer, now - cData->visited, user->handle_info);
5541 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
5548 static MODCMD_FUNC(chan_opt_defaulttopic)
5554 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5556 reply("CSMSG_TOPIC_LOCKED", channel->name);
5560 topic = unsplit_string(argv+1, argc-1, NULL);
5562 free(channel->channel_info->topic);
5563 if(topic[0] == '*' && topic[1] == 0)
5565 topic = channel->channel_info->topic = NULL;
5569 topic = channel->channel_info->topic = strdup(topic);
5570 if(channel->channel_info->topic_mask
5571 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
5572 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5574 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
5577 if(channel->channel_info->topic)
5578 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
5580 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
5584 static MODCMD_FUNC(chan_opt_topicmask)
5588 struct chanData *cData = channel->channel_info;
5591 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5593 reply("CSMSG_TOPIC_LOCKED", channel->name);
5597 mask = unsplit_string(argv+1, argc-1, NULL);
5599 if(cData->topic_mask)
5600 free(cData->topic_mask);
5601 if(mask[0] == '*' && mask[1] == 0)
5603 cData->topic_mask = 0;
5607 cData->topic_mask = strdup(mask);
5609 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
5610 else if(!match_ircglob(cData->topic, cData->topic_mask))
5611 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5615 if(channel->channel_info->topic_mask)
5616 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
5618 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
5622 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
5626 char *greeting = unsplit_string(argv+1, argc-1, NULL);
5630 if(greeting[0] == '*' && greeting[1] == 0)
5634 unsigned int length = strlen(greeting);
5635 if(length > chanserv_conf.greeting_length)
5637 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
5640 *data = strdup(greeting);
5649 reply(name, user_find_message(user, "MSG_NONE"));
5653 static MODCMD_FUNC(chan_opt_greeting)
5655 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
5658 static MODCMD_FUNC(chan_opt_usergreeting)
5660 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
5663 static MODCMD_FUNC(chan_opt_modes)
5665 struct mod_chanmode *new_modes;
5670 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
5672 reply("CSMSG_NO_ACCESS");
5675 if(argv[1][0] == '*' && argv[1][1] == 0)
5677 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
5679 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)))
5681 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5684 else if(new_modes->argc > 1)
5686 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5687 mod_chanmode_free(new_modes);
5692 channel->channel_info->modes = *new_modes;
5693 modcmd_chanmode_announce(new_modes);
5694 mod_chanmode_free(new_modes);
5698 mod_chanmode_format(&channel->channel_info->modes, modes);
5700 reply("CSMSG_SET_MODES", modes);
5702 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
5706 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5708 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5710 struct chanData *cData = channel->channel_info;
5715 /* Set flag according to value. */
5716 if(enabled_string(argv[1]))
5718 cData->flags |= mask;
5721 else if(disabled_string(argv[1]))
5723 cData->flags &= ~mask;
5728 reply("MSG_INVALID_BINARY", argv[1]);
5734 /* Find current option value. */
5735 value = (cData->flags & mask) ? 1 : 0;
5739 reply(name, user_find_message(user, "MSG_ON"));
5741 reply(name, user_find_message(user, "MSG_OFF"));
5745 static MODCMD_FUNC(chan_opt_nodelete)
5747 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
5749 reply("MSG_SETTING_PRIVILEGED", argv[0]);
5753 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
5756 static MODCMD_FUNC(chan_opt_dynlimit)
5758 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
5761 static MODCMD_FUNC(chan_opt_offchannel)
5763 struct chanData *cData = channel->channel_info;
5768 /* Set flag according to value. */
5769 if(enabled_string(argv[1]))
5771 if(!IsOffChannel(cData))
5772 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
5773 cData->flags |= CHANNEL_OFFCHANNEL;
5776 else if(disabled_string(argv[1]))
5778 if(IsOffChannel(cData))
5780 struct mod_chanmode change;
5781 mod_chanmode_init(&change);
5783 change.args[0].mode = MODE_CHANOP;
5784 change.args[0].u.member = AddChannelUser(chanserv, channel);
5785 mod_chanmode_announce(chanserv, channel, &change);
5787 cData->flags &= ~CHANNEL_OFFCHANNEL;
5792 reply("MSG_INVALID_BINARY", argv[1]);
5798 /* Find current option value. */
5799 value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
5803 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
5805 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
5809 static MODCMD_FUNC(chan_opt_unreviewed)
5811 struct chanData *cData = channel->channel_info;
5812 int value = (cData->flags & CHANNEL_UNREVIEWED) ? 1 : 0;
5818 /* The two directions can have different ACLs. */
5819 if(enabled_string(argv[1]))
5821 else if(disabled_string(argv[1]))
5825 reply("MSG_INVALID_BINARY", argv[1]);
5829 if (new_value != value)
5831 struct svccmd *subcmd;
5832 char subcmd_name[32];
5834 snprintf(subcmd_name, sizeof(subcmd_name), "%s %s", argv[0], (new_value ? "on" : "off"));
5835 subcmd = dict_find(cmd->parent->commands, subcmd_name, NULL);
5838 reply("MSG_COMMAND_DISABLED", subcmd_name);
5841 else if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
5845 cData->flags |= CHANNEL_UNREVIEWED;
5848 free(cData->registrar);
5849 cData->registrar = strdup(user->handle_info->handle);
5850 cData->flags &= ~CHANNEL_UNREVIEWED;
5857 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_ON"));
5859 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_OFF"));
5863 static MODCMD_FUNC(chan_opt_defaults)
5865 struct userData *uData;
5866 struct chanData *cData;
5867 const char *confirm;
5868 enum levelOption lvlOpt;
5869 enum charOption chOpt;
5871 cData = channel->channel_info;
5872 uData = GetChannelUser(cData, user->handle_info);
5873 if(!uData || (uData->access < UL_OWNER))
5875 reply("CSMSG_OWNER_DEFAULTS", channel->name);
5878 confirm = make_confirmation_string(uData);
5879 if((argc < 2) || strcmp(argv[1], confirm))
5881 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
5884 cData->flags = (CHANNEL_DEFAULT_FLAGS & ~CHANNEL_PRESERVED_FLAGS)
5885 | (cData->flags & CHANNEL_PRESERVED_FLAGS);
5886 cData->modes = chanserv_conf.default_modes;
5887 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
5888 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
5889 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
5890 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
5891 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
5896 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5898 struct chanData *cData = channel->channel_info;
5899 struct userData *uData;
5900 unsigned short value;
5904 if(!check_user_level(channel, user, option, 1, 1))
5906 reply("CSMSG_CANNOT_SET");
5909 value = user_level_from_name(argv[1], UL_OWNER+1);
5910 if(!value && strcmp(argv[1], "0"))
5912 reply("CSMSG_INVALID_ACCESS", argv[1]);
5915 uData = GetChannelUser(cData, user->handle_info);
5916 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
5918 reply("CSMSG_BAD_SETLEVEL");
5924 if(value > cData->lvlOpts[lvlGiveOps])
5926 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
5931 if(value < cData->lvlOpts[lvlGiveVoice])
5933 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
5938 /* This test only applies to owners, since non-owners
5939 * trying to set an option to above their level get caught
5940 * by the CSMSG_BAD_SETLEVEL test above.
5942 if(value > uData->access)
5944 reply("CSMSG_BAD_SETTERS");
5951 cData->lvlOpts[option] = value;
5953 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
5957 static MODCMD_FUNC(chan_opt_enfops)
5959 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
5962 static MODCMD_FUNC(chan_opt_giveops)
5964 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
5967 static MODCMD_FUNC(chan_opt_enfmodes)
5969 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
5972 static MODCMD_FUNC(chan_opt_enftopic)
5974 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
5977 static MODCMD_FUNC(chan_opt_pubcmd)
5979 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
5982 static MODCMD_FUNC(chan_opt_setters)
5984 return channel_level_option(lvlSetters, CSFUNC_ARGS);
5987 static MODCMD_FUNC(chan_opt_ctcpusers)
5989 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
5992 static MODCMD_FUNC(chan_opt_userinfo)
5994 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
5997 static MODCMD_FUNC(chan_opt_givevoice)
5999 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
6002 static MODCMD_FUNC(chan_opt_topicsnarf)
6004 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
6007 static MODCMD_FUNC(chan_opt_vote)
6009 return channel_level_option(lvlVote, CSFUNC_ARGS);
6012 static MODCMD_FUNC(chan_opt_inviteme)
6014 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
6018 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
6020 struct chanData *cData = channel->channel_info;
6021 int count = charOptions[option].count, idx;
6025 idx = atoi(argv[1]);
6027 if(!isdigit(argv[1][0]) || (idx < 0) || (idx >= count))
6029 reply("CSMSG_INVALID_NUMERIC", idx);
6030 /* Show possible values. */
6031 for(idx = 0; idx < count; idx++)
6032 reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
6036 cData->chOpts[option] = charOptions[option].values[idx].value;
6040 /* Find current option value. */
6043 (idx < count) && (cData->chOpts[option] != charOptions[option].values[idx].value);
6047 /* Somehow, the option value is corrupt; reset it to the default. */
6048 cData->chOpts[option] = charOptions[option].default_value;
6053 reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
6057 static MODCMD_FUNC(chan_opt_protect)
6059 return channel_multiple_option(chProtect, CSFUNC_ARGS);
6062 static MODCMD_FUNC(chan_opt_toys)
6064 return channel_multiple_option(chToys, CSFUNC_ARGS);
6067 static MODCMD_FUNC(chan_opt_ctcpreaction)
6069 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
6072 static MODCMD_FUNC(chan_opt_topicrefresh)
6074 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
6077 static struct svccmd_list set_shows_list;
6080 handle_svccmd_unbind(struct svccmd *target) {
6082 for(ii=0; ii<set_shows_list.used; ++ii)
6083 if(target == set_shows_list.list[ii])
6084 set_shows_list.used = 0;
6087 static CHANSERV_FUNC(cmd_set)
6089 struct svccmd *subcmd;
6093 /* Check if we need to (re-)initialize set_shows_list. */
6094 if(!set_shows_list.used)
6096 if(!set_shows_list.size)
6098 set_shows_list.size = chanserv_conf.set_shows->used;
6099 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
6101 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
6103 const char *name = chanserv_conf.set_shows->list[ii];
6104 sprintf(buf, "%s %s", argv[0], name);
6105 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6108 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
6111 svccmd_list_append(&set_shows_list, subcmd);
6117 reply("CSMSG_CHANNEL_OPTIONS");
6118 for(ii = 0; ii < set_shows_list.used; ii++)
6120 subcmd = set_shows_list.list[ii];
6121 subcmd->command->func(user, channel, 1, argv+1, subcmd);
6126 sprintf(buf, "%s %s", argv[0], argv[1]);
6127 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6130 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
6133 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
6135 reply("CSMSG_NO_ACCESS");
6141 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
6145 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
6147 struct userData *uData;
6149 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6152 reply("CSMSG_NOT_USER", channel->name);
6158 /* Just show current option value. */
6160 else if(enabled_string(argv[1]))
6162 uData->flags |= mask;
6164 else if(disabled_string(argv[1]))
6166 uData->flags &= ~mask;
6170 reply("MSG_INVALID_BINARY", argv[1]);
6174 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
6178 static MODCMD_FUNC(user_opt_noautoop)
6180 struct userData *uData;
6182 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6185 reply("CSMSG_NOT_USER", channel->name);
6188 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
6189 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
6191 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
6194 static MODCMD_FUNC(user_opt_autoinvite)
6196 if((argc > 1) && !check_user_level(channel, user, lvlInviteMe, 1, 0))
6198 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
6200 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
6203 static MODCMD_FUNC(user_opt_info)
6205 struct userData *uData;
6208 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6212 /* If they got past the command restrictions (which require access)
6213 * but fail this test, we have some fool with security override on.
6215 reply("CSMSG_NOT_USER", channel->name);
6222 infoline = unsplit_string(argv + 1, argc - 1, NULL);
6223 if(strlen(infoline) > chanserv_conf.max_userinfo_length)
6225 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
6228 bp = strcspn(infoline, "\001");
6231 reply("CSMSG_BAD_INFOLINE", infoline[bp]);
6236 if(infoline[0] == '*' && infoline[1] == 0)
6239 uData->info = strdup(infoline);
6242 reply("CSMSG_USET_INFO", uData->info);
6244 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
6248 struct svccmd_list uset_shows_list;
6250 static CHANSERV_FUNC(cmd_uset)
6252 struct svccmd *subcmd;
6256 /* Check if we need to (re-)initialize uset_shows_list. */
6257 if(!uset_shows_list.used)
6261 "NoAutoOp", "AutoInvite", "Info"
6264 if(!uset_shows_list.size)
6266 uset_shows_list.size = ArrayLength(options);
6267 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
6269 for(ii = 0; ii < ArrayLength(options); ii++)
6271 const char *name = options[ii];
6272 sprintf(buf, "%s %s", argv[0], name);
6273 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6276 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
6279 svccmd_list_append(&uset_shows_list, subcmd);
6285 /* Do this so options are presented in a consistent order. */
6286 reply("CSMSG_USER_OPTIONS");
6287 for(ii = 0; ii < uset_shows_list.used; ii++)
6288 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
6292 sprintf(buf, "%s %s", argv[0], argv[1]);
6293 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6296 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
6300 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
6303 static CHANSERV_FUNC(cmd_giveownership)
6305 struct handle_info *new_owner_hi;
6306 struct userData *new_owner;
6307 struct userData *curr_user;
6308 struct userData *invoker;
6309 struct chanData *cData = channel->channel_info;
6310 struct do_not_register *dnr;
6311 const char *confirm;
6313 unsigned short co_access;
6314 char reason[MAXLEN];
6317 curr_user = GetChannelAccess(cData, user->handle_info);
6318 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
6319 if(!curr_user || (curr_user->access != UL_OWNER))
6321 struct userData *owner = NULL;
6322 for(curr_user = channel->channel_info->users;
6324 curr_user = curr_user->next)
6326 if(curr_user->access != UL_OWNER)
6330 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
6337 else if(!force && (now < cData->ownerTransfer + chanserv_conf.giveownership_period))
6339 char delay[INTERVALLEN];
6340 intervalString(delay, cData->ownerTransfer + chanserv_conf.giveownership_period - now, user->handle_info);
6341 reply("CSMSG_TRANSFER_WAIT", delay, channel->name);
6344 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
6346 if(new_owner_hi == user->handle_info)
6348 reply("CSMSG_NO_TRANSFER_SELF");
6351 new_owner = GetChannelAccess(cData, new_owner_hi);
6356 new_owner = add_channel_user(cData, new_owner_hi, UL_OWNER - 1, 0, NULL);
6360 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
6364 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
6366 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
6369 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
6370 if(!IsHelping(user))
6371 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
6373 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi->handle);
6376 invoker = GetChannelUser(cData, user->handle_info);
6377 if(invoker->access <= UL_OWNER)
6379 confirm = make_confirmation_string(curr_user);
6380 if((argc < 3) || strcmp(argv[2], confirm))
6382 reply("CSMSG_CONFIRM_GIVEOWNERSHIP", new_owner_hi->handle, confirm);
6386 if(new_owner->access >= UL_COOWNER)
6387 co_access = new_owner->access;
6389 co_access = UL_COOWNER;
6390 new_owner->access = UL_OWNER;
6392 curr_user->access = co_access;
6393 cData->ownerTransfer = now;
6394 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
6395 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
6396 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
6400 static CHANSERV_FUNC(cmd_suspend)
6402 struct handle_info *hi;
6403 struct userData *actor, *real_actor, *target;
6404 unsigned int override = 0;
6407 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6408 actor = GetChannelUser(channel->channel_info, user->handle_info);
6409 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
6410 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6412 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6415 if(target->access >= actor->access)
6417 reply("MSG_USER_OUTRANKED", hi->handle);
6420 if(target->flags & USER_SUSPENDED)
6422 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
6427 target->present = 0;
6430 if(!real_actor || target->access >= real_actor->access)
6431 override = CMD_LOG_OVERRIDE;
6432 target->flags |= USER_SUSPENDED;
6433 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
6434 return 1 | override;
6437 static CHANSERV_FUNC(cmd_unsuspend)
6439 struct handle_info *hi;
6440 struct userData *actor, *real_actor, *target;
6441 unsigned int override = 0;
6444 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6445 actor = GetChannelUser(channel->channel_info, user->handle_info);
6446 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
6447 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6449 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6452 if(target->access >= actor->access)
6454 reply("MSG_USER_OUTRANKED", hi->handle);
6457 if(!(target->flags & USER_SUSPENDED))
6459 reply("CSMSG_NOT_SUSPENDED", hi->handle);
6462 if(!real_actor || target->access >= real_actor->access)
6463 override = CMD_LOG_OVERRIDE;
6464 target->flags &= ~USER_SUSPENDED;
6465 scan_user_presence(target, NULL);
6466 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
6467 return 1 | override;
6470 static MODCMD_FUNC(cmd_deleteme)
6472 struct handle_info *hi;
6473 struct userData *target;
6474 const char *confirm_string;
6475 unsigned short access_level;
6478 hi = user->handle_info;
6479 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6481 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6484 if(target->access == UL_OWNER)
6486 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
6489 confirm_string = make_confirmation_string(target);
6490 if((argc < 2) || strcmp(argv[1], confirm_string))
6492 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
6495 access_level = target->access;
6496 channel_name = strdup(channel->name);
6497 del_channel_user(target, 1);
6498 reply("CSMSG_DELETED_YOU", access_level, channel_name);
6503 static CHANSERV_FUNC(cmd_addvote)
6505 struct chanData *cData = channel->channel_info;
6506 struct userData *uData, *target;
6507 struct handle_info *hi;
6508 if (!cData) return 0;
6510 hi = user->handle_info;
6511 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6513 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6516 if(target->access < 300) {
6517 reply("CSMSG_NO_ACCESS");
6521 reply("CSMSG_ADDVOTE_FULL");
6525 msg = unsplit_string(argv + 1, argc - 1, NULL);
6526 cData->vote = strdup(msg);
6527 cData->vote_start=0;
6528 dict_delete(cData->vote_options);
6529 cData->vote_options = dict_new();
6530 dict_set_free_data(cData->vote_options, free_vote_options);
6531 for(uData = channel->channel_info->users; uData; uData = uData->next)
6536 reply("CSMSG_ADDVOTE_DONE");
6540 static CHANSERV_FUNC(cmd_delvote)
6542 struct chanData *cData = channel->channel_info;
6543 struct userData *target;
6544 struct handle_info *hi;
6545 if (!cData) return 0;
6546 hi = user->handle_info;
6547 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6549 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6552 if(target->access < 300) {
6553 reply("CSMSG_NO_ACCESS");
6557 reply("CSMSG_NO_VOTE");
6562 reply("CSMSG_DELVOTE_DONE");
6566 static CHANSERV_FUNC(cmd_addoption)
6568 struct chanData *cData = channel->channel_info;
6569 struct userData *target;
6570 struct handle_info *hi;
6571 if (!cData) return 0;
6573 hi = user->handle_info;
6574 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6576 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6579 if(target->access < 300) {
6580 reply("CSMSG_NO_ACCESS");
6584 reply("CSMSG_NO_VOTE");
6590 msg = unsplit_string(argv + 1, argc - 1, NULL);
6593 unsigned int lastid = 1;
6594 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6595 struct vote_option *cvOpt = iter_data(it);
6596 if(cvOpt->option_id > lastid)
6597 lastid = cvOpt->option_id;
6599 struct vote_option *vOpt;
6600 vOpt = calloc(1, sizeof(*vOpt));
6601 vOpt->name = strdup(msg);
6602 vOpt->option_id = (lastid + 1);
6604 sprintf(str,"%i",(lastid + 1));
6605 vOpt->option_str = strdup(str);
6607 dict_insert(cData->vote_options,vOpt->option_str,vOpt);
6609 reply("CSMSG_ADDOPTION_DONE",dict_size(cData->vote_options),lastid,(lastid + 1));
6613 static CHANSERV_FUNC(cmd_deloption)
6615 struct chanData *cData = channel->channel_info;
6616 struct userData *uData, *target;
6617 struct handle_info *hi;
6618 if (!cData) return 0;
6620 hi = user->handle_info;
6621 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6623 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6626 if(target->access < 300) {
6627 reply("CSMSG_NO_ACCESS");
6631 reply("CSMSG_NO_VOTE");
6634 if(cData->vote_start) {
6635 if(dict_size(cData->vote_options) < 3) {
6636 reply("CSMSG_VOTE_NEED_OPTIONS");
6641 int find_id = atoi(argv[1]);
6643 unsigned int found = 0;
6646 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6648 if (find_id == ii) {
6649 struct vote_option *vOpt = iter_data(it);
6650 found = vOpt->option_id;
6652 sprintf(str,"%i",vOpt->option_id);
6653 dict_remove(cData->vote_options, str);
6658 for(uData = channel->channel_info->users; uData; uData = uData->next) {
6659 if(uData->votefor == found) {
6664 reply("CSMSG_DELOPTION_DONE");
6667 reply("CSMSG_DELOPTION_NONE");
6672 static CHANSERV_FUNC(cmd_vote)
6674 struct chanData *cData = channel->channel_info;
6675 struct userData *target;
6676 struct handle_info *hi;
6677 unsigned int votedfor = 0;
6678 char *votedfor_str = NULL;
6680 if (!cData || !cData->vote) {
6681 reply("CSMSG_NO_VOTE");
6684 if(argc > 1 && cData->vote_start) {
6685 hi = user->handle_info;
6686 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6688 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6691 if(!check_user_level(channel, user, lvlVote, 1, 0)) {
6692 reply("CSMSG_NO_ACCESS");
6696 reply("CSMSG_VOTE_VOTED");
6699 int find_id = atoi(argv[1]);
6702 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6704 if (find_id == ii) {
6705 struct vote_option *vOpt = iter_data(it);
6708 target->votefor = vOpt->option_id;
6709 votedfor = vOpt->option_id;
6710 votedfor_str = vOpt->name;
6714 reply("CSMSG_VOTE_INVALID");
6718 if (!cData->vote_start) {
6719 reply("CSMSG_VOTE_NOT_STARTED");
6721 reply("CSMSG_VOTE_QUESTION",cData->vote);
6723 unsigned int voteid = 0;
6726 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6727 struct vote_option *vOpt = iter_data(it);
6729 reply("CSMSG_VOTE_OPTION",voteid,vOpt->name,vOpt->voted);
6731 if(argc > 1 && cData->vote_start && votedfor_str) {
6732 reply("CSMSG_VOTE_DONE",votedfor_str);
6737 static CHANSERV_FUNC(cmd_startvote)
6739 struct chanData *cData = channel->channel_info;
6740 struct userData *target;
6741 struct handle_info *hi;
6742 if (!cData) return 0;
6743 hi = user->handle_info;
6744 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6746 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6749 if(target->access < 300) {
6750 reply("CSMSG_NO_ACCESS");
6754 reply("CSMSG_NO_VOTE");
6757 if(cData->vote_start) {
6758 reply("CSMSG_STARTVOTE_RUNNING");
6761 if(dict_size(cData->vote_options) < 2) {
6762 reply("CSMSG_VOTE_NEED_OPTIONS");
6765 cData->vote_start = 1;
6766 char response[MAXLEN];
6767 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_TOP"), user->nick);
6768 irc_privmsg(cmd->parent->bot, channel->name, response);
6769 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_QUESTION"), cData->vote);
6770 irc_privmsg(cmd->parent->bot, channel->name, response);
6771 unsigned int voteid = 0;
6773 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6774 struct vote_option *vOpt = iter_data(it);
6776 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_OPTION"), voteid, vOpt->name);
6777 irc_privmsg(cmd->parent->bot, channel->name, response);
6779 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_ACCESS"), cData->lvlOpts[lvlVote]); //Todo
6780 irc_privmsg(cmd->parent->bot, channel->name, response);
6781 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_HOWTO")); //Todo
6782 irc_privmsg(cmd->parent->bot, channel->name, response);
6786 static CHANSERV_FUNC(cmd_endvote)
6788 struct chanData *cData = channel->channel_info;
6789 struct userData *target;
6790 struct handle_info *hi;
6791 if (!cData) return 0;
6792 hi = user->handle_info;
6793 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6795 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6798 if(target->access < 300) {
6799 reply("CSMSG_NO_ACCESS");
6803 reply("CSMSG_NO_VOTE");
6806 if(!cData->vote_start) {
6807 reply("CSMSG_ENDVOTE_STOPPED");
6810 cData->vote_start = 0;
6811 reply("CSMSG_ENDVOTE_DONE");
6815 static CHANSERV_FUNC(cmd_voteresults)
6817 struct chanData *cData = channel->channel_info;
6818 struct userData *target;
6819 struct handle_info *hi;
6820 if (!cData) return 0;
6822 reply("CSMSG_NO_VOTE");
6825 if (argc > 1 && !irccasecmp(argv[1], "*")) {
6826 hi = user->handle_info;
6827 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6829 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6832 if(target->access < 300) {
6833 reply("CSMSG_NO_ACCESS");
6836 char response[MAXLEN];
6837 sprintf(response, user_find_message(user, "CSMSG_VOTERES_QUESTION"), cData->vote);
6838 irc_privmsg(cmd->parent->bot, channel->name, response);
6839 unsigned int voteid = 0;
6841 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6842 struct vote_option *vOpt = iter_data(it);
6844 sprintf(response, user_find_message(user, "CSMSG_VOTERES_OPTION"), voteid, vOpt->name, vOpt->voted);
6845 irc_privmsg(cmd->parent->bot, channel->name, response);
6848 reply("CSMSG_VOTE_QUESTION",cData->vote);
6849 unsigned int voteid = 0;
6851 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6852 struct vote_option *vOpt = iter_data(it);
6854 reply("CSMSG_VOTE_OPTION",voteid,vOpt->name,vOpt->voted);
6861 chanserv_refresh_topics(UNUSED_ARG(void *data))
6863 unsigned int refresh_num = (now - self->link_time) / chanserv_conf.refresh_period;
6864 struct chanData *cData;
6867 for(cData = channelList; cData; cData = cData->next)
6869 if(IsSuspended(cData))
6871 opt = cData->chOpts[chTopicRefresh];
6874 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
6877 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
6878 cData->last_refresh = refresh_num;
6880 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
6883 static CHANSERV_FUNC(cmd_unf)
6887 char response[MAXLEN];
6888 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
6889 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6890 irc_privmsg(cmd->parent->bot, channel->name, response);
6893 reply("CSMSG_UNF_RESPONSE");
6897 static CHANSERV_FUNC(cmd_ping)
6901 char response[MAXLEN];
6902 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
6903 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6904 irc_privmsg(cmd->parent->bot, channel->name, response);
6907 reply("CSMSG_PING_RESPONSE");
6911 static CHANSERV_FUNC(cmd_wut)
6915 char response[MAXLEN];
6916 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
6917 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6918 irc_privmsg(cmd->parent->bot, channel->name, response);
6921 reply("CSMSG_WUT_RESPONSE");
6925 static CHANSERV_FUNC(cmd_8ball)
6927 unsigned int i, j, accum;
6932 for(i=1; i<argc; i++)
6933 for(j=0; argv[i][j]; j++)
6934 accum = (accum << 5) - accum + toupper(argv[i][j]);
6935 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
6938 char response[MAXLEN];
6939 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
6940 irc_privmsg(cmd->parent->bot, channel->name, response);
6943 send_message_type(4, user, cmd->parent->bot, "%s", resp);
6947 static CHANSERV_FUNC(cmd_d)
6949 unsigned long sides, count, modifier, ii, total;
6950 char response[MAXLEN], *sep;
6954 if((count = strtoul(argv[1], &sep, 10)) < 1)
6964 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
6965 && (sides = strtoul(sep+1, &sep, 10)) > 1)
6969 else if((sep[0] == '-') && isdigit(sep[1]))
6970 modifier = strtoul(sep, NULL, 10);
6971 else if((sep[0] == '+') && isdigit(sep[1]))
6972 modifier = strtoul(sep+1, NULL, 10);
6979 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
6984 reply("CSMSG_BAD_DICE_COUNT", count, 10);
6987 for(total = ii = 0; ii < count; ++ii)
6988 total += (rand() % sides) + 1;
6991 if((count > 1) || modifier)
6993 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
6994 sprintf(response, fmt, total, count, sides, modifier);
6998 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
6999 sprintf(response, fmt, total, sides);
7002 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
7004 send_message_type(4, user, cmd->parent->bot, "%s", response);
7008 static CHANSERV_FUNC(cmd_huggle)
7010 /* CTCP must be via PRIVMSG, never notice */
7012 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
7014 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
7019 chanserv_adjust_limit(void *data)
7021 struct mod_chanmode change;
7022 struct chanData *cData = data;
7023 struct chanNode *channel = cData->channel;
7026 if(IsSuspended(cData))
7029 cData->limitAdjusted = now;
7030 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
7031 if(cData->modes.modes_set & MODE_LIMIT)
7033 if(limit > cData->modes.new_limit)
7034 limit = cData->modes.new_limit;
7035 else if(limit == cData->modes.new_limit)
7039 mod_chanmode_init(&change);
7040 change.modes_set = MODE_LIMIT;
7041 change.new_limit = limit;
7042 mod_chanmode_announce(chanserv, channel, &change);
7046 handle_new_channel(struct chanNode *channel)
7048 struct chanData *cData;
7050 if(!(cData = channel->channel_info))
7053 if(cData->modes.modes_set || cData->modes.modes_clear)
7054 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
7056 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
7057 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
7060 void handle_new_channel_created(char *chan, struct userNode *user) {
7061 if(user->handle_info && chanserv_conf.new_channel_authed) {
7062 send_target_message(5, chan, chanserv, "%s", chanserv_conf.new_channel_authed);
7063 } else if(!user->handle_info && chanserv_conf.new_channel_unauthed) {
7064 send_target_message(5, chan, chanserv, "%s", chanserv_conf.new_channel_unauthed);
7066 if(chanserv_conf.new_channel_msg)
7067 send_target_message(5, chan, chanserv, "%s", chanserv_conf.new_channel_msg);
7070 /* Welcome to my worst nightmare. Warning: Read (or modify)
7071 the code below at your own risk. */
7073 handle_join(struct modeNode *mNode)
7075 struct mod_chanmode change;
7076 struct userNode *user = mNode->user;
7077 struct chanNode *channel = mNode->channel;
7078 struct chanData *cData;
7079 struct userData *uData = NULL;
7080 struct banData *bData;
7081 struct handle_info *handle;
7082 unsigned int modes = 0, info = 0;
7086 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
7089 cData = channel->channel_info;
7090 if(channel->members.used > cData->max) {
7091 cData->max = channel->members.used;
7092 cData->max_time = now;
7095 for(i = 0; i < channel->invited.used; i++)
7097 if(channel->invited.list[i] == user) {
7098 userList_remove(&channel->invited, user);
7102 /* Check for bans. If they're joining through a ban, one of two
7104 * 1: Join during a netburst, by riding the break. Kick them
7105 * unless they have ops or voice in the channel.
7106 * 2: They're allowed to join through the ban (an invite in
7107 * ircu2.10, or a +e on Hybrid, or something).
7108 * If they're not joining through a ban, and the banlist is not
7109 * full, see if they're on the banlist for the channel. If so,
7112 if(user->uplink->burst && !mNode->modes)
7115 for(ii = 0; ii < channel->banlist.used; ii++)
7117 if(user_matches_glob(user, channel->banlist.list[ii]->ban, MATCH_USENICK))
7119 /* Riding a netburst. Naughty. */
7120 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
7126 mod_chanmode_init(&change);
7128 if(channel->banlist.used < MAXBANS)
7130 /* Not joining through a ban. */
7131 for(bData = cData->bans;
7132 bData && !user_matches_glob(user, bData->mask, MATCH_USENICK);
7133 bData = bData->next);
7137 char kick_reason[MAXLEN];
7138 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
7140 bData->triggered = now;
7141 if(bData != cData->bans)
7143 /* Shuffle the ban to the head of the list. */
7145 bData->next->prev = bData->prev;
7147 bData->prev->next = bData->next;
7150 bData->next = cData->bans;
7153 cData->bans->prev = bData;
7154 cData->bans = bData;
7157 change.args[0].mode = MODE_BAN;
7158 change.args[0].u.hostmask = bData->mask;
7159 mod_chanmode_announce(chanserv, channel, &change);
7160 KickChannelUser(user, channel, chanserv, kick_reason);
7165 /* ChanServ will not modify the limits in join-flooded channels,
7166 or when there are enough slots left below the limit. */
7167 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
7168 && !channel->join_flooded
7169 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
7171 /* The user count has begun "bumping" into the channel limit,
7172 so set a timer to raise the limit a bit. Any previous
7173 timers are removed so three incoming users within the delay
7174 results in one limit change, not three. */
7176 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7177 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7180 if(channel->join_flooded)
7182 /* don't automatically give ops or voice during a join flood */
7184 else if(cData->lvlOpts[lvlGiveOps] == 0)
7185 modes |= MODE_CHANOP;
7186 else if(cData->lvlOpts[lvlGiveVoice] == 0)
7187 modes |= MODE_VOICE;
7189 greeting = cData->greeting;
7190 if(user->handle_info)
7192 handle = user->handle_info;
7194 if(IsHelper(user) && !IsHelping(user))
7197 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7199 if(channel == chanserv_conf.support_channels.list[ii])
7201 HANDLE_SET_FLAG(user->handle_info, HELPING);
7207 uData = GetTrueChannelAccess(cData, handle);
7208 if(uData && !IsUserSuspended(uData))
7210 /* Ops and above were handled by the above case. */
7211 if(IsUserAutoOp(uData))
7213 if(uData->access >= cData->lvlOpts[lvlGiveOps])
7214 modes |= MODE_CHANOP;
7215 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
7216 modes |= MODE_VOICE;
7218 if(uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
7219 cData->visited = now;
7220 if(cData->user_greeting)
7221 greeting = cData->user_greeting;
7223 && (uData->access >= cData->lvlOpts[lvlUserInfo])
7224 && ((now - uData->seen) >= chanserv_conf.info_delay)
7232 /* If user joining normally (not during burst), apply op or voice,
7233 * and send greeting/userinfo as appropriate.
7235 if(!user->uplink->burst)
7239 if(modes & MODE_CHANOP)
7240 modes &= ~MODE_VOICE;
7241 change.args[0].mode = modes;
7242 change.args[0].u.member = mNode;
7243 mod_chanmode_announce(chanserv, channel, &change);
7246 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
7247 if(uData && info && (modes || !(channel->modes & MODE_DELAYJOINS)))
7248 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
7254 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
7256 struct mod_chanmode change;
7257 struct userData *channel;
7258 unsigned int ii, jj;
7260 if(!user->handle_info)
7263 mod_chanmode_init(&change);
7265 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
7267 struct chanNode *cn;
7268 struct modeNode *mn;
7269 if(IsUserSuspended(channel)
7270 || IsSuspended(channel->channel)
7271 || !(cn = channel->channel->channel))
7274 mn = GetUserMode(cn, user);
7277 if(!IsUserSuspended(channel)
7278 && IsUserAutoInvite(channel)
7279 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
7281 && !user->uplink->burst)
7282 irc_invite(chanserv, user, cn);
7286 if(channel->access >= UL_PRESENT && !HANDLE_FLAGGED(channel->handle, BOT))
7287 channel->channel->visited = now;
7289 if(IsUserAutoOp(channel))
7291 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
7292 change.args[0].mode = MODE_CHANOP;
7293 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
7294 change.args[0].mode = MODE_VOICE;
7296 change.args[0].mode = 0;
7297 change.args[0].u.member = mn;
7298 if(change.args[0].mode)
7299 mod_chanmode_announce(chanserv, cn, &change);
7302 channel->seen = now;
7303 channel->present = 1;
7306 for(ii = 0; ii < user->channels.used; ++ii)
7308 struct chanNode *chan = user->channels.list[ii]->channel;
7309 struct banData *ban;
7311 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
7312 || !chan->channel_info
7313 || IsSuspended(chan->channel_info))
7315 for(jj = 0; jj < chan->banlist.used; ++jj)
7316 if(user_matches_glob(user, chan->banlist.list[jj]->ban, MATCH_USENICK))
7318 if(jj < chan->banlist.used)
7320 for(ban = chan->channel_info->bans; ban; ban = ban->next)
7322 char kick_reason[MAXLEN];
7323 if(!user_matches_glob(user, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
7325 change.args[0].mode = MODE_BAN;
7326 change.args[0].u.hostmask = ban->mask;
7327 mod_chanmode_announce(chanserv, chan, &change);
7328 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
7329 KickChannelUser(user, chan, chanserv, kick_reason);
7330 ban->triggered = now;
7335 if(IsSupportHelper(user))
7337 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7339 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
7341 HANDLE_SET_FLAG(user->handle_info, HELPING);
7349 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
7351 struct chanData *cData;
7352 struct userData *uData;
7354 cData = mn->channel->channel_info;
7355 if(!cData || IsSuspended(cData) || IsLocal(mn->user))
7358 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
7360 /* Allow for a bit of padding so that the limit doesn't
7361 track the user count exactly, which could get annoying. */
7362 if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
7364 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7365 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7369 if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
7371 scan_user_presence(uData, mn->user);
7373 if (uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
7374 cData->visited = now;
7377 if(IsHelping(mn->user) && IsSupportHelper(mn->user))
7380 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii) {
7381 struct chanNode *channel;
7382 struct userNode *exclude;
7383 /* When looking at the channel that is being /part'ed, we
7384 * have to skip over the client that is leaving. For
7385 * other channels, we must not do that.
7387 channel = chanserv_conf.support_channels.list[ii];
7388 exclude = (channel == mn->channel) ? mn->user : NULL;
7389 if(find_handle_in_channel(channel, mn->user->handle_info, exclude))
7392 if(ii == chanserv_conf.support_channels.used)
7393 HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
7398 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
7400 struct userData *uData;
7402 if(!channel->channel_info || !kicker || IsService(kicker)
7403 || (kicker == victim) || IsSuspended(channel->channel_info)
7404 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
7407 if(protect_user(victim, kicker, channel->channel_info))
7409 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED_2");
7410 KickChannelUser(kicker, channel, chanserv, reason);
7413 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
7418 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
7420 struct chanData *cData;
7422 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
7425 cData = channel->channel_info;
7426 if(bad_topic(channel, user, channel->topic))
7428 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
7429 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
7430 SetChannelTopic(channel, chanserv, old_topic, 1);
7431 else if(cData->topic)
7432 SetChannelTopic(channel, chanserv, cData->topic, 1);
7435 /* With topicsnarf, grab the topic and save it as the default topic. */
7436 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
7439 cData->topic = strdup(channel->topic);
7445 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
7447 struct mod_chanmode *bounce = NULL;
7448 unsigned int bnc, ii;
7451 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
7454 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
7455 && mode_lock_violated(&channel->channel_info->modes, change))
7457 char correct[MAXLEN];
7458 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
7459 mod_chanmode_format(&channel->channel_info->modes, correct);
7460 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
7462 for(ii = bnc = 0; ii < change->argc; ++ii)
7464 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
7466 const struct userNode *victim = change->args[ii].u.member->user;
7467 if(!protect_user(victim, user, channel->channel_info))
7470 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7473 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
7474 bounce->args[bnc].u.member = GetUserMode(channel, user);
7475 if(bounce->args[bnc].u.member)
7479 bounce->args[bnc].mode = MODE_CHANOP;
7480 bounce->args[bnc].u.member = change->args[ii].u.member;
7482 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
7484 else if(change->args[ii].mode & MODE_CHANOP)
7486 const struct userNode *victim = change->args[ii].u.member->user;
7487 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
7490 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7491 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
7492 bounce->args[bnc].u.member = change->args[ii].u.member;
7495 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
7497 const char *ban = change->args[ii].u.hostmask;
7498 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
7501 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7502 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
7503 bounce->args[bnc].u.hostmask = strdup(ban);
7505 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
7510 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
7511 mod_chanmode_announce(chanserv, channel, bounce);
7512 for(ii = 0; ii < change->argc; ++ii)
7513 if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
7514 free((char*)bounce->args[ii].u.hostmask);
7515 mod_chanmode_free(bounce);
7520 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
7522 struct chanNode *channel;
7523 struct banData *bData;
7524 struct mod_chanmode change;
7525 unsigned int ii, jj;
7526 char kick_reason[MAXLEN];
7528 mod_chanmode_init(&change);
7530 change.args[0].mode = MODE_BAN;
7531 for(ii = 0; ii < user->channels.used; ++ii)
7533 channel = user->channels.list[ii]->channel;
7534 /* Need not check for bans if they're opped or voiced. */
7535 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
7537 /* Need not check for bans unless channel registration is active. */
7538 if(!channel->channel_info || IsSuspended(channel->channel_info))
7540 /* Look for a matching ban already on the channel. */
7541 for(jj = 0; jj < channel->banlist.used; ++jj)
7542 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
7544 /* Need not act if we found one. */
7545 if(jj < channel->banlist.used)
7547 /* Look for a matching ban in this channel. */
7548 for(bData = channel->channel_info->bans; bData; bData = bData->next)
7550 if(!user_matches_glob(user, bData->mask, MATCH_USENICK | MATCH_VISIBLE))
7552 change.args[0].u.hostmask = bData->mask;
7553 mod_chanmode_announce(chanserv, channel, &change);
7554 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
7555 KickChannelUser(user, channel, chanserv, kick_reason);
7556 bData->triggered = now;
7557 break; /* we don't need to check any more bans in the channel */
7562 static void handle_rename(struct handle_info *handle, const char *old_handle)
7564 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
7568 dict_remove2(handle_dnrs, old_handle, 1);
7569 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
7570 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
7575 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
7577 struct userNode *h_user;
7579 if(handle->channels)
7581 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
7582 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
7584 while(handle->channels)
7585 del_channel_user(handle->channels, 1);
7590 handle_server_link(UNUSED_ARG(struct server *server))
7592 struct chanData *cData;
7594 for(cData = channelList; cData; cData = cData->next)
7596 if(!IsSuspended(cData))
7597 cData->may_opchan = 1;
7598 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
7599 && !cData->channel->join_flooded
7600 && ((cData->channel->limit - cData->channel->members.used)
7601 < chanserv_conf.adjust_threshold))
7603 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7604 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7610 chanserv_conf_read(void)
7614 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
7615 struct mod_chanmode *change;
7616 struct string_list *strlist;
7617 struct chanNode *chan;
7620 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
7622 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
7625 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7626 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7627 chanserv_conf.support_channels.used = 0;
7628 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
7630 for(ii = 0; ii < strlist->used; ++ii)
7632 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7635 chan = AddChannel(strlist->list[ii], now, str2, NULL);
7637 channelList_append(&chanserv_conf.support_channels, chan);
7640 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
7643 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7646 chan = AddChannel(str, now, str2, NULL);
7648 channelList_append(&chanserv_conf.support_channels, chan);
7650 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
7651 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
7652 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
7653 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
7654 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
7655 chanserv_conf.greeting_length = str ? atoi(str) : 200;
7656 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
7657 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
7658 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
7659 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
7660 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
7661 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
7662 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
7663 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
7664 str = database_get_data(conf_node, KEY_DNR_EXPIRE_FREQ, RECDB_QSTRING);
7665 chanserv_conf.dnr_expire_frequency = str ? ParseInterval(str) : 3600;
7666 str = database_get_data(conf_node, KEY_INVITED_INTERVAL, RECDB_QSTRING);
7667 chanserv_conf.invited_timeout = str ? ParseInterval(str) : 600*2;
7668 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
7669 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
7670 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
7671 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
7672 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
7673 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
7674 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
7675 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
7676 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
7678 NickChange(chanserv, str, 0);
7679 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
7680 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
7681 str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
7682 chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
7683 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
7684 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
7685 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
7686 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
7687 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
7688 chanserv_conf.max_owned = str ? atoi(str) : 5;
7689 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
7690 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
7691 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
7692 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
7693 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
7694 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
7695 str = database_get_data(conf_node, KEY_NEW_CHANNEL_AUTHED, RECDB_QSTRING);
7696 chanserv_conf.new_channel_authed = str ? str : NULL;
7697 str = database_get_data(conf_node, KEY_NEW_CHANNEL_UNAUTHED, RECDB_QSTRING);
7698 chanserv_conf.new_channel_unauthed = str ? str : NULL;
7699 str = database_get_data(conf_node, KEY_NEW_CHANNEL_MSG, RECDB_QSTRING);
7700 chanserv_conf.new_channel_msg = str ? str : NULL;
7701 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
7704 safestrncpy(mode_line, str, sizeof(mode_line));
7705 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
7706 if((change = mod_chanmode_parse(NULL, NULL, modes, ii, MCP_KEY_FREE|MCP_NO_APASS, 0))
7707 && (change->argc < 2))
7709 chanserv_conf.default_modes = *change;
7710 mod_chanmode_free(change);
7712 free_string_list(chanserv_conf.set_shows);
7713 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
7715 strlist = string_list_copy(strlist);
7718 static const char *list[] = {
7719 /* free form text */
7720 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
7721 /* options based on user level */
7722 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
7723 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
7724 /* multiple choice options */
7725 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
7726 /* binary options */
7727 "DynLimit", "NoDelete", "expire", "Vote",
7731 strlist = alloc_string_list(ArrayLength(list)-1);
7732 for(ii=0; list[ii]; ii++)
7733 string_list_append(strlist, strdup(list[ii]));
7735 chanserv_conf.set_shows = strlist;
7736 /* We don't look things up now, in case the list refers to options
7737 * defined by modules initialized after this point. Just mark the
7738 * function list as invalid, so it will be initialized.
7740 set_shows_list.used = 0;
7741 free_string_list(chanserv_conf.eightball);
7742 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
7745 strlist = string_list_copy(strlist);
7749 strlist = alloc_string_list(4);
7750 string_list_append(strlist, strdup("Yes."));
7751 string_list_append(strlist, strdup("No."));
7752 string_list_append(strlist, strdup("Maybe so."));
7754 chanserv_conf.eightball = strlist;
7755 free_string_list(chanserv_conf.old_ban_names);
7756 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
7758 strlist = string_list_copy(strlist);
7760 strlist = alloc_string_list(2);
7761 chanserv_conf.old_ban_names = strlist;
7762 str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
7763 off_channel = str ? atoi(str) : 0;
7767 chanserv_note_type_read(const char *key, struct record_data *rd)
7770 struct note_type *ntype;
7773 if(!(obj = GET_RECORD_OBJECT(rd)))
7775 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
7778 if(!(ntype = chanserv_create_note_type(key)))
7780 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
7784 /* Figure out set access */
7785 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
7787 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7788 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
7790 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
7792 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
7793 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
7795 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
7797 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
7801 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
7802 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7803 ntype->set_access.min_opserv = 0;
7806 /* Figure out visibility */
7807 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
7808 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7809 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
7810 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7811 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
7812 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
7813 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
7814 ntype->visible_type = NOTE_VIS_ALL;
7816 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7818 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
7819 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
7823 vote_option_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7825 struct vote_option *vOpt;
7828 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7830 log_module(CS_LOG, LOG_ERROR, "Invalid vote option in %s.", chan->channel->name);
7834 vOpt = calloc(1, sizeof(*vOpt));
7835 vOpt->name = strdup(database_get_data(rd->d.object, KEY_VOTE_OPTION_NAME, RECDB_QSTRING));
7836 str = database_get_data(rd->d.object, KEY_VOTE_OPTION_VOTED, RECDB_QSTRING);
7837 vOpt->voted = str ? atoi(str) : 0;
7838 vOpt->option_id = str ? atoi(key) : 0;
7839 vOpt->option_str = strdup(key);
7840 dict_insert(chan->vote_options,vOpt->option_str,vOpt);
7844 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7846 struct handle_info *handle;
7847 struct userData *uData;
7848 char *seen, *inf, *flags, *voted, *votefor;
7849 unsigned long last_seen;
7850 unsigned short access_level;
7852 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7854 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
7858 access_level = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
7859 if(access_level > UL_OWNER)
7861 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
7865 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
7866 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
7867 last_seen = seen ? strtoul(seen, NULL, 0) : now;
7868 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
7869 voted = database_get_data(rd->d.object, KEY_VOTE_VOTED, RECDB_QSTRING);
7870 votefor = database_get_data(rd->d.object, KEY_VOTE_VOTEDFOR, RECDB_QSTRING);
7871 handle = get_handle_info(key);
7874 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
7878 uData = add_channel_user(chan, handle, access_level, last_seen, inf);
7879 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
7881 uData->voted = voted ? strtoul(voted, NULL, 0) : 0;
7882 uData->votefor = votefor ? strtoul(votefor, NULL, 0) : 0;
7890 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7892 struct banData *bData;
7893 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
7894 unsigned long set_time, triggered_time, expires_time;
7896 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7898 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
7902 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
7903 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
7904 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
7905 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
7906 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
7907 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
7908 if (!reason || !owner)
7911 set_time = set ? strtoul(set, NULL, 0) : now;
7912 triggered_time = triggered ? strtoul(triggered, NULL, 0) : 0;
7914 expires_time = strtoul(s_expires, NULL, 0);
7916 expires_time = set_time + atoi(s_duration);
7920 if(!reason || (expires_time && (expires_time < now)))
7923 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
7926 static struct suspended *
7927 chanserv_read_suspended(dict_t obj)
7929 struct suspended *suspended = calloc(1, sizeof(*suspended));
7933 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
7934 suspended->expires = str ? strtoul(str, NULL, 0) : 0;
7935 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
7936 suspended->revoked = str ? strtoul(str, NULL, 0) : 0;
7937 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
7938 suspended->issued = str ? strtoul(str, NULL, 0) : 0;
7939 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
7940 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
7941 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
7942 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
7947 chanserv_channel_read(const char *key, struct record_data *hir)
7949 struct suspended *suspended;
7950 struct mod_chanmode *modes;
7951 struct chanNode *cNode;
7952 struct chanData *cData;
7953 struct dict *channel, *obj;
7954 char *str, *argv[10];
7958 channel = hir->d.object;
7960 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
7963 cNode = AddChannel(key, now, NULL, NULL);
7966 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
7969 cData = register_channel(cNode, str);
7972 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
7976 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
7978 enum levelOption lvlOpt;
7979 enum charOption chOpt;
7981 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
7982 cData->flags = atoi(str);
7984 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7986 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
7988 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
7989 else if(levelOptions[lvlOpt].old_flag)
7991 if(cData->flags & levelOptions[lvlOpt].old_flag)
7992 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
7994 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
7998 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8000 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
8002 cData->chOpts[chOpt] = str[0];
8005 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
8007 enum levelOption lvlOpt;
8008 enum charOption chOpt;
8011 cData->flags = base64toint(str, 5);
8012 count = strlen(str += 5);
8013 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8016 if(levelOptions[lvlOpt].old_flag)
8018 if(cData->flags & levelOptions[lvlOpt].old_flag)
8019 lvl = levelOptions[lvlOpt].flag_value;
8021 lvl = levelOptions[lvlOpt].default_value;
8023 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
8025 case 'c': lvl = UL_COOWNER; break;
8026 case 'm': lvl = UL_MASTER; break;
8027 case 'n': lvl = UL_OWNER+1; break;
8028 case 'o': lvl = UL_OP; break;
8029 case 'p': lvl = UL_PEON; break;
8030 case 'w': lvl = UL_OWNER; break;
8031 default: lvl = 0; break;
8033 cData->lvlOpts[lvlOpt] = lvl;
8035 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8036 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
8039 if((str = database_get_data(hir->d.object, KEY_EXPIRE, RECDB_QSTRING)))
8041 cData->expiry = atoi(str);
8042 if(cData->expiry > 0) {
8043 if(cData->expiry > now) {
8044 timeq_add(cData->expiry, chanserv_expire_channel, cData);
8046 timeq_add(1, chanserv_expire_channel, cData);
8053 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
8055 suspended = chanserv_read_suspended(obj);
8056 cData->suspended = suspended;
8057 suspended->cData = cData;
8058 /* We could use suspended->expires and suspended->revoked to
8059 * set the CHANNEL_SUSPENDED flag, but we don't. */
8061 else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
8063 suspended = calloc(1, sizeof(*suspended));
8064 suspended->issued = 0;
8065 suspended->revoked = 0;
8066 suspended->suspender = strdup(str);
8067 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
8068 suspended->expires = str ? atoi(str) : 0;
8069 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
8070 suspended->reason = strdup(str ? str : "No reason");
8071 suspended->previous = NULL;
8072 cData->suspended = suspended;
8073 suspended->cData = cData;
8077 cData->flags &= ~CHANNEL_SUSPENDED;
8078 suspended = NULL; /* to squelch a warning */
8081 if(IsSuspended(cData)) {
8082 if(suspended->expires > now)
8083 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
8084 else if(suspended->expires)
8085 cData->flags &= ~CHANNEL_SUSPENDED;
8088 if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
8089 struct mod_chanmode change;
8090 mod_chanmode_init(&change);
8092 change.args[0].mode = MODE_CHANOP;
8093 change.args[0].u.member = AddChannelUser(chanserv, cNode);
8094 mod_chanmode_announce(chanserv, cNode, &change);
8097 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
8098 cData->registered = str ? strtoul(str, NULL, 0) : now;
8099 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
8100 cData->visited = str ? strtoul(str, NULL, 0) : now;
8101 str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
8102 cData->ownerTransfer = str ? strtoul(str, NULL, 0) : 0;
8103 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
8104 cData->max = str ? atoi(str) : 0;
8105 str = database_get_data(channel, KEY_MAX_TIME, RECDB_QSTRING);
8106 cData->max_time = str ? atoi(str) : 0;
8107 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
8108 cData->greeting = str ? strdup(str) : NULL;
8109 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
8110 cData->user_greeting = str ? strdup(str) : NULL;
8111 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
8112 cData->topic_mask = str ? strdup(str) : NULL;
8113 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
8114 cData->topic = str ? strdup(str) : NULL;
8116 str = database_get_data(channel, KEY_VOTE, RECDB_QSTRING);
8118 cData->vote = str ? strdup(str) : NULL;
8119 dict_delete(cData->vote_options);
8120 cData->vote_options = dict_new();
8121 dict_set_free_data(cData->vote_options, free_vote_options);
8122 str = database_get_data(channel, KEY_VOTE_START, RECDB_QSTRING);
8123 cData->vote_start = str ? atoi(str) : 0;
8124 obj = database_get_data(channel, KEY_VOTE_OPTIONS, RECDB_OBJECT);
8125 for(it = dict_first(obj); it; it = iter_next(it)) {
8126 vote_option_read_helper(iter_key(it), iter_data(it), cData);
8130 if(!IsSuspended(cData)
8131 && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
8132 && (argc = split_line(str, 0, ArrayLength(argv), argv))
8133 && (modes = mod_chanmode_parse(cNode, NULL, argv, argc, MCP_KEY_FREE|MCP_NO_APASS, 0))) {
8134 cData->modes = *modes;
8136 cData->modes.modes_set |= MODE_REGISTERED;
8137 if(cData->modes.argc > 1)
8138 cData->modes.argc = 1;
8139 mod_chanmode_announce(chanserv, cNode, &cData->modes);
8140 mod_chanmode_free(modes);
8143 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
8144 for(it = dict_first(obj); it; it = iter_next(it))
8145 user_read_helper(iter_key(it), iter_data(it), cData);
8147 if(!cData->users && !IsProtected(cData))
8149 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
8150 unregister_channel(cData, "has empty user list.");
8154 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
8155 for(it = dict_first(obj); it; it = iter_next(it))
8156 ban_read_helper(iter_key(it), iter_data(it), cData);
8158 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
8159 for(it = dict_first(obj); it; it = iter_next(it))
8161 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
8162 struct record_data *rd = iter_data(it);
8163 const char *note, *setter;
8165 if(rd->type != RECDB_OBJECT)
8167 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
8171 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
8173 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
8175 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
8179 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
8180 if(!setter) setter = "<unknown>";
8181 chanserv_add_channel_note(cData, ntype, setter, note);
8189 chanserv_dnr_read(const char *key, struct record_data *hir)
8191 const char *setter, *reason, *str;
8192 struct do_not_register *dnr;
8193 unsigned long expiry;
8195 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
8198 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
8201 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
8204 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
8207 str = database_get_data(hir->d.object, KEY_EXPIRES, RECDB_QSTRING);
8208 expiry = str ? strtoul(str, NULL, 0) : 0;
8209 if(expiry && expiry <= now)
8211 dnr = chanserv_add_dnr(key, setter, expiry, reason);
8214 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
8216 dnr->set = atoi(str);
8222 chanserv_saxdb_read(struct dict *database)
8224 struct dict *section;
8227 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
8228 for(it = dict_first(section); it; it = iter_next(it))
8229 chanserv_note_type_read(iter_key(it), iter_data(it));
8231 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
8232 for(it = dict_first(section); it; it = iter_next(it))
8233 chanserv_channel_read(iter_key(it), iter_data(it));
8235 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
8236 for(it = dict_first(section); it; it = iter_next(it))
8237 chanserv_dnr_read(iter_key(it), iter_data(it));
8243 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
8245 int high_present = 0;
8246 saxdb_start_record(ctx, KEY_USERS, 1);
8247 for(; uData; uData = uData->next)
8249 if((uData->access >= UL_PRESENT) && uData->present && !HANDLE_FLAGGED(uData->handle, BOT))
8251 saxdb_start_record(ctx, uData->handle->handle, 0);
8252 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
8253 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
8255 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
8256 if(uData->channel->vote && uData->voted)
8257 saxdb_write_int(ctx, KEY_VOTE_VOTED, uData->voted);
8258 if(uData->channel->vote && uData->votefor)
8259 saxdb_write_int(ctx, KEY_VOTE_VOTEDFOR, uData->votefor);
8261 saxdb_write_string(ctx, KEY_INFO, uData->info);
8262 saxdb_end_record(ctx);
8264 saxdb_end_record(ctx);
8265 return high_present;
8269 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
8273 saxdb_start_record(ctx, KEY_BANS, 1);
8274 for(; bData; bData = bData->next)
8276 saxdb_start_record(ctx, bData->mask, 0);
8277 saxdb_write_int(ctx, KEY_SET, bData->set);
8278 if(bData->triggered)
8279 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
8281 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
8283 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
8285 saxdb_write_string(ctx, KEY_REASON, bData->reason);
8286 saxdb_end_record(ctx);
8288 saxdb_end_record(ctx);
8292 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
8294 saxdb_start_record(ctx, name, 0);
8295 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
8296 saxdb_write_string(ctx, KEY_REASON, susp->reason);
8298 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
8300 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
8302 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
8304 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
8305 saxdb_end_record(ctx);
8309 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
8313 enum levelOption lvlOpt;
8314 enum charOption chOpt;
8317 saxdb_start_record(ctx, channel->channel->name, 1);
8319 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
8320 saxdb_write_int(ctx, KEY_MAX, channel->max);
8321 saxdb_write_int(ctx, KEY_MAX_TIME, channel->max_time);
8323 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
8324 if(channel->registrar)
8325 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
8326 if(channel->greeting)
8327 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
8328 if(channel->user_greeting)
8329 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
8330 if(channel->topic_mask)
8331 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
8332 if(channel->suspended)
8333 chanserv_write_suspended(ctx, "suspended", channel->suspended);
8335 saxdb_write_int(ctx, KEY_EXPIRE, channel->expiry);
8338 saxdb_write_string(ctx, KEY_VOTE, channel->vote);
8339 if(channel->vote_start)
8340 saxdb_write_int(ctx, KEY_VOTE_START, channel->vote_start);
8341 if (dict_size(channel->vote_options)) {
8342 saxdb_start_record(ctx, KEY_VOTE_OPTIONS, 1);
8343 for (it = dict_first(channel->vote_options); it; it = iter_next(it)) {
8344 struct vote_option *vOpt = iter_data(it);
8346 sprintf(str,"%i",vOpt->option_id);
8347 saxdb_start_record(ctx, str, 0);
8349 saxdb_write_int(ctx, KEY_VOTE_OPTION_VOTED, vOpt->voted);
8351 saxdb_write_string(ctx, KEY_VOTE_OPTION_NAME, vOpt->name);
8352 saxdb_end_record(ctx);
8354 saxdb_end_record(ctx);
8358 saxdb_start_record(ctx, KEY_OPTIONS, 0);
8359 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
8360 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8361 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
8362 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8364 buf[0] = channel->chOpts[chOpt];
8366 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
8368 saxdb_end_record(ctx);
8370 if(channel->modes.modes_set || channel->modes.modes_clear)
8372 mod_chanmode_format(&channel->modes, buf);
8373 saxdb_write_string(ctx, KEY_MODES, buf);
8376 high_present = chanserv_write_users(ctx, channel->users);
8377 chanserv_write_bans(ctx, channel->bans);
8379 if(dict_size(channel->notes))
8383 saxdb_start_record(ctx, KEY_NOTES, 1);
8384 for(it = dict_first(channel->notes); it; it = iter_next(it))
8386 struct note *note = iter_data(it);
8387 saxdb_start_record(ctx, iter_key(it), 0);
8388 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
8389 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
8390 saxdb_end_record(ctx);
8392 saxdb_end_record(ctx);
8395 if(channel->ownerTransfer)
8396 saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
8397 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
8398 saxdb_end_record(ctx);
8402 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
8406 saxdb_start_record(ctx, ntype->name, 0);
8407 switch(ntype->set_access_type)
8409 case NOTE_SET_CHANNEL_ACCESS:
8410 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
8412 case NOTE_SET_CHANNEL_SETTER:
8413 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
8415 case NOTE_SET_PRIVILEGED: default:
8416 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
8419 switch(ntype->visible_type)
8421 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
8422 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
8423 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
8425 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
8426 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
8427 saxdb_end_record(ctx);
8431 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
8433 struct do_not_register *dnr;
8434 dict_iterator_t it, next;
8436 for(it = dict_first(dnrs); it; it = next)
8438 next = iter_next(it);
8439 dnr = iter_data(it);
8440 if(dnr->expires && dnr->expires <= now)
8442 dict_remove(dnrs, iter_key(it));
8445 saxdb_start_record(ctx, dnr->chan_name, 0);
8447 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
8449 saxdb_write_int(ctx, KEY_EXPIRES, dnr->expires);
8450 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
8451 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
8452 saxdb_end_record(ctx);
8457 chanserv_saxdb_write(struct saxdb_context *ctx)
8460 struct chanData *channel;
8463 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
8464 for(it = dict_first(note_types); it; it = iter_next(it))
8465 chanserv_write_note_type(ctx, iter_data(it));
8466 saxdb_end_record(ctx);
8469 saxdb_start_record(ctx, KEY_DNR, 1);
8470 write_dnrs_helper(ctx, handle_dnrs);
8471 write_dnrs_helper(ctx, plain_dnrs);
8472 write_dnrs_helper(ctx, mask_dnrs);
8473 saxdb_end_record(ctx);
8476 saxdb_start_record(ctx, KEY_CHANNELS, 1);
8477 for(channel = channelList; channel; channel = channel->next)
8478 chanserv_write_channel(ctx, channel);
8479 saxdb_end_record(ctx);
8485 chanserv_db_cleanup(void) {
8487 unreg_part_func(handle_part);
8489 unregister_channel(channelList, "terminating.");
8490 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
8491 UnlockChannel(chanserv_conf.support_channels.list[ii]);
8492 free(chanserv_conf.support_channels.list);
8493 dict_delete(handle_dnrs);
8494 dict_delete(plain_dnrs);
8495 dict_delete(mask_dnrs);
8496 dict_delete(note_types);
8497 free_string_list(chanserv_conf.eightball);
8498 free_string_list(chanserv_conf.old_ban_names);
8499 free_string_list(chanserv_conf.set_shows);
8500 free(set_shows_list.list);
8501 free(uset_shows_list.list);
8504 struct userData *helper = helperList;
8505 helperList = helperList->next;
8510 #if defined(GCC_VARMACROS)
8511 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ARGS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ARGS)
8512 #elif defined(C99_VARMACROS)
8513 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, __VA_ARGS__)
8515 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
8516 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
8519 init_chanserv(const char *nick)
8521 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
8522 conf_register_reload(chanserv_conf_read);
8526 reg_server_link_func(handle_server_link);
8527 reg_new_channel_func(handle_new_channel);
8528 reg_join_func(handle_join);
8529 reg_part_func(handle_part);
8530 reg_kick_func(handle_kick);
8531 reg_topic_func(handle_topic);
8532 reg_mode_change_func(handle_mode);
8533 reg_nick_change_func(handle_nick_change);
8534 reg_auth_func(handle_auth);
8537 reg_handle_rename_func(handle_rename);
8538 reg_unreg_func(handle_unreg);
8540 handle_dnrs = dict_new();
8541 dict_set_free_data(handle_dnrs, free);
8542 plain_dnrs = dict_new();
8543 dict_set_free_data(plain_dnrs, free);
8544 mask_dnrs = dict_new();
8545 dict_set_free_data(mask_dnrs, free);
8547 reg_svccmd_unbind_func(handle_svccmd_unbind);
8548 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
8549 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
8550 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
8551 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
8552 DEFINE_COMMAND(dnrsearch, 3, 0, "template", "noregister", NULL);
8553 modcmd_register(chanserv_module, "dnrsearch print", NULL, 0, 0, NULL);
8554 modcmd_register(chanserv_module, "dnrsearch remove", NULL, 0, 0, NULL);
8555 modcmd_register(chanserv_module, "dnrsearch count", NULL, 0, 0, NULL);
8556 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
8557 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
8558 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
8559 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
8560 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
8562 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
8563 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
8565 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8566 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8567 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8568 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8569 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
8571 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
8572 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
8573 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
8574 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8575 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8577 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8578 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
8579 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8580 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
8582 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
8583 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
8584 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8585 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8586 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
8587 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8588 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8589 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8591 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8592 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8593 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8594 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
8595 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
8596 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
8597 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
8598 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
8599 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8600 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
8601 DEFINE_COMMAND(invitemeall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8602 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
8603 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
8604 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8605 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8607 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
8608 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8609 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8610 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8611 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
8613 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
8614 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
8616 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
8617 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8618 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8619 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8620 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8621 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8622 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8623 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8624 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8625 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8626 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8628 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
8629 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
8631 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
8632 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
8633 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
8634 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
8636 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
8637 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
8638 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
8639 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
8640 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
8642 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8643 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8644 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8645 DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8646 DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8647 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8649 DEFINE_COMMAND(addvote, 1, MODCMD_REQUIRE_AUTHED, NULL);
8650 DEFINE_COMMAND(delvote, 1, MODCMD_REQUIRE_AUTHED, NULL);
8651 DEFINE_COMMAND(addoption, 1, MODCMD_REQUIRE_AUTHED, NULL);
8652 DEFINE_COMMAND(deloption, 1, MODCMD_REQUIRE_AUTHED, NULL);
8653 DEFINE_COMMAND(vote, 1, MODCMD_REQUIRE_AUTHED, NULL);
8654 DEFINE_COMMAND(startvote, 1, MODCMD_REQUIRE_AUTHED, NULL);
8655 DEFINE_COMMAND(endvote, 1, MODCMD_REQUIRE_AUTHED, NULL);
8656 DEFINE_COMMAND(voteresults, 1, MODCMD_REQUIRE_AUTHED, NULL);
8658 /* Channel options */
8659 DEFINE_CHANNEL_OPTION(defaulttopic);
8660 DEFINE_CHANNEL_OPTION(topicmask);
8661 DEFINE_CHANNEL_OPTION(greeting);
8662 DEFINE_CHANNEL_OPTION(usergreeting);
8663 DEFINE_CHANNEL_OPTION(modes);
8664 DEFINE_CHANNEL_OPTION(enfops);
8665 DEFINE_CHANNEL_OPTION(giveops);
8666 DEFINE_CHANNEL_OPTION(protect);
8667 DEFINE_CHANNEL_OPTION(enfmodes);
8668 DEFINE_CHANNEL_OPTION(enftopic);
8669 DEFINE_CHANNEL_OPTION(pubcmd);
8670 DEFINE_CHANNEL_OPTION(givevoice);
8671 DEFINE_CHANNEL_OPTION(userinfo);
8672 DEFINE_CHANNEL_OPTION(dynlimit);
8673 DEFINE_CHANNEL_OPTION(topicsnarf);
8674 DEFINE_CHANNEL_OPTION(vote);
8675 DEFINE_CHANNEL_OPTION(nodelete);
8676 DEFINE_CHANNEL_OPTION(toys);
8677 DEFINE_CHANNEL_OPTION(setters);
8678 DEFINE_CHANNEL_OPTION(topicrefresh);
8679 DEFINE_CHANNEL_OPTION(ctcpusers);
8680 DEFINE_CHANNEL_OPTION(ctcpreaction);
8681 DEFINE_CHANNEL_OPTION(inviteme);
8682 DEFINE_CHANNEL_OPTION(unreviewed);
8683 modcmd_register(chanserv_module, "set expire", chan_opt_expire, 1, 0, "flags", "+helping", NULL);
8684 modcmd_register(chanserv_module, "set unreviewed on", NULL, 0, 0, "flags", "+helping", NULL);
8685 modcmd_register(chanserv_module, "set unreviewed off", NULL, 0, 0, "flags", "+oper", NULL);
8687 DEFINE_CHANNEL_OPTION(offchannel);
8688 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
8690 /* Alias set topic to set defaulttopic for compatibility. */
8691 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
8694 DEFINE_USER_OPTION(noautoop);
8695 DEFINE_USER_OPTION(autoinvite);
8696 DEFINE_USER_OPTION(info);
8698 /* Alias uset autovoice to uset autoop. */
8699 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
8701 note_types = dict_new();
8702 dict_set_free_data(note_types, chanserv_deref_note_type);
8705 const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
8706 chanserv = AddLocalUser(nick, nick, NULL, "Channel Services", modes);
8707 service_register(chanserv)->trigger = '!';
8708 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
8710 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
8712 if(chanserv_conf.channel_expire_frequency)
8713 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
8715 if(chanserv_conf.dnr_expire_frequency)
8716 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
8718 if(chanserv_conf.refresh_period)
8720 unsigned long next_refresh;
8721 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
8722 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
8725 reg_exit_func(chanserv_db_cleanup);
8726 message_register_table(msgtab);