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, 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)
4719 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
4722 nick = alloca(strlen(user->nick)+3);
4723 sprintf(nick, "(%s)", user->nick);
4727 table.contents[table.length][0] = nick;
4730 table_send(chanserv, to->nick, 0, NULL, table);
4733 static CHANSERV_FUNC(cmd_ircops)
4735 reply("CSMSG_STAFF_OPERS");
4736 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
4740 static CHANSERV_FUNC(cmd_helpers)
4742 reply("CSMSG_STAFF_HELPERS");
4743 send_staff_list(user, &curr_helpers, FLAGS_OPER);
4747 static CHANSERV_FUNC(cmd_staff)
4749 reply("CSMSG_NETWORK_STAFF");
4750 cmd_ircops(CSFUNC_ARGS);
4751 cmd_helpers(CSFUNC_ARGS);
4755 static CHANSERV_FUNC(cmd_peek)
4757 struct modeNode *mn;
4758 char modes[MODELEN];
4760 struct helpfile_table table;
4761 int opcount = 0, voicecount = 0, srvcount = 0;
4763 irc_make_chanmode(channel, modes);
4765 reply("CSMSG_PEEK_INFO", channel->name);
4766 reply("CSMSG_PEEK_TOPIC", channel->topic);
4767 reply("CSMSG_PEEK_MODES", modes);
4771 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4772 table.contents = alloca(channel->members.used*sizeof(*table.contents));
4773 for(n = 0; n < channel->members.used; n++)
4775 mn = channel->members.list[n];
4776 if(IsLocal(mn->user))
4778 else if(mn->modes & MODE_CHANOP)
4780 else if(mn->modes & MODE_VOICE)
4783 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
4785 table.contents[table.length] = alloca(sizeof(**table.contents));
4786 table.contents[table.length][0] = mn->user->nick;
4790 reply("CSMSG_PEEK_USERS", channel->members.used, opcount, voicecount,
4791 (channel->members.used - opcount - voicecount - srvcount));
4795 reply("CSMSG_PEEK_OPS");
4796 table_send(chanserv, user->nick, 0, NULL, table);
4799 reply("CSMSG_PEEK_NO_OPS");
4803 static MODCMD_FUNC(cmd_wipeinfo)
4805 struct handle_info *victim;
4806 struct userData *ud, *actor, *real_actor;
4807 unsigned int override = 0;
4810 actor = GetChannelUser(channel->channel_info, user->handle_info);
4811 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
4812 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4814 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4816 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4819 if((ud->access >= actor->access) && (ud != actor))
4821 reply("MSG_USER_OUTRANKED", victim->handle);
4824 if((ud != real_actor) && (!real_actor || (ud->access >= real_actor->access)))
4825 override = CMD_LOG_OVERRIDE;
4829 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4830 return 1 | override;
4833 static CHANSERV_FUNC(cmd_resync)
4835 struct mod_chanmode *changes;
4836 struct chanData *cData = channel->channel_info;
4837 unsigned int ii, used;
4839 changes = mod_chanmode_alloc(channel->members.used * 2);
4840 for(ii = used = 0; ii < channel->members.used; ++ii)
4842 struct modeNode *mn = channel->members.list[ii];
4843 struct userData *uData;
4845 if(IsService(mn->user))
4848 uData = GetChannelAccess(cData, mn->user->handle_info);
4849 if(!cData->lvlOpts[lvlGiveOps]
4850 || (uData && uData->access >= cData->lvlOpts[lvlGiveOps]))
4852 if(!(mn->modes & MODE_CHANOP))
4854 if(!uData || IsUserAutoOp(uData))
4856 changes->args[used].mode = MODE_CHANOP;
4857 changes->args[used++].u.member = mn;
4858 if(!(mn->modes & MODE_VOICE))
4860 changes->args[used].mode = MODE_VOICE;
4861 changes->args[used++].u.member = mn;
4866 else if(!cData->lvlOpts[lvlGiveVoice]
4867 || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice]))
4869 if(mn->modes & MODE_CHANOP)
4871 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4872 changes->args[used++].u.member = mn;
4874 if(!(mn->modes & MODE_VOICE) && (!uData || IsUserAutoOp(uData)))
4876 changes->args[used].mode = MODE_VOICE;
4877 changes->args[used++].u.member = mn;
4884 changes->args[used].mode = MODE_REMOVE | mn->modes;
4885 changes->args[used++].u.member = mn;
4889 changes->argc = used;
4890 modcmd_chanmode_announce(changes);
4891 mod_chanmode_free(changes);
4892 reply("CSMSG_RESYNCED_USERS", channel->name);
4896 static CHANSERV_FUNC(cmd_seen)
4898 struct userData *uData;
4899 struct handle_info *handle;
4900 char seen[INTERVALLEN];
4904 if(!irccasecmp(argv[1], chanserv->nick))
4906 reply("CSMSG_IS_CHANSERV");
4910 if(!(handle = get_handle_info(argv[1])))
4912 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4916 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4918 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4923 reply("CSMSG_USER_PRESENT", handle->handle);
4924 else if(uData->seen)
4925 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
4927 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4929 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4930 reply("CSMSG_USER_VACATION", handle->handle);
4935 static MODCMD_FUNC(cmd_names)
4937 struct userNode *targ;
4938 struct userData *targData;
4939 unsigned int ii, pos;
4942 for(ii=pos=0; ii<channel->members.used; ++ii)
4944 targ = channel->members.list[ii]->user;
4945 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4948 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
4951 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4955 if(IsUserSuspended(targData))
4957 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4960 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4961 reply("CSMSG_END_NAMES", channel->name);
4966 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
4968 switch(ntype->visible_type)
4970 case NOTE_VIS_ALL: return 1;
4971 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
4972 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
4977 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
4979 struct userData *uData;
4981 switch(ntype->set_access_type)
4983 case NOTE_SET_CHANNEL_ACCESS:
4984 if(!user->handle_info)
4986 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
4988 return uData->access >= ntype->set_access.min_ulevel;
4989 case NOTE_SET_CHANNEL_SETTER:
4990 return check_user_level(channel, user, lvlSetters, 1, 0);
4991 case NOTE_SET_PRIVILEGED: default:
4992 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
4996 static CHANSERV_FUNC(cmd_note)
4998 struct chanData *cData;
5000 struct note_type *ntype;
5002 cData = channel->channel_info;
5005 reply("CSMSG_NOT_REGISTERED", channel->name);
5009 /* If no arguments, show all visible notes for the channel. */
5015 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
5017 note = iter_data(it);
5018 if(!note_type_visible_to_user(cData, note->type, user))
5021 reply("CSMSG_NOTELIST_HEADER", channel->name);
5022 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
5025 reply("CSMSG_NOTELIST_END", channel->name);
5027 reply("CSMSG_NOTELIST_EMPTY", channel->name);
5029 /* If one argument, show the named note. */
5032 if((note = dict_find(cData->notes, argv[1], NULL))
5033 && note_type_visible_to_user(cData, note->type, user))
5035 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
5037 else if((ntype = dict_find(note_types, argv[1], NULL))
5038 && note_type_visible_to_user(NULL, ntype, user))
5040 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
5045 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
5049 /* Assume they're trying to set a note. */
5053 ntype = dict_find(note_types, argv[1], NULL);
5056 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
5059 else if(note_type_settable_by_user(channel, ntype, user))
5061 note_text = unsplit_string(argv+2, argc-2, NULL);
5062 if((note = dict_find(cData->notes, argv[1], NULL)))
5063 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
5064 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
5065 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
5067 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
5069 /* The note is viewable to staff only, so return 0
5070 to keep the invocation from getting logged (or
5071 regular users can see it in !events). */
5077 reply("CSMSG_NO_ACCESS");
5084 static CHANSERV_FUNC(cmd_delnote)
5089 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
5090 || !note_type_settable_by_user(channel, note->type, user))
5092 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
5095 dict_remove(channel->channel_info->notes, note->type->name);
5096 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
5100 static CHANSERV_FUNC(cmd_events)
5102 struct logSearch discrim;
5103 struct logReport report;
5104 unsigned int matches, limit;
5106 limit = (argc > 1) ? atoi(argv[1]) : 10;
5107 if(limit < 1 || limit > 200)
5110 memset(&discrim, 0, sizeof(discrim));
5111 discrim.masks.bot = chanserv;
5112 discrim.masks.channel_name = channel->name;
5114 discrim.masks.command = argv[2];
5115 discrim.limit = limit;
5116 discrim.max_time = INT_MAX;
5117 discrim.severities = 1 << LOG_COMMAND;
5118 report.reporter = chanserv;
5120 reply("CSMSG_EVENT_SEARCH_RESULTS");
5121 matches = log_entry_search(&discrim, log_report_entry, &report);
5123 reply("MSG_MATCH_COUNT", matches);
5125 reply("MSG_NO_MATCHES");
5129 static CHANSERV_FUNC(cmd_say)
5135 msg = unsplit_string(argv + 1, argc - 1, NULL);
5136 send_channel_message(channel, cmd->parent->bot, "%s", msg);
5138 else if(*argv[1] == '*' && argv[1][1] != '\0')
5140 struct handle_info *hi;
5141 struct userNode *authed;
5144 msg = unsplit_string(argv + 2, argc - 2, NULL);
5146 if (!(hi = get_handle_info(argv[1] + 1)))
5148 reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
5152 for (authed = hi->users; authed; authed = authed->next_authed)
5153 send_target_message(5, authed->nick, cmd->parent->bot, "%s", msg);
5155 else if(GetUserH(argv[1]))
5158 msg = unsplit_string(argv + 2, argc - 2, NULL);
5159 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
5163 reply("MSG_NOT_TARGET_NAME");
5169 static CHANSERV_FUNC(cmd_emote)
5175 /* CTCP is so annoying. */
5176 msg = unsplit_string(argv + 1, argc - 1, NULL);
5177 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
5179 else if(*argv[1] == '*' && argv[1][1] != '\0')
5181 struct handle_info *hi;
5182 struct userNode *authed;
5185 msg = unsplit_string(argv + 2, argc - 2, NULL);
5187 if (!(hi = get_handle_info(argv[1] + 1)))
5189 reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
5193 for (authed = hi->users; authed; authed = authed->next_authed)
5194 send_target_message(5, authed->nick, cmd->parent->bot, "\001ACTION %s\001", msg);
5196 else if(GetUserH(argv[1]))
5198 msg = unsplit_string(argv + 2, argc - 2, NULL);
5199 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
5203 reply("MSG_NOT_TARGET_NAME");
5209 struct channelList *
5210 chanserv_support_channels(void)
5212 return &chanserv_conf.support_channels;
5215 static CHANSERV_FUNC(cmd_expire)
5217 int channel_count = registered_channels;
5218 expire_channels(NULL);
5219 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
5224 chanserv_expire_suspension(void *data)
5226 struct suspended *suspended = data;
5227 struct chanNode *channel;
5230 /* Update the channel registration data structure. */
5231 if(!suspended->expires || (now < suspended->expires))
5232 suspended->revoked = now;
5233 channel = suspended->cData->channel;
5234 suspended->cData->channel = channel;
5235 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
5237 /* If appropriate, re-join ChanServ to the channel. */
5238 if(!IsOffChannel(suspended->cData))
5240 spamserv_cs_suspend(channel, 0, 0, NULL);
5241 ss_cs_join_channel(channel, 1);
5244 /* Mark everyone currently in the channel as present. */
5245 for(ii = 0; ii < channel->members.used; ++ii)
5247 struct userData *uData = GetChannelAccess(suspended->cData, channel->members.list[ii]->user->handle_info);
5256 static CHANSERV_FUNC(cmd_csuspend)
5258 struct suspended *suspended;
5259 char reason[MAXLEN];
5260 unsigned long expiry, duration;
5261 struct userData *uData;
5265 if(IsProtected(channel->channel_info))
5267 reply("CSMSG_SUSPEND_NODELETE", channel->name);
5271 if(argv[1][0] == '!')
5273 else if(IsSuspended(channel->channel_info))
5275 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
5276 show_suspension_info(cmd, user, channel->channel_info->suspended);
5280 if(!strcmp(argv[1], "0"))
5282 else if((duration = ParseInterval(argv[1])))
5283 expiry = now + duration;
5286 reply("MSG_INVALID_DURATION", argv[1]);
5290 unsplit_string(argv + 2, argc - 2, reason);
5292 suspended = calloc(1, sizeof(*suspended));
5293 suspended->revoked = 0;
5294 suspended->issued = now;
5295 suspended->suspender = strdup(user->handle_info->handle);
5296 suspended->expires = expiry;
5297 suspended->reason = strdup(reason);
5298 suspended->cData = channel->channel_info;
5299 suspended->previous = suspended->cData->suspended;
5300 suspended->cData->suspended = suspended;
5302 if(suspended->expires)
5303 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
5305 if(IsSuspended(channel->channel_info))
5307 suspended->previous->revoked = now;
5308 if(suspended->previous->expires)
5309 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
5310 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
5311 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5315 /* Mark all users in channel as absent. */
5316 for(uData = channel->channel_info->users; uData; uData = uData->next)
5325 /* Mark the channel as suspended, then part. */
5326 channel->channel_info->flags |= CHANNEL_SUSPENDED;
5327 spamserv_cs_suspend(channel, expiry, 1, suspended->reason);
5328 DelChannelUser(chanserv, channel, suspended->reason, 0);
5329 reply("CSMSG_SUSPENDED", channel->name);
5330 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
5331 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5336 static CHANSERV_FUNC(cmd_cunsuspend)
5338 struct suspended *suspended;
5339 char message[MAXLEN];
5341 if(!IsSuspended(channel->channel_info))
5343 reply("CSMSG_NOT_SUSPENDED", channel->name);
5347 suspended = channel->channel_info->suspended;
5349 /* Expire the suspension and join ChanServ to the channel. */
5350 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
5351 chanserv_expire_suspension(suspended);
5352 reply("CSMSG_UNSUSPENDED", channel->name);
5353 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
5354 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
5358 typedef struct chanservSearch
5363 unsigned long unvisited;
5364 unsigned long registered;
5366 unsigned long flags;
5370 typedef void (*channel_search_func)(struct chanData *channel, void *data);
5373 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
5378 search = malloc(sizeof(struct chanservSearch));
5379 memset(search, 0, sizeof(*search));
5382 for(i = 0; i < argc; i++)
5384 /* Assume all criteria require arguments. */
5387 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
5391 if(!irccasecmp(argv[i], "name"))
5392 search->name = argv[++i];
5393 else if(!irccasecmp(argv[i], "registrar"))
5394 search->registrar = argv[++i];
5395 else if(!irccasecmp(argv[i], "unvisited"))
5396 search->unvisited = ParseInterval(argv[++i]);
5397 else if(!irccasecmp(argv[i], "registered"))
5398 search->registered = ParseInterval(argv[++i]);
5399 else if(!irccasecmp(argv[i], "flags"))
5402 if(!irccasecmp(argv[i], "nodelete"))
5403 search->flags |= CHANNEL_NODELETE;
5404 else if(!irccasecmp(argv[i], "suspended"))
5405 search->flags |= CHANNEL_SUSPENDED;
5406 else if(!irccasecmp(argv[i], "unreviewed"))
5407 search->flags |= CHANNEL_UNREVIEWED;
5410 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
5414 else if(!irccasecmp(argv[i], "limit"))
5415 search->limit = strtoul(argv[++i], NULL, 10);
5418 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
5423 if(search->name && !strcmp(search->name, "*"))
5425 if(search->registrar && !strcmp(search->registrar, "*"))
5426 search->registrar = 0;
5435 chanserv_channel_match(struct chanData *channel, search_t search)
5437 const char *name = channel->channel->name;
5438 if((search->name && !match_ircglob(name, search->name)) ||
5439 (search->registrar && !channel->registrar) ||
5440 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
5441 (search->unvisited && (now - channel->visited) < search->unvisited) ||
5442 (search->registered && (now - channel->registered) > search->registered) ||
5443 (search->flags && ((search->flags & channel->flags) != search->flags)))
5450 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
5452 struct chanData *channel;
5453 unsigned int matches = 0;
5455 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
5457 if(!chanserv_channel_match(channel, search))
5467 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
5472 search_print(struct chanData *channel, void *data)
5474 send_message_type(4, data, chanserv, "%s", channel->channel->name);
5477 static CHANSERV_FUNC(cmd_search)
5480 unsigned int matches;
5481 channel_search_func action;
5485 if(!irccasecmp(argv[1], "count"))
5486 action = search_count;
5487 else if(!irccasecmp(argv[1], "print"))
5488 action = search_print;
5491 reply("CSMSG_ACTION_INVALID", argv[1]);
5495 search = chanserv_search_create(user, argc - 2, argv + 2);
5499 if(action == search_count)
5500 search->limit = INT_MAX;
5502 if(action == search_print)
5503 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
5505 matches = chanserv_channel_search(search, action, user);
5508 reply("MSG_MATCH_COUNT", matches);
5510 reply("MSG_NO_MATCHES");
5516 static CHANSERV_FUNC(cmd_unvisited)
5518 struct chanData *cData;
5519 unsigned long interval = chanserv_conf.channel_expire_delay;
5520 char buffer[INTERVALLEN];
5521 unsigned int limit = 25, matches = 0;
5525 interval = ParseInterval(argv[1]);
5527 limit = atoi(argv[2]);
5530 intervalString(buffer, interval, user->handle_info);
5531 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
5533 for(cData = channelList; cData && matches < limit; cData = cData->next)
5535 if((now - cData->visited) < interval)
5538 intervalString(buffer, now - cData->visited, user->handle_info);
5539 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
5546 static MODCMD_FUNC(chan_opt_defaulttopic)
5552 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5554 reply("CSMSG_TOPIC_LOCKED", channel->name);
5558 topic = unsplit_string(argv+1, argc-1, NULL);
5560 free(channel->channel_info->topic);
5561 if(topic[0] == '*' && topic[1] == 0)
5563 topic = channel->channel_info->topic = NULL;
5567 topic = channel->channel_info->topic = strdup(topic);
5568 if(channel->channel_info->topic_mask
5569 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
5570 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5572 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
5575 if(channel->channel_info->topic)
5576 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
5578 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
5582 static MODCMD_FUNC(chan_opt_topicmask)
5586 struct chanData *cData = channel->channel_info;
5589 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5591 reply("CSMSG_TOPIC_LOCKED", channel->name);
5595 mask = unsplit_string(argv+1, argc-1, NULL);
5597 if(cData->topic_mask)
5598 free(cData->topic_mask);
5599 if(mask[0] == '*' && mask[1] == 0)
5601 cData->topic_mask = 0;
5605 cData->topic_mask = strdup(mask);
5607 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
5608 else if(!match_ircglob(cData->topic, cData->topic_mask))
5609 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5613 if(channel->channel_info->topic_mask)
5614 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
5616 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
5620 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
5624 char *greeting = unsplit_string(argv+1, argc-1, NULL);
5628 if(greeting[0] == '*' && greeting[1] == 0)
5632 unsigned int length = strlen(greeting);
5633 if(length > chanserv_conf.greeting_length)
5635 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
5638 *data = strdup(greeting);
5647 reply(name, user_find_message(user, "MSG_NONE"));
5651 static MODCMD_FUNC(chan_opt_greeting)
5653 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
5656 static MODCMD_FUNC(chan_opt_usergreeting)
5658 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
5661 static MODCMD_FUNC(chan_opt_modes)
5663 struct mod_chanmode *new_modes;
5668 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
5670 reply("CSMSG_NO_ACCESS");
5673 if(argv[1][0] == '*' && argv[1][1] == 0)
5675 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
5677 else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_IGN_REGISTERED|MCP_NO_APASS, 0)))
5679 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5682 else if(new_modes->argc > 1)
5684 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5685 mod_chanmode_free(new_modes);
5690 channel->channel_info->modes = *new_modes;
5691 modcmd_chanmode_announce(new_modes);
5692 mod_chanmode_free(new_modes);
5696 mod_chanmode_format(&channel->channel_info->modes, modes);
5698 reply("CSMSG_SET_MODES", modes);
5700 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
5704 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5706 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5708 struct chanData *cData = channel->channel_info;
5713 /* Set flag according to value. */
5714 if(enabled_string(argv[1]))
5716 cData->flags |= mask;
5719 else if(disabled_string(argv[1]))
5721 cData->flags &= ~mask;
5726 reply("MSG_INVALID_BINARY", argv[1]);
5732 /* Find current option value. */
5733 value = (cData->flags & mask) ? 1 : 0;
5737 reply(name, user_find_message(user, "MSG_ON"));
5739 reply(name, user_find_message(user, "MSG_OFF"));
5743 static MODCMD_FUNC(chan_opt_nodelete)
5745 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
5747 reply("MSG_SETTING_PRIVILEGED", argv[0]);
5751 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
5754 static MODCMD_FUNC(chan_opt_dynlimit)
5756 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
5759 static MODCMD_FUNC(chan_opt_offchannel)
5761 struct chanData *cData = channel->channel_info;
5766 /* Set flag according to value. */
5767 if(enabled_string(argv[1]))
5769 if(!IsOffChannel(cData))
5770 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
5771 cData->flags |= CHANNEL_OFFCHANNEL;
5774 else if(disabled_string(argv[1]))
5776 if(IsOffChannel(cData))
5778 struct mod_chanmode change;
5779 mod_chanmode_init(&change);
5781 change.args[0].mode = MODE_CHANOP;
5782 change.args[0].u.member = AddChannelUser(chanserv, channel);
5783 mod_chanmode_announce(chanserv, channel, &change);
5785 cData->flags &= ~CHANNEL_OFFCHANNEL;
5790 reply("MSG_INVALID_BINARY", argv[1]);
5796 /* Find current option value. */
5797 value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
5801 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
5803 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
5807 static MODCMD_FUNC(chan_opt_unreviewed)
5809 struct chanData *cData = channel->channel_info;
5810 int value = (cData->flags & CHANNEL_UNREVIEWED) ? 1 : 0;
5816 /* The two directions can have different ACLs. */
5817 if(enabled_string(argv[1]))
5819 else if(disabled_string(argv[1]))
5823 reply("MSG_INVALID_BINARY", argv[1]);
5827 if (new_value != value)
5829 struct svccmd *subcmd;
5830 char subcmd_name[32];
5832 snprintf(subcmd_name, sizeof(subcmd_name), "%s %s", argv[0], (new_value ? "on" : "off"));
5833 subcmd = dict_find(cmd->parent->commands, subcmd_name, NULL);
5836 reply("MSG_COMMAND_DISABLED", subcmd_name);
5839 else if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
5843 cData->flags |= CHANNEL_UNREVIEWED;
5846 free(cData->registrar);
5847 cData->registrar = strdup(user->handle_info->handle);
5848 cData->flags &= ~CHANNEL_UNREVIEWED;
5855 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_ON"));
5857 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_OFF"));
5861 static MODCMD_FUNC(chan_opt_defaults)
5863 struct userData *uData;
5864 struct chanData *cData;
5865 const char *confirm;
5866 enum levelOption lvlOpt;
5867 enum charOption chOpt;
5869 cData = channel->channel_info;
5870 uData = GetChannelUser(cData, user->handle_info);
5871 if(!uData || (uData->access < UL_OWNER))
5873 reply("CSMSG_OWNER_DEFAULTS", channel->name);
5876 confirm = make_confirmation_string(uData);
5877 if((argc < 2) || strcmp(argv[1], confirm))
5879 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
5882 cData->flags = (CHANNEL_DEFAULT_FLAGS & ~CHANNEL_PRESERVED_FLAGS)
5883 | (cData->flags & CHANNEL_PRESERVED_FLAGS);
5884 cData->modes = chanserv_conf.default_modes;
5885 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
5886 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
5887 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
5888 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
5889 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
5894 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5896 struct chanData *cData = channel->channel_info;
5897 struct userData *uData;
5898 unsigned short value;
5902 if(!check_user_level(channel, user, option, 1, 1))
5904 reply("CSMSG_CANNOT_SET");
5907 value = user_level_from_name(argv[1], UL_OWNER+1);
5908 if(!value && strcmp(argv[1], "0"))
5910 reply("CSMSG_INVALID_ACCESS", argv[1]);
5913 uData = GetChannelUser(cData, user->handle_info);
5914 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
5916 reply("CSMSG_BAD_SETLEVEL");
5922 if(value > cData->lvlOpts[lvlGiveOps])
5924 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
5929 if(value < cData->lvlOpts[lvlGiveVoice])
5931 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
5936 /* This test only applies to owners, since non-owners
5937 * trying to set an option to above their level get caught
5938 * by the CSMSG_BAD_SETLEVEL test above.
5940 if(value > uData->access)
5942 reply("CSMSG_BAD_SETTERS");
5949 cData->lvlOpts[option] = value;
5951 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
5955 static MODCMD_FUNC(chan_opt_enfops)
5957 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
5960 static MODCMD_FUNC(chan_opt_giveops)
5962 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
5965 static MODCMD_FUNC(chan_opt_enfmodes)
5967 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
5970 static MODCMD_FUNC(chan_opt_enftopic)
5972 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
5975 static MODCMD_FUNC(chan_opt_pubcmd)
5977 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
5980 static MODCMD_FUNC(chan_opt_setters)
5982 return channel_level_option(lvlSetters, CSFUNC_ARGS);
5985 static MODCMD_FUNC(chan_opt_ctcpusers)
5987 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
5990 static MODCMD_FUNC(chan_opt_userinfo)
5992 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
5995 static MODCMD_FUNC(chan_opt_givevoice)
5997 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
6000 static MODCMD_FUNC(chan_opt_topicsnarf)
6002 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
6005 static MODCMD_FUNC(chan_opt_vote)
6007 return channel_level_option(lvlVote, CSFUNC_ARGS);
6010 static MODCMD_FUNC(chan_opt_inviteme)
6012 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
6016 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
6018 struct chanData *cData = channel->channel_info;
6019 int count = charOptions[option].count, idx;
6023 idx = atoi(argv[1]);
6025 if(!isdigit(argv[1][0]) || (idx < 0) || (idx >= count))
6027 reply("CSMSG_INVALID_NUMERIC", idx);
6028 /* Show possible values. */
6029 for(idx = 0; idx < count; idx++)
6030 reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
6034 cData->chOpts[option] = charOptions[option].values[idx].value;
6038 /* Find current option value. */
6041 (idx < count) && (cData->chOpts[option] != charOptions[option].values[idx].value);
6045 /* Somehow, the option value is corrupt; reset it to the default. */
6046 cData->chOpts[option] = charOptions[option].default_value;
6051 reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
6055 static MODCMD_FUNC(chan_opt_protect)
6057 return channel_multiple_option(chProtect, CSFUNC_ARGS);
6060 static MODCMD_FUNC(chan_opt_toys)
6062 return channel_multiple_option(chToys, CSFUNC_ARGS);
6065 static MODCMD_FUNC(chan_opt_ctcpreaction)
6067 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
6070 static MODCMD_FUNC(chan_opt_topicrefresh)
6072 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
6075 static struct svccmd_list set_shows_list;
6078 handle_svccmd_unbind(struct svccmd *target) {
6080 for(ii=0; ii<set_shows_list.used; ++ii)
6081 if(target == set_shows_list.list[ii])
6082 set_shows_list.used = 0;
6085 static CHANSERV_FUNC(cmd_set)
6087 struct svccmd *subcmd;
6091 /* Check if we need to (re-)initialize set_shows_list. */
6092 if(!set_shows_list.used)
6094 if(!set_shows_list.size)
6096 set_shows_list.size = chanserv_conf.set_shows->used;
6097 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
6099 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
6101 const char *name = chanserv_conf.set_shows->list[ii];
6102 sprintf(buf, "%s %s", argv[0], name);
6103 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6106 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
6109 svccmd_list_append(&set_shows_list, subcmd);
6115 reply("CSMSG_CHANNEL_OPTIONS");
6116 for(ii = 0; ii < set_shows_list.used; ii++)
6118 subcmd = set_shows_list.list[ii];
6119 subcmd->command->func(user, channel, 1, argv+1, subcmd);
6124 sprintf(buf, "%s %s", argv[0], argv[1]);
6125 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6128 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
6131 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
6133 reply("CSMSG_NO_ACCESS");
6139 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
6143 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
6145 struct userData *uData;
6147 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6150 reply("CSMSG_NOT_USER", channel->name);
6156 /* Just show current option value. */
6158 else if(enabled_string(argv[1]))
6160 uData->flags |= mask;
6162 else if(disabled_string(argv[1]))
6164 uData->flags &= ~mask;
6168 reply("MSG_INVALID_BINARY", argv[1]);
6172 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
6176 static MODCMD_FUNC(user_opt_noautoop)
6178 struct userData *uData;
6180 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6183 reply("CSMSG_NOT_USER", channel->name);
6186 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
6187 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
6189 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
6192 static MODCMD_FUNC(user_opt_autoinvite)
6194 if((argc > 1) && !check_user_level(channel, user, lvlInviteMe, 1, 0))
6196 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
6198 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
6201 static MODCMD_FUNC(user_opt_info)
6203 struct userData *uData;
6206 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6210 /* If they got past the command restrictions (which require access)
6211 * but fail this test, we have some fool with security override on.
6213 reply("CSMSG_NOT_USER", channel->name);
6220 infoline = unsplit_string(argv + 1, argc - 1, NULL);
6221 if(strlen(infoline) > chanserv_conf.max_userinfo_length)
6223 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
6226 bp = strcspn(infoline, "\001");
6229 reply("CSMSG_BAD_INFOLINE", infoline[bp]);
6234 if(infoline[0] == '*' && infoline[1] == 0)
6237 uData->info = strdup(infoline);
6240 reply("CSMSG_USET_INFO", uData->info);
6242 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
6246 struct svccmd_list uset_shows_list;
6248 static CHANSERV_FUNC(cmd_uset)
6250 struct svccmd *subcmd;
6254 /* Check if we need to (re-)initialize uset_shows_list. */
6255 if(!uset_shows_list.used)
6259 "NoAutoOp", "AutoInvite", "Info"
6262 if(!uset_shows_list.size)
6264 uset_shows_list.size = ArrayLength(options);
6265 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
6267 for(ii = 0; ii < ArrayLength(options); ii++)
6269 const char *name = options[ii];
6270 sprintf(buf, "%s %s", argv[0], name);
6271 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6274 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
6277 svccmd_list_append(&uset_shows_list, subcmd);
6283 /* Do this so options are presented in a consistent order. */
6284 reply("CSMSG_USER_OPTIONS");
6285 for(ii = 0; ii < uset_shows_list.used; ii++)
6286 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
6290 sprintf(buf, "%s %s", argv[0], argv[1]);
6291 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6294 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
6298 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
6301 static CHANSERV_FUNC(cmd_giveownership)
6303 struct handle_info *new_owner_hi;
6304 struct userData *new_owner;
6305 struct userData *curr_user;
6306 struct userData *invoker;
6307 struct chanData *cData = channel->channel_info;
6308 struct do_not_register *dnr;
6309 const char *confirm;
6311 unsigned short co_access;
6312 char reason[MAXLEN];
6315 curr_user = GetChannelAccess(cData, user->handle_info);
6316 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
6317 if(!curr_user || (curr_user->access != UL_OWNER))
6319 struct userData *owner = NULL;
6320 for(curr_user = channel->channel_info->users;
6322 curr_user = curr_user->next)
6324 if(curr_user->access != UL_OWNER)
6328 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
6335 else if(!force && (now < cData->ownerTransfer + chanserv_conf.giveownership_period))
6337 char delay[INTERVALLEN];
6338 intervalString(delay, cData->ownerTransfer + chanserv_conf.giveownership_period - now, user->handle_info);
6339 reply("CSMSG_TRANSFER_WAIT", delay, channel->name);
6342 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
6344 if(new_owner_hi == user->handle_info)
6346 reply("CSMSG_NO_TRANSFER_SELF");
6349 new_owner = GetChannelAccess(cData, new_owner_hi);
6354 new_owner = add_channel_user(cData, new_owner_hi, UL_OWNER - 1, 0, NULL);
6358 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
6362 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
6364 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
6367 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
6368 if(!IsHelping(user))
6369 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
6371 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi->handle);
6374 invoker = GetChannelUser(cData, user->handle_info);
6375 if(invoker->access <= UL_OWNER)
6377 confirm = make_confirmation_string(curr_user);
6378 if((argc < 3) || strcmp(argv[2], confirm))
6380 reply("CSMSG_CONFIRM_GIVEOWNERSHIP", new_owner_hi->handle, confirm);
6384 if(new_owner->access >= UL_COOWNER)
6385 co_access = new_owner->access;
6387 co_access = UL_COOWNER;
6388 new_owner->access = UL_OWNER;
6390 curr_user->access = co_access;
6391 cData->ownerTransfer = now;
6392 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
6393 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
6394 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
6398 static CHANSERV_FUNC(cmd_suspend)
6400 struct handle_info *hi;
6401 struct userData *actor, *real_actor, *target;
6402 unsigned int override = 0;
6405 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6406 actor = GetChannelUser(channel->channel_info, user->handle_info);
6407 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
6408 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6410 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6413 if(target->access >= actor->access)
6415 reply("MSG_USER_OUTRANKED", hi->handle);
6418 if(target->flags & USER_SUSPENDED)
6420 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
6425 target->present = 0;
6428 if(!real_actor || target->access >= real_actor->access)
6429 override = CMD_LOG_OVERRIDE;
6430 target->flags |= USER_SUSPENDED;
6431 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
6432 return 1 | override;
6435 static CHANSERV_FUNC(cmd_unsuspend)
6437 struct handle_info *hi;
6438 struct userData *actor, *real_actor, *target;
6439 unsigned int override = 0;
6442 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6443 actor = GetChannelUser(channel->channel_info, user->handle_info);
6444 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
6445 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6447 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6450 if(target->access >= actor->access)
6452 reply("MSG_USER_OUTRANKED", hi->handle);
6455 if(!(target->flags & USER_SUSPENDED))
6457 reply("CSMSG_NOT_SUSPENDED", hi->handle);
6460 if(!real_actor || target->access >= real_actor->access)
6461 override = CMD_LOG_OVERRIDE;
6462 target->flags &= ~USER_SUSPENDED;
6463 scan_user_presence(target, NULL);
6464 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
6465 return 1 | override;
6468 static MODCMD_FUNC(cmd_deleteme)
6470 struct handle_info *hi;
6471 struct userData *target;
6472 const char *confirm_string;
6473 unsigned short access_level;
6476 hi = user->handle_info;
6477 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6479 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6482 if(target->access == UL_OWNER)
6484 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
6487 confirm_string = make_confirmation_string(target);
6488 if((argc < 2) || strcmp(argv[1], confirm_string))
6490 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
6493 access_level = target->access;
6494 channel_name = strdup(channel->name);
6495 del_channel_user(target, 1);
6496 reply("CSMSG_DELETED_YOU", access_level, channel_name);
6501 static CHANSERV_FUNC(cmd_addvote)
6503 struct chanData *cData = channel->channel_info;
6504 struct userData *uData, *target;
6505 struct handle_info *hi;
6506 if (!cData) return 0;
6508 hi = user->handle_info;
6509 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6511 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6514 if(target->access < 300) {
6515 reply("CSMSG_NO_ACCESS");
6519 reply("CSMSG_ADDVOTE_FULL");
6523 msg = unsplit_string(argv + 1, argc - 1, NULL);
6524 cData->vote = strdup(msg);
6525 cData->vote_start=0;
6526 dict_delete(cData->vote_options);
6527 cData->vote_options = dict_new();
6528 dict_set_free_data(cData->vote_options, free_vote_options);
6529 for(uData = channel->channel_info->users; uData; uData = uData->next)
6534 reply("CSMSG_ADDVOTE_DONE");
6538 static CHANSERV_FUNC(cmd_delvote)
6540 struct chanData *cData = channel->channel_info;
6541 struct userData *target;
6542 struct handle_info *hi;
6543 if (!cData) return 0;
6544 hi = user->handle_info;
6545 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6547 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6550 if(target->access < 300) {
6551 reply("CSMSG_NO_ACCESS");
6555 reply("CSMSG_NO_VOTE");
6560 reply("CSMSG_DELVOTE_DONE");
6564 static CHANSERV_FUNC(cmd_addoption)
6566 struct chanData *cData = channel->channel_info;
6567 struct userData *target;
6568 struct handle_info *hi;
6569 if (!cData) return 0;
6571 hi = user->handle_info;
6572 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6574 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6577 if(target->access < 300) {
6578 reply("CSMSG_NO_ACCESS");
6582 reply("CSMSG_NO_VOTE");
6588 msg = unsplit_string(argv + 1, argc - 1, NULL);
6591 unsigned int lastid = 1;
6592 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6593 struct vote_option *cvOpt = iter_data(it);
6594 if(cvOpt->option_id > lastid)
6595 lastid = cvOpt->option_id;
6597 struct vote_option *vOpt;
6598 vOpt = calloc(1, sizeof(*vOpt));
6599 vOpt->name = strdup(msg);
6600 vOpt->option_id = (lastid + 1);
6602 sprintf(str,"%i",(lastid + 1));
6603 vOpt->option_str = strdup(str);
6605 dict_insert(cData->vote_options,vOpt->option_str,vOpt);
6607 reply("CSMSG_ADDOPTION_DONE",dict_size(cData->vote_options),lastid,(lastid + 1));
6611 static CHANSERV_FUNC(cmd_deloption)
6613 struct chanData *cData = channel->channel_info;
6614 struct userData *uData, *target;
6615 struct handle_info *hi;
6616 if (!cData) return 0;
6618 hi = user->handle_info;
6619 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6621 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6624 if(target->access < 300) {
6625 reply("CSMSG_NO_ACCESS");
6629 reply("CSMSG_NO_VOTE");
6632 if(cData->vote_start) {
6633 if(dict_size(cData->vote_options) < 3) {
6634 reply("CSMSG_VOTE_NEED_OPTIONS");
6639 int find_id = atoi(argv[1]);
6641 unsigned int found = 0;
6644 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6646 if (find_id == ii) {
6647 struct vote_option *vOpt = iter_data(it);
6648 found = vOpt->option_id;
6650 sprintf(str,"%i",vOpt->option_id);
6651 dict_remove(cData->vote_options, str);
6656 for(uData = channel->channel_info->users; uData; uData = uData->next) {
6657 if(uData->votefor == found) {
6662 reply("CSMSG_DELOPTION_DONE");
6665 reply("CSMSG_DELOPTION_NONE");
6670 static CHANSERV_FUNC(cmd_vote)
6672 struct chanData *cData = channel->channel_info;
6673 struct userData *target;
6674 struct handle_info *hi;
6675 unsigned int votedfor = 0;
6676 char *votedfor_str = NULL;
6678 if (!cData || !cData->vote) {
6679 reply("CSMSG_NO_VOTE");
6682 if(argc > 1 && cData->vote_start) {
6683 hi = user->handle_info;
6684 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6686 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6689 if(!check_user_level(channel, user, lvlVote, 1, 0)) {
6690 reply("CSMSG_NO_ACCESS");
6694 reply("CSMSG_VOTE_VOTED");
6697 int find_id = atoi(argv[1]);
6700 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6702 if (find_id == ii) {
6703 struct vote_option *vOpt = iter_data(it);
6706 target->votefor = vOpt->option_id;
6707 votedfor = vOpt->option_id;
6708 votedfor_str = vOpt->name;
6712 reply("CSMSG_VOTE_INVALID");
6716 if (!cData->vote_start) {
6717 reply("CSMSG_VOTE_NOT_STARTED");
6719 reply("CSMSG_VOTE_QUESTION",cData->vote);
6721 unsigned int voteid = 0;
6724 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6725 struct vote_option *vOpt = iter_data(it);
6727 reply("CSMSG_VOTE_OPTION",voteid,vOpt->name,vOpt->voted);
6729 if(argc > 1 && cData->vote_start && votedfor_str) {
6730 reply("CSMSG_VOTE_DONE",votedfor_str);
6735 static CHANSERV_FUNC(cmd_startvote)
6737 struct chanData *cData = channel->channel_info;
6738 struct userData *target;
6739 struct handle_info *hi;
6740 if (!cData) return 0;
6741 hi = user->handle_info;
6742 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6744 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6747 if(target->access < 300) {
6748 reply("CSMSG_NO_ACCESS");
6752 reply("CSMSG_NO_VOTE");
6755 if(cData->vote_start) {
6756 reply("CSMSG_STARTVOTE_RUNNING");
6759 if(dict_size(cData->vote_options) < 2) {
6760 reply("CSMSG_VOTE_NEED_OPTIONS");
6763 cData->vote_start = 1;
6764 char response[MAXLEN];
6765 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_TOP"), user->nick);
6766 irc_privmsg(cmd->parent->bot, channel->name, response);
6767 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_QUESTION"), cData->vote);
6768 irc_privmsg(cmd->parent->bot, channel->name, response);
6769 unsigned int voteid = 0;
6771 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6772 struct vote_option *vOpt = iter_data(it);
6774 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_OPTION"), voteid, vOpt->name);
6775 irc_privmsg(cmd->parent->bot, channel->name, response);
6777 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_ACCESS"), cData->lvlOpts[lvlVote]); //Todo
6778 irc_privmsg(cmd->parent->bot, channel->name, response);
6779 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_HOWTO")); //Todo
6780 irc_privmsg(cmd->parent->bot, channel->name, response);
6784 static CHANSERV_FUNC(cmd_endvote)
6786 struct chanData *cData = channel->channel_info;
6787 struct userData *target;
6788 struct handle_info *hi;
6789 if (!cData) return 0;
6790 hi = user->handle_info;
6791 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6793 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6796 if(target->access < 300) {
6797 reply("CSMSG_NO_ACCESS");
6801 reply("CSMSG_NO_VOTE");
6804 if(!cData->vote_start) {
6805 reply("CSMSG_ENDVOTE_STOPPED");
6808 cData->vote_start = 0;
6809 reply("CSMSG_ENDVOTE_DONE");
6813 static CHANSERV_FUNC(cmd_voteresults)
6815 struct chanData *cData = channel->channel_info;
6816 struct userData *target;
6817 struct handle_info *hi;
6818 if (!cData) return 0;
6820 reply("CSMSG_NO_VOTE");
6823 if (argc > 1 && !irccasecmp(argv[1], "*")) {
6824 hi = user->handle_info;
6825 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6827 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6830 if(target->access < 300) {
6831 reply("CSMSG_NO_ACCESS");
6834 char response[MAXLEN];
6835 sprintf(response, user_find_message(user, "CSMSG_VOTERES_QUESTION"), cData->vote);
6836 irc_privmsg(cmd->parent->bot, channel->name, response);
6837 unsigned int voteid = 0;
6839 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6840 struct vote_option *vOpt = iter_data(it);
6842 sprintf(response, user_find_message(user, "CSMSG_VOTERES_OPTION"), voteid, vOpt->name, vOpt->voted);
6843 irc_privmsg(cmd->parent->bot, channel->name, response);
6846 reply("CSMSG_VOTE_QUESTION",cData->vote);
6847 unsigned int voteid = 0;
6849 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6850 struct vote_option *vOpt = iter_data(it);
6852 reply("CSMSG_VOTE_OPTION",voteid,vOpt->name,vOpt->voted);
6859 chanserv_refresh_topics(UNUSED_ARG(void *data))
6861 unsigned int refresh_num = (now - self->link_time) / chanserv_conf.refresh_period;
6862 struct chanData *cData;
6865 for(cData = channelList; cData; cData = cData->next)
6867 if(IsSuspended(cData))
6869 opt = cData->chOpts[chTopicRefresh];
6872 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
6875 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
6876 cData->last_refresh = refresh_num;
6878 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
6881 static CHANSERV_FUNC(cmd_unf)
6885 char response[MAXLEN];
6886 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
6887 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6888 irc_privmsg(cmd->parent->bot, channel->name, response);
6891 reply("CSMSG_UNF_RESPONSE");
6895 static CHANSERV_FUNC(cmd_ping)
6899 char response[MAXLEN];
6900 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
6901 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6902 irc_privmsg(cmd->parent->bot, channel->name, response);
6905 reply("CSMSG_PING_RESPONSE");
6909 static CHANSERV_FUNC(cmd_wut)
6913 char response[MAXLEN];
6914 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
6915 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6916 irc_privmsg(cmd->parent->bot, channel->name, response);
6919 reply("CSMSG_WUT_RESPONSE");
6923 static CHANSERV_FUNC(cmd_8ball)
6925 unsigned int i, j, accum;
6930 for(i=1; i<argc; i++)
6931 for(j=0; argv[i][j]; j++)
6932 accum = (accum << 5) - accum + toupper(argv[i][j]);
6933 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
6936 char response[MAXLEN];
6937 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
6938 irc_privmsg(cmd->parent->bot, channel->name, response);
6941 send_message_type(4, user, cmd->parent->bot, "%s", resp);
6945 static CHANSERV_FUNC(cmd_d)
6947 unsigned long sides, count, modifier, ii, total;
6948 char response[MAXLEN], *sep;
6952 if((count = strtoul(argv[1], &sep, 10)) < 1)
6962 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
6963 && (sides = strtoul(sep+1, &sep, 10)) > 1)
6967 else if((sep[0] == '-') && isdigit(sep[1]))
6968 modifier = strtoul(sep, NULL, 10);
6969 else if((sep[0] == '+') && isdigit(sep[1]))
6970 modifier = strtoul(sep+1, NULL, 10);
6977 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
6982 reply("CSMSG_BAD_DICE_COUNT", count, 10);
6985 for(total = ii = 0; ii < count; ++ii)
6986 total += (rand() % sides) + 1;
6989 if((count > 1) || modifier)
6991 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
6992 sprintf(response, fmt, total, count, sides, modifier);
6996 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
6997 sprintf(response, fmt, total, sides);
7000 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
7002 send_message_type(4, user, cmd->parent->bot, "%s", response);
7006 static CHANSERV_FUNC(cmd_huggle)
7008 /* CTCP must be via PRIVMSG, never notice */
7010 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
7012 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
7017 chanserv_adjust_limit(void *data)
7019 struct mod_chanmode change;
7020 struct chanData *cData = data;
7021 struct chanNode *channel = cData->channel;
7024 if(IsSuspended(cData))
7027 cData->limitAdjusted = now;
7028 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
7029 if(cData->modes.modes_set & MODE_LIMIT)
7031 if(limit > cData->modes.new_limit)
7032 limit = cData->modes.new_limit;
7033 else if(limit == cData->modes.new_limit)
7037 mod_chanmode_init(&change);
7038 change.modes_set = MODE_LIMIT;
7039 change.new_limit = limit;
7040 mod_chanmode_announce(chanserv, channel, &change);
7044 handle_new_channel(struct chanNode *channel)
7046 struct chanData *cData;
7048 if(!(cData = channel->channel_info))
7051 if(cData->modes.modes_set || cData->modes.modes_clear)
7052 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
7054 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
7055 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
7058 void handle_new_channel_created(char *chan, struct userNode *user) {
7059 if(user->handle_info && chanserv_conf.new_channel_authed) {
7060 send_target_message(5, chan, chanserv, "%s", chanserv_conf.new_channel_authed);
7061 } else if(!user->handle_info && chanserv_conf.new_channel_unauthed) {
7062 send_target_message(5, chan, chanserv, "%s", chanserv_conf.new_channel_unauthed);
7064 if(chanserv_conf.new_channel_msg)
7065 send_target_message(5, chan, chanserv, "%s", chanserv_conf.new_channel_msg);
7068 /* Welcome to my worst nightmare. Warning: Read (or modify)
7069 the code below at your own risk. */
7071 handle_join(struct modeNode *mNode)
7073 struct mod_chanmode change;
7074 struct userNode *user = mNode->user;
7075 struct chanNode *channel = mNode->channel;
7076 struct chanData *cData;
7077 struct userData *uData = NULL;
7078 struct banData *bData;
7079 struct handle_info *handle;
7080 unsigned int modes = 0, info = 0;
7084 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
7087 cData = channel->channel_info;
7088 if(channel->members.used > cData->max) {
7089 cData->max = channel->members.used;
7090 cData->max_time = now;
7093 for(i = 0; i < channel->invited.used; i++)
7095 if(channel->invited.list[i] == user) {
7096 userList_remove(&channel->invited, user);
7100 /* Check for bans. If they're joining through a ban, one of two
7102 * 1: Join during a netburst, by riding the break. Kick them
7103 * unless they have ops or voice in the channel.
7104 * 2: They're allowed to join through the ban (an invite in
7105 * ircu2.10, or a +e on Hybrid, or something).
7106 * If they're not joining through a ban, and the banlist is not
7107 * full, see if they're on the banlist for the channel. If so,
7110 if(user->uplink->burst && !mNode->modes)
7113 for(ii = 0; ii < channel->banlist.used; ii++)
7115 if(user_matches_glob(user, channel->banlist.list[ii]->ban, MATCH_USENICK))
7117 /* Riding a netburst. Naughty. */
7118 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
7124 mod_chanmode_init(&change);
7126 if(channel->banlist.used < MAXBANS)
7128 /* Not joining through a ban. */
7129 for(bData = cData->bans;
7130 bData && !user_matches_glob(user, bData->mask, MATCH_USENICK);
7131 bData = bData->next);
7135 char kick_reason[MAXLEN];
7136 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
7138 bData->triggered = now;
7139 if(bData != cData->bans)
7141 /* Shuffle the ban to the head of the list. */
7143 bData->next->prev = bData->prev;
7145 bData->prev->next = bData->next;
7148 bData->next = cData->bans;
7151 cData->bans->prev = bData;
7152 cData->bans = bData;
7155 change.args[0].mode = MODE_BAN;
7156 change.args[0].u.hostmask = bData->mask;
7157 mod_chanmode_announce(chanserv, channel, &change);
7158 KickChannelUser(user, channel, chanserv, kick_reason);
7163 /* ChanServ will not modify the limits in join-flooded channels,
7164 or when there are enough slots left below the limit. */
7165 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
7166 && !channel->join_flooded
7167 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
7169 /* The user count has begun "bumping" into the channel limit,
7170 so set a timer to raise the limit a bit. Any previous
7171 timers are removed so three incoming users within the delay
7172 results in one limit change, not three. */
7174 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7175 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7178 if(channel->join_flooded)
7180 /* don't automatically give ops or voice during a join flood */
7182 else if(cData->lvlOpts[lvlGiveOps] == 0)
7183 modes |= MODE_CHANOP;
7184 else if(cData->lvlOpts[lvlGiveVoice] == 0)
7185 modes |= MODE_VOICE;
7187 greeting = cData->greeting;
7188 if(user->handle_info)
7190 handle = user->handle_info;
7192 if(IsHelper(user) && !IsHelping(user))
7195 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7197 if(channel == chanserv_conf.support_channels.list[ii])
7199 HANDLE_SET_FLAG(user->handle_info, HELPING);
7205 uData = GetTrueChannelAccess(cData, handle);
7206 if(uData && !IsUserSuspended(uData))
7208 /* Ops and above were handled by the above case. */
7209 if(IsUserAutoOp(uData))
7211 if(uData->access >= cData->lvlOpts[lvlGiveOps])
7212 modes |= MODE_CHANOP;
7213 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
7214 modes |= MODE_VOICE;
7216 if(uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
7217 cData->visited = now;
7218 if(cData->user_greeting)
7219 greeting = cData->user_greeting;
7221 && (uData->access >= cData->lvlOpts[lvlUserInfo])
7222 && ((now - uData->seen) >= chanserv_conf.info_delay)
7230 /* If user joining normally (not during burst), apply op or voice,
7231 * and send greeting/userinfo as appropriate.
7233 if(!user->uplink->burst)
7237 if(modes & MODE_CHANOP)
7238 modes &= ~MODE_VOICE;
7239 change.args[0].mode = modes;
7240 change.args[0].u.member = mNode;
7241 mod_chanmode_announce(chanserv, channel, &change);
7244 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
7245 if(uData && info && (modes || !(channel->modes & MODE_DELAYJOINS)))
7246 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
7252 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
7254 struct mod_chanmode change;
7255 struct userData *channel;
7256 unsigned int ii, jj;
7258 if(!user->handle_info)
7261 mod_chanmode_init(&change);
7263 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
7265 struct chanNode *cn;
7266 struct modeNode *mn;
7267 if(IsUserSuspended(channel)
7268 || IsSuspended(channel->channel)
7269 || !(cn = channel->channel->channel))
7272 mn = GetUserMode(cn, user);
7275 if(!IsUserSuspended(channel)
7276 && IsUserAutoInvite(channel)
7277 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
7279 && !user->uplink->burst)
7280 irc_invite(chanserv, user, cn);
7284 if(channel->access >= UL_PRESENT && !HANDLE_FLAGGED(channel->handle, BOT))
7285 channel->channel->visited = now;
7287 if(IsUserAutoOp(channel))
7289 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
7290 change.args[0].mode = MODE_CHANOP;
7291 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
7292 change.args[0].mode = MODE_VOICE;
7294 change.args[0].mode = 0;
7295 change.args[0].u.member = mn;
7296 if(change.args[0].mode)
7297 mod_chanmode_announce(chanserv, cn, &change);
7300 channel->seen = now;
7301 channel->present = 1;
7304 for(ii = 0; ii < user->channels.used; ++ii)
7306 struct chanNode *chan = user->channels.list[ii]->channel;
7307 struct banData *ban;
7309 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
7310 || !chan->channel_info
7311 || IsSuspended(chan->channel_info))
7313 for(jj = 0; jj < chan->banlist.used; ++jj)
7314 if(user_matches_glob(user, chan->banlist.list[jj]->ban, MATCH_USENICK))
7316 if(jj < chan->banlist.used)
7318 for(ban = chan->channel_info->bans; ban; ban = ban->next)
7320 char kick_reason[MAXLEN];
7321 if(!user_matches_glob(user, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
7323 change.args[0].mode = MODE_BAN;
7324 change.args[0].u.hostmask = ban->mask;
7325 mod_chanmode_announce(chanserv, chan, &change);
7326 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
7327 KickChannelUser(user, chan, chanserv, kick_reason);
7328 ban->triggered = now;
7333 if(IsSupportHelper(user))
7335 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7337 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
7339 HANDLE_SET_FLAG(user->handle_info, HELPING);
7347 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
7349 struct chanData *cData;
7350 struct userData *uData;
7352 cData = mn->channel->channel_info;
7353 if(!cData || IsSuspended(cData) || IsLocal(mn->user))
7356 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
7358 /* Allow for a bit of padding so that the limit doesn't
7359 track the user count exactly, which could get annoying. */
7360 if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
7362 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7363 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7367 if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
7369 scan_user_presence(uData, mn->user);
7371 if (uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
7372 cData->visited = now;
7375 if(IsHelping(mn->user) && IsSupportHelper(mn->user))
7378 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii) {
7379 struct chanNode *channel;
7380 struct userNode *exclude;
7381 /* When looking at the channel that is being /part'ed, we
7382 * have to skip over the client that is leaving. For
7383 * other channels, we must not do that.
7385 channel = chanserv_conf.support_channels.list[ii];
7386 exclude = (channel == mn->channel) ? mn->user : NULL;
7387 if(find_handle_in_channel(channel, mn->user->handle_info, exclude))
7390 if(ii == chanserv_conf.support_channels.used)
7391 HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
7396 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
7398 struct userData *uData;
7400 if(!channel->channel_info || !kicker || IsService(kicker)
7401 || (kicker == victim) || IsSuspended(channel->channel_info)
7402 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
7405 if(protect_user(victim, kicker, channel->channel_info))
7407 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED_2");
7408 KickChannelUser(kicker, channel, chanserv, reason);
7411 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
7416 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
7418 struct chanData *cData;
7420 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
7423 cData = channel->channel_info;
7424 if(bad_topic(channel, user, channel->topic))
7426 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
7427 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
7428 SetChannelTopic(channel, chanserv, old_topic, 1);
7429 else if(cData->topic)
7430 SetChannelTopic(channel, chanserv, cData->topic, 1);
7433 /* With topicsnarf, grab the topic and save it as the default topic. */
7434 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
7437 cData->topic = strdup(channel->topic);
7443 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
7445 struct mod_chanmode *bounce = NULL;
7446 unsigned int bnc, ii;
7449 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
7452 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
7453 && mode_lock_violated(&channel->channel_info->modes, change))
7455 char correct[MAXLEN];
7456 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
7457 mod_chanmode_format(&channel->channel_info->modes, correct);
7458 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
7460 for(ii = bnc = 0; ii < change->argc; ++ii)
7462 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
7464 const struct userNode *victim = change->args[ii].u.member->user;
7465 if(!protect_user(victim, user, channel->channel_info))
7468 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7471 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
7472 bounce->args[bnc].u.member = GetUserMode(channel, user);
7473 if(bounce->args[bnc].u.member)
7477 bounce->args[bnc].mode = MODE_CHANOP;
7478 bounce->args[bnc].u.member = change->args[ii].u.member;
7480 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
7482 else if(change->args[ii].mode & MODE_CHANOP)
7484 const struct userNode *victim = change->args[ii].u.member->user;
7485 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
7488 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7489 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
7490 bounce->args[bnc].u.member = change->args[ii].u.member;
7493 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
7495 const char *ban = change->args[ii].u.hostmask;
7496 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
7499 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7500 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
7501 bounce->args[bnc].u.hostmask = strdup(ban);
7503 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
7508 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
7509 mod_chanmode_announce(chanserv, channel, bounce);
7510 for(ii = 0; ii < change->argc; ++ii)
7511 if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
7512 free((char*)bounce->args[ii].u.hostmask);
7513 mod_chanmode_free(bounce);
7518 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
7520 struct chanNode *channel;
7521 struct banData *bData;
7522 struct mod_chanmode change;
7523 unsigned int ii, jj;
7524 char kick_reason[MAXLEN];
7526 mod_chanmode_init(&change);
7528 change.args[0].mode = MODE_BAN;
7529 for(ii = 0; ii < user->channels.used; ++ii)
7531 channel = user->channels.list[ii]->channel;
7532 /* Need not check for bans if they're opped or voiced. */
7533 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
7535 /* Need not check for bans unless channel registration is active. */
7536 if(!channel->channel_info || IsSuspended(channel->channel_info))
7538 /* Look for a matching ban already on the channel. */
7539 for(jj = 0; jj < channel->banlist.used; ++jj)
7540 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
7542 /* Need not act if we found one. */
7543 if(jj < channel->banlist.used)
7545 /* Look for a matching ban in this channel. */
7546 for(bData = channel->channel_info->bans; bData; bData = bData->next)
7548 if(!user_matches_glob(user, bData->mask, MATCH_USENICK | MATCH_VISIBLE))
7550 change.args[0].u.hostmask = bData->mask;
7551 mod_chanmode_announce(chanserv, channel, &change);
7552 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
7553 KickChannelUser(user, channel, chanserv, kick_reason);
7554 bData->triggered = now;
7555 break; /* we don't need to check any more bans in the channel */
7560 static void handle_rename(struct handle_info *handle, const char *old_handle)
7562 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
7566 dict_remove2(handle_dnrs, old_handle, 1);
7567 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
7568 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
7573 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
7575 struct userNode *h_user;
7577 if(handle->channels)
7579 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
7580 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
7582 while(handle->channels)
7583 del_channel_user(handle->channels, 1);
7588 handle_server_link(UNUSED_ARG(struct server *server))
7590 struct chanData *cData;
7592 for(cData = channelList; cData; cData = cData->next)
7594 if(!IsSuspended(cData))
7595 cData->may_opchan = 1;
7596 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
7597 && !cData->channel->join_flooded
7598 && ((cData->channel->limit - cData->channel->members.used)
7599 < chanserv_conf.adjust_threshold))
7601 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7602 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7608 chanserv_conf_read(void)
7612 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
7613 struct mod_chanmode *change;
7614 struct string_list *strlist;
7615 struct chanNode *chan;
7618 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
7620 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
7623 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7624 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7625 chanserv_conf.support_channels.used = 0;
7626 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
7628 for(ii = 0; ii < strlist->used; ++ii)
7630 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7633 chan = AddChannel(strlist->list[ii], now, str2, NULL);
7635 channelList_append(&chanserv_conf.support_channels, chan);
7638 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
7641 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7644 chan = AddChannel(str, now, str2, NULL);
7646 channelList_append(&chanserv_conf.support_channels, chan);
7648 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
7649 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
7650 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
7651 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
7652 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
7653 chanserv_conf.greeting_length = str ? atoi(str) : 200;
7654 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
7655 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
7656 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
7657 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
7658 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
7659 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
7660 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
7661 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
7662 str = database_get_data(conf_node, KEY_DNR_EXPIRE_FREQ, RECDB_QSTRING);
7663 chanserv_conf.dnr_expire_frequency = str ? ParseInterval(str) : 3600;
7664 str = database_get_data(conf_node, KEY_INVITED_INTERVAL, RECDB_QSTRING);
7665 chanserv_conf.invited_timeout = str ? ParseInterval(str) : 600*2;
7666 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
7667 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
7668 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
7669 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
7670 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
7671 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
7672 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
7673 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
7674 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
7676 NickChange(chanserv, str, 0);
7677 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
7678 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
7679 str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
7680 chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
7681 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
7682 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
7683 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
7684 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
7685 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
7686 chanserv_conf.max_owned = str ? atoi(str) : 5;
7687 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
7688 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
7689 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
7690 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
7691 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
7692 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
7693 str = database_get_data(conf_node, KEY_NEW_CHANNEL_AUTHED, RECDB_QSTRING);
7694 chanserv_conf.new_channel_authed = str ? str : NULL;
7695 str = database_get_data(conf_node, KEY_NEW_CHANNEL_UNAUTHED, RECDB_QSTRING);
7696 chanserv_conf.new_channel_unauthed = str ? str : NULL;
7697 str = database_get_data(conf_node, KEY_NEW_CHANNEL_MSG, RECDB_QSTRING);
7698 chanserv_conf.new_channel_msg = str ? str : NULL;
7699 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
7702 safestrncpy(mode_line, str, sizeof(mode_line));
7703 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
7704 if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE|MCP_NO_APASS, 0))
7705 && (change->argc < 2))
7707 chanserv_conf.default_modes = *change;
7708 mod_chanmode_free(change);
7710 free_string_list(chanserv_conf.set_shows);
7711 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
7713 strlist = string_list_copy(strlist);
7716 static const char *list[] = {
7717 /* free form text */
7718 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
7719 /* options based on user level */
7720 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
7721 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
7722 /* multiple choice options */
7723 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
7724 /* binary options */
7725 "DynLimit", "NoDelete", "expire", "Vote",
7729 strlist = alloc_string_list(ArrayLength(list)-1);
7730 for(ii=0; list[ii]; ii++)
7731 string_list_append(strlist, strdup(list[ii]));
7733 chanserv_conf.set_shows = strlist;
7734 /* We don't look things up now, in case the list refers to options
7735 * defined by modules initialized after this point. Just mark the
7736 * function list as invalid, so it will be initialized.
7738 set_shows_list.used = 0;
7739 free_string_list(chanserv_conf.eightball);
7740 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
7743 strlist = string_list_copy(strlist);
7747 strlist = alloc_string_list(4);
7748 string_list_append(strlist, strdup("Yes."));
7749 string_list_append(strlist, strdup("No."));
7750 string_list_append(strlist, strdup("Maybe so."));
7752 chanserv_conf.eightball = strlist;
7753 free_string_list(chanserv_conf.old_ban_names);
7754 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
7756 strlist = string_list_copy(strlist);
7758 strlist = alloc_string_list(2);
7759 chanserv_conf.old_ban_names = strlist;
7760 str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
7761 off_channel = str ? atoi(str) : 0;
7765 chanserv_note_type_read(const char *key, struct record_data *rd)
7768 struct note_type *ntype;
7771 if(!(obj = GET_RECORD_OBJECT(rd)))
7773 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
7776 if(!(ntype = chanserv_create_note_type(key)))
7778 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
7782 /* Figure out set access */
7783 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
7785 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7786 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
7788 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
7790 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
7791 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
7793 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
7795 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
7799 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
7800 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7801 ntype->set_access.min_opserv = 0;
7804 /* Figure out visibility */
7805 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
7806 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7807 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
7808 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7809 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
7810 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
7811 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
7812 ntype->visible_type = NOTE_VIS_ALL;
7814 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7816 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
7817 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
7821 vote_option_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7823 struct vote_option *vOpt;
7826 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7828 log_module(CS_LOG, LOG_ERROR, "Invalid vote option in %s.", chan->channel->name);
7832 vOpt = calloc(1, sizeof(*vOpt));
7833 vOpt->name = strdup(database_get_data(rd->d.object, KEY_VOTE_OPTION_NAME, RECDB_QSTRING));
7834 str = database_get_data(rd->d.object, KEY_VOTE_OPTION_VOTED, RECDB_QSTRING);
7835 vOpt->voted = str ? atoi(str) : 0;
7836 vOpt->option_id = str ? atoi(key) : 0;
7837 vOpt->option_str = strdup(key);
7838 dict_insert(chan->vote_options,vOpt->option_str,vOpt);
7842 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7844 struct handle_info *handle;
7845 struct userData *uData;
7846 char *seen, *inf, *flags, *voted, *votefor;
7847 unsigned long last_seen;
7848 unsigned short access_level;
7850 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7852 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
7856 access_level = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
7857 if(access_level > UL_OWNER)
7859 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
7863 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
7864 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
7865 last_seen = seen ? strtoul(seen, NULL, 0) : now;
7866 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
7867 voted = database_get_data(rd->d.object, KEY_VOTE_VOTED, RECDB_QSTRING);
7868 votefor = database_get_data(rd->d.object, KEY_VOTE_VOTEDFOR, RECDB_QSTRING);
7869 handle = get_handle_info(key);
7872 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
7876 uData = add_channel_user(chan, handle, access_level, last_seen, inf);
7877 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
7879 uData->voted = voted ? strtoul(voted, NULL, 0) : 0;
7880 uData->votefor = votefor ? strtoul(votefor, NULL, 0) : 0;
7888 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7890 struct banData *bData;
7891 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
7892 unsigned long set_time, triggered_time, expires_time;
7894 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7896 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
7900 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
7901 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
7902 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
7903 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
7904 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
7905 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
7906 if (!reason || !owner)
7909 set_time = set ? strtoul(set, NULL, 0) : now;
7910 triggered_time = triggered ? strtoul(triggered, NULL, 0) : 0;
7912 expires_time = strtoul(s_expires, NULL, 0);
7914 expires_time = set_time + atoi(s_duration);
7918 if(!reason || (expires_time && (expires_time < now)))
7921 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
7924 static struct suspended *
7925 chanserv_read_suspended(dict_t obj)
7927 struct suspended *suspended = calloc(1, sizeof(*suspended));
7931 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
7932 suspended->expires = str ? strtoul(str, NULL, 0) : 0;
7933 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
7934 suspended->revoked = str ? strtoul(str, NULL, 0) : 0;
7935 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
7936 suspended->issued = str ? strtoul(str, NULL, 0) : 0;
7937 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
7938 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
7939 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
7940 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
7945 chanserv_channel_read(const char *key, struct record_data *hir)
7947 struct suspended *suspended;
7948 struct mod_chanmode *modes;
7949 struct chanNode *cNode;
7950 struct chanData *cData;
7951 struct dict *channel, *obj;
7952 char *str, *argv[10];
7956 channel = hir->d.object;
7958 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
7961 cNode = AddChannel(key, now, NULL, NULL);
7964 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
7967 cData = register_channel(cNode, str);
7970 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
7974 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
7976 enum levelOption lvlOpt;
7977 enum charOption chOpt;
7979 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
7980 cData->flags = atoi(str);
7982 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7984 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
7986 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
7987 else if(levelOptions[lvlOpt].old_flag)
7989 if(cData->flags & levelOptions[lvlOpt].old_flag)
7990 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
7992 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
7996 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7998 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
8000 cData->chOpts[chOpt] = str[0];
8003 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
8005 enum levelOption lvlOpt;
8006 enum charOption chOpt;
8009 cData->flags = base64toint(str, 5);
8010 count = strlen(str += 5);
8011 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8014 if(levelOptions[lvlOpt].old_flag)
8016 if(cData->flags & levelOptions[lvlOpt].old_flag)
8017 lvl = levelOptions[lvlOpt].flag_value;
8019 lvl = levelOptions[lvlOpt].default_value;
8021 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
8023 case 'c': lvl = UL_COOWNER; break;
8024 case 'm': lvl = UL_MASTER; break;
8025 case 'n': lvl = UL_OWNER+1; break;
8026 case 'o': lvl = UL_OP; break;
8027 case 'p': lvl = UL_PEON; break;
8028 case 'w': lvl = UL_OWNER; break;
8029 default: lvl = 0; break;
8031 cData->lvlOpts[lvlOpt] = lvl;
8033 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8034 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
8037 if((str = database_get_data(hir->d.object, KEY_EXPIRE, RECDB_QSTRING)))
8039 cData->expiry = atoi(str);
8040 if(cData->expiry > 0) {
8041 if(cData->expiry > now) {
8042 timeq_add(cData->expiry, chanserv_expire_channel, cData);
8044 timeq_add(1, chanserv_expire_channel, cData);
8051 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
8053 suspended = chanserv_read_suspended(obj);
8054 cData->suspended = suspended;
8055 suspended->cData = cData;
8056 /* We could use suspended->expires and suspended->revoked to
8057 * set the CHANNEL_SUSPENDED flag, but we don't. */
8059 else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
8061 suspended = calloc(1, sizeof(*suspended));
8062 suspended->issued = 0;
8063 suspended->revoked = 0;
8064 suspended->suspender = strdup(str);
8065 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
8066 suspended->expires = str ? atoi(str) : 0;
8067 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
8068 suspended->reason = strdup(str ? str : "No reason");
8069 suspended->previous = NULL;
8070 cData->suspended = suspended;
8071 suspended->cData = cData;
8075 cData->flags &= ~CHANNEL_SUSPENDED;
8076 suspended = NULL; /* to squelch a warning */
8079 if(IsSuspended(cData)) {
8080 if(suspended->expires > now)
8081 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
8082 else if(suspended->expires)
8083 cData->flags &= ~CHANNEL_SUSPENDED;
8086 if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
8087 struct mod_chanmode change;
8088 mod_chanmode_init(&change);
8090 change.args[0].mode = MODE_CHANOP;
8091 change.args[0].u.member = AddChannelUser(chanserv, cNode);
8092 mod_chanmode_announce(chanserv, cNode, &change);
8095 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
8096 cData->registered = str ? strtoul(str, NULL, 0) : now;
8097 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
8098 cData->visited = str ? strtoul(str, NULL, 0) : now;
8099 str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
8100 cData->ownerTransfer = str ? strtoul(str, NULL, 0) : 0;
8101 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
8102 cData->max = str ? atoi(str) : 0;
8103 str = database_get_data(channel, KEY_MAX_TIME, RECDB_QSTRING);
8104 cData->max_time = str ? atoi(str) : 0;
8105 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
8106 cData->greeting = str ? strdup(str) : NULL;
8107 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
8108 cData->user_greeting = str ? strdup(str) : NULL;
8109 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
8110 cData->topic_mask = str ? strdup(str) : NULL;
8111 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
8112 cData->topic = str ? strdup(str) : NULL;
8114 str = database_get_data(channel, KEY_VOTE, RECDB_QSTRING);
8116 cData->vote = str ? strdup(str) : NULL;
8117 dict_delete(cData->vote_options);
8118 cData->vote_options = dict_new();
8119 dict_set_free_data(cData->vote_options, free_vote_options);
8120 str = database_get_data(channel, KEY_VOTE_START, RECDB_QSTRING);
8121 cData->vote_start = str ? atoi(str) : 0;
8122 obj = database_get_data(channel, KEY_VOTE_OPTIONS, RECDB_OBJECT);
8123 for(it = dict_first(obj); it; it = iter_next(it)) {
8124 vote_option_read_helper(iter_key(it), iter_data(it), cData);
8128 if(!IsSuspended(cData)
8129 && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
8130 && (argc = split_line(str, 0, ArrayLength(argv), argv))
8131 && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE|MCP_NO_APASS, 0))) {
8132 cData->modes = *modes;
8134 cData->modes.modes_set |= MODE_REGISTERED;
8135 if(cData->modes.argc > 1)
8136 cData->modes.argc = 1;
8137 mod_chanmode_announce(chanserv, cNode, &cData->modes);
8138 mod_chanmode_free(modes);
8141 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
8142 for(it = dict_first(obj); it; it = iter_next(it))
8143 user_read_helper(iter_key(it), iter_data(it), cData);
8145 if(!cData->users && !IsProtected(cData))
8147 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
8148 unregister_channel(cData, "has empty user list.");
8152 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
8153 for(it = dict_first(obj); it; it = iter_next(it))
8154 ban_read_helper(iter_key(it), iter_data(it), cData);
8156 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
8157 for(it = dict_first(obj); it; it = iter_next(it))
8159 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
8160 struct record_data *rd = iter_data(it);
8161 const char *note, *setter;
8163 if(rd->type != RECDB_OBJECT)
8165 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
8169 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
8171 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
8173 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
8177 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
8178 if(!setter) setter = "<unknown>";
8179 chanserv_add_channel_note(cData, ntype, setter, note);
8187 chanserv_dnr_read(const char *key, struct record_data *hir)
8189 const char *setter, *reason, *str;
8190 struct do_not_register *dnr;
8191 unsigned long expiry;
8193 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
8196 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
8199 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
8202 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
8205 str = database_get_data(hir->d.object, KEY_EXPIRES, RECDB_QSTRING);
8206 expiry = str ? strtoul(str, NULL, 0) : 0;
8207 if(expiry && expiry <= now)
8209 dnr = chanserv_add_dnr(key, setter, expiry, reason);
8212 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
8214 dnr->set = atoi(str);
8220 chanserv_saxdb_read(struct dict *database)
8222 struct dict *section;
8225 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
8226 for(it = dict_first(section); it; it = iter_next(it))
8227 chanserv_note_type_read(iter_key(it), iter_data(it));
8229 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
8230 for(it = dict_first(section); it; it = iter_next(it))
8231 chanserv_channel_read(iter_key(it), iter_data(it));
8233 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
8234 for(it = dict_first(section); it; it = iter_next(it))
8235 chanserv_dnr_read(iter_key(it), iter_data(it));
8241 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
8243 int high_present = 0;
8244 saxdb_start_record(ctx, KEY_USERS, 1);
8245 for(; uData; uData = uData->next)
8247 if((uData->access >= UL_PRESENT) && uData->present && !HANDLE_FLAGGED(uData->handle, BOT))
8249 saxdb_start_record(ctx, uData->handle->handle, 0);
8250 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
8251 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
8253 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
8254 if(uData->channel->vote && uData->voted)
8255 saxdb_write_int(ctx, KEY_VOTE_VOTED, uData->voted);
8256 if(uData->channel->vote && uData->votefor)
8257 saxdb_write_int(ctx, KEY_VOTE_VOTEDFOR, uData->votefor);
8259 saxdb_write_string(ctx, KEY_INFO, uData->info);
8260 saxdb_end_record(ctx);
8262 saxdb_end_record(ctx);
8263 return high_present;
8267 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
8271 saxdb_start_record(ctx, KEY_BANS, 1);
8272 for(; bData; bData = bData->next)
8274 saxdb_start_record(ctx, bData->mask, 0);
8275 saxdb_write_int(ctx, KEY_SET, bData->set);
8276 if(bData->triggered)
8277 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
8279 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
8281 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
8283 saxdb_write_string(ctx, KEY_REASON, bData->reason);
8284 saxdb_end_record(ctx);
8286 saxdb_end_record(ctx);
8290 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
8292 saxdb_start_record(ctx, name, 0);
8293 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
8294 saxdb_write_string(ctx, KEY_REASON, susp->reason);
8296 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
8298 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
8300 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
8302 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
8303 saxdb_end_record(ctx);
8307 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
8311 enum levelOption lvlOpt;
8312 enum charOption chOpt;
8315 saxdb_start_record(ctx, channel->channel->name, 1);
8317 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
8318 saxdb_write_int(ctx, KEY_MAX, channel->max);
8319 saxdb_write_int(ctx, KEY_MAX_TIME, channel->max_time);
8321 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
8322 if(channel->registrar)
8323 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
8324 if(channel->greeting)
8325 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
8326 if(channel->user_greeting)
8327 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
8328 if(channel->topic_mask)
8329 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
8330 if(channel->suspended)
8331 chanserv_write_suspended(ctx, "suspended", channel->suspended);
8333 saxdb_write_int(ctx, KEY_EXPIRE, channel->expiry);
8336 saxdb_write_string(ctx, KEY_VOTE, channel->vote);
8337 if(channel->vote_start)
8338 saxdb_write_int(ctx, KEY_VOTE_START, channel->vote_start);
8339 if (dict_size(channel->vote_options)) {
8340 saxdb_start_record(ctx, KEY_VOTE_OPTIONS, 1);
8341 for (it = dict_first(channel->vote_options); it; it = iter_next(it)) {
8342 struct vote_option *vOpt = iter_data(it);
8344 sprintf(str,"%i",vOpt->option_id);
8345 saxdb_start_record(ctx, str, 0);
8347 saxdb_write_int(ctx, KEY_VOTE_OPTION_VOTED, vOpt->voted);
8349 saxdb_write_string(ctx, KEY_VOTE_OPTION_NAME, vOpt->name);
8350 saxdb_end_record(ctx);
8352 saxdb_end_record(ctx);
8356 saxdb_start_record(ctx, KEY_OPTIONS, 0);
8357 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
8358 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8359 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
8360 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8362 buf[0] = channel->chOpts[chOpt];
8364 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
8366 saxdb_end_record(ctx);
8368 if(channel->modes.modes_set || channel->modes.modes_clear)
8370 mod_chanmode_format(&channel->modes, buf);
8371 saxdb_write_string(ctx, KEY_MODES, buf);
8374 high_present = chanserv_write_users(ctx, channel->users);
8375 chanserv_write_bans(ctx, channel->bans);
8377 if(dict_size(channel->notes))
8381 saxdb_start_record(ctx, KEY_NOTES, 1);
8382 for(it = dict_first(channel->notes); it; it = iter_next(it))
8384 struct note *note = iter_data(it);
8385 saxdb_start_record(ctx, iter_key(it), 0);
8386 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
8387 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
8388 saxdb_end_record(ctx);
8390 saxdb_end_record(ctx);
8393 if(channel->ownerTransfer)
8394 saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
8395 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
8396 saxdb_end_record(ctx);
8400 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
8404 saxdb_start_record(ctx, ntype->name, 0);
8405 switch(ntype->set_access_type)
8407 case NOTE_SET_CHANNEL_ACCESS:
8408 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
8410 case NOTE_SET_CHANNEL_SETTER:
8411 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
8413 case NOTE_SET_PRIVILEGED: default:
8414 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
8417 switch(ntype->visible_type)
8419 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
8420 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
8421 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
8423 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
8424 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
8425 saxdb_end_record(ctx);
8429 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
8431 struct do_not_register *dnr;
8432 dict_iterator_t it, next;
8434 for(it = dict_first(dnrs); it; it = next)
8436 next = iter_next(it);
8437 dnr = iter_data(it);
8438 if(dnr->expires && dnr->expires <= now)
8440 dict_remove(dnrs, iter_key(it));
8443 saxdb_start_record(ctx, dnr->chan_name, 0);
8445 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
8447 saxdb_write_int(ctx, KEY_EXPIRES, dnr->expires);
8448 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
8449 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
8450 saxdb_end_record(ctx);
8455 chanserv_saxdb_write(struct saxdb_context *ctx)
8458 struct chanData *channel;
8461 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
8462 for(it = dict_first(note_types); it; it = iter_next(it))
8463 chanserv_write_note_type(ctx, iter_data(it));
8464 saxdb_end_record(ctx);
8467 saxdb_start_record(ctx, KEY_DNR, 1);
8468 write_dnrs_helper(ctx, handle_dnrs);
8469 write_dnrs_helper(ctx, plain_dnrs);
8470 write_dnrs_helper(ctx, mask_dnrs);
8471 saxdb_end_record(ctx);
8474 saxdb_start_record(ctx, KEY_CHANNELS, 1);
8475 for(channel = channelList; channel; channel = channel->next)
8476 chanserv_write_channel(ctx, channel);
8477 saxdb_end_record(ctx);
8483 chanserv_db_cleanup(void) {
8485 unreg_part_func(handle_part);
8487 unregister_channel(channelList, "terminating.");
8488 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
8489 UnlockChannel(chanserv_conf.support_channels.list[ii]);
8490 free(chanserv_conf.support_channels.list);
8491 dict_delete(handle_dnrs);
8492 dict_delete(plain_dnrs);
8493 dict_delete(mask_dnrs);
8494 dict_delete(note_types);
8495 free_string_list(chanserv_conf.eightball);
8496 free_string_list(chanserv_conf.old_ban_names);
8497 free_string_list(chanserv_conf.set_shows);
8498 free(set_shows_list.list);
8499 free(uset_shows_list.list);
8502 struct userData *helper = helperList;
8503 helperList = helperList->next;
8508 #if defined(GCC_VARMACROS)
8509 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ARGS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ARGS)
8510 #elif defined(C99_VARMACROS)
8511 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, __VA_ARGS__)
8513 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
8514 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
8517 init_chanserv(const char *nick)
8519 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
8520 conf_register_reload(chanserv_conf_read);
8524 reg_server_link_func(handle_server_link);
8525 reg_new_channel_func(handle_new_channel);
8526 reg_join_func(handle_join);
8527 reg_part_func(handle_part);
8528 reg_kick_func(handle_kick);
8529 reg_topic_func(handle_topic);
8530 reg_mode_change_func(handle_mode);
8531 reg_nick_change_func(handle_nick_change);
8532 reg_auth_func(handle_auth);
8535 reg_handle_rename_func(handle_rename);
8536 reg_unreg_func(handle_unreg);
8538 handle_dnrs = dict_new();
8539 dict_set_free_data(handle_dnrs, free);
8540 plain_dnrs = dict_new();
8541 dict_set_free_data(plain_dnrs, free);
8542 mask_dnrs = dict_new();
8543 dict_set_free_data(mask_dnrs, free);
8545 reg_svccmd_unbind_func(handle_svccmd_unbind);
8546 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
8547 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
8548 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
8549 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
8550 DEFINE_COMMAND(dnrsearch, 3, 0, "template", "noregister", NULL);
8551 modcmd_register(chanserv_module, "dnrsearch print", NULL, 0, 0, NULL);
8552 modcmd_register(chanserv_module, "dnrsearch remove", NULL, 0, 0, NULL);
8553 modcmd_register(chanserv_module, "dnrsearch count", NULL, 0, 0, NULL);
8554 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
8555 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
8556 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
8557 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
8558 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
8560 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
8561 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
8563 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8564 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8565 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8566 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8567 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
8569 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
8570 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
8571 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
8572 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8573 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8575 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8576 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
8577 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8578 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
8580 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
8581 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
8582 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8583 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8584 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
8585 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8586 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8587 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8589 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8590 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8591 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8592 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
8593 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
8594 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
8595 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
8596 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
8597 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8598 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
8599 DEFINE_COMMAND(invitemeall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8600 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
8601 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
8602 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8603 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8605 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
8606 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8607 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8608 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8609 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
8611 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
8612 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
8614 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
8615 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8616 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8617 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8618 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8619 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8620 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8621 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8622 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8623 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8624 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8626 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
8627 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
8629 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
8630 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
8631 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
8632 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
8634 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
8635 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
8636 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
8637 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
8638 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
8640 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8641 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8642 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8643 DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8644 DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8645 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8647 DEFINE_COMMAND(addvote, 1, MODCMD_REQUIRE_AUTHED, NULL);
8648 DEFINE_COMMAND(delvote, 1, MODCMD_REQUIRE_AUTHED, NULL);
8649 DEFINE_COMMAND(addoption, 1, MODCMD_REQUIRE_AUTHED, NULL);
8650 DEFINE_COMMAND(deloption, 1, MODCMD_REQUIRE_AUTHED, NULL);
8651 DEFINE_COMMAND(vote, 1, MODCMD_REQUIRE_AUTHED, NULL);
8652 DEFINE_COMMAND(startvote, 1, MODCMD_REQUIRE_AUTHED, NULL);
8653 DEFINE_COMMAND(endvote, 1, MODCMD_REQUIRE_AUTHED, NULL);
8654 DEFINE_COMMAND(voteresults, 1, MODCMD_REQUIRE_AUTHED, NULL);
8656 /* Channel options */
8657 DEFINE_CHANNEL_OPTION(defaulttopic);
8658 DEFINE_CHANNEL_OPTION(topicmask);
8659 DEFINE_CHANNEL_OPTION(greeting);
8660 DEFINE_CHANNEL_OPTION(usergreeting);
8661 DEFINE_CHANNEL_OPTION(modes);
8662 DEFINE_CHANNEL_OPTION(enfops);
8663 DEFINE_CHANNEL_OPTION(giveops);
8664 DEFINE_CHANNEL_OPTION(protect);
8665 DEFINE_CHANNEL_OPTION(enfmodes);
8666 DEFINE_CHANNEL_OPTION(enftopic);
8667 DEFINE_CHANNEL_OPTION(pubcmd);
8668 DEFINE_CHANNEL_OPTION(givevoice);
8669 DEFINE_CHANNEL_OPTION(userinfo);
8670 DEFINE_CHANNEL_OPTION(dynlimit);
8671 DEFINE_CHANNEL_OPTION(topicsnarf);
8672 DEFINE_CHANNEL_OPTION(vote);
8673 DEFINE_CHANNEL_OPTION(nodelete);
8674 DEFINE_CHANNEL_OPTION(toys);
8675 DEFINE_CHANNEL_OPTION(setters);
8676 DEFINE_CHANNEL_OPTION(topicrefresh);
8677 DEFINE_CHANNEL_OPTION(ctcpusers);
8678 DEFINE_CHANNEL_OPTION(ctcpreaction);
8679 DEFINE_CHANNEL_OPTION(inviteme);
8680 DEFINE_CHANNEL_OPTION(unreviewed);
8681 modcmd_register(chanserv_module, "set expire", chan_opt_expire, 1, 0, "flags", "+helping", NULL);
8682 modcmd_register(chanserv_module, "set unreviewed on", NULL, 0, 0, "flags", "+helping", NULL);
8683 modcmd_register(chanserv_module, "set unreviewed off", NULL, 0, 0, "flags", "+oper", NULL);
8685 DEFINE_CHANNEL_OPTION(offchannel);
8686 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
8688 /* Alias set topic to set defaulttopic for compatibility. */
8689 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
8692 DEFINE_USER_OPTION(noautoop);
8693 DEFINE_USER_OPTION(autoinvite);
8694 DEFINE_USER_OPTION(info);
8696 /* Alias uset autovoice to uset autoop. */
8697 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
8699 note_types = dict_new();
8700 dict_set_free_data(note_types, chanserv_deref_note_type);
8703 const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
8704 chanserv = AddLocalUser(nick, nick, NULL, "Channel Services", modes);
8705 service_register(chanserv)->trigger = '!';
8706 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
8708 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
8710 if(chanserv_conf.channel_expire_frequency)
8711 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
8713 if(chanserv_conf.dnr_expire_frequency)
8714 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
8716 if(chanserv_conf.refresh_period)
8718 unsigned long next_refresh;
8719 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
8720 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
8723 reg_exit_func(chanserv_db_cleanup);
8724 message_register_table(msgtab);