1 /* chanserv.c - Channel service bot
2 * Copyright 2000-2007 srvx Development Team
4 * This file is part of srvx.
6 * srvx is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with srvx; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
25 #include "opserv.h" /* for opserv_bad_channel() and devnull management */
26 #include "nickserv.h" /* for oper_outranks() */
31 #define CHANSERV_CONF_NAME "services/chanserv"
33 /* ChanServ options */
34 #define KEY_SUPPORT_CHANNEL "support_channel"
35 #define KEY_SUPPORT_CHANNEL_MODES "support_channel_modes"
36 #define KEY_DB_BACKUP_FREQ "db_backup_freq"
37 #define KEY_INFO_DELAY "info_delay"
38 #define KEY_MAX_GREETLEN "max_greetlen"
39 #define KEY_ADJUST_THRESHOLD "adjust_threshold"
40 #define KEY_ADJUST_DELAY "adjust_delay"
41 #define KEY_CHAN_EXPIRE_FREQ "chan_expire_freq"
42 #define KEY_CHAN_EXPIRE_DELAY "chan_expire_delay"
43 #define KEY_DNR_EXPIRE_FREQ "dnr_expire_freq"
44 #define KEY_MAX_CHAN_USERS "max_chan_users"
45 #define KEY_MAX_CHAN_BANS "max_chan_bans"
46 #define KEY_NICK "nick"
47 #define KEY_OLD_CHANSERV_NAME "old_chanserv_name"
48 #define KEY_8BALL_RESPONSES "8ball"
49 #define KEY_OLD_BAN_NAMES "old_ban_names"
50 #define KEY_REFRESH_PERIOD "refresh_period"
51 #define KEY_CTCP_SHORT_BAN_DURATION "ctcp_short_ban_duration"
52 #define KEY_CTCP_LONG_BAN_DURATION "ctcp_long_ban_duration"
53 #define KEY_MAX_OWNED "max_owned"
54 #define KEY_IRC_OPERATOR_EPITHET "irc_operator_epithet"
55 #define KEY_NETWORK_HELPER_EPITHET "network_helper_epithet"
56 #define KEY_SUPPORT_HELPER_EPITHET "support_helper_epithet"
57 #define KEY_NODELETE_LEVEL "nodelete_level"
58 #define KEY_MAX_USERINFO_LENGTH "max_userinfo_length"
59 #define KEY_GIVEOWNERSHIP_PERIOD "giveownership_timeout"
60 #define KEY_INVITED_INTERVAL "invite_timeout"
61 #define KEY_REVOKE_MODE_A "revoke_mode_a"
62 #define KEY_NEW_CHANNEL_AUTHED "new_channel_authed_join"
63 #define KEY_NEW_CHANNEL_UNAUTHED "new_channel_unauthed_join"
64 #define KEY_NEW_CHANNEL_MSG "new_channel_message"
66 /* ChanServ database */
67 #define KEY_CHANNELS "channels"
68 #define KEY_NOTE_TYPES "note_types"
70 /* Note type parameters */
71 #define KEY_NOTE_OPSERV_ACCESS "opserv_access"
72 #define KEY_NOTE_CHANNEL_ACCESS "channel_access"
73 #define KEY_NOTE_SETTER_ACCESS "setter_access"
74 #define KEY_NOTE_VISIBILITY "visibility"
75 #define KEY_NOTE_VIS_PRIVILEGED "privileged"
76 #define KEY_NOTE_VIS_CHANNEL_USERS "channel_users"
77 #define KEY_NOTE_VIS_ALL "all"
78 #define KEY_NOTE_MAX_LENGTH "max_length"
79 #define KEY_NOTE_SETTER "setter"
80 #define KEY_NOTE_NOTE "note"
82 /* Do-not-register channels */
84 #define KEY_DNR_SET "set"
85 #define KEY_DNR_SETTER "setter"
86 #define KEY_DNR_REASON "reason"
89 #define KEY_REGISTERED "registered"
90 #define KEY_REGISTRAR "registrar"
91 #define KEY_SUSPENDED "suspended"
92 #define KEY_PREVIOUS "previous"
93 #define KEY_SUSPENDER "suspender"
94 #define KEY_ISSUED "issued"
95 #define KEY_REVOKED "revoked"
96 #define KEY_SUSPEND_EXPIRES "suspend_expires"
97 #define KEY_SUSPEND_REASON "suspend_reason"
98 #define KEY_GIVEOWNERSHIP "giveownership"
99 #define KEY_STAFF_ISSUER "staff_issuer"
100 #define KEY_OLD_OWNER "old_owner"
101 #define KEY_TARGET "target"
102 #define KEY_TARGET_ACCESS "target_access"
103 #define KEY_VISITED "visited"
104 #define KEY_TOPIC "topic"
105 #define KEY_GREETING "greeting"
106 #define KEY_USER_GREETING "user_greeting"
107 #define KEY_MODES "modes"
108 #define KEY_FLAGS "flags"
109 #define KEY_OPTIONS "options"
110 #define KEY_USERS "users"
111 #define KEY_BANS "bans"
112 #define KEY_MAX "max"
113 #define KEY_MAX_TIME "max_time"
114 #define KEY_NOTES "notes"
115 #define KEY_TOPIC_MASK "topic_mask"
116 #define KEY_OWNER_TRANSFER "owner_transfer"
117 #define KEY_EXPIRE "expire"
120 #define KEY_LEVEL "level"
121 #define KEY_INFO "info"
122 #define KEY_SEEN "seen"
125 #define KEY_VOTE "vote"
126 #define KEY_VOTE_START "votestart"
127 #define KEY_VOTE_OPTIONS "voptions"
128 #define KEY_VOTE_OPTION_NAME "voptionname"
129 #define KEY_VOTE_VOTED "vvoted"
130 #define KEY_VOTE_VOTEDFOR "vvotefor"
131 #define KEY_VOTE_OPTION_ID "voptionid"
132 #define KEY_VOTE_OPTION_VOTED "voptionvoted"
135 #define KEY_OWNER "owner"
136 #define KEY_REASON "reason"
137 #define KEY_SET "set"
138 #define KEY_DURATION "duration"
139 #define KEY_EXPIRES "expires"
140 #define KEY_TRIGGERED "triggered"
142 #define CHANNEL_DEFAULT_FLAGS (CHANNEL_OFFCHANNEL | CHANNEL_UNREVIEWED)
143 #define CHANNEL_PRESERVED_FLAGS (CHANNEL_UNREVIEWED)
144 #define CHANNEL_DEFAULT_OPTIONS "lmoooanpcnat"
146 /* Administrative messages */
147 static const struct message_entry msgtab[] = {
148 { "CSMSG_CHANNELS_EXPIRED", "%i channels expired." },
150 /* Channel registration */
151 { "CSMSG_REG_SUCCESS", "You now have ownership of $b%s$b." },
152 { "CSMSG_PROXY_SUCCESS", "%s now has ownership of $b%s$b." },
153 { "CSMSG_ALREADY_REGGED", "$b%s$b is registered to someone else." },
154 { "CSMSG_MUST_BE_OPPED", "You must be a channel operator in $b%s$b to register it." },
155 { "CSMSG_PROXY_FORBIDDEN", "You may not register a channel for someone else." },
156 { "CSMSG_OWN_TOO_MANY", "%s already owns enough channels (at least %d); use FORCE to override." },
158 /* Do-not-register channels */
159 { "CSMSG_NOT_DNR", "$b%s$b is not a valid channel name or *account." },
160 { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
161 { "CSMSG_DNR_INFO", "$b%s$b is do-not-register (by $b%s$b): %s" },
162 { "CSMSG_DNR_INFO_SET", "$b%s$b is do-not-register (set %s by $b%s$b): %s" },
163 { "CSMSG_DNR_INFO_SET_EXPIRES", "$b%s$b is do-not-register (set %s by $b%s$b; expires %s): %s" },
164 { "CSMSG_MORE_DNRS", "%d more do-not-register entries skipped." },
165 { "CSMSG_DNR_CHANNEL", "Only network staff may register $b%s$b." },
166 { "CSMSG_DNR_CHANNEL_MOVE", "Only network staff may move $b%s$b." },
167 { "CSMSG_DNR_ACCOUNT", "Only network staff may register channels to $b%s$b." },
168 { "CSMSG_NOREGISTER_CHANNEL", "$b%s$b has been added to the do-not-register list." },
169 { "CSMSG_NO_SUCH_DNR", "$b%s$b is not in the do-not-register list." },
170 { "CSMSG_DNR_REMOVED", "$b%s$b has been removed from the do-not-register list." },
171 { "CSMSG_DNR_BAD_ACTION", "$b%s$b is not a recognized do-not-register action." },
172 { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
174 /* Channel unregistration */
175 { "CSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
176 { "CSMSG_UNREG_NODELETE", "$b%s$b is protected from unregistration." },
177 { "CSMSG_CHAN_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended (%s)." },
178 { "CSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must use 'unregister %s'." },
181 { "CSMSG_MOVE_SUCCESS", "Channel registration has been moved to $b%s$b." },
182 { "CSMSG_MOVE_NODELETE", "$b%s$b is protected from unregistration, and cannot be moved." },
184 /* Channel merging */
185 { "CSMSG_MERGE_SUCCESS", "Channel successfully merged into $b%s$b." },
186 { "CSMSG_MERGE_SELF", "Merging cannot be performed if the source and target channels are the same." },
187 { "CSMSG_MERGE_NODELETE", "You may not merge a channel that is marked NoDelete." },
188 { "CSMSG_MERGE_SUSPENDED", "Merging cannot be performed if the source or target channel is suspended." },
189 { "CSMSG_MERGE_NOT_OWNER", "You must be the owner of the target channel (or a helper) to merge into the channel." },
191 /* Handle unregistration */
192 { "CSMSG_HANDLE_UNREGISTERED", "As a result of your account unregistration, you have been deleted from all of your channels' userlists." },
195 { "CSMSG_NOT_USER", "You lack access to $b%s$b." },
196 { "CSMSG_NO_CHAN_USER", "%s lacks access to $b%s$b." },
197 { "CSMSG_NO_ACCESS", "You lack sufficient access to use this command." },
198 { "CSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$C$b." },
199 { "CSMSG_MAXIMUM_BANS", "This channel has reached the ban count limit of $b%d$b." },
200 { "CSMSG_MAXIMUM_USERS", "This channel has reached the user count limit of $b%d$b." },
201 { "CSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel, and cannot be registered." },
202 { "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." },
203 { "CSMSG_ALREADY_OPPED", "You are already opped in $b%s$b." },
204 { "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." },
205 { "CSMSG_ALREADY_DOWN", "You are not opped or voiced in $b%s$b." },
206 { "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." },
207 { "CSMSG_OUT_OF_CHANNEL", "For some reason I don't seem to be in $b%s$b." },
208 { "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." },
210 /* Removing yourself from a channel. */
211 { "CSMSG_NO_OWNER_DELETEME", "You cannot delete your owner access in $b%s$b." },
212 { "CSMSG_CONFIRM_DELETEME", "To really remove yourself, you must use 'deleteme %s'." },
213 { "CSMSG_DELETED_YOU", "Your $b%d$b access has been deleted from $b%s$b." },
215 /* User management */
216 { "CSMSG_ADDED_USER", "Added %s to the %s user list with access %d." },
217 { "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." },
218 { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be lower than maximum (%d)." },
219 { "CSMSG_DELETED_USERS", "Deleted accounts matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list." },
220 { "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." },
221 { "CSMSG_INCORRECT_ACCESS", "%s has access $b%d$b, not %s." },
222 { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with access %d)." },
223 { "CSMSG_CANNOT_TRIM", "You must include a minimum inactivity duration of at least 60 seconds to trim." },
225 { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." },
226 { "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." },
227 { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
228 { "CSMSG_TRANSFER_WAIT", "You must wait %s before you can give ownership of $b%s$b to someone else." },
229 { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
230 { "CSMSG_CONFIRM_GIVEOWNERSHIP", "To really give ownership to $b%1$s$b, you must use 'giveownership *%1$s %2$s'." },
231 { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
234 { "CSMSG_BAN_ADDED", "Permanently banned $b%s$b from %s." },
235 { "CSMSG_TIMED_BAN_ADDED", "Banned $b%s$b from %s for %s." },
236 { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
237 { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
238 { "CSMSG_REASON_CHANGE", "Reason for ban $b%s$b changed." },
239 { "CSMSG_BAN_EXTENDED", "Extended ban for $b%s$b expires in %s." },
240 { "CSMSG_BAN_REMOVED", "Matching ban(s) for $b%s$b removed." },
241 { "CSMSG_TRIMMED_BANS", "Trimmed $b%d bans$b from the %s ban list that were inactive for at least %s." },
242 { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
243 { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
244 { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
245 { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
246 { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
247 { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
248 { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban found for $b%s$b." },
249 { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
251 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
253 /* Channel management */
254 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
255 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
256 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
258 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
259 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
260 { "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" },
261 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic is at most %d characters and matches the topic mask pattern." },
262 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
263 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
264 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
266 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
267 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
268 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
269 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
270 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
271 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
272 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you must use 'set defaults %s'." },
273 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
274 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
275 { "CSMSG_BAD_GIVEVOICE", "You cannot change GiveVoice to above GiveOps (%d)." },
276 { "CSMSG_BAD_GIVEOPS", "You cannot change GiveOps to below GiveVoice (%d)." },
277 { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
278 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
279 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
280 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
281 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
282 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
283 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
284 { "CSMSG_SET_MODES", "$bModes $b %s" },
285 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
286 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s" },
287 { "CSMSG_SET_OFFCHANNEL", "$bOffChannel $b %s" },
288 { "CSMSG_SET_USERINFO", "$bUserInfo $b %d" },
289 { "CSMSG_SET_GIVE_VOICE", "$bGiveVoice $b %d" },
290 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" },
291 { "CSMSG_SET_VOTE", "$bVote $b %d" },
292 { "CSMSG_SET_INVITEME", "$bInviteMe $b %d" },
293 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d" },
294 { "CSMSG_SET_GIVE_OPS", "$bGiveOps $b %d" },
295 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d" },
296 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d" },
297 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d" },
298 { "CSMSG_SET_SETTERS", "$bSetters $b %d" },
299 { "CSMSG_SET_CTCPUSERS", "$bCTCPUsers $b %d" },
300 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
301 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
302 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
303 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
304 { "CSMSG_SET_UNREVIEWED", "$bUnreviewed $b %s" },
305 { "CSMSG_SET_EXPIRE", "$bExpire $b %s" },
306 { "CSMSG_SET_EXPIRE_OFF", "$bExpire $b off" },
307 { "CSMSG_USET_NOAUTOOP", "$bNoAutoOp $b %s" },
308 { "CSMSG_USET_NOAUTOVOICE", "$bNoAutoVoice $b %s" },
309 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
310 { "CSMSG_USET_INFO", "$bInfo $b %s" },
312 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
313 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
314 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
315 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
316 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
317 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
318 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
319 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
320 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
321 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
322 { "CSMSG_PROTECT_NONE", "No users will be protected." },
323 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
324 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
325 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
326 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
327 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
328 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
329 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
330 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
331 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
332 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
333 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
334 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
336 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
337 { "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" },
338 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s." },
339 { "CSMSG_ALREADY_PRESENT", "%s is already in $b%s$b." },
340 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
341 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s for $S to invite you." },
342 { "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
343 { "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
345 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
346 { "CSMSG_NO_BANS", "No channel bans found on $b%s$b." },
347 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
349 /* Channel userlist */
350 { "CSMSG_ACCESS_ALL_HEADER", "%s users from level %d to %d:" },
351 { "CSMSG_ACCESS_SEARCH_HEADER", "%s users from level %d to %d matching %s:" },
352 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
353 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%d$b in %s." },
354 { "CSMSG_TOTAL_USERS", "There are $b%d$b users in %s." },
356 /* Channel note list */
357 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
358 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
359 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
360 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
361 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
362 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
363 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
364 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
365 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
366 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
367 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
368 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
369 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
370 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
371 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
373 /* Channel [un]suspension */
374 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
375 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
376 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
377 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
378 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
379 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
380 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
382 /* Access information */
383 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
384 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
385 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
386 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
387 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
388 { "CSMSG_USER_HAS_ACCESS", "%s has access $b%d$b in %s." },
389 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
390 { "CSMSG_HELPER_HAS_ACCESS", "%s has access $b%d$b in %s and has $bsecurity override$b enabled." },
391 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
392 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
393 { "CSMSG_OPERATOR_TITLE", "IRC operator" },
394 { "CSMSG_UC_H_TITLE", "network helper" },
395 { "CSMSG_LC_H_TITLE", "support helper" },
396 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
397 { "CSMSG_MYACCESS_COUNT", "%s has access in $b%d$b channels and is owner of $b%d$b channel(s)." },
398 { "CSMSG_MYACCESS_COUNT_1", "%s has access in $b%d$b channel and is owner of $b%d$b channel(s)." },
401 /* Seen information */
402 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
403 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
404 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
405 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
407 /* Names information */
408 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
409 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
411 /* Channel information */
412 { "CSMSG_CHANNEL_INFO", "$b%s$b Information:" },
413 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
414 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
415 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
416 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
417 { "CSMSG_CHANNEL_MAX_TIME", "$bRecord Visitors: $b%i (%s ago.)" },
418 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
419 { "CSMSG_CHANNEL_BANS", "$bBan Count: $b%i" },
420 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
421 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
422 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
423 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
424 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
425 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
426 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
427 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
428 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
429 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
430 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
431 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
432 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
433 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
435 { "CSMSG_PEEK_INFO", "$b%s$b Status:" },
436 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
437 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
438 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d (%d ops, %d voices, %d regulars)" },
439 { "CSMSG_PEEK_OPS", "$bOps:$b" },
440 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
442 /* Network information */
443 { "CSMSG_NETWORK_INFO", "Network Information:" },
444 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
445 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
446 { "CSMSG_NETWORK_BANS", "$bTotal Ban Count: $b%i" },
447 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
448 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
449 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
450 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
451 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
454 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
455 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
456 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
458 /* Channel searches */
459 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
460 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
461 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
462 { "CSMSG_CHANNEL_SEARCH_RESULTS", "The following channels were found:" },
464 /* Channel configuration */
465 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
466 { "CSMSG_INVALID_CFLAG", "$b%s$b is not a recognized channel flag." },
467 { "CSMSG_CHANNEL_OPTIONS", "Channel Options:" },
468 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
471 { "CSMSG_USER_OPTIONS", "User Options:" },
472 { "CSMSG_USER_PROTECTED_2", "That user is protected." },
475 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
476 { "CSMSG_PING_RESPONSE", "Pong!" },
477 { "CSMSG_WUT_RESPONSE", "wut" },
478 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
479 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
480 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
481 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
482 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
483 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
484 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
487 { "CSMSG_ADDVOTE_DONE", "Vote added. Use $baddoption$b to add at least 2 vote options and then $bstartvote$b to start the voting." },
488 { "CSMSG_ADDVOTE_FULL", "There is already a vote in this channel. Use $bdelvote$b to delete it." },
489 { "CSMSG_DELVOTE_DONE", "Vote deleted." },
490 { "CSMSG_NO_VOTE", "There is no vote in this channel." },
491 { "CSMSG_ADDOPTION_DONE", "Vote option added with id %i (%i - %i)." },
492 { "CSMSG_DELOPTION_DONE", "Vote option deleted." },
493 { "CSMSG_DELOPTION_NONE", "Vote option does not exist." },
494 { "CSMSG_VOTE_NEED_OPTIONS", "There must be at least 2 options in a vote." },
495 { "CSMSG_VOTE_NOT_STARTED", "The vote is not started. Use $bstartvote$b to allow voting." },
496 { "CSMSG_VOTE_QUESTION", "Question: %s" },
497 { "CSMSG_VOTE_OPTION", "$b%i$b: %s ($b%i$b votes)" },
498 { "CSMSG_VOTERES_QUESTION", "Question: %s" },
499 { "CSMSG_VOTERES_OPTION", "
\ 2%i
\ 2: %s (
\ 2%i
\ 2 votes)" },
500 { "CSMSG_STARTVOTE_TOP", "
\ 2%s
\ 2 has started a voting:" },
501 { "CSMSG_STARTVOTE_QUESTION", "
\ 2Question:
\ 2 %s" },
502 { "CSMSG_STARTVOTE_OPTION", "
\ 2%i:
\ 2 %s" },
503 { "CSMSG_STARTVOTE_ACCESS", "All channel users with at least
\ 2%i
\ 2 access can vote." },
504 { "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." },
505 { "CSMSG_STARTVOTE_RUNNING", "The vote is already running." },
506 { "CSMSG_VOTE_VOTED", "You have already voted." },
507 { "CSMSG_VOTE_INVALID", "Vote option does not exist." },
508 { "CSMSG_VOTE_DONE", "You voted for $b%s$b." },
509 { "CSMSG_ENDVOTE_DONE", "The vote has been finished." },
510 { "CSMSG_ENDVOTE_STOPPED", "The vote is not running." },
513 { "CSMSG_EVENT_SEARCH_RESULTS", "The following channel events were found:" },
517 /* eject_user and unban_user flags */
518 #define ACTION_KICK 0x0001
519 #define ACTION_BAN 0x0002
520 #define ACTION_ADD_BAN 0x0004
521 #define ACTION_ADD_TIMED_BAN 0x0008
522 #define ACTION_UNBAN 0x0010
523 #define ACTION_DEL_BAN 0x0020
525 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
526 #define MODELEN 40 + KEYLEN
530 #define CSFUNC_ARGS user, channel, argc, argv, cmd
532 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
533 #define CHANSERV_SYNTAX() svccmd_send_help(user, chanserv, cmd)
534 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
535 reply("MSG_MISSING_PARAMS", argv[0]); \
539 DECLARE_LIST(dnrList, struct do_not_register *);
540 DEFINE_LIST(dnrList, struct do_not_register *)
542 static int eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action);
544 struct userNode *chanserv;
547 static dict_t plain_dnrs, mask_dnrs, handle_dnrs;
548 static struct log_type *CS_LOG;
552 struct channelList support_channels;
553 struct mod_chanmode default_modes;
555 unsigned long db_backup_frequency;
556 unsigned long channel_expire_frequency;
557 unsigned long dnr_expire_frequency;
559 unsigned long invited_timeout;
561 unsigned long info_delay;
562 unsigned long adjust_delay;
563 unsigned long channel_expire_delay;
564 unsigned int nodelete_level;
566 unsigned int adjust_threshold;
567 int join_flood_threshold;
569 unsigned int greeting_length;
570 unsigned int refresh_period;
571 unsigned int giveownership_period;
573 unsigned int max_owned;
574 unsigned int max_chan_users;
575 unsigned int max_chan_bans;
576 unsigned int max_userinfo_length;
578 unsigned int revoke_mode_a;
580 struct string_list *set_shows;
581 struct string_list *eightball;
582 struct string_list *old_ban_names;
584 const char *ctcp_short_ban_duration;
585 const char *ctcp_long_ban_duration;
587 const char *irc_operator_epithet;
588 const char *network_helper_epithet;
589 const char *support_helper_epithet;
591 const char *new_channel_authed;
592 const char *new_channel_unauthed;
593 const char *new_channel_msg;
598 struct userNode *user;
599 struct userNode *bot;
600 struct chanNode *channel;
602 unsigned short lowest;
603 unsigned short highest;
604 struct userData **users;
605 struct helpfile_table table;
610 struct userNode *user;
611 struct chanNode *chan;
614 enum note_access_type
616 NOTE_SET_CHANNEL_ACCESS,
617 NOTE_SET_CHANNEL_SETTER,
621 enum note_visible_type
624 NOTE_VIS_CHANNEL_USERS,
630 enum note_access_type set_access_type;
632 unsigned int min_opserv;
633 unsigned short min_ulevel;
635 enum note_visible_type visible_type;
636 unsigned int max_length;
643 struct note_type *type;
644 char setter[NICKSERV_HANDLE_LEN+1];
648 static unsigned int registered_channels;
649 static unsigned int banCount;
651 static const struct {
654 unsigned short level;
657 { "peon", "Peon", UL_PEON, '+' },
658 { "op", "Op", UL_OP, '@' },
659 { "master", "Master", UL_MASTER, '%' },
660 { "coowner", "Coowner", UL_COOWNER, '*' },
661 { "owner", "Owner", UL_OWNER, '!' },
662 { "helper", "BUG:", UL_HELPER, 'X' }
665 static const struct {
668 unsigned short default_value;
669 unsigned int old_idx;
670 unsigned int old_flag;
671 unsigned short flag_value;
673 { "CSMSG_SET_GIVE_VOICE", "givevoice", 100, ~0, CHANNEL_VOICE_ALL, 0 },
674 { "CSMSG_SET_GIVE_OPS", "giveops", 200, 2, 0, 0 },
675 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
676 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
677 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
678 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
679 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
680 { "CSMSG_SET_CTCPUSERS", "ctcpusers", 0, 9, 0, 0 },
681 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES, 1 },
682 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE, 200 },
683 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF, 1 },
684 { "CSMSG_SET_VOTE", "vote", 100, ~0, 0, 0 }
687 struct charOptionValues {
690 } protectValues[] = {
691 { 'a', "CSMSG_PROTECT_ALL" },
692 { 'e', "CSMSG_PROTECT_EQUAL" },
693 { 'l', "CSMSG_PROTECT_LOWER" },
694 { 'n', "CSMSG_PROTECT_NONE" }
696 { 'd', "CSMSG_TOYS_DISABLED" },
697 { 'n', "CSMSG_TOYS_PRIVATE" },
698 { 'p', "CSMSG_TOYS_PUBLIC" }
699 }, topicRefreshValues[] = {
700 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
701 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
702 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
703 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
704 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
705 }, ctcpReactionValues[] = {
706 { 'k', "CSMSG_CTCPREACTION_KICK" },
707 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
708 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
709 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
712 static const struct {
716 unsigned int old_idx;
718 struct charOptionValues *values;
720 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues), protectValues },
721 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues), toysValues },
722 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues), topicRefreshValues },
723 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 't', 10, ArrayLength(ctcpReactionValues), ctcpReactionValues }
726 struct userData *helperList;
727 struct chanData *channelList;
728 static struct module *chanserv_module;
729 static unsigned int userCount;
731 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
732 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
733 static void unregister_channel(struct chanData *channel, const char *reason);
736 user_level_from_name(const char *name, unsigned short clamp_level)
738 unsigned int level = 0, ii;
740 level = strtoul(name, NULL, 10);
741 else for(ii = 0; (ii < ArrayLength(accessLevels)) && !level; ++ii)
742 if(!irccasecmp(name, accessLevels[ii].name))
743 level = accessLevels[ii].level;
744 if(level > clamp_level)
750 parse_level_range(unsigned short *minl, unsigned short *maxl, const char *arg)
753 *minl = strtoul(arg, &sep, 10);
761 *maxl = strtoul(sep+1, &sep, 10);
769 _GetChannelUser(struct chanData *channel, struct handle_info *handle, int override, int allow_suspended)
771 struct userData *uData, **head;
773 if(!channel || !handle)
776 if(override && HANDLE_FLAGGED(handle, HELPING)
777 && ((handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel)))
779 for(uData = helperList;
780 uData && uData->handle != handle;
781 uData = uData->next);
785 uData = calloc(1, sizeof(struct userData));
786 uData->handle = handle;
788 uData->access = UL_HELPER;
794 uData->next = helperList;
796 helperList->prev = uData;
804 for(uData = channel->users; uData; uData = uData->next)
805 if((uData->handle == handle) && (allow_suspended || !IsUserSuspended(uData)))
808 head = &(channel->users);
811 if(uData && (uData != *head))
813 /* Shuffle the user to the head of whatever list he was in. */
815 uData->next->prev = uData->prev;
817 uData->prev->next = uData->next;
823 (**head).prev = uData;
830 /* Returns non-zero if user has at least the minimum access.
831 * exempt_owner is set when handling !set, so the owner can set things
834 int check_user_level(struct chanNode *channel, struct userNode *user, enum levelOption opt, int allow_override, int exempt_owner)
836 struct userData *uData;
837 struct chanData *cData = channel->channel_info;
838 unsigned short minimum = cData->lvlOpts[opt];
841 uData = _GetChannelUser(cData, user->handle_info, allow_override, 0);
844 if(minimum <= uData->access)
846 if((minimum > UL_OWNER) && (uData->access == UL_OWNER) && exempt_owner)
851 /* Scan for other users authenticated to the same handle
852 still in the channel. If so, keep them listed as present.
854 user is optional, if not null, it skips checking that userNode
855 (for the handle_part function) */
857 scan_user_presence(struct userData *uData, struct userNode *user)
861 if(IsSuspended(uData->channel)
862 || IsUserSuspended(uData)
863 || !(mn = find_handle_in_channel(uData->channel->channel, uData->handle, user)))
875 chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, const char *text, UNUSED_ARG(struct userNode *bot), UNUSED_ARG(unsigned int is_notice))
877 unsigned int eflags, argc;
879 static char *bad_ctcp_reason = "CTCPs to this channel are forbidden.";
881 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
882 if(!channel->channel_info
883 || IsSuspended(channel->channel_info)
885 || !ircncasecmp(text, "ACTION ", 7))
887 /* Figure out the minimum level needed to CTCP the channel */
888 if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
890 /* We need to enforce against them; do so. */
892 argv[0] = (char*)text;
893 argv[1] = user->nick;
895 if(GetUserMode(channel, user))
896 eflags |= ACTION_KICK;
897 switch(channel->channel_info->chOpts[chCTCPReaction]) {
898 default: case 'k': /* just do the kick */ break;
900 eflags |= ACTION_BAN;
903 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
904 argv[argc++] = (char*)chanserv_conf.ctcp_short_ban_duration;
907 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
908 argv[argc++] = (char*)chanserv_conf.ctcp_long_ban_duration;
911 argv[argc++] = bad_ctcp_reason;
912 eject_user(chanserv, channel, argc, argv, NULL, eflags);
916 chanserv_create_note_type(const char *name)
918 struct note_type *ntype = calloc(1, sizeof(*ntype) + strlen(name));
919 strcpy(ntype->name, name);
921 dict_insert(note_types, ntype->name, ntype);
926 free_vote_options(void *data)
928 struct vote_option *vOpt = data;
930 free(vOpt->option_str);
935 chanserv_deref_note_type(void *data)
937 struct note_type *ntype = data;
939 if(--ntype->refs > 0)
945 chanserv_flush_note_type(struct note_type *ntype)
947 struct chanData *cData;
948 for(cData = channelList; cData; cData = cData->next)
949 dict_remove(cData->notes, ntype->name);
953 chanserv_truncate_notes(struct note_type *ntype)
955 struct chanData *cData;
957 unsigned int size = sizeof(*note) + ntype->max_length;
959 for(cData = channelList; cData; cData = cData->next) {
960 note = dict_find(cData->notes, ntype->name, NULL);
963 if(strlen(note->note) <= ntype->max_length)
965 dict_remove2(cData->notes, ntype->name, 1);
966 note = realloc(note, size);
967 note->note[ntype->max_length] = 0;
968 dict_insert(cData->notes, ntype->name, note);
972 static int note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user);
975 chanserv_add_channel_note(struct chanData *channel, struct note_type *type, const char *setter, const char *text)
978 unsigned int len = strlen(text);
980 if(len > type->max_length) len = type->max_length;
981 note = calloc(1, sizeof(*note) + len);
983 strncpy(note->setter, setter, sizeof(note->setter)-1);
984 memcpy(note->note, text, len);
986 dict_insert(channel->notes, type->name, note);
992 chanserv_free_note(void *data)
994 struct note *note = data;
996 chanserv_deref_note_type(note->type);
997 assert(note->type->refs > 0); /* must use delnote to remove the type */
1001 static MODCMD_FUNC(cmd_createnote) {
1002 struct note_type *ntype;
1003 unsigned int arg = 1, existed = 0, max_length;
1005 if((ntype = dict_find(note_types, argv[1], NULL)))
1008 ntype = chanserv_create_note_type(argv[arg]);
1009 if(!irccasecmp(argv[++arg], "privileged"))
1012 ntype->set_access_type = NOTE_SET_PRIVILEGED;
1013 ntype->set_access.min_opserv = strtoul(argv[arg], NULL, 0);
1015 else if(!irccasecmp(argv[arg], "channel"))
1017 unsigned short ulvl = user_level_from_name(argv[++arg], UL_OWNER);
1020 reply("CSMSG_INVALID_ACCESS", argv[arg]);
1023 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
1024 ntype->set_access.min_ulevel = ulvl;
1026 else if(!irccasecmp(argv[arg], "setter"))
1028 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
1032 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
1036 if(!irccasecmp(argv[++arg], "privileged"))
1037 ntype->visible_type = NOTE_VIS_PRIVILEGED;
1038 else if(!irccasecmp(argv[arg], "channel_users"))
1039 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
1040 else if(!irccasecmp(argv[arg], "all"))
1041 ntype->visible_type = NOTE_VIS_ALL;
1043 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
1047 if((arg+1) >= argc) {
1048 reply("MSG_MISSING_PARAMS", argv[0]);
1051 max_length = strtoul(argv[++arg], NULL, 0);
1052 if(max_length < 20 || max_length > 450)
1054 reply("CSMSG_BAD_MAX_LENGTH", argv[arg]);
1057 if(existed && (max_length < ntype->max_length))
1059 ntype->max_length = max_length;
1060 chanserv_truncate_notes(ntype);
1062 ntype->max_length = max_length;
1065 reply("CSMSG_NOTE_MODIFIED", ntype->name);
1067 reply("CSMSG_NOTE_CREATED", ntype->name);
1072 dict_remove(note_types, ntype->name);
1076 static MODCMD_FUNC(cmd_removenote) {
1077 struct note_type *ntype;
1080 ntype = dict_find(note_types, argv[1], NULL);
1081 force = (argc > 2) && !irccasecmp(argv[2], "force");
1084 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
1091 reply("CSMSG_NOTE_TYPE_USED", ntype->name);
1094 chanserv_flush_note_type(ntype);
1096 dict_remove(note_types, argv[1]);
1097 reply("CSMSG_NOTE_DELETED", argv[1]);
1102 chanserv_expire_channel(void *data)
1104 struct chanData *channel = data;
1105 char reason[MAXLEN];
1106 sprintf(reason, "channel expired.");
1107 channel->expiry = 0;
1108 spamserv_cs_unregister(NULL, channel->channel, expire, NULL);
1109 unregister_channel(channel, reason);
1112 static MODCMD_FUNC(chan_opt_expire)
1114 struct chanData *cData = channel->channel_info;
1115 unsigned long value = cData->expiry;
1119 if((!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
1121 reply("MSG_SETTING_PRIVILEGED", argv[0]);
1124 unsigned long expiry,duration;
1126 /* The two directions can have different ACLs. */
1127 if(!strcmp(argv[1], "0"))
1129 else if((duration = ParseInterval(argv[1])))
1130 expiry = now + duration;
1133 reply("MSG_INVALID_DURATION", argv[1]);
1137 if (expiry != value)
1141 timeq_del(value, chanserv_expire_channel, cData, 0);
1144 cData->expiry = value;
1147 timeq_add(expiry, chanserv_expire_channel, cData);
1152 if(cData->expiry > now) {
1153 char expirestr[INTERVALLEN];
1154 reply("CSMSG_SET_EXPIRE", intervalString(expirestr, cData->expiry - now, user->handle_info));
1156 reply("CSMSG_SET_EXPIRE_OFF");
1161 mode_lock_violated(const struct mod_chanmode *orig, const struct mod_chanmode *change)
1165 if(orig->modes_set & change->modes_clear)
1167 if(orig->modes_clear & change->modes_set)
1169 if((orig->modes_set & MODE_KEY) && (change->modes_set & MODE_KEY)
1170 && strcmp(orig->new_key, change->new_key))
1172 if((orig->modes_set & MODE_LIMIT) && (change->modes_set & MODE_LIMIT)
1173 && (orig->new_limit != change->new_limit))
1178 static char max_length_text[MAXLEN+1][16];
1180 static struct helpfile_expansion
1181 chanserv_expand_variable(const char *variable)
1183 struct helpfile_expansion exp;
1185 if(!irccasecmp(variable, "notes"))
1188 exp.type = HF_TABLE;
1189 exp.value.table.length = 1;
1190 exp.value.table.width = 3;
1191 exp.value.table.flags = 0;
1192 exp.value.table.contents = calloc(dict_size(note_types)+1, sizeof(char**));
1193 exp.value.table.contents[0] = calloc(exp.value.table.width, sizeof(char*));
1194 exp.value.table.contents[0][0] = "Note Type";
1195 exp.value.table.contents[0][1] = "Visibility";
1196 exp.value.table.contents[0][2] = "Max Length";
1197 for(it=dict_first(note_types); it; it=iter_next(it))
1199 struct note_type *ntype = iter_data(it);
1202 if(!note_type_visible_to_user(NULL, ntype, message_dest)) continue;
1203 row = exp.value.table.length++;
1204 exp.value.table.contents[row] = calloc(exp.value.table.width, sizeof(char*));
1205 exp.value.table.contents[row][0] = ntype->name;
1206 exp.value.table.contents[row][1] = (ntype->visible_type == NOTE_VIS_ALL) ? "all" :
1207 (ntype->visible_type == NOTE_VIS_CHANNEL_USERS) ? "chan users" :
1209 if(!max_length_text[ntype->max_length][0])
1210 snprintf(max_length_text[ntype->max_length], sizeof(max_length_text[ntype->max_length]), "%u", ntype->max_length);
1211 exp.value.table.contents[row][2] = max_length_text[ntype->max_length];
1216 exp.type = HF_STRING;
1217 exp.value.str = NULL;
1221 static struct chanData*
1222 register_channel(struct chanNode *cNode, char *registrar)
1224 struct chanData *channel;
1225 enum levelOption lvlOpt;
1226 enum charOption chOpt;
1228 channel = calloc(1, sizeof(struct chanData));
1230 channel->notes = dict_new();
1231 dict_set_free_data(channel->notes, chanserv_free_note);
1233 channel->registrar = strdup(registrar);
1234 channel->registered = now;
1235 channel->visited = now;
1236 channel->limitAdjusted = now;
1237 channel->ownerTransfer = now;
1238 channel->flags = CHANNEL_DEFAULT_FLAGS;
1239 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
1240 channel->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
1241 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
1242 channel->chOpts[chOpt] = charOptions[chOpt].default_value;
1244 channel->prev = NULL;
1245 channel->next = channelList;
1248 channelList->prev = channel;
1249 channelList = channel;
1250 registered_channels++;
1252 channel->channel = cNode;
1254 cNode->channel_info = channel;
1256 channel->vote = NULL;
1261 static struct userData*
1262 add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned short access_level, unsigned long seen, const char *info)
1264 struct userData *ud;
1266 if(access_level > UL_OWNER)
1269 ud = calloc(1, sizeof(*ud));
1270 ud->channel = channel;
1271 ud->handle = handle;
1273 ud->access = access_level;
1274 ud->info = info ? strdup(info) : NULL;
1277 ud->next = channel->users;
1279 channel->users->prev = ud;
1280 channel->users = ud;
1282 channel->userCount++;
1286 ud->u_next = ud->handle->channels;
1288 ud->u_next->u_prev = ud;
1289 ud->handle->channels = ud;
1295 del_channel_user(struct userData *user, int do_gc)
1297 struct chanData *channel = user->channel;
1299 channel->userCount--;
1303 user->prev->next = user->next;
1305 channel->users = user->next;
1307 user->next->prev = user->prev;
1310 user->u_prev->u_next = user->u_next;
1312 user->handle->channels = user->u_next;
1314 user->u_next->u_prev = user->u_prev;
1318 if(do_gc && !channel->users && !IsProtected(channel)) {
1319 spamserv_cs_unregister(NULL, channel->channel, lost_all_users, NULL);
1320 unregister_channel(channel, "lost all users.");
1324 static void expire_ban(void *data);
1327 add_channel_ban(struct chanData *channel, const char *mask, char *owner, unsigned long set, unsigned long triggered, unsigned long expires, char *reason)
1330 unsigned int ii, l1, l2;
1335 bd = malloc(sizeof(struct banData));
1337 bd->channel = channel;
1339 bd->triggered = triggered;
1340 bd->expires = expires;
1342 for(ii = 0; ii < chanserv_conf.old_ban_names->used; ++ii)
1344 extern const char *hidden_host_suffix;
1345 const char *old_name = chanserv_conf.old_ban_names->list[ii];
1349 l2 = strlen(old_name);
1352 if(irccasecmp(mask + l1 - l2, old_name))
1354 new_mask = alloca(MAXLEN);
1355 sprintf(new_mask, "%.*s%s", (int)(l1-l2), mask, hidden_host_suffix);
1358 safestrncpy(bd->mask, mask, sizeof(bd->mask));
1360 safestrncpy(bd->owner, owner, sizeof(bd->owner));
1361 bd->reason = strdup(reason);
1364 timeq_add(expires, expire_ban, bd);
1367 bd->next = channel->bans;
1369 channel->bans->prev = bd;
1371 channel->banCount++;
1378 del_channel_ban(struct banData *ban)
1380 ban->channel->banCount--;
1384 ban->prev->next = ban->next;
1386 ban->channel->bans = ban->next;
1389 ban->next->prev = ban->prev;
1392 timeq_del(0, expire_ban, ban, TIMEQ_IGNORE_WHEN);
1401 expire_ban(void *data)
1403 struct banData *bd = data;
1404 if(!IsSuspended(bd->channel))
1406 struct banList bans;
1407 struct mod_chanmode change;
1409 bans = bd->channel->channel->banlist;
1410 mod_chanmode_init(&change);
1411 for(ii=0; ii<bans.used; ii++)
1413 if(!strcmp(bans.list[ii]->ban, bd->mask))
1416 change.args[0].mode = MODE_REMOVE|MODE_BAN;
1417 change.args[0].u.hostmask = bd->mask;
1418 mod_chanmode_announce(chanserv, bd->channel->channel, &change);
1424 del_channel_ban(bd);
1427 static void chanserv_expire_suspension(void *data);
1430 unregister_channel(struct chanData *channel, const char *reason)
1432 struct mod_chanmode change;
1433 char msgbuf[MAXLEN];
1435 /* After channel unregistration, the following must be cleaned
1437 - Channel information.
1440 - Channel suspension data.
1441 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1447 timeq_del(0, NULL, channel, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
1449 if(off_channel > 0 || chanserv_conf.revoke_mode_a) {
1450 mod_chanmode_init(&change);
1452 change.modes_clear |= MODE_REGISTERED;
1453 if(chanserv_conf.revoke_mode_a)
1454 change.modes_clear |= MODE_ACCESS;
1455 mod_chanmode_announce(chanserv, channel->channel, &change);
1458 while(channel->users)
1459 del_channel_user(channel->users, 0);
1461 while(channel->bans)
1462 del_channel_ban(channel->bans);
1464 free(channel->topic);
1465 free(channel->registrar);
1466 free(channel->greeting);
1467 free(channel->user_greeting);
1468 free(channel->topic_mask);
1471 channel->prev->next = channel->next;
1473 channelList = channel->next;
1476 channel->next->prev = channel->prev;
1478 if(channel->suspended)
1480 struct chanNode *cNode = channel->channel;
1481 struct suspended *suspended, *next_suspended;
1483 for(suspended = channel->suspended; suspended; suspended = next_suspended)
1485 next_suspended = suspended->previous;
1486 free(suspended->suspender);
1487 free(suspended->reason);
1488 if(suspended->expires)
1489 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
1494 cNode->channel_info = NULL;
1497 timeq_del(channel->expiry, chanserv_expire_channel, channel, 0);
1498 channel->channel->channel_info = NULL;
1500 dict_delete(channel->notes);
1501 sprintf(msgbuf, "%s %s", channel->channel->name, reason);
1502 if(!IsSuspended(channel))
1503 DelChannelUser(chanserv, channel->channel, msgbuf, 0);
1504 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, msgbuf);
1505 UnlockChannel(channel->channel);
1507 registered_channels--;
1511 expire_channels(void *data)
1513 struct chanData *channel, *next;
1514 struct userData *user;
1515 char delay[INTERVALLEN], reason[INTERVALLEN + 64];
1517 intervalString(delay, chanserv_conf.channel_expire_delay, NULL);
1518 sprintf(reason, "Channel registration automatically expired after %s of disuse.", delay);
1520 for(channel = channelList; channel; channel = next)
1522 next = channel->next;
1524 /* See if the channel can be expired. */
1525 if(((now - channel->visited) <= chanserv_conf.channel_expire_delay)
1526 || IsProtected(channel))
1529 /* Make sure there are no high-ranking users still in the channel. */
1530 for(user=channel->users; user; user=user->next)
1531 if(user->present && (user->access >= UL_PRESENT) && !HANDLE_FLAGGED(user->handle, BOT))
1536 /* Unregister the channel */
1537 log_module(CS_LOG, LOG_INFO, "(%s) Channel registration expired.", channel->channel->name);
1538 spamserv_cs_unregister(NULL, channel->channel, expire, NULL);
1539 unregister_channel(channel, "registration expired.");
1542 if(chanserv_conf.channel_expire_frequency && !data)
1543 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
1547 expire_dnrs(UNUSED_ARG(void *data))
1549 dict_iterator_t it, next;
1550 struct do_not_register *dnr;
1552 for(it = dict_first(handle_dnrs); it; it = next)
1554 dnr = iter_data(it);
1555 next = iter_next(it);
1556 if(dnr->expires && dnr->expires <= now)
1557 dict_remove(handle_dnrs, dnr->chan_name + 1);
1559 for(it = dict_first(plain_dnrs); it; it = next)
1561 dnr = iter_data(it);
1562 next = iter_next(it);
1563 if(dnr->expires && dnr->expires <= now)
1564 dict_remove(plain_dnrs, dnr->chan_name + 1);
1566 for(it = dict_first(mask_dnrs); it; it = next)
1568 dnr = iter_data(it);
1569 next = iter_next(it);
1570 if(dnr->expires && dnr->expires <= now)
1571 dict_remove(mask_dnrs, dnr->chan_name + 1);
1574 if(chanserv_conf.dnr_expire_frequency)
1575 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
1579 protect_user(const struct userNode *victim, const struct userNode *aggressor, struct chanData *channel)
1581 char protect = channel->chOpts[chProtect];
1582 struct userData *cs_victim, *cs_aggressor;
1584 /* Don't protect if no one is to be protected, someone is attacking
1585 himself, or if the aggressor is an IRC Operator. */
1586 if(protect == 'n' || victim == aggressor || IsOper(aggressor))
1589 /* Don't protect if the victim isn't authenticated (because they
1590 can't be a channel user), unless we are to protect non-users
1592 cs_victim = GetChannelAccess(channel, victim->handle_info);
1593 if(protect != 'a' && !cs_victim)
1596 /* Protect if the aggressor isn't a user because at this point,
1597 the aggressor can only be less than or equal to the victim. */
1598 cs_aggressor = GetChannelAccess(channel, aggressor->handle_info);
1602 /* If the aggressor was a user, then the victim can't be helped. */
1609 if(cs_victim->access > cs_aggressor->access)
1614 if(cs_victim->access >= cs_aggressor->access)
1623 validate_op(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1625 struct chanData *cData = channel->channel_info;
1626 struct userData *cs_victim;
1628 if((!(cs_victim = GetChannelUser(cData, victim->handle_info))
1629 || (cs_victim->access < cData->lvlOpts[lvlGiveOps]))
1630 && !check_user_level(channel, user, lvlEnfOps, 0, 0))
1632 send_message(user, chanserv, "CSMSG_OPBY_LOCKED");
1640 validate_deop(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1642 if(IsService(victim))
1644 send_message(user, chanserv, "MSG_SERVICE_IMMUNE", victim->nick);
1648 if(protect_user(victim, user, channel->channel_info))
1650 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
1657 static struct do_not_register *
1658 chanserv_add_dnr(const char *chan_name, const char *setter, unsigned long expires, const char *reason)
1660 struct do_not_register *dnr = calloc(1, sizeof(*dnr)+strlen(reason));
1661 safestrncpy(dnr->chan_name, chan_name, sizeof(dnr->chan_name));
1662 safestrncpy(dnr->setter, setter, sizeof(dnr->setter));
1663 strcpy(dnr->reason, reason);
1665 dnr->expires = expires;
1666 if(dnr->chan_name[0] == '*')
1667 dict_insert(handle_dnrs, dnr->chan_name+1, dnr);
1668 else if(strpbrk(dnr->chan_name, "*?"))
1669 dict_insert(mask_dnrs, dnr->chan_name, dnr);
1671 dict_insert(plain_dnrs, dnr->chan_name, dnr);
1675 static struct dnrList
1676 chanserv_find_dnrs(const char *chan_name, const char *handle, unsigned int max)
1678 struct dnrList list;
1679 dict_iterator_t it, next;
1680 struct do_not_register *dnr;
1682 dnrList_init(&list);
1684 if(handle && (dnr = dict_find(handle_dnrs, handle, NULL)))
1686 if(dnr->expires && dnr->expires <= now)
1687 dict_remove(handle_dnrs, handle);
1688 else if(list.used < max)
1689 dnrList_append(&list, dnr);
1692 if(chan_name && (dnr = dict_find(plain_dnrs, chan_name, NULL)))
1694 if(dnr->expires && dnr->expires <= now)
1695 dict_remove(plain_dnrs, chan_name);
1696 else if(list.used < max)
1697 dnrList_append(&list, dnr);
1702 for(it = dict_first(mask_dnrs); it && list.used < max; it = next)
1704 next = iter_next(it);
1705 if(!match_ircglob(chan_name, iter_key(it)))
1707 dnr = iter_data(it);
1708 if(dnr->expires && dnr->expires <= now)
1709 dict_remove(mask_dnrs, iter_key(it));
1711 dnrList_append(&list, dnr);
1718 static int dnr_print_func(struct do_not_register *dnr, void *extra)
1720 struct userNode *user;
1721 char buf1[INTERVALLEN];
1722 char buf2[INTERVALLEN];
1729 strftime(buf1, sizeof(buf1), "%d %b %Y", localtime(&feh));
1734 strftime(buf2, sizeof(buf2), "%d %b %Y", localtime(&feh));
1735 send_message(user, chanserv, "CSMSG_DNR_INFO_SET_EXPIRES", dnr->chan_name, buf1, dnr->setter, buf2, dnr->reason);
1739 send_message(user, chanserv, "CSMSG_DNR_INFO_SET", dnr->chan_name, buf1, dnr->setter, dnr->reason);
1742 send_message(user, chanserv, "CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1747 chanserv_show_dnrs(struct userNode *user, struct svccmd *cmd, const char *chan_name, const char *handle)
1749 struct dnrList list;
1752 list = chanserv_find_dnrs(chan_name, handle, UINT_MAX);
1753 for(ii = 0; (ii < list.used) && (ii < 10); ++ii)
1754 dnr_print_func(list.list[ii], user);
1756 reply("CSMSG_MORE_DNRS", list.used - ii);
1761 struct do_not_register *
1762 chanserv_is_dnr(const char *chan_name, struct handle_info *handle)
1764 struct dnrList list;
1765 struct do_not_register *dnr;
1767 list = chanserv_find_dnrs(chan_name, handle ? handle->handle : NULL, 1);
1768 dnr = list.used ? list.list[0] : NULL;
1773 static unsigned int send_dnrs(struct userNode *user, dict_t dict)
1775 struct do_not_register *dnr;
1776 dict_iterator_t it, next;
1777 unsigned int matches = 0;
1779 for(it = dict_first(dict); it; it = next)
1781 dnr = iter_data(it);
1782 next = iter_next(it);
1783 if(dnr->expires && dnr->expires <= now)
1785 dict_remove(dict, iter_key(it));
1788 dnr_print_func(dnr, user);
1795 static CHANSERV_FUNC(cmd_noregister)
1799 unsigned long expiry, duration;
1800 unsigned int matches;
1804 reply("CSMSG_DNR_SEARCH_RESULTS");
1805 matches = send_dnrs(user, handle_dnrs);
1806 matches += send_dnrs(user, plain_dnrs);
1807 matches += send_dnrs(user, mask_dnrs);
1809 reply("MSG_MATCH_COUNT", matches);
1811 reply("MSG_NO_MATCHES");
1817 if(!IsChannelName(target) && (*target != '*'))
1819 reply("CSMSG_NOT_DNR", target);
1827 reply("MSG_INVALID_DURATION", argv[2]);
1831 if(!strcmp(argv[2], "0"))
1833 else if((duration = ParseInterval(argv[2])))
1834 expiry = now + duration;
1837 reply("MSG_INVALID_DURATION", argv[2]);
1841 reason = unsplit_string(argv + 3, argc - 3, NULL);
1842 if((*target == '*') && !get_handle_info(target + 1))
1844 reply("MSG_HANDLE_UNKNOWN", target + 1);
1847 chanserv_add_dnr(target, user->handle_info->handle, expiry, reason);
1848 reply("CSMSG_NOREGISTER_CHANNEL", target);
1852 reply("CSMSG_DNR_SEARCH_RESULTS");
1854 matches = chanserv_show_dnrs(user, cmd, NULL, target + 1);
1856 matches = chanserv_show_dnrs(user, cmd, target, NULL);
1858 reply("MSG_NO_MATCHES");
1862 static CHANSERV_FUNC(cmd_allowregister)
1864 const char *chan_name = argv[1];
1866 if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
1867 || dict_remove(plain_dnrs, chan_name)
1868 || dict_remove(mask_dnrs, chan_name))
1870 reply("CSMSG_DNR_REMOVED", chan_name);
1873 reply("CSMSG_NO_SUCH_DNR", chan_name);
1878 struct userNode *source;
1882 unsigned long min_set, max_set;
1883 unsigned long min_expires, max_expires;
1888 dnr_search_matches(const struct do_not_register *dnr, const struct dnr_search *search)
1890 return !((dnr->set < search->min_set)
1891 || (dnr->set > search->max_set)
1892 || (dnr->expires < search->min_expires)
1893 || (search->max_expires
1894 && ((dnr->expires == 0)
1895 || (dnr->expires > search->max_expires)))
1896 || (search->chan_mask
1897 && !match_ircglob(dnr->chan_name, search->chan_mask))
1898 || (search->setter_mask
1899 && !match_ircglob(dnr->setter, search->setter_mask))
1900 || (search->reason_mask
1901 && !match_ircglob(dnr->reason, search->reason_mask)));
1904 static struct dnr_search *
1905 dnr_search_create(struct userNode *user, struct svccmd *cmd, unsigned int argc, char *argv[])
1907 struct dnr_search *discrim;
1910 discrim = calloc(1, sizeof(*discrim));
1911 discrim->source = user;
1912 discrim->chan_mask = NULL;
1913 discrim->setter_mask = NULL;
1914 discrim->reason_mask = NULL;
1915 discrim->max_set = INT_MAX;
1916 discrim->limit = 50;
1918 for(ii=0; ii<argc; ++ii)
1922 reply("MSG_MISSING_PARAMS", argv[ii]);
1925 else if(0 == irccasecmp(argv[ii], "channel"))
1927 discrim->chan_mask = argv[++ii];
1929 else if(0 == irccasecmp(argv[ii], "setter"))
1931 discrim->setter_mask = argv[++ii];
1933 else if(0 == irccasecmp(argv[ii], "reason"))
1935 discrim->reason_mask = argv[++ii];
1937 else if(0 == irccasecmp(argv[ii], "limit"))
1939 discrim->limit = strtoul(argv[++ii], NULL, 0);
1941 else if(0 == irccasecmp(argv[ii], "set"))
1943 const char *cmp = argv[++ii];
1946 discrim->min_set = now - ParseInterval(cmp + 2);
1948 discrim->min_set = now - (ParseInterval(cmp + 1) - 1);
1949 } else if(cmp[0] == '=') {
1950 discrim->min_set = discrim->max_set = now - ParseInterval(cmp + 1);
1951 } else if(cmp[0] == '>') {
1953 discrim->max_set = now - ParseInterval(cmp + 2);
1955 discrim->max_set = now - (ParseInterval(cmp + 1) - 1);
1957 discrim->max_set = now - (ParseInterval(cmp) - 1);
1960 else if(0 == irccasecmp(argv[ii], "expires"))
1962 const char *cmp = argv[++ii];
1965 discrim->max_expires = now + ParseInterval(cmp + 2);
1967 discrim->max_expires = now + (ParseInterval(cmp + 1) - 1);
1968 } else if(cmp[0] == '=') {
1969 discrim->min_expires = discrim->max_expires = now + ParseInterval(cmp + 1);
1970 } else if(cmp[0] == '>') {
1972 discrim->min_expires = now + ParseInterval(cmp + 2);
1974 discrim->min_expires = now + (ParseInterval(cmp + 1) - 1);
1976 discrim->min_expires = now + (ParseInterval(cmp) - 1);
1981 reply("MSG_INVALID_CRITERIA", argv[ii]);
1992 typedef int (*dnr_search_func)(struct do_not_register *match, void *extra);
1995 dnr_search(struct dnr_search *discrim, dnr_search_func dsf, void *data)
1997 struct do_not_register *dnr;
1998 dict_iterator_t next;
2003 /* Initialize local variables. */
2006 if(discrim->chan_mask)
2008 int shift = (discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*') ? 2 : 0;
2009 if('\0' == discrim->chan_mask[shift + strcspn(discrim->chan_mask+shift, "*?")])
2013 if(target_fixed && discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*')
2015 /* Check against account-based DNRs. */
2016 dnr = dict_find(handle_dnrs, discrim->chan_mask + 2, NULL);
2017 if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
2020 else if(target_fixed)
2022 /* Check against channel-based DNRs. */
2023 dnr = dict_find(plain_dnrs, discrim->chan_mask, NULL);
2024 if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
2029 /* Exhaustively search account DNRs. */
2030 for(it = dict_first(handle_dnrs); it; it = next)
2032 next = iter_next(it);
2033 dnr = iter_data(it);
2034 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
2038 /* Do the same for channel DNRs. */
2039 for(it = dict_first(plain_dnrs); it; it = next)
2041 next = iter_next(it);
2042 dnr = iter_data(it);
2043 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
2047 /* Do the same for wildcarded channel DNRs. */
2048 for(it = dict_first(mask_dnrs); it; it = next)
2050 next = iter_next(it);
2051 dnr = iter_data(it);
2052 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
2060 dnr_remove_func(struct do_not_register *match, void *extra)
2062 struct userNode *user;
2065 chan_name = alloca(strlen(match->chan_name) + 1);
2066 strcpy(chan_name, match->chan_name);
2068 if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
2069 || dict_remove(plain_dnrs, chan_name)
2070 || dict_remove(mask_dnrs, chan_name))
2072 send_message(user, chanserv, "CSMSG_DNR_REMOVED", chan_name);
2078 dnr_count_func(struct do_not_register *match, void *extra)
2080 return 0; (void)match; (void)extra;
2083 static MODCMD_FUNC(cmd_dnrsearch)
2085 struct dnr_search *discrim;
2086 dnr_search_func action;
2087 struct svccmd *subcmd;
2088 unsigned int matches;
2091 sprintf(buf, "dnrsearch %s", argv[1]);
2092 subcmd = dict_find(cmd->parent->commands, buf, NULL);
2095 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
2098 if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
2100 if(!irccasecmp(argv[1], "print"))
2101 action = dnr_print_func;
2102 else if(!irccasecmp(argv[1], "remove"))
2103 action = dnr_remove_func;
2104 else if(!irccasecmp(argv[1], "count"))
2105 action = dnr_count_func;
2108 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
2112 discrim = dnr_search_create(user, cmd, argc-2, argv+2);
2116 if(action == dnr_print_func)
2117 reply("CSMSG_DNR_SEARCH_RESULTS");
2118 matches = dnr_search(discrim, action, user);
2120 reply("MSG_MATCH_COUNT", matches);
2122 reply("MSG_NO_MATCHES");
2128 chanserv_get_owned_count(struct handle_info *hi)
2130 struct userData *cList;
2133 for(owned=0, cList=hi->channels; cList; cList=cList->u_next)
2134 if(cList->access == UL_OWNER)
2139 static CHANSERV_FUNC(cmd_register)
2141 struct handle_info *handle;
2142 struct chanData *cData;
2143 struct modeNode *mn;
2144 char reason[MAXLEN];
2146 unsigned int new_channel, force=0;
2147 struct do_not_register *dnr;
2151 if(channel->channel_info)
2153 reply("CSMSG_ALREADY_REGGED", channel->name);
2157 if(channel->bad_channel)
2159 reply("CSMSG_ILLEGAL_CHANNEL", channel->name);
2164 && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
2166 reply("CSMSG_MUST_BE_OPPED", channel->name);
2171 chan_name = channel->name;
2175 if((argc < 2) || !IsChannelName(argv[1]))
2177 reply("MSG_NOT_CHANNEL_NAME");
2181 if(opserv_bad_channel(argv[1]))
2183 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2188 chan_name = argv[1];
2191 if(argc >= (new_channel+2))
2193 if(!IsHelping(user))
2195 reply("CSMSG_PROXY_FORBIDDEN");
2199 if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
2201 force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
2202 dnr = chanserv_is_dnr(chan_name, handle);
2206 handle = user->handle_info;
2207 dnr = chanserv_is_dnr(chan_name, handle);
2211 if(!IsHelping(user))
2212 reply("CSMSG_DNR_CHANNEL", chan_name);
2214 chanserv_show_dnrs(user, cmd, chan_name, handle->handle);
2218 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2220 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
2225 channel = AddChannel(argv[1], now, NULL, NULL);
2227 cData = register_channel(channel, user->handle_info->handle);
2228 scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL), NULL);
2229 cData->modes = chanserv_conf.default_modes;
2231 cData->modes.modes_set |= MODE_REGISTERED;
2232 if (IsOffChannel(cData))
2234 mod_chanmode_announce(chanserv, channel, &cData->modes);
2238 struct mod_chanmode *change = mod_chanmode_dup(&cData->modes, 1);
2239 change->args[change->argc].mode = MODE_CHANOP;
2240 change->args[change->argc].u.member = AddChannelUser(chanserv, channel);
2242 mod_chanmode_announce(chanserv, channel, change);
2243 mod_chanmode_free(change);
2246 /* Initialize the channel's max user record. */
2247 cData->max = channel->members.used;
2248 cData->max_time = 0;
2250 if(handle != user->handle_info)
2251 reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
2253 reply("CSMSG_REG_SUCCESS", channel->name);
2255 sprintf(reason, "%s registered to %s by %s.", channel->name, handle->handle, user->handle_info->handle);
2256 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2261 make_confirmation_string(struct userData *uData)
2263 static char strbuf[16];
2268 for(src = uData->handle->handle; *src; )
2269 accum = accum * 31 + toupper(*src++);
2271 for(src = uData->channel->channel->name; *src; )
2272 accum = accum * 31 + toupper(*src++);
2273 sprintf(strbuf, "%08x", accum);
2277 static CHANSERV_FUNC(cmd_unregister)
2280 char reason[MAXLEN];
2281 struct chanData *cData;
2282 struct userData *uData;
2284 cData = channel->channel_info;
2287 reply("CSMSG_NOT_REGISTERED", channel->name);
2291 uData = GetChannelUser(cData, user->handle_info);
2292 if(!uData || (uData->access < UL_OWNER))
2294 reply("CSMSG_NO_ACCESS");
2298 if(IsProtected(cData) && !IsOper(user))
2300 reply("CSMSG_UNREG_NODELETE", channel->name);
2304 if(!IsHelping(user))
2306 const char *confirm_string;
2307 if(IsSuspended(cData))
2309 reply("CSMSG_CHAN_SUSPENDED", channel->name, cData->suspended->reason);
2312 confirm_string = make_confirmation_string(uData);
2313 if((argc < 2) || strcmp(argv[1], confirm_string))
2315 reply("CSMSG_CONFIRM_UNREG", confirm_string);
2320 sprintf(reason, "unregistered by %s.", user->handle_info->handle);
2321 name = strdup(channel->name);
2322 unregister_channel(cData, reason);
2323 spamserv_cs_unregister(user, channel, manually, "unregistered");
2324 reply("CSMSG_UNREG_SUCCESS", name);
2330 ss_cs_join_channel(struct chanNode *channel, int spamserv_join)
2332 extern struct userNode *spamserv;
2333 struct mod_chanmode *change;
2335 if(spamserv && spamserv_join && get_chanInfo(channel->name))
2337 change = mod_chanmode_alloc(2);
2339 change->args[0].mode = MODE_CHANOP;
2340 change->args[0].u.member = AddChannelUser(chanserv, channel);
2341 change->args[1].mode = MODE_CHANOP;
2342 change->args[1].u.member = AddChannelUser(spamserv, channel);
2346 change = mod_chanmode_alloc(1);
2348 change->args[0].mode = MODE_CHANOP;
2349 change->args[0].u.member = AddChannelUser(chanserv, channel);
2352 mod_chanmode_announce(chanserv, channel, change);
2353 mod_chanmode_free(change);
2356 static CHANSERV_FUNC(cmd_move)
2358 struct mod_chanmode change;
2359 struct chanNode *target;
2360 struct modeNode *mn;
2361 struct userData *uData;
2362 char reason[MAXLEN];
2363 struct do_not_register *dnr;
2364 int chanserv_join = 0, spamserv_join;
2368 if(IsProtected(channel->channel_info))
2370 reply("CSMSG_MOVE_NODELETE", channel->name);
2374 if(!IsChannelName(argv[1]))
2376 reply("MSG_NOT_CHANNEL_NAME");
2380 if(opserv_bad_channel(argv[1]))
2382 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2386 if(!IsHelping(user) || (argc < 3) || irccasecmp(argv[2], "force"))
2388 for(uData = channel->channel_info->users; uData; uData = uData->next)
2390 if((uData->access == UL_OWNER) && (dnr = chanserv_is_dnr(argv[1], uData->handle)))
2392 if(!IsHelping(user))
2393 reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]);
2395 chanserv_show_dnrs(user, cmd, argv[1], uData->handle->handle);
2401 mod_chanmode_init(&change);
2402 if(!(target = GetChannel(argv[1])))
2404 target = AddChannel(argv[1], now, NULL, NULL);
2405 if(!IsSuspended(channel->channel_info))
2408 else if(target->channel_info)
2410 reply("CSMSG_ALREADY_REGGED", target->name);
2413 else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
2414 && !IsHelping(user))
2416 reply("CSMSG_MUST_BE_OPPED", target->name);
2419 else if(!IsSuspended(channel->channel_info))
2424 /* Clear MODE_REGISTERED from old channel, add it to new. */
2426 change.modes_clear = MODE_REGISTERED;
2427 mod_chanmode_announce(chanserv, channel, &change);
2428 change.modes_clear = 0;
2429 change.modes_set = MODE_REGISTERED;
2430 mod_chanmode_announce(chanserv, target, &change);
2433 /* Move the channel_info to the target channel; it
2434 shouldn't be necessary to clear timeq callbacks
2435 for the old channel. */
2436 target->channel_info = channel->channel_info;
2437 target->channel_info->channel = target;
2438 channel->channel_info = NULL;
2440 /* Check whether users are present in the new channel. */
2441 for(uData = target->channel_info->users; uData; uData = uData->next)
2442 scan_user_presence(uData, NULL);
2444 spamserv_join = spamserv_cs_move_merge(user, channel, target, 1);
2447 ss_cs_join_channel(target, spamserv_join);
2449 sprintf(reason, "%s moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
2450 if(!IsSuspended(target->channel_info))
2452 char reason2[MAXLEN];
2453 sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
2454 DelChannelUser(chanserv, channel, reason2, 0);
2456 UnlockChannel(channel);
2457 LockChannel(target);
2458 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2459 reply("CSMSG_MOVE_SUCCESS", target->name);
2464 merge_users(struct chanData *source, struct chanData *target)
2466 struct userData *suData, *tuData, *next;
2472 /* Insert the source's users into the scratch area. */
2473 for(suData = source->users; suData; suData = suData->next)
2474 dict_insert(merge, suData->handle->handle, suData);
2476 /* Iterate through the target's users, looking for
2477 users common to both channels. The lower access is
2478 removed from either the scratch area or target user
2480 for(tuData = target->users; tuData; tuData = next)
2482 struct userData *choice;
2484 next = tuData->next;
2486 /* If a source user exists with the same handle as a target
2487 channel's user, resolve the conflict by removing one. */
2488 suData = dict_find(merge, tuData->handle->handle, NULL);
2492 /* Pick the data we want to keep. */
2493 /* If the access is the same, use the later seen time. */
2494 if(suData->access == tuData->access)
2495 choice = (suData->seen > tuData->seen) ? suData : tuData;
2496 else /* Otherwise, keep the higher access level. */
2497 choice = (suData->access > tuData->access) ? suData : tuData;
2498 /* Use the later seen time. */
2499 if(suData->seen < tuData->seen)
2500 suData->seen = tuData->seen;
2502 tuData->seen = suData->seen;
2504 /* Remove the user that wasn't picked. */
2505 if(choice == tuData)
2507 dict_remove(merge, suData->handle->handle);
2508 del_channel_user(suData, 0);
2511 del_channel_user(tuData, 0);
2514 /* Move the remaining users to the target channel. */
2515 for(it = dict_first(merge); it; it = iter_next(it))
2517 suData = iter_data(it);
2519 /* Insert the user into the target channel's linked list. */
2520 suData->prev = NULL;
2521 suData->next = target->users;
2522 suData->channel = target;
2525 target->users->prev = suData;
2526 target->users = suData;
2528 /* Update the user counts for the target channel; the
2529 source counts are left alone. */
2530 target->userCount++;
2532 /* Check whether the user is in the target channel. */
2533 scan_user_presence(suData, NULL);
2536 /* Possible to assert (source->users == NULL) here. */
2537 source->users = NULL;
2542 merge_bans(struct chanData *source, struct chanData *target)
2544 struct banData *sbData, *tbData, *sNext, *tNext, *tFront;
2546 /* Hold on to the original head of the target ban list
2547 to avoid comparing source bans with source bans. */
2548 tFront = target->bans;
2550 /* Perform a totally expensive O(n*m) merge, ick. */
2551 for(sbData = source->bans; sbData; sbData = sNext)
2553 /* Flag to track whether the ban's been moved
2554 to the destination yet. */
2557 /* Possible to assert (sbData->prev == NULL) here. */
2558 sNext = sbData->next;
2560 for(tbData = tFront; tbData; tbData = tNext)
2562 tNext = tbData->next;
2564 /* Perform two comparisons between each source
2565 and target ban, conflicts are resolved by
2566 keeping the broader ban and copying the later
2567 expiration and triggered time. */
2568 if(match_ircglobs(tbData->mask, sbData->mask))
2570 /* There is a broader ban in the target channel that
2571 overrides one in the source channel; remove the
2572 source ban and break. */
2573 if(sbData->expires > tbData->expires)
2574 tbData->expires = sbData->expires;
2575 if(sbData->triggered > tbData->triggered)
2576 tbData->triggered = sbData->triggered;
2577 del_channel_ban(sbData);
2580 else if(match_ircglobs(sbData->mask, tbData->mask))
2582 /* There is a broader ban in the source channel that
2583 overrides one in the target channel; remove the
2584 target ban, fall through and move the source over. */
2585 if(tbData->expires > sbData->expires)
2586 sbData->expires = tbData->expires;
2587 if(tbData->triggered > sbData->triggered)
2588 sbData->triggered = tbData->triggered;
2589 if(tbData == tFront)
2591 del_channel_ban(tbData);
2594 /* Source bans can override multiple target bans, so
2595 we allow a source to run through this loop multiple
2596 times, but we can only move it once. */
2601 /* Remove the source ban from the source ban list. */
2603 sbData->next->prev = sbData->prev;
2605 /* Modify the source ban's associated channel. */
2606 sbData->channel = target;
2608 /* Insert the ban into the target channel's linked list. */
2609 sbData->prev = NULL;
2610 sbData->next = target->bans;
2613 target->bans->prev = sbData;
2614 target->bans = sbData;
2616 /* Update the user counts for the target channel. */
2621 /* Possible to assert (source->bans == NULL) here. */
2622 source->bans = NULL;
2626 merge_data(struct chanData *source, struct chanData *target)
2628 /* Use more recent visited and owner-transfer time; use older
2629 * registered time. Bitwise or may_opchan. Use higher max.
2630 * Do not touch last_refresh, ban count or user counts.
2632 if(source->visited > target->visited)
2633 target->visited = source->visited;
2634 if(source->registered < target->registered)
2635 target->registered = source->registered;
2636 if(source->ownerTransfer > target->ownerTransfer)
2637 target->ownerTransfer = source->ownerTransfer;
2638 if(source->may_opchan)
2639 target->may_opchan = 1;
2640 if(source->max > target->max) {
2641 target->max = source->max;
2642 target->max_time = source->max_time;
2647 merge_channel(struct chanData *source, struct chanData *target)
2649 merge_users(source, target);
2650 merge_bans(source, target);
2651 merge_data(source, target);
2654 static CHANSERV_FUNC(cmd_merge)
2656 struct userData *target_user;
2657 struct chanNode *target;
2658 char reason[MAXLEN];
2662 /* Make sure the target channel exists and is registered to the user
2663 performing the command. */
2664 if(!(target = GetChannel(argv[1])))
2666 reply("MSG_INVALID_CHANNEL");
2670 if(!target->channel_info)
2672 reply("CSMSG_NOT_REGISTERED", target->name);
2676 if(IsProtected(channel->channel_info))
2678 reply("CSMSG_MERGE_NODELETE");
2682 if(IsSuspended(target->channel_info))
2684 reply("CSMSG_MERGE_SUSPENDED");
2688 if(channel == target)
2690 reply("CSMSG_MERGE_SELF");
2694 target_user = GetChannelUser(target->channel_info, user->handle_info);
2695 if(!target_user || (target_user->access < UL_OWNER))
2697 reply("CSMSG_MERGE_NOT_OWNER");
2701 /* Merge the channel structures and associated data. */
2702 merge_channel(channel->channel_info, target->channel_info);
2703 spamserv_cs_move_merge(user, channel, target, 0);
2704 sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
2705 unregister_channel(channel->channel_info, reason);
2706 reply("CSMSG_MERGE_SUCCESS", target->name);
2710 static CHANSERV_FUNC(cmd_opchan)
2712 struct mod_chanmode change;
2713 if(!IsHelping(user) && !channel->channel_info->may_opchan)
2715 reply("CSMSG_ALREADY_OPCHANNED", channel->name);
2718 channel->channel_info->may_opchan = 0;
2719 mod_chanmode_init(&change);
2721 change.args[0].mode = MODE_CHANOP;
2722 change.args[0].u.member = GetUserMode(channel, chanserv);
2723 if(!change.args[0].u.member)
2725 reply("CSMSG_OUT_OF_CHANNEL", channel->name);
2728 mod_chanmode_announce(chanserv, channel, &change);
2729 reply("CSMSG_OPCHAN_DONE", channel->name);
2733 static CHANSERV_FUNC(cmd_adduser)
2735 struct userData *actee;
2736 struct userData *actor, *real_actor;
2737 struct handle_info *handle;
2738 unsigned short access_level, override = 0;
2742 if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
2744 reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
2748 access_level = user_level_from_name(argv[2], UL_OWNER);
2751 reply("CSMSG_INVALID_ACCESS", argv[2]);
2755 actor = GetChannelUser(channel->channel_info, user->handle_info);
2756 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2758 if(actor->access <= access_level)
2760 reply("CSMSG_NO_BUMP_ACCESS");
2764 /* Trying to add someone with equal/more access? */
2765 if (!real_actor || real_actor->access <= access_level)
2766 override = CMD_LOG_OVERRIDE;
2768 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2771 if((actee = GetTrueChannelAccess(channel->channel_info, handle)))
2773 reply("CSMSG_USER_EXISTS", handle->handle, channel->name, actee->access);
2777 actee = add_channel_user(channel->channel_info, handle, access_level, 0, NULL);
2778 scan_user_presence(actee, NULL);
2779 reply("CSMSG_ADDED_USER", handle->handle, channel->name, access_level);
2780 return 1 | override;
2783 static CHANSERV_FUNC(cmd_clvl)
2785 struct handle_info *handle;
2786 struct userData *victim;
2787 struct userData *actor, *real_actor;
2788 unsigned short new_access, override = 0;
2789 int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
2793 actor = GetChannelUser(channel->channel_info, user->handle_info);
2794 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2796 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2799 if(handle == user->handle_info && !privileged)
2801 reply("CSMSG_NO_SELF_CLVL");
2805 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2807 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2811 if(actor->access <= victim->access && !privileged)
2813 reply("MSG_USER_OUTRANKED", handle->handle);
2817 new_access = user_level_from_name(argv[2], UL_OWNER);
2821 reply("CSMSG_INVALID_ACCESS", argv[2]);
2825 if(new_access >= actor->access && !privileged)
2827 reply("CSMSG_NO_BUMP_ACCESS");
2831 /* Trying to clvl a equal/higher user? */
2832 if(!real_actor || (real_actor->access <= victim->access && handle != user->handle_info))
2833 override = CMD_LOG_OVERRIDE;
2834 /* Trying to clvl someone to equal/higher access? */
2835 if(!real_actor || new_access >= real_actor->access)
2836 override = CMD_LOG_OVERRIDE;
2837 /* Helpers clvling themselves get caught by the "clvl someone to equal/higher access" check.
2838 * If they lower their own access it's not a big problem.
2841 victim->access = new_access;
2842 reply("CSMSG_CHANGED_ACCESS", handle->handle, new_access, channel->name);
2843 return 1 | override;
2846 static CHANSERV_FUNC(cmd_deluser)
2848 struct handle_info *handle;
2849 struct userData *victim;
2850 struct userData *actor, *real_actor;
2851 unsigned short access_level, override = 0;
2856 actor = GetChannelUser(channel->channel_info, user->handle_info);
2857 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2859 if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
2862 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2864 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2870 access_level = user_level_from_name(argv[1], UL_OWNER);
2873 reply("CSMSG_INVALID_ACCESS", argv[1]);
2876 if(access_level != victim->access)
2878 reply("CSMSG_INCORRECT_ACCESS", handle->handle, victim->access, argv[1]);
2884 access_level = victim->access;
2887 if((actor->access <= victim->access) && !IsHelping(user))
2889 reply("MSG_USER_OUTRANKED", victim->handle->handle);
2893 /* If people delete themselves it is an override, but they
2894 * could've used deleteme so we don't log it as an override
2896 if(!real_actor || (real_actor->access <= victim->access && real_actor != victim))
2897 override = CMD_LOG_OVERRIDE;
2899 chan_name = strdup(channel->name);
2900 del_channel_user(victim, 1);
2901 reply("CSMSG_DELETED_USER", handle->handle, access_level, chan_name);
2903 return 1 | override;
2907 cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
2909 struct userData *actor, *real_actor, *uData, *next;
2910 unsigned int override = 0;
2912 actor = GetChannelUser(channel->channel_info, user->handle_info);
2913 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2915 if(min_access > max_access)
2917 reply("CSMSG_BAD_RANGE", min_access, max_access);
2921 if(actor->access <= max_access)
2923 reply("CSMSG_NO_ACCESS");
2927 if(!real_actor || real_actor->access <= max_access)
2928 override = CMD_LOG_OVERRIDE;
2930 for(uData = channel->channel_info->users; uData; uData = next)
2934 if((uData->access >= min_access)
2935 && (uData->access <= max_access)
2936 && match_ircglob(uData->handle->handle, mask))
2937 del_channel_user(uData, 1);
2940 reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
2941 return 1 | override;
2944 static CHANSERV_FUNC(cmd_mdelowner)
2946 return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
2949 static CHANSERV_FUNC(cmd_mdelcoowner)
2951 return cmd_mdel_user(user, channel, UL_COOWNER, UL_COOWNER, argv[1], cmd);
2954 static CHANSERV_FUNC(cmd_mdelmaster)
2956 return cmd_mdel_user(user, channel, UL_MASTER, UL_MASTER, argv[1], cmd);
2959 static CHANSERV_FUNC(cmd_mdelop)
2961 return cmd_mdel_user(user, channel, UL_OP, UL_OP, argv[1], cmd);
2964 static CHANSERV_FUNC(cmd_mdelpeon)
2966 return cmd_mdel_user(user, channel, UL_PEON, UL_PEON, argv[1], cmd);
2970 cmd_trim_bans(struct userNode *user, struct chanNode *channel, unsigned long duration)
2972 struct banData *bData, *next;
2973 char interval[INTERVALLEN];
2975 unsigned long limit;
2978 limit = now - duration;
2979 for(bData = channel->channel_info->bans; bData; bData = next)
2983 if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
2986 del_channel_ban(bData);
2990 intervalString(interval, duration, user->handle_info);
2991 send_message(user, chanserv, "CSMSG_TRIMMED_BANS", count, channel->name, interval);
2996 cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration, int vacation)
2998 struct userData *actor, *uData, *next;
2999 char interval[INTERVALLEN];
3001 unsigned long limit;
3003 actor = GetChannelUser(channel->channel_info, user->handle_info);
3004 if(min_access > max_access)
3006 send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
3010 if(!actor || actor->access <= max_access)
3012 send_message(user, chanserv, "CSMSG_NO_ACCESS");
3017 limit = now - duration;
3018 for(uData = channel->channel_info->users; uData; uData = next)
3022 if((uData->seen > limit)
3024 || (HANDLE_FLAGGED(uData->handle, FROZEN) && !vacation))
3027 if(((uData->access >= min_access) && (uData->access <= max_access))
3028 || (!max_access && (uData->access < actor->access)))
3030 del_channel_user(uData, 1);
3038 max_access = (actor->access > UL_OWNER) ? UL_OWNER : (actor->access - 1);
3040 send_message(user, chanserv, "CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration, user->handle_info));
3044 static CHANSERV_FUNC(cmd_trim)
3046 unsigned long duration;
3047 unsigned short min_level, max_level;
3052 vacation = argc > 3 && !strcmp(argv[3], "vacation");
3053 duration = ParseInterval(argv[2]);
3056 reply("CSMSG_CANNOT_TRIM");
3060 if(!irccasecmp(argv[1], "bans"))
3062 cmd_trim_bans(user, channel, duration);
3065 else if(!irccasecmp(argv[1], "users"))
3067 cmd_trim_users(user, channel, 0, 0, duration, vacation);
3070 else if(parse_level_range(&min_level, &max_level, argv[1]))
3072 cmd_trim_users(user, channel, min_level, max_level, duration, vacation);
3075 else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
3077 cmd_trim_users(user, channel, min_level, min_level, duration, vacation);
3082 reply("CSMSG_INVALID_TRIM", argv[1]);
3087 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
3088 to the user. cmd_all takes advantage of this. */
3089 static CHANSERV_FUNC(cmd_up)
3091 struct mod_chanmode change;
3092 struct userData *uData;
3095 mod_chanmode_init(&change);
3097 change.args[0].u.member = GetUserMode(channel, user);
3098 if(!change.args[0].u.member)
3101 reply("MSG_CHANNEL_ABSENT", channel->name);
3105 uData = GetChannelAccess(channel->channel_info, user->handle_info);
3109 reply("CSMSG_GODMODE_UP", argv[0]);
3112 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveOps])
3114 change.args[0].mode = MODE_CHANOP;
3115 errmsg = "CSMSG_ALREADY_OPPED";
3117 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveVoice])
3119 change.args[0].mode = MODE_VOICE;
3120 errmsg = "CSMSG_ALREADY_VOICED";
3125 reply("CSMSG_NO_ACCESS");
3128 change.args[0].mode &= ~change.args[0].u.member->modes;
3129 if(!change.args[0].mode)
3132 reply(errmsg, channel->name);
3135 modcmd_chanmode_announce(&change);
3139 static CHANSERV_FUNC(cmd_down)
3141 struct mod_chanmode change;
3143 mod_chanmode_init(&change);
3145 change.args[0].u.member = GetUserMode(channel, user);
3146 if(!change.args[0].u.member)
3149 reply("MSG_CHANNEL_ABSENT", channel->name);
3153 if(!change.args[0].u.member->modes)
3156 reply("CSMSG_ALREADY_DOWN", channel->name);
3160 change.args[0].mode = MODE_REMOVE | change.args[0].u.member->modes;
3161 modcmd_chanmode_announce(&change);
3165 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)
3167 struct userData *cList;
3169 for(cList = user->handle_info->channels; cList; cList = cList->u_next)
3171 if(IsSuspended(cList->channel)
3172 || IsUserSuspended(cList)
3173 || !GetUserMode(cList->channel->channel, user))
3176 mcmd(user, cList->channel->channel, 0, NULL, cmd);
3182 static CHANSERV_FUNC(cmd_upall)
3184 return cmd_all(CSFUNC_ARGS, cmd_up);
3187 static CHANSERV_FUNC(cmd_downall)
3189 return cmd_all(CSFUNC_ARGS, cmd_down);
3192 typedef int validate_func_t(struct userNode *user, struct chanNode *channel, struct userNode *victim);
3193 typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
3196 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)
3198 unsigned int ii, valid;
3199 struct userNode *victim;
3200 struct mod_chanmode *change;
3202 change = mod_chanmode_alloc(argc - 1);
3204 for(ii=valid=0; ++ii < argc; )
3206 if(!(victim = GetUserH(argv[ii])))
3208 change->args[valid].mode = mode;
3209 change->args[valid].u.member = GetUserMode(channel, victim);
3210 if(!change->args[valid].u.member)
3212 if(validate && !validate(user, channel, victim))
3217 change->argc = valid;
3218 if(valid < (argc-1))
3219 reply("CSMSG_PROCESS_FAILED");
3222 modcmd_chanmode_announce(change);
3223 reply(action, channel->name);
3225 mod_chanmode_free(change);
3229 static CHANSERV_FUNC(cmd_op)
3231 return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
3234 static CHANSERV_FUNC(cmd_deop)
3236 return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
3239 static CHANSERV_FUNC(cmd_voice)
3241 return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
3244 static CHANSERV_FUNC(cmd_devoice)
3246 return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
3249 static CHANSERV_FUNC(cmd_opme)
3251 struct mod_chanmode change;
3254 mod_chanmode_init(&change);
3256 change.args[0].u.member = GetUserMode(channel, user);
3257 if(!change.args[0].u.member)
3260 reply("MSG_CHANNEL_ABSENT", channel->name);
3264 struct devnull_class *devnull;
3265 if(user->handle_info->devnull && (devnull = devnull_get(user->handle_info->devnull)) && (devnull->modes & DEVNULL_MODE_OPME))
3267 change.args[0].mode = MODE_CHANOP;
3268 errmsg = "CSMSG_ALREADY_OPPED";
3273 reply("CSMSG_NO_ACCESS");
3276 change.args[0].mode &= ~change.args[0].u.member->modes;
3277 if(!change.args[0].mode)
3280 reply(errmsg, channel->name);
3283 modcmd_chanmode_announce(&change);
3288 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, unsigned int *victimCount, struct modeNode **victims)
3294 for(ii=0; ii<channel->members.used; ii++)
3296 struct modeNode *mn = channel->members.list[ii];
3298 if(IsService(mn->user))
3301 if(!user_matches_glob(mn->user, ban, MATCH_USENICK | MATCH_VISIBLE))
3304 if(protect_user(mn->user, user, channel->channel_info))
3308 victims[(*victimCount)++] = mn;
3314 eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3316 struct userNode *victim;
3317 struct modeNode **victims;
3318 unsigned int offset, n, victimCount, duration = 0;
3319 char *reason = "Bye.", *ban, *name;
3320 char interval[INTERVALLEN];
3322 offset = (action & ACTION_ADD_TIMED_BAN) ? 3 : 2;
3323 REQUIRE_PARAMS(offset);
3324 if(argc > offset && IsNetServ(user))
3326 if(*argv[offset] == '$') {
3327 struct userNode *hib;
3328 const char *accountnameb = argv[offset] + 1;
3329 if(!(hib = GetUserH(accountnameb)))
3331 reply("MSG_HANDLE_UNKNOWN", accountnameb);
3340 reason = unsplit_string(argv + offset, argc - offset, NULL);
3341 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
3343 /* Truncate the reason to a length of TOPICLEN, as
3344 the ircd does; however, leave room for an ellipsis
3345 and the kicker's nick. */
3346 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
3350 if((victim = GetUserH(argv[1])))
3352 victims = alloca(sizeof(victims[0]));
3353 victims[0] = GetUserMode(channel, victim);
3354 /* XXX: The comparison with ACTION_KICK is just because all
3355 * other actions can work on users outside the channel, and we
3356 * want to allow those (e.g. unbans) in that case. If we add
3357 * some other ejection action for in-channel users, change
3359 victimCount = victims[0] ? 1 : 0;
3361 if(IsService(victim))
3363 reply("MSG_SERVICE_IMMUNE", victim->nick);
3367 if((action == ACTION_KICK) && !victimCount)
3369 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
3373 if(protect_user(victim, user, channel->channel_info))
3375 reply("CSMSG_USER_PROTECTED", victim->nick);
3379 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
3380 name = victim->nick;
3382 else if(!is_ircmask(argv[1]) && (*argv[1] == '*'))
3384 struct handle_info *hi;
3385 extern const char *titlehost_suffix;
3386 char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
3387 const char *accountname = argv[1] + 1;
3389 if(!(hi = get_handle_info(accountname)))
3391 reply("MSG_HANDLE_UNKNOWN", accountname);
3395 snprintf(banmask, sizeof(banmask), "*!*@%s.*.%s", hi->handle, titlehost_suffix);
3396 victims = alloca(sizeof(victims[0]) * channel->members.used);
3398 if(bad_channel_ban(channel, user, banmask, &victimCount, victims))
3400 reply("CSMSG_MASK_PROTECTED", banmask);
3404 if((action == ACTION_KICK) && (victimCount == 0))
3406 reply("CSMSG_NO_MATCHING_USERS", channel->name, banmask);
3410 name = ban = strdup(banmask);
3414 if(!is_ircmask(argv[1]))
3416 reply("MSG_NICK_UNKNOWN", argv[1]);
3420 victims = alloca(sizeof(victims[0]) * channel->members.used);
3422 if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
3424 reply("CSMSG_MASK_PROTECTED", argv[1]);
3428 if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
3430 reply("CSMSG_LAME_MASK", argv[1]);
3434 if((action == ACTION_KICK) && (victimCount == 0))
3436 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
3440 name = ban = strdup(argv[1]);
3443 /* Truncate the ban in place if necessary; we must ensure
3444 that 'ban' is a valid ban mask before sanitizing it. */
3445 sanitize_ircmask(ban);
3447 if(action & ACTION_ADD_BAN)
3449 struct banData *bData, *next;
3451 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans)
3453 reply("CSMSG_MAXIMUM_BANS", chanserv_conf.max_chan_bans);
3458 if(action & ACTION_ADD_TIMED_BAN)
3460 duration = ParseInterval(argv[2]);
3464 reply("CSMSG_DURATION_TOO_LOW");
3468 else if(duration > (86400 * 365 * 2))
3470 reply("CSMSG_DURATION_TOO_HIGH");
3476 for(bData = channel->channel_info->bans; bData; bData = next)
3478 if(match_ircglobs(bData->mask, ban))
3480 int exact = !irccasecmp(bData->mask, ban);
3482 /* The ban is redundant; there is already a ban
3483 with the same effect in place. */
3487 free(bData->reason);
3488 bData->reason = strdup(reason);
3489 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
3491 reply("CSMSG_REASON_CHANGE", ban);
3495 if(exact && bData->expires)
3499 /* If the ban matches an existing one exactly,
3500 extend the expiration time if the provided
3501 duration is longer. */
3502 if(duration && (now + duration > bData->expires))
3504 bData->expires = now + duration;
3515 /* Delete the expiration timeq entry and
3516 requeue if necessary. */
3517 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
3520 timeq_add(bData->expires, expire_ban, bData);
3524 /* automated kickban */
3527 reply("CSMSG_BAN_EXTENDED", ban, intervalString(interval, duration, user->handle_info));
3529 reply("CSMSG_BAN_ADDED", name, channel->name);
3535 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3542 if(match_ircglobs(ban, bData->mask))
3544 /* The ban we are adding makes previously existing
3545 bans redundant; silently remove them. */
3546 del_channel_ban(bData);
3550 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);
3552 name = ban = strdup(bData->mask);
3556 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
3558 extern const char *hidden_host_suffix;
3559 const char *old_name = chanserv_conf.old_ban_names->list[n];
3561 unsigned int l1, l2;
3564 l2 = strlen(old_name);
3567 if(irccasecmp(ban + l1 - l2, old_name))
3569 new_mask = malloc(MAXLEN);
3570 sprintf(new_mask, "%.*s%s", (int)(l1-l2), ban, hidden_host_suffix);
3572 name = ban = new_mask;
3577 if(action & ACTION_BAN)
3579 unsigned int exists;
3580 struct mod_chanmode *change;
3582 if(channel->banlist.used >= MAXBANS)
3585 reply("CSMSG_BANLIST_FULL", channel->name);
3590 exists = ChannelBanExists(channel, ban);
3591 change = mod_chanmode_alloc(victimCount + 1);
3592 for(n = 0; n < victimCount; ++n)
3594 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_VOICE;
3595 change->args[n].u.member = victims[n];
3599 change->args[n].mode = MODE_BAN;
3600 change->args[n++].u.hostmask = ban;
3604 modcmd_chanmode_announce(change);
3606 mod_chanmode_announce(chanserv, channel, change);
3607 mod_chanmode_free(change);
3609 if(exists && (action == ACTION_BAN))
3612 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3618 if(action & ACTION_KICK)
3620 char kick_reason[MAXLEN];
3621 sprintf(kick_reason, "(%s) %s", user->nick, reason);
3623 for(n = 0; n < victimCount; n++)
3624 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
3629 /* No response, since it was automated. */
3631 else if(action & ACTION_ADD_BAN)
3634 reply("CSMSG_TIMED_BAN_ADDED", name, channel->name, intervalString(interval, duration, user->handle_info));
3636 reply("CSMSG_BAN_ADDED", name, channel->name);
3638 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
3639 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
3640 else if(action & ACTION_BAN)
3641 reply("CSMSG_BAN_DONE", name, channel->name);
3642 else if(action & ACTION_KICK && victimCount)
3643 reply("CSMSG_KICK_DONE", name, channel->name);
3649 static CHANSERV_FUNC(cmd_kickban)
3651 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
3654 static CHANSERV_FUNC(cmd_kick)
3656 return eject_user(CSFUNC_ARGS, ACTION_KICK);
3659 static CHANSERV_FUNC(cmd_ban)
3661 return eject_user(CSFUNC_ARGS, ACTION_BAN);
3664 static CHANSERV_FUNC(cmd_addban)
3666 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN);
3669 static CHANSERV_FUNC(cmd_addtimedban)
3671 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN);
3674 struct mod_chanmode *
3675 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
3677 struct mod_chanmode *change;
3678 unsigned char *match;
3679 unsigned int ii, count;
3681 match = alloca(bans->used);
3684 for(ii = count = 0; ii < bans->used; ++ii)
3686 match[ii] = user_matches_glob(actee, bans->list[ii]->ban,
3687 MATCH_USENICK | MATCH_VISIBLE);
3694 for(ii = count = 0; ii < bans->used; ++ii)
3696 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
3703 change = mod_chanmode_alloc(count);
3704 for(ii = count = 0; ii < bans->used; ++ii)
3708 change->args[count].mode = MODE_REMOVE | MODE_BAN;
3709 change->args[count++].u.hostmask = strdup(bans->list[ii]->ban);
3711 assert(count == change->argc);
3716 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3718 struct userNode *actee;
3724 /* may want to allow a comma delimited list of users... */
3725 if(!(actee = GetUserH(argv[1])))
3727 if(!is_ircmask(argv[1]) && *argv[1] == '*')
3729 char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
3730 const char *accountname = argv[1] + 1;
3732 snprintf(banmask, sizeof(banmask), "*!*@%s.*", accountname);
3733 mask = strdup(banmask);
3735 else if(!is_ircmask(argv[1]))
3737 reply("MSG_NICK_UNKNOWN", argv[1]);
3742 mask = strdup(argv[1]);
3746 /* We don't sanitize the mask here because ircu
3748 if(action & ACTION_UNBAN)
3750 struct mod_chanmode *change;
3751 change = find_matching_bans(&channel->banlist, actee, mask);
3756 modcmd_chanmode_announce(change);
3757 for(ii = 0; ii < change->argc; ++ii)
3758 free((char*)change->args[ii].u.hostmask);
3759 mod_chanmode_free(change);
3764 if(action & ACTION_DEL_BAN)
3766 struct banData *ban, *next;
3768 ban = channel->channel_info->bans;
3772 for( ; ban && !user_matches_glob(actee, ban->mask,
3773 MATCH_USENICK | MATCH_VISIBLE);
3776 for( ; ban && !match_ircglobs(mask, ban->mask);
3781 del_channel_ban(ban);
3788 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3790 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3796 static CHANSERV_FUNC(cmd_unban)
3798 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3801 static CHANSERV_FUNC(cmd_delban)
3803 /* it doesn't necessarily have to remove the channel ban - may want
3804 to make that an option. */
3805 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_BAN);
3808 static CHANSERV_FUNC(cmd_unbanme)
3810 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3811 long flags = ACTION_UNBAN;
3813 /* remove permanent bans if the user has the proper access. */
3814 if(uData->access >= UL_MASTER)
3815 flags |= ACTION_DEL_BAN;
3817 argv[1] = user->nick;
3818 return unban_user(user, channel, 2, argv, cmd, flags);
3821 static CHANSERV_FUNC(cmd_unbanall)
3823 struct mod_chanmode *change;
3826 if(!channel->banlist.used)
3828 reply("CSMSG_NO_BANS", channel->name);
3832 change = mod_chanmode_alloc(channel->banlist.used);
3833 for(ii=0; ii<channel->banlist.used; ii++)
3835 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3836 change->args[ii].u.hostmask = strdup(channel->banlist.list[ii]->ban);
3838 modcmd_chanmode_announce(change);
3839 for(ii = 0; ii < change->argc; ++ii)
3840 free((char*)change->args[ii].u.hostmask);
3841 mod_chanmode_free(change);
3842 reply("CSMSG_BANS_REMOVED", channel->name);
3846 static CHANSERV_FUNC(cmd_open)
3848 struct mod_chanmode *change;
3851 change = find_matching_bans(&channel->banlist, user, NULL);
3853 change = mod_chanmode_alloc(0);
3854 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3855 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3856 && channel->channel_info->modes.modes_set)
3857 change->modes_clear &= ~channel->channel_info->modes.modes_set;
3858 modcmd_chanmode_announce(change);
3859 reply("CSMSG_CHANNEL_OPENED", channel->name);
3860 for(ii = 0; ii < change->argc; ++ii)
3861 free((char*)change->args[ii].u.hostmask);
3862 mod_chanmode_free(change);
3866 static CHANSERV_FUNC(cmd_myaccess)
3868 static struct string_buffer sbuf;
3869 struct handle_info *target_handle;
3870 struct userData *uData;
3875 target_handle = user->handle_info;
3876 else if(!IsStaff(user))
3878 reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
3881 else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3884 if(!oper_outranks(user, target_handle))
3887 if(!target_handle->channels)
3889 reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
3893 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3894 for(uData = target_handle->channels; uData; uData = uData->u_next)
3896 struct chanData *cData = uData->channel;
3898 unsigned int base_len;
3900 if(uData->access > UL_OWNER)
3902 if(uData->access == UL_OWNER)
3905 if(IsProtected(cData)
3906 && (target_handle != user->handle_info)
3907 && !GetTrueChannelAccess(cData, user->handle_info)
3908 && !IsNetworkHelper(user))
3911 string_buffer_append_printf(&sbuf, "[%s (%d,", cData->channel->name, uData->access);
3912 base_len = sbuf.used;
3913 if(IsUserSuspended(uData))
3914 string_buffer_append(&sbuf, 's');
3915 if(IsUserAutoOp(uData))
3917 if(uData->access >= cData->lvlOpts[lvlGiveOps])
3918 string_buffer_append(&sbuf, 'o');
3919 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
3920 string_buffer_append(&sbuf, 'v');
3922 if(IsUserAutoInvite(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe]))
3923 string_buffer_append(&sbuf, 'i');
3924 if(sbuf.used==base_len)
3927 string_buffer_append_printf(&sbuf, ")] %s", uData->info);
3929 string_buffer_append_string(&sbuf, ")]");
3930 string_buffer_append(&sbuf, '\0');
3931 send_message_type(4, user, cmd->parent->bot, "%s", sbuf.list);
3935 reply("CSMSG_MYACCESS_COUNT_1", target_handle->handle, ccount, ocount);
3937 reply("CSMSG_MYACCESS_COUNT", target_handle->handle, ccount, ocount);
3943 static CHANSERV_FUNC(cmd_access)
3945 struct userNode *target;
3946 struct handle_info *target_handle;
3947 struct userData *uData;
3949 char prefix[MAXLEN];
3954 target_handle = target->handle_info;
3956 else if((target = GetUserH(argv[1])))
3958 target_handle = target->handle_info;
3960 else if(argv[1][0] == '*')
3962 if(!(target_handle = get_handle_info(argv[1]+1)))
3964 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3970 reply("MSG_NICK_UNKNOWN", argv[1]);
3974 assert(target || target_handle);
3976 if(target == chanserv)
3978 reply("CSMSG_IS_CHANSERV");
3986 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
3991 reply("MSG_USER_AUTHENTICATE", target->nick);
3994 reply("MSG_AUTHENTICATE");
4000 const char *epithet = NULL, *type = NULL;
4003 epithet = chanserv_conf.irc_operator_epithet;
4004 type = user_find_message(user, "CSMSG_OPERATOR_TITLE");
4006 else if(IsNetworkHelper(target))
4008 epithet = chanserv_conf.network_helper_epithet;
4009 type = user_find_message(user, "CSMSG_UC_H_TITLE");
4011 else if(IsSupportHelper(target))
4013 epithet = chanserv_conf.support_helper_epithet;
4014 type = user_find_message(user, "CSMSG_LC_H_TITLE");
4018 if(target_handle->epithet)
4019 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
4021 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
4023 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
4027 sprintf(prefix, "%s", target_handle->handle);
4030 if(!channel->channel_info)
4032 reply("CSMSG_NOT_REGISTERED", channel->name);
4036 helping = HANDLE_FLAGGED(target_handle, HELPING)
4037 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
4038 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
4040 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
4041 /* To prevent possible information leaks, only show infolines
4042 * if the requestor is in the channel or it's their own
4044 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
4046 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
4048 /* Likewise, only say it's suspended if the user has active
4049 * access in that channel or it's their own entry. */
4050 if(IsUserSuspended(uData)
4051 && (GetChannelUser(channel->channel_info, user->handle_info)
4052 || (user->handle_info == uData->handle)))
4054 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
4059 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
4066 def_list(struct listData *list)
4070 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
4072 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
4073 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4074 if(list->table.length == 1)
4076 msg = user_find_message(list->user, "MSG_NONE");
4077 send_message_type(4, list->user, list->bot, " %s", msg);
4082 userData_access_comp(const void *arg_a, const void *arg_b)
4084 const struct userData *a = *(struct userData**)arg_a;
4085 const struct userData *b = *(struct userData**)arg_b;
4087 if(a->access != b->access)
4088 res = b->access - a->access;
4090 res = irccasecmp(a->handle->handle, b->handle->handle);
4095 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
4097 void (*send_list)(struct listData *);
4098 struct userData *uData;
4099 struct listData lData;
4100 unsigned int matches;
4104 lData.bot = cmd->parent->bot;
4105 lData.channel = channel;
4106 lData.lowest = lowest;
4107 lData.highest = highest;
4108 lData.search = (argc > 1) ? argv[1] : NULL;
4109 send_list = def_list;
4111 if(user->handle_info)
4113 switch(user->handle_info->userlist_style)
4115 case HI_STYLE_DEF: send_list = def_list; break;
4116 case HI_STYLE_ZOOT: send_list = def_list; break;
4120 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
4122 for(uData = channel->channel_info->users; uData; uData = uData->next)
4124 if((uData->access < lowest)
4125 || (uData->access > highest)
4126 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
4128 lData.users[matches++] = uData;
4130 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
4132 lData.table.length = matches+1;
4133 lData.table.width = 4;
4134 lData.table.flags = TABLE_NO_FREE;
4135 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
4136 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
4137 lData.table.contents[0] = ary;
4140 ary[2] = "Last Seen";
4142 for(matches = 1; matches < lData.table.length; ++matches)
4144 char seen[INTERVALLEN];
4146 uData = lData.users[matches-1];
4147 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
4148 lData.table.contents[matches] = ary;
4149 ary[0] = strtab(uData->access);
4150 ary[1] = uData->handle->handle;
4153 else if(!uData->seen)
4156 ary[2] = intervalString(seen, now - uData->seen, user->handle_info);
4157 ary[2] = strdup(ary[2]);
4158 if(IsUserSuspended(uData))
4159 ary[3] = "Suspended";
4160 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
4161 ary[3] = "Vacation";
4162 else if(HANDLE_FLAGGED(uData->handle, BOT))
4168 for(matches = 1; matches < lData.table.length; ++matches)
4170 free((char*)lData.table.contents[matches][2]);
4171 free(lData.table.contents[matches]);
4173 free(lData.table.contents[0]);
4174 free(lData.table.contents);
4175 reply("CSMSG_TOTAL_USERS",(lData.table.length - 1),channel->name);
4179 static CHANSERV_FUNC(cmd_users)
4181 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
4184 static CHANSERV_FUNC(cmd_wlist)
4186 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
4189 static CHANSERV_FUNC(cmd_clist)
4191 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
4194 static CHANSERV_FUNC(cmd_mlist)
4196 return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
4199 static CHANSERV_FUNC(cmd_olist)
4201 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
4204 static CHANSERV_FUNC(cmd_plist)
4206 return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
4209 static CHANSERV_FUNC(cmd_bans)
4211 struct userNode *search_u = NULL;
4212 struct helpfile_table tbl;
4213 unsigned int matches = 0, timed = 0, search_wilds = 0, ii;
4214 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
4215 const char *msg_never, *triggered, *expires;
4216 struct banData *ban, **bans;
4220 else if(strchr(search = argv[1], '!'))
4223 search_wilds = search[strcspn(search, "?*")];
4225 else if(!(search_u = GetUserH(search)))
4226 reply("MSG_NICK_UNKNOWN", search);
4228 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
4230 for(ban = channel->channel_info->bans; ban; ban = ban->next)
4234 if(!user_matches_glob(search_u, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
4239 if(search_wilds ? !match_ircglobs(search, ban->mask) : !match_ircglob(search, ban->mask))
4242 bans[matches++] = ban;
4247 tbl.length = matches + 1;
4248 tbl.width = 4 + timed;
4250 tbl.flags = TABLE_NO_FREE;
4251 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
4252 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4253 tbl.contents[0][0] = "Mask";
4254 tbl.contents[0][1] = "Set By";
4255 tbl.contents[0][2] = "Triggered";
4258 tbl.contents[0][3] = "Expires";
4259 tbl.contents[0][4] = "Reason";
4262 tbl.contents[0][3] = "Reason";
4265 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4267 free(tbl.contents[0]);
4272 msg_never = user_find_message(user, "MSG_NEVER");
4273 for(ii = 0; ii < matches; )
4279 else if(ban->expires)
4280 expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
4282 expires = msg_never;
4285 triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
4287 triggered = msg_never;
4289 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4290 tbl.contents[ii][0] = ban->mask;
4291 tbl.contents[ii][1] = ban->owner;
4292 tbl.contents[ii][2] = strdup(triggered);
4295 tbl.contents[ii][3] = strdup(expires);
4296 tbl.contents[ii][4] = ban->reason;
4299 tbl.contents[ii][3] = ban->reason;
4301 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4302 reply("MSG_MATCH_COUNT", matches);
4303 for(ii = 1; ii < tbl.length; ++ii)
4305 free((char*)tbl.contents[ii][2]);
4307 free((char*)tbl.contents[ii][3]);
4308 free(tbl.contents[ii]);
4310 free(tbl.contents[0]);
4316 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
4318 struct chanData *cData = channel->channel_info;
4319 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
4321 if(cData->topic_mask)
4322 return !match_ircglob(new_topic, cData->topic_mask);
4323 else if(cData->topic)
4324 return irccasecmp(new_topic, cData->topic);
4329 static CHANSERV_FUNC(cmd_topic)
4331 struct chanData *cData;
4334 cData = channel->channel_info;
4339 SetChannelTopic(channel, chanserv, cData->topic, 1);
4340 reply("CSMSG_TOPIC_SET", cData->topic);
4344 reply("CSMSG_NO_TOPIC", channel->name);
4348 topic = unsplit_string(argv + 1, argc - 1, NULL);
4349 /* If they say "!topic *", use an empty topic. */
4350 if((topic[0] == '*') && (topic[1] == 0))
4352 if(bad_topic(channel, user, topic))
4354 char *topic_mask = cData->topic_mask;
4357 char new_topic[TOPICLEN+1], tchar;
4358 int pos=0, starpos=-1, dpos=0, len;
4360 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
4367 len = strlen(topic);
4368 if((dpos + len) > TOPICLEN)
4369 len = TOPICLEN + 1 - dpos;
4370 memcpy(new_topic+dpos, topic, len);
4374 case '\\': tchar = topic_mask[pos++]; /* and fall through */
4375 default: new_topic[dpos++] = tchar; break;
4378 if((dpos > TOPICLEN) || tchar)
4381 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
4382 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
4385 new_topic[dpos] = 0;
4386 SetChannelTopic(channel, chanserv, new_topic, 1);
4388 reply("CSMSG_TOPIC_LOCKED", channel->name);
4393 SetChannelTopic(channel, chanserv, topic, 1);
4395 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
4397 /* Grab the topic and save it as the default topic. */
4399 cData->topic = strdup(channel->topic);
4405 static CHANSERV_FUNC(cmd_mode)
4407 struct userData *uData;
4408 struct mod_chanmode *change;
4414 change = &channel->channel_info->modes;
4415 if(change->modes_set || change->modes_clear) {
4416 modcmd_chanmode_announce(change);
4417 reply("CSMSG_DEFAULTED_MODES", channel->name);
4419 reply("CSMSG_NO_MODES", channel->name);
4423 uData = GetChannelUser(channel->channel_info, user->handle_info);
4425 base_oplevel = MAXOPLEVEL;
4426 else if (uData->access >= UL_OWNER)
4429 base_oplevel = 1 + UL_OWNER - uData->access;
4430 change = mod_chanmode_parse(channel, user, argv+1, argc-1, MCP_KEY_FREE|MCP_IGN_REGISTERED|MCP_NO_APASS, base_oplevel);
4433 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
4437 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
4438 && mode_lock_violated(&channel->channel_info->modes, change))
4441 mod_chanmode_format(&channel->channel_info->modes, modes);
4442 reply("CSMSG_MODE_LOCKED", modes, channel->name);
4446 modcmd_chanmode_announce(change);
4447 mod_chanmode_format(change, fmt);
4448 mod_chanmode_free(change);
4449 reply("CSMSG_MODES_SET", fmt);
4454 chanserv_del_invite_mark(void *data)
4456 struct ChanUser *chanuser = data;
4457 struct chanNode *channel = chanuser->chan;
4459 if(!channel) return;
4460 for(i = 0; i < channel->invited.used; i++)
4462 if(channel->invited.list[i] == chanuser->user) {
4463 userList_remove(&channel->invited, chanuser->user);
4469 static CHANSERV_FUNC(cmd_invite)
4471 struct userNode *invite;
4472 struct ChanUser *chanuser;
4477 if(!(invite = GetUserH(argv[1])))
4479 reply("MSG_NICK_UNKNOWN", argv[1]);
4486 if(GetUserMode(channel, invite))
4488 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
4492 for(i = 0; i < channel->invited.used; i++)
4494 if(channel->invited.list[i] == invite) {
4495 reply("CSMSG_ALREADY_INVITED", invite->nick, channel->name);
4504 char *reason = unsplit_string(argv + 2, argc - 2, NULL);
4505 send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
4508 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
4510 irc_invite(chanserv, invite, channel);
4512 reply("CSMSG_INVITED_USER", argv[1], channel->name);
4514 userList_append(&channel->invited, invite);
4515 chanuser = calloc(1, sizeof(*chanuser));
4516 chanuser->user=invite;
4517 chanuser->chan=channel;
4518 timeq_add(now + chanserv_conf.invited_timeout, chanserv_del_invite_mark, chanuser);
4523 static CHANSERV_FUNC(cmd_inviteme)
4525 if(GetUserMode(channel, user))
4527 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
4530 if(channel->channel_info
4531 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
4533 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
4536 irc_invite(cmd->parent->bot, user, channel);
4540 static CHANSERV_FUNC(cmd_invitemeall)
4542 struct handle_info *target = user->handle_info;
4543 struct userData *uData;
4545 if(!target->channels)
4547 reply("CSMSG_SQUAT_ACCESS", target->handle);
4551 for(uData = target->channels; uData; uData = uData->u_next)
4553 struct chanData *cData = uData->channel;
4554 if(uData->access >= cData->lvlOpts[lvlInviteMe])
4556 irc_invite(cmd->parent->bot, user, cData->channel);
4563 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
4566 char buf1[INTERVALLEN], buf2[INTERVALLEN];
4568 /* We display things based on two dimensions:
4569 * - Issue time: present or absent
4570 * - Expiration: revoked, expired, expires in future, or indefinite expiration
4571 * (in order of precedence, so something both expired and revoked
4572 * only counts as revoked)
4574 combo = (suspended->issued ? 4 : 0)
4575 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
4577 case 0: /* no issue time, indefinite expiration */
4578 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
4580 case 1: /* no issue time, expires in future */
4581 intervalString(buf1, suspended->expires-now, user->handle_info);
4582 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
4584 case 2: /* no issue time, expired */
4585 intervalString(buf1, now-suspended->expires, user->handle_info);
4586 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
4588 case 3: /* no issue time, revoked */
4589 intervalString(buf1, now-suspended->revoked, user->handle_info);
4590 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
4592 case 4: /* issue time set, indefinite expiration */
4593 intervalString(buf1, now-suspended->issued, user->handle_info);
4594 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
4596 case 5: /* issue time set, expires in future */
4597 intervalString(buf1, now-suspended->issued, user->handle_info);
4598 intervalString(buf2, suspended->expires-now, user->handle_info);
4599 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
4601 case 6: /* issue time set, expired */
4602 intervalString(buf1, now-suspended->issued, user->handle_info);
4603 intervalString(buf2, now-suspended->expires, user->handle_info);
4604 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
4606 case 7: /* issue time set, revoked */
4607 intervalString(buf1, now-suspended->issued, user->handle_info);
4608 intervalString(buf2, now-suspended->revoked, user->handle_info);
4609 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
4612 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
4618 show_giveownership_info(struct svccmd *cmd, struct userNode *user, struct giveownership *giveownership)
4621 const char *fmt = "%a %b %d %H:%M %Y";
4622 strftime(buf, sizeof(buf), fmt, localtime(&giveownership->issued));
4624 if(giveownership->staff_issuer)
4626 if(giveownership->reason)
4627 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF_REASON", giveownership->old_owner,
4628 giveownership->target, giveownership->target_access,
4629 giveownership->staff_issuer, buf, giveownership->reason);
4631 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF", giveownership->old_owner,
4632 giveownership->target, giveownership->target_access,
4633 giveownership->staff_issuer, buf);
4637 reply("CSMSG_CHANNEL_OWNERSHIP_NORMAL", giveownership->old_owner, giveownership->target, giveownership->target_access, buf);
4641 static CHANSERV_FUNC(cmd_info)
4643 char modes[MAXLEN], buffer[INTERVALLEN];
4644 struct userData *uData, *owner;
4645 struct chanData *cData;
4646 struct do_not_register *dnr;
4651 cData = channel->channel_info;
4652 reply("CSMSG_CHANNEL_INFO", channel->name);
4654 uData = GetChannelUser(cData, user->handle_info);
4655 if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
4657 mod_chanmode_format(&cData->modes, modes);
4658 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
4659 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
4662 for(it = dict_first(cData->notes); it; it = iter_next(it))
4666 note = iter_data(it);
4667 if(!note_type_visible_to_user(cData, note->type, user))
4670 padding = PADLEN - 1 - strlen(iter_key(it));
4671 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
4674 if(cData->max_time) {
4675 reply("CSMSG_CHANNEL_MAX_TIME", cData->max, intervalString(buffer, now - cData->max_time, user->handle_info));
4677 reply("CSMSG_CHANNEL_MAX", cData->max);
4679 for(owner = cData->users; owner; owner = owner->next)
4680 if(owner->access == UL_OWNER)
4681 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
4682 reply("CSMSG_CHANNEL_USERS", cData->userCount);
4683 reply("CSMSG_CHANNEL_BANS", cData->banCount);
4684 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
4686 privileged = IsStaff(user);
4688 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
4689 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
4690 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
4692 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
4693 chanserv_show_dnrs(user, cmd, channel->name, NULL);
4695 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
4697 struct suspended *suspended;
4698 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
4699 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
4700 show_suspension_info(cmd, user, suspended);
4702 else if(IsSuspended(cData))
4704 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
4705 show_suspension_info(cmd, user, cData->suspended);
4708 if(cData->giveownership && ((uData && (uData->access >= UL_COOWNER)) || IsStaff(user)))
4710 struct giveownership *giveownership;
4711 reply("CSMSG_CHANNEL_OWNERSHIP_HISTORY", channel->name);
4712 for(giveownership = cData->giveownership; giveownership; giveownership = giveownership->previous)
4713 show_giveownership_info(cmd, user, giveownership);
4718 static CHANSERV_FUNC(cmd_netinfo)
4720 extern unsigned long boot_time;
4721 extern unsigned long burst_length;
4722 char interval[INTERVALLEN];
4724 reply("CSMSG_NETWORK_INFO");
4725 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
4726 reply("CSMSG_NETWORK_USERS", dict_size(clients));
4727 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
4728 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
4729 reply("CSMSG_NETWORK_BANS", banCount);
4730 reply("CSMSG_NETWORK_CHANUSERS", userCount);
4731 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
4732 reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
4737 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
4739 struct helpfile_table table;
4741 struct userNode *user;
4746 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4747 table.contents = alloca(list->used*sizeof(*table.contents));
4748 for(nn=0; nn<list->used; nn++)
4750 user = list->list[nn];
4751 if(user->modes & skip_flags)
4757 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
4760 nick = alloca(strlen(user->nick)+3);
4761 sprintf(nick, "(%s)", user->nick);
4765 table.contents[table.length][0] = nick;
4768 table_send(chanserv, to->nick, 0, NULL, table);
4771 static CHANSERV_FUNC(cmd_ircops)
4773 reply("CSMSG_STAFF_OPERS");
4774 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
4778 static CHANSERV_FUNC(cmd_helpers)
4780 reply("CSMSG_STAFF_HELPERS");
4781 send_staff_list(user, &curr_helpers, FLAGS_OPER);
4785 static CHANSERV_FUNC(cmd_staff)
4787 reply("CSMSG_NETWORK_STAFF");
4788 cmd_ircops(CSFUNC_ARGS);
4789 cmd_helpers(CSFUNC_ARGS);
4793 static CHANSERV_FUNC(cmd_peek)
4795 struct modeNode *mn;
4796 char modes[MODELEN];
4798 struct helpfile_table table;
4799 int opcount = 0, voicecount = 0, srvcount = 0;
4801 irc_make_chanmode(channel, modes);
4803 reply("CSMSG_PEEK_INFO", channel->name);
4804 reply("CSMSG_PEEK_TOPIC", channel->topic);
4805 reply("CSMSG_PEEK_MODES", modes);
4809 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4810 table.contents = alloca(channel->members.used*sizeof(*table.contents));
4811 for(n = 0; n < channel->members.used; n++)
4813 mn = channel->members.list[n];
4814 if(IsLocal(mn->user))
4816 else if(mn->modes & MODE_CHANOP)
4818 else if(mn->modes & MODE_VOICE)
4821 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
4823 table.contents[table.length] = alloca(sizeof(**table.contents));
4824 table.contents[table.length][0] = mn->user->nick;
4828 reply("CSMSG_PEEK_USERS", channel->members.used, opcount, voicecount,
4829 (channel->members.used - opcount - voicecount - srvcount));
4833 reply("CSMSG_PEEK_OPS");
4834 table_send(chanserv, user->nick, 0, NULL, table);
4837 reply("CSMSG_PEEK_NO_OPS");
4841 static MODCMD_FUNC(cmd_wipeinfo)
4843 struct handle_info *victim;
4844 struct userData *ud, *actor, *real_actor;
4845 unsigned int override = 0;
4848 actor = GetChannelUser(channel->channel_info, user->handle_info);
4849 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
4850 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4852 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4854 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4857 if((ud->access >= actor->access) && (ud != actor))
4859 reply("MSG_USER_OUTRANKED", victim->handle);
4862 if((ud != real_actor) && (!real_actor || (ud->access >= real_actor->access)))
4863 override = CMD_LOG_OVERRIDE;
4867 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4868 return 1 | override;
4871 static CHANSERV_FUNC(cmd_resync)
4873 struct mod_chanmode *changes;
4874 struct chanData *cData = channel->channel_info;
4875 unsigned int ii, used;
4877 changes = mod_chanmode_alloc(channel->members.used * 2);
4878 for(ii = used = 0; ii < channel->members.used; ++ii)
4880 struct modeNode *mn = channel->members.list[ii];
4881 struct userData *uData;
4883 if(IsService(mn->user))
4886 uData = GetChannelAccess(cData, mn->user->handle_info);
4887 if(!cData->lvlOpts[lvlGiveOps]
4888 || (uData && uData->access >= cData->lvlOpts[lvlGiveOps]))
4890 if(!(mn->modes & MODE_CHANOP))
4892 if(!uData || IsUserAutoOp(uData))
4894 changes->args[used].mode = MODE_CHANOP;
4895 changes->args[used++].u.member = mn;
4896 if(!(mn->modes & MODE_VOICE))
4898 changes->args[used].mode = MODE_VOICE;
4899 changes->args[used++].u.member = mn;
4904 else if(!cData->lvlOpts[lvlGiveVoice]
4905 || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice]))
4907 if(mn->modes & MODE_CHANOP)
4909 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4910 changes->args[used++].u.member = mn;
4912 if(!(mn->modes & MODE_VOICE) && (!uData || IsUserAutoOp(uData)))
4914 changes->args[used].mode = MODE_VOICE;
4915 changes->args[used++].u.member = mn;
4922 changes->args[used].mode = MODE_REMOVE | mn->modes;
4923 changes->args[used++].u.member = mn;
4927 changes->argc = used;
4928 modcmd_chanmode_announce(changes);
4929 mod_chanmode_free(changes);
4930 reply("CSMSG_RESYNCED_USERS", channel->name);
4934 static CHANSERV_FUNC(cmd_seen)
4936 struct userData *uData;
4937 struct handle_info *handle;
4938 char seen[INTERVALLEN];
4942 if(!irccasecmp(argv[1], chanserv->nick))
4944 reply("CSMSG_IS_CHANSERV");
4948 if(!(handle = get_handle_info(argv[1])))
4950 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4954 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4956 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4961 reply("CSMSG_USER_PRESENT", handle->handle);
4962 else if(uData->seen)
4963 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
4965 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4967 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4968 reply("CSMSG_USER_VACATION", handle->handle);
4973 static MODCMD_FUNC(cmd_names)
4975 struct userNode *targ;
4976 struct userData *targData;
4977 unsigned int ii, pos;
4980 for(ii=pos=0; ii<channel->members.used; ++ii)
4982 targ = channel->members.list[ii]->user;
4983 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4986 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
4989 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4993 if(IsUserSuspended(targData))
4995 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4998 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4999 reply("CSMSG_END_NAMES", channel->name);
5004 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
5006 switch(ntype->visible_type)
5008 case NOTE_VIS_ALL: return 1;
5009 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
5010 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
5015 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
5017 struct userData *uData;
5019 switch(ntype->set_access_type)
5021 case NOTE_SET_CHANNEL_ACCESS:
5022 if(!user->handle_info)
5024 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
5026 return uData->access >= ntype->set_access.min_ulevel;
5027 case NOTE_SET_CHANNEL_SETTER:
5028 return check_user_level(channel, user, lvlSetters, 1, 0);
5029 case NOTE_SET_PRIVILEGED: default:
5030 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
5034 static CHANSERV_FUNC(cmd_note)
5036 struct chanData *cData;
5038 struct note_type *ntype;
5040 cData = channel->channel_info;
5043 reply("CSMSG_NOT_REGISTERED", channel->name);
5047 /* If no arguments, show all visible notes for the channel. */
5053 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
5055 note = iter_data(it);
5056 if(!note_type_visible_to_user(cData, note->type, user))
5059 reply("CSMSG_NOTELIST_HEADER", channel->name);
5060 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
5063 reply("CSMSG_NOTELIST_END", channel->name);
5065 reply("CSMSG_NOTELIST_EMPTY", channel->name);
5067 /* If one argument, show the named note. */
5070 if((note = dict_find(cData->notes, argv[1], NULL))
5071 && note_type_visible_to_user(cData, note->type, user))
5073 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
5075 else if((ntype = dict_find(note_types, argv[1], NULL))
5076 && note_type_visible_to_user(NULL, ntype, user))
5078 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
5083 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
5087 /* Assume they're trying to set a note. */
5091 ntype = dict_find(note_types, argv[1], NULL);
5094 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
5097 else if(note_type_settable_by_user(channel, ntype, user))
5099 note_text = unsplit_string(argv+2, argc-2, NULL);
5100 if((note = dict_find(cData->notes, argv[1], NULL)))
5101 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
5102 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
5103 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
5105 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
5107 /* The note is viewable to staff only, so return 0
5108 to keep the invocation from getting logged (or
5109 regular users can see it in !events). */
5115 reply("CSMSG_NO_ACCESS");
5122 static CHANSERV_FUNC(cmd_delnote)
5127 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
5128 || !note_type_settable_by_user(channel, note->type, user))
5130 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
5133 dict_remove(channel->channel_info->notes, note->type->name);
5134 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
5138 static CHANSERV_FUNC(cmd_events)
5140 struct logSearch discrim;
5141 struct logReport report;
5142 unsigned int matches, limit;
5144 limit = (argc > 1) ? atoi(argv[1]) : 10;
5145 if(limit < 1 || limit > 200)
5148 memset(&discrim, 0, sizeof(discrim));
5149 discrim.masks.bot = chanserv;
5150 discrim.masks.channel_name = channel->name;
5152 discrim.masks.command = argv[2];
5153 discrim.limit = limit;
5154 discrim.max_time = INT_MAX;
5155 discrim.severities = 1 << LOG_COMMAND;
5156 report.reporter = chanserv;
5158 reply("CSMSG_EVENT_SEARCH_RESULTS");
5159 matches = log_entry_search(&discrim, log_report_entry, &report);
5161 reply("MSG_MATCH_COUNT", matches);
5163 reply("MSG_NO_MATCHES");
5167 static CHANSERV_FUNC(cmd_say)
5173 msg = unsplit_string(argv + 1, argc - 1, NULL);
5174 send_channel_message(channel, cmd->parent->bot, "%s", msg);
5176 else if(*argv[1] == '*' && argv[1][1] != '\0')
5178 struct handle_info *hi;
5179 struct userNode *authed;
5182 msg = unsplit_string(argv + 2, argc - 2, NULL);
5184 if (!(hi = get_handle_info(argv[1] + 1)))
5186 reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
5190 for (authed = hi->users; authed; authed = authed->next_authed)
5191 send_target_message(5, authed->nick, cmd->parent->bot, "%s", msg);
5193 else if(GetUserH(argv[1]))
5196 msg = unsplit_string(argv + 2, argc - 2, NULL);
5197 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
5201 reply("MSG_NOT_TARGET_NAME");
5207 static CHANSERV_FUNC(cmd_emote)
5213 /* CTCP is so annoying. */
5214 msg = unsplit_string(argv + 1, argc - 1, NULL);
5215 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
5217 else if(*argv[1] == '*' && argv[1][1] != '\0')
5219 struct handle_info *hi;
5220 struct userNode *authed;
5223 msg = unsplit_string(argv + 2, argc - 2, NULL);
5225 if (!(hi = get_handle_info(argv[1] + 1)))
5227 reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
5231 for (authed = hi->users; authed; authed = authed->next_authed)
5232 send_target_message(5, authed->nick, cmd->parent->bot, "\001ACTION %s\001", msg);
5234 else if(GetUserH(argv[1]))
5236 msg = unsplit_string(argv + 2, argc - 2, NULL);
5237 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
5241 reply("MSG_NOT_TARGET_NAME");
5247 struct channelList *
5248 chanserv_support_channels(void)
5250 return &chanserv_conf.support_channels;
5253 static CHANSERV_FUNC(cmd_expire)
5255 int channel_count = registered_channels;
5256 expire_channels(chanserv);
5257 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
5262 chanserv_expire_suspension(void *data)
5264 struct suspended *suspended = data;
5265 struct chanNode *channel;
5268 /* Update the channel registration data structure. */
5269 if(!suspended->expires || (now < suspended->expires))
5270 suspended->revoked = now;
5271 channel = suspended->cData->channel;
5272 suspended->cData->channel = channel;
5273 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
5275 /* If appropriate, re-join ChanServ to the channel. */
5276 if(!IsOffChannel(suspended->cData))
5278 spamserv_cs_suspend(channel, 0, 0, NULL);
5279 ss_cs_join_channel(channel, 1);
5282 /* Mark everyone currently in the channel as present. */
5283 for(ii = 0; ii < channel->members.used; ++ii)
5285 struct userData *uData = GetChannelAccess(suspended->cData, channel->members.list[ii]->user->handle_info);
5294 static CHANSERV_FUNC(cmd_csuspend)
5296 struct suspended *suspended;
5297 char reason[MAXLEN];
5298 unsigned long expiry, duration;
5299 struct userData *uData;
5303 if(IsProtected(channel->channel_info))
5305 reply("CSMSG_SUSPEND_NODELETE", channel->name);
5309 if(argv[1][0] == '!')
5311 else if(IsSuspended(channel->channel_info))
5313 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
5314 show_suspension_info(cmd, user, channel->channel_info->suspended);
5318 if(!strcmp(argv[1], "0"))
5320 else if((duration = ParseInterval(argv[1])))
5321 expiry = now + duration;
5324 reply("MSG_INVALID_DURATION", argv[1]);
5328 unsplit_string(argv + 2, argc - 2, reason);
5330 suspended = calloc(1, sizeof(*suspended));
5331 suspended->revoked = 0;
5332 suspended->issued = now;
5333 suspended->suspender = strdup(user->handle_info->handle);
5334 suspended->expires = expiry;
5335 suspended->reason = strdup(reason);
5336 suspended->cData = channel->channel_info;
5337 suspended->previous = suspended->cData->suspended;
5338 suspended->cData->suspended = suspended;
5340 if(suspended->expires)
5341 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
5343 if(IsSuspended(channel->channel_info))
5345 suspended->previous->revoked = now;
5346 if(suspended->previous->expires)
5347 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
5348 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
5349 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5353 /* Mark all users in channel as absent. */
5354 for(uData = channel->channel_info->users; uData; uData = uData->next)
5363 /* Mark the channel as suspended, then part. */
5364 channel->channel_info->flags |= CHANNEL_SUSPENDED;
5365 spamserv_cs_suspend(channel, expiry, 1, suspended->reason);
5366 DelChannelUser(chanserv, channel, suspended->reason, 0);
5367 reply("CSMSG_SUSPENDED", channel->name);
5368 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
5369 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5374 static CHANSERV_FUNC(cmd_cunsuspend)
5376 struct suspended *suspended;
5377 char message[MAXLEN];
5379 if(!IsSuspended(channel->channel_info))
5381 reply("CSMSG_NOT_SUSPENDED", channel->name);
5385 suspended = channel->channel_info->suspended;
5387 /* Expire the suspension and join ChanServ to the channel. */
5388 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
5389 chanserv_expire_suspension(suspended);
5390 reply("CSMSG_UNSUSPENDED", channel->name);
5391 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
5392 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
5396 typedef struct chanservSearch
5401 unsigned long unvisited;
5402 unsigned long registered;
5404 unsigned long flags;
5408 typedef void (*channel_search_func)(struct chanData *channel, void *data);
5411 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
5416 search = malloc(sizeof(struct chanservSearch));
5417 memset(search, 0, sizeof(*search));
5420 for(i = 0; i < argc; i++)
5422 /* Assume all criteria require arguments. */
5425 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
5429 if(!irccasecmp(argv[i], "name"))
5430 search->name = argv[++i];
5431 else if(!irccasecmp(argv[i], "registrar"))
5432 search->registrar = argv[++i];
5433 else if(!irccasecmp(argv[i], "unvisited"))
5434 search->unvisited = ParseInterval(argv[++i]);
5435 else if(!irccasecmp(argv[i], "registered"))
5436 search->registered = ParseInterval(argv[++i]);
5437 else if(!irccasecmp(argv[i], "flags"))
5440 if(!irccasecmp(argv[i], "nodelete"))
5441 search->flags |= CHANNEL_NODELETE;
5442 else if(!irccasecmp(argv[i], "suspended"))
5443 search->flags |= CHANNEL_SUSPENDED;
5444 else if(!irccasecmp(argv[i], "unreviewed"))
5445 search->flags |= CHANNEL_UNREVIEWED;
5448 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
5452 else if(!irccasecmp(argv[i], "limit"))
5453 search->limit = strtoul(argv[++i], NULL, 10);
5456 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
5461 if(search->name && !strcmp(search->name, "*"))
5463 if(search->registrar && !strcmp(search->registrar, "*"))
5464 search->registrar = 0;
5473 chanserv_channel_match(struct chanData *channel, search_t search)
5475 const char *name = channel->channel->name;
5476 if((search->name && !match_ircglob(name, search->name)) ||
5477 (search->registrar && !channel->registrar) ||
5478 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
5479 (search->unvisited && (now - channel->visited) < search->unvisited) ||
5480 (search->registered && (now - channel->registered) > search->registered) ||
5481 (search->flags && ((search->flags & channel->flags) != search->flags)))
5488 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
5490 struct chanData *channel;
5491 unsigned int matches = 0;
5493 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
5495 if(!chanserv_channel_match(channel, search))
5505 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
5510 search_print(struct chanData *channel, void *data)
5512 send_message_type(4, data, chanserv, "%s", channel->channel->name);
5515 static CHANSERV_FUNC(cmd_search)
5518 unsigned int matches;
5519 channel_search_func action;
5523 if(!irccasecmp(argv[1], "count"))
5524 action = search_count;
5525 else if(!irccasecmp(argv[1], "print"))
5526 action = search_print;
5529 reply("CSMSG_ACTION_INVALID", argv[1]);
5533 search = chanserv_search_create(user, argc - 2, argv + 2);
5537 if(action == search_count)
5538 search->limit = INT_MAX;
5540 if(action == search_print)
5541 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
5543 matches = chanserv_channel_search(search, action, user);
5546 reply("MSG_MATCH_COUNT", matches);
5548 reply("MSG_NO_MATCHES");
5554 static CHANSERV_FUNC(cmd_unvisited)
5556 struct chanData *cData;
5557 unsigned long interval = chanserv_conf.channel_expire_delay;
5558 char buffer[INTERVALLEN];
5559 unsigned int limit = 25, matches = 0;
5563 interval = ParseInterval(argv[1]);
5565 limit = atoi(argv[2]);
5568 intervalString(buffer, interval, user->handle_info);
5569 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
5571 for(cData = channelList; cData && matches < limit; cData = cData->next)
5573 if((now - cData->visited) < interval)
5576 intervalString(buffer, now - cData->visited, user->handle_info);
5577 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
5584 static MODCMD_FUNC(chan_opt_defaulttopic)
5590 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5592 reply("CSMSG_TOPIC_LOCKED", channel->name);
5596 topic = unsplit_string(argv+1, argc-1, NULL);
5598 free(channel->channel_info->topic);
5599 if(topic[0] == '*' && topic[1] == 0)
5601 topic = channel->channel_info->topic = NULL;
5605 topic = channel->channel_info->topic = strdup(topic);
5606 if(channel->channel_info->topic_mask
5607 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
5608 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5610 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
5613 if(channel->channel_info->topic)
5614 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
5616 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
5620 static MODCMD_FUNC(chan_opt_topicmask)
5624 struct chanData *cData = channel->channel_info;
5627 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5629 reply("CSMSG_TOPIC_LOCKED", channel->name);
5633 mask = unsplit_string(argv+1, argc-1, NULL);
5635 if(cData->topic_mask)
5636 free(cData->topic_mask);
5637 if(mask[0] == '*' && mask[1] == 0)
5639 cData->topic_mask = 0;
5643 cData->topic_mask = strdup(mask);
5645 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
5646 else if(!match_ircglob(cData->topic, cData->topic_mask))
5647 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5651 if(channel->channel_info->topic_mask)
5652 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
5654 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
5658 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
5662 char *greeting = unsplit_string(argv+1, argc-1, NULL);
5666 if(greeting[0] == '*' && greeting[1] == 0)
5670 unsigned int length = strlen(greeting);
5671 if(length > chanserv_conf.greeting_length)
5673 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
5676 *data = strdup(greeting);
5685 reply(name, user_find_message(user, "MSG_NONE"));
5689 static MODCMD_FUNC(chan_opt_greeting)
5691 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
5694 static MODCMD_FUNC(chan_opt_usergreeting)
5696 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
5699 static MODCMD_FUNC(chan_opt_modes)
5701 struct mod_chanmode *new_modes;
5706 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
5708 reply("CSMSG_NO_ACCESS");
5711 if(argv[1][0] == '*' && argv[1][1] == 0)
5713 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
5715 else if(!(new_modes = mod_chanmode_parse(channel, user, argv+1, argc-1, MCP_KEY_FREE|MCP_IGN_REGISTERED|MCP_NO_APASS|(IsOper(user) && IsHelping(user) ? MCP_OPERMODE : 0), 0)))
5717 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5720 else if(new_modes->argc > 1)
5722 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5723 mod_chanmode_free(new_modes);
5728 channel->channel_info->modes = *new_modes;
5729 modcmd_chanmode_announce(new_modes);
5730 mod_chanmode_free(new_modes);
5734 mod_chanmode_format(&channel->channel_info->modes, modes);
5736 reply("CSMSG_SET_MODES", modes);
5738 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
5742 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5744 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5746 struct chanData *cData = channel->channel_info;
5751 /* Set flag according to value. */
5752 if(enabled_string(argv[1]))
5754 cData->flags |= mask;
5757 else if(disabled_string(argv[1]))
5759 cData->flags &= ~mask;
5764 reply("MSG_INVALID_BINARY", argv[1]);
5770 /* Find current option value. */
5771 value = (cData->flags & mask) ? 1 : 0;
5775 reply(name, user_find_message(user, "MSG_ON"));
5777 reply(name, user_find_message(user, "MSG_OFF"));
5781 static MODCMD_FUNC(chan_opt_nodelete)
5783 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
5785 reply("MSG_SETTING_PRIVILEGED", argv[0]);
5789 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
5792 static MODCMD_FUNC(chan_opt_dynlimit)
5794 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
5797 static MODCMD_FUNC(chan_opt_offchannel)
5799 struct chanData *cData = channel->channel_info;
5804 /* Set flag according to value. */
5805 if(enabled_string(argv[1]))
5807 if(!IsOffChannel(cData))
5808 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
5809 cData->flags |= CHANNEL_OFFCHANNEL;
5812 else if(disabled_string(argv[1]))
5814 if(IsOffChannel(cData))
5816 struct mod_chanmode change;
5817 mod_chanmode_init(&change);
5819 change.args[0].mode = MODE_CHANOP;
5820 change.args[0].u.member = AddChannelUser(chanserv, channel);
5821 mod_chanmode_announce(chanserv, channel, &change);
5823 cData->flags &= ~CHANNEL_OFFCHANNEL;
5828 reply("MSG_INVALID_BINARY", argv[1]);
5834 /* Find current option value. */
5835 value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
5839 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
5841 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
5845 static MODCMD_FUNC(chan_opt_unreviewed)
5847 struct chanData *cData = channel->channel_info;
5848 int value = (cData->flags & CHANNEL_UNREVIEWED) ? 1 : 0;
5854 /* The two directions can have different ACLs. */
5855 if(enabled_string(argv[1]))
5857 else if(disabled_string(argv[1]))
5861 reply("MSG_INVALID_BINARY", argv[1]);
5865 if (new_value != value)
5867 struct svccmd *subcmd;
5868 char subcmd_name[32];
5870 snprintf(subcmd_name, sizeof(subcmd_name), "%s %s", argv[0], (new_value ? "on" : "off"));
5871 subcmd = dict_find(cmd->parent->commands, subcmd_name, NULL);
5874 reply("MSG_COMMAND_DISABLED", subcmd_name);
5877 else if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
5881 cData->flags |= CHANNEL_UNREVIEWED;
5884 free(cData->registrar);
5885 cData->registrar = strdup(user->handle_info->handle);
5886 cData->flags &= ~CHANNEL_UNREVIEWED;
5893 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_ON"));
5895 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_OFF"));
5899 static MODCMD_FUNC(chan_opt_defaults)
5901 struct userData *uData;
5902 struct chanData *cData;
5903 const char *confirm;
5904 enum levelOption lvlOpt;
5905 enum charOption chOpt;
5907 cData = channel->channel_info;
5908 uData = GetChannelUser(cData, user->handle_info);
5909 if(!uData || (uData->access < UL_OWNER))
5911 reply("CSMSG_OWNER_DEFAULTS", channel->name);
5914 confirm = make_confirmation_string(uData);
5915 if((argc < 2) || strcmp(argv[1], confirm))
5917 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
5920 cData->flags = (CHANNEL_DEFAULT_FLAGS & ~CHANNEL_PRESERVED_FLAGS)
5921 | (cData->flags & CHANNEL_PRESERVED_FLAGS);
5922 cData->modes = chanserv_conf.default_modes;
5923 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
5924 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
5925 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
5926 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
5927 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
5932 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5934 struct chanData *cData = channel->channel_info;
5935 struct userData *uData;
5936 unsigned short value;
5940 if(!check_user_level(channel, user, option, 1, 1))
5942 reply("CSMSG_CANNOT_SET");
5945 value = user_level_from_name(argv[1], UL_OWNER+1);
5946 if(!value && strcmp(argv[1], "0"))
5948 reply("CSMSG_INVALID_ACCESS", argv[1]);
5951 uData = GetChannelUser(cData, user->handle_info);
5952 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
5954 reply("CSMSG_BAD_SETLEVEL");
5960 if(value > cData->lvlOpts[lvlGiveOps])
5962 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
5967 if(value < cData->lvlOpts[lvlGiveVoice])
5969 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
5974 /* This test only applies to owners, since non-owners
5975 * trying to set an option to above their level get caught
5976 * by the CSMSG_BAD_SETLEVEL test above.
5978 if(value > uData->access)
5980 reply("CSMSG_BAD_SETTERS");
5987 cData->lvlOpts[option] = value;
5989 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
5993 static MODCMD_FUNC(chan_opt_enfops)
5995 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
5998 static MODCMD_FUNC(chan_opt_giveops)
6000 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
6003 static MODCMD_FUNC(chan_opt_enfmodes)
6005 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
6008 static MODCMD_FUNC(chan_opt_enftopic)
6010 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
6013 static MODCMD_FUNC(chan_opt_pubcmd)
6015 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
6018 static MODCMD_FUNC(chan_opt_setters)
6020 return channel_level_option(lvlSetters, CSFUNC_ARGS);
6023 static MODCMD_FUNC(chan_opt_ctcpusers)
6025 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
6028 static MODCMD_FUNC(chan_opt_userinfo)
6030 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
6033 static MODCMD_FUNC(chan_opt_givevoice)
6035 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
6038 static MODCMD_FUNC(chan_opt_topicsnarf)
6040 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
6043 static MODCMD_FUNC(chan_opt_vote)
6045 return channel_level_option(lvlVote, CSFUNC_ARGS);
6048 static MODCMD_FUNC(chan_opt_inviteme)
6050 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
6054 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
6056 struct chanData *cData = channel->channel_info;
6057 int count = charOptions[option].count, idx;
6061 idx = atoi(argv[1]);
6063 if(!isdigit(argv[1][0]) || (idx < 0) || (idx >= count))
6065 reply("CSMSG_INVALID_NUMERIC", idx);
6066 /* Show possible values. */
6067 for(idx = 0; idx < count; idx++)
6068 reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
6072 cData->chOpts[option] = charOptions[option].values[idx].value;
6076 /* Find current option value. */
6079 (idx < count) && (cData->chOpts[option] != charOptions[option].values[idx].value);
6083 /* Somehow, the option value is corrupt; reset it to the default. */
6084 cData->chOpts[option] = charOptions[option].default_value;
6089 reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
6093 static MODCMD_FUNC(chan_opt_protect)
6095 return channel_multiple_option(chProtect, CSFUNC_ARGS);
6098 static MODCMD_FUNC(chan_opt_toys)
6100 return channel_multiple_option(chToys, CSFUNC_ARGS);
6103 static MODCMD_FUNC(chan_opt_ctcpreaction)
6105 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
6108 static MODCMD_FUNC(chan_opt_topicrefresh)
6110 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
6113 static struct svccmd_list set_shows_list;
6116 handle_svccmd_unbind(struct svccmd *target) {
6118 for(ii=0; ii<set_shows_list.used; ++ii)
6119 if(target == set_shows_list.list[ii])
6120 set_shows_list.used = 0;
6123 static CHANSERV_FUNC(cmd_set)
6125 struct svccmd *subcmd;
6129 /* Check if we need to (re-)initialize set_shows_list. */
6130 if(!set_shows_list.used)
6132 if(!set_shows_list.size)
6134 set_shows_list.size = chanserv_conf.set_shows->used;
6135 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
6137 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
6139 const char *name = chanserv_conf.set_shows->list[ii];
6140 sprintf(buf, "%s %s", argv[0], name);
6141 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6144 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
6147 svccmd_list_append(&set_shows_list, subcmd);
6153 reply("CSMSG_CHANNEL_OPTIONS");
6154 for(ii = 0; ii < set_shows_list.used; ii++)
6156 subcmd = set_shows_list.list[ii];
6157 subcmd->command->func(user, channel, 1, argv+1, subcmd);
6162 sprintf(buf, "%s %s", argv[0], argv[1]);
6163 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6166 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
6169 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
6171 reply("CSMSG_NO_ACCESS");
6177 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
6181 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
6183 struct userData *uData;
6185 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6188 reply("CSMSG_NOT_USER", channel->name);
6194 /* Just show current option value. */
6196 else if(enabled_string(argv[1]))
6198 uData->flags |= mask;
6200 else if(disabled_string(argv[1]))
6202 uData->flags &= ~mask;
6206 reply("MSG_INVALID_BINARY", argv[1]);
6210 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
6214 static MODCMD_FUNC(user_opt_noautoop)
6216 struct userData *uData;
6218 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6221 reply("CSMSG_NOT_USER", channel->name);
6224 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
6225 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
6227 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
6230 static MODCMD_FUNC(user_opt_autoinvite)
6232 if((argc > 1) && !check_user_level(channel, user, lvlInviteMe, 1, 0))
6234 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
6236 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
6239 static MODCMD_FUNC(user_opt_info)
6241 struct userData *uData;
6244 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6248 /* If they got past the command restrictions (which require access)
6249 * but fail this test, we have some fool with security override on.
6251 reply("CSMSG_NOT_USER", channel->name);
6258 infoline = unsplit_string(argv + 1, argc - 1, NULL);
6259 if(strlen(infoline) > chanserv_conf.max_userinfo_length)
6261 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
6264 bp = strcspn(infoline, "\001");
6267 reply("CSMSG_BAD_INFOLINE", infoline[bp]);
6272 if(infoline[0] == '*' && infoline[1] == 0)
6275 uData->info = strdup(infoline);
6278 reply("CSMSG_USET_INFO", uData->info);
6280 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
6284 struct svccmd_list uset_shows_list;
6286 static CHANSERV_FUNC(cmd_uset)
6288 struct svccmd *subcmd;
6292 /* Check if we need to (re-)initialize uset_shows_list. */
6293 if(!uset_shows_list.used)
6297 "NoAutoOp", "AutoInvite", "Info"
6300 if(!uset_shows_list.size)
6302 uset_shows_list.size = ArrayLength(options);
6303 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
6305 for(ii = 0; ii < ArrayLength(options); ii++)
6307 const char *name = options[ii];
6308 sprintf(buf, "%s %s", argv[0], name);
6309 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6312 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
6315 svccmd_list_append(&uset_shows_list, subcmd);
6321 /* Do this so options are presented in a consistent order. */
6322 reply("CSMSG_USER_OPTIONS");
6323 for(ii = 0; ii < uset_shows_list.used; ii++)
6324 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
6328 sprintf(buf, "%s %s", argv[0], argv[1]);
6329 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6332 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
6336 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
6339 static CHANSERV_FUNC(cmd_giveownership)
6341 struct handle_info *new_owner_hi;
6342 struct userData *new_owner;
6343 struct userData *curr_user;
6344 struct userData *invoker;
6345 struct chanData *cData = channel->channel_info;
6346 struct do_not_register *dnr;
6347 const char *confirm;
6348 struct giveownership *giveownership;
6349 unsigned int force, override;
6350 unsigned short co_access, new_owner_old_access;
6351 char reason[MAXLEN], transfer_reason[MAXLEN];
6354 curr_user = GetChannelAccess(cData, user->handle_info);
6355 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
6357 struct userData *uData = _GetChannelUser(channel->channel_info, user->handle_info, 1, 0);
6358 override = ((cmd->effective_flags & MODCMD_REQUIRE_CHANUSER)
6359 && (uData->access > 500)
6360 && (!(uData = _GetChannelUser(channel->channel_info, user->handle_info, 0, 0))
6361 || uData->access < 500));
6363 if(!curr_user || (curr_user->access != UL_OWNER))
6365 struct userData *owner = NULL;
6366 for(curr_user = channel->channel_info->users;
6368 curr_user = curr_user->next)
6370 if(curr_user->access != UL_OWNER)
6374 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
6381 else if(!force && (now < cData->ownerTransfer + chanserv_conf.giveownership_period))
6383 char delay[INTERVALLEN];
6384 intervalString(delay, cData->ownerTransfer + chanserv_conf.giveownership_period - now, user->handle_info);
6385 reply("CSMSG_TRANSFER_WAIT", delay, channel->name);
6388 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
6390 if(new_owner_hi == user->handle_info)
6392 reply("CSMSG_NO_TRANSFER_SELF");
6395 new_owner = GetChannelAccess(cData, new_owner_hi);
6400 new_owner = add_channel_user(cData, new_owner_hi, UL_OWNER - 1, 0, NULL);
6404 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
6408 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
6410 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
6413 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
6414 if(!IsHelping(user))
6415 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
6417 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi->handle);
6420 invoker = GetChannelUser(cData, user->handle_info);
6421 if(invoker->access <= UL_OWNER)
6423 confirm = make_confirmation_string(curr_user);
6424 if((argc < 3) || strcmp(argv[2], confirm))
6426 reply("CSMSG_CONFIRM_GIVEOWNERSHIP", new_owner_hi->handle, confirm);
6430 new_owner_old_access = new_owner->access;
6431 if(new_owner->access >= UL_COOWNER)
6432 co_access = new_owner->access;
6434 co_access = UL_COOWNER;
6435 new_owner->access = UL_OWNER;
6437 curr_user->access = co_access;
6438 cData->ownerTransfer = now;
6439 giveownership = calloc(1, sizeof(*giveownership));
6440 giveownership->issued = now;
6441 giveownership->old_owner = curr_user->handle->handle;
6442 giveownership->target = new_owner_hi->handle;
6443 giveownership->target_access = new_owner_old_access;
6446 if(argc > (2 + force))
6448 unsplit_string(argv + 2 + force, argc - 2 - force, transfer_reason);
6449 giveownership->reason = strdup(transfer_reason);
6451 giveownership->staff_issuer = strdup(user->handle_info->handle);
6454 giveownership->previous = channel->channel_info->giveownership;
6455 channel->channel_info->giveownership = giveownership;
6456 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
6457 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
6458 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
6462 static CHANSERV_FUNC(cmd_suspend)
6464 struct handle_info *hi;
6465 struct userData *actor, *real_actor, *target;
6466 unsigned int override = 0;
6469 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6470 actor = GetChannelUser(channel->channel_info, user->handle_info);
6471 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
6472 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6474 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6477 if(target->access >= actor->access)
6479 reply("MSG_USER_OUTRANKED", hi->handle);
6482 if(target->flags & USER_SUSPENDED)
6484 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
6489 target->present = 0;
6492 if(!real_actor || target->access >= real_actor->access)
6493 override = CMD_LOG_OVERRIDE;
6494 target->flags |= USER_SUSPENDED;
6495 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
6496 return 1 | override;
6499 static CHANSERV_FUNC(cmd_unsuspend)
6501 struct handle_info *hi;
6502 struct userData *actor, *real_actor, *target;
6503 unsigned int override = 0;
6506 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6507 actor = GetChannelUser(channel->channel_info, user->handle_info);
6508 real_actor = GetChannelAccess(channel->channel_info, 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 >= actor->access)
6516 reply("MSG_USER_OUTRANKED", hi->handle);
6519 if(!(target->flags & USER_SUSPENDED))
6521 reply("CSMSG_NOT_SUSPENDED", hi->handle);
6524 if(!real_actor || target->access >= real_actor->access)
6525 override = CMD_LOG_OVERRIDE;
6526 target->flags &= ~USER_SUSPENDED;
6527 scan_user_presence(target, NULL);
6528 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
6529 return 1 | override;
6532 static MODCMD_FUNC(cmd_deleteme)
6534 struct handle_info *hi;
6535 struct userData *target;
6536 const char *confirm_string;
6537 unsigned short access_level;
6540 hi = user->handle_info;
6541 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6543 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6546 if(target->access == UL_OWNER)
6548 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
6551 confirm_string = make_confirmation_string(target);
6552 if((argc < 2) || strcmp(argv[1], confirm_string))
6554 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
6557 access_level = target->access;
6558 channel_name = strdup(channel->name);
6559 del_channel_user(target, 1);
6560 reply("CSMSG_DELETED_YOU", access_level, channel_name);
6565 static CHANSERV_FUNC(cmd_addvote)
6567 struct chanData *cData = channel->channel_info;
6568 struct userData *uData, *target;
6569 struct handle_info *hi;
6570 if (!cData) return 0;
6572 hi = user->handle_info;
6573 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6575 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6578 if(target->access < 300) {
6579 reply("CSMSG_NO_ACCESS");
6583 reply("CSMSG_ADDVOTE_FULL");
6587 msg = unsplit_string(argv + 1, argc - 1, NULL);
6588 cData->vote = strdup(msg);
6589 cData->vote_start=0;
6590 dict_delete(cData->vote_options);
6591 cData->vote_options = dict_new();
6592 dict_set_free_data(cData->vote_options, free_vote_options);
6593 for(uData = channel->channel_info->users; uData; uData = uData->next)
6598 reply("CSMSG_ADDVOTE_DONE");
6602 static CHANSERV_FUNC(cmd_delvote)
6604 struct chanData *cData = channel->channel_info;
6605 struct userData *target;
6606 struct handle_info *hi;
6607 if (!cData) return 0;
6608 hi = user->handle_info;
6609 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6611 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6614 if(target->access < 300) {
6615 reply("CSMSG_NO_ACCESS");
6619 reply("CSMSG_NO_VOTE");
6624 reply("CSMSG_DELVOTE_DONE");
6628 static CHANSERV_FUNC(cmd_addoption)
6630 struct chanData *cData = channel->channel_info;
6631 struct userData *target;
6632 struct handle_info *hi;
6633 if (!cData) return 0;
6635 hi = user->handle_info;
6636 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6638 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6641 if(target->access < 300) {
6642 reply("CSMSG_NO_ACCESS");
6646 reply("CSMSG_NO_VOTE");
6652 msg = unsplit_string(argv + 1, argc - 1, NULL);
6655 unsigned int lastid = 1;
6656 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6657 struct vote_option *cvOpt = iter_data(it);
6658 if(cvOpt->option_id > lastid)
6659 lastid = cvOpt->option_id;
6661 struct vote_option *vOpt;
6662 vOpt = calloc(1, sizeof(*vOpt));
6663 vOpt->name = strdup(msg);
6664 vOpt->option_id = (lastid + 1);
6666 sprintf(str,"%i",(lastid + 1));
6667 vOpt->option_str = strdup(str);
6669 dict_insert(cData->vote_options,vOpt->option_str,vOpt);
6671 reply("CSMSG_ADDOPTION_DONE",dict_size(cData->vote_options),lastid,(lastid + 1));
6675 static CHANSERV_FUNC(cmd_deloption)
6677 struct chanData *cData = channel->channel_info;
6678 struct userData *uData, *target;
6679 struct handle_info *hi;
6680 if (!cData) return 0;
6682 hi = user->handle_info;
6683 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6685 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6688 if(target->access < 300) {
6689 reply("CSMSG_NO_ACCESS");
6693 reply("CSMSG_NO_VOTE");
6696 if(cData->vote_start) {
6697 if(dict_size(cData->vote_options) < 3) {
6698 reply("CSMSG_VOTE_NEED_OPTIONS");
6703 int find_id = atoi(argv[1]);
6705 unsigned int found = 0;
6708 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6710 if (find_id == ii) {
6711 struct vote_option *vOpt = iter_data(it);
6712 found = vOpt->option_id;
6714 sprintf(str,"%i",vOpt->option_id);
6715 dict_remove(cData->vote_options, str);
6720 for(uData = channel->channel_info->users; uData; uData = uData->next) {
6721 if(uData->votefor == found) {
6726 reply("CSMSG_DELOPTION_DONE");
6729 reply("CSMSG_DELOPTION_NONE");
6734 static CHANSERV_FUNC(cmd_vote)
6736 struct chanData *cData = channel->channel_info;
6737 struct userData *target;
6738 struct handle_info *hi;
6739 unsigned int votedfor = 0;
6740 char *votedfor_str = NULL;
6742 if (!cData || !cData->vote) {
6743 reply("CSMSG_NO_VOTE");
6746 if(argc > 1 && cData->vote_start) {
6747 hi = user->handle_info;
6748 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6750 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6753 if(!check_user_level(channel, user, lvlVote, 1, 0)) {
6754 reply("CSMSG_NO_ACCESS");
6758 reply("CSMSG_VOTE_VOTED");
6761 int find_id = atoi(argv[1]);
6764 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6766 if (find_id == ii) {
6767 struct vote_option *vOpt = iter_data(it);
6770 target->votefor = vOpt->option_id;
6771 votedfor = vOpt->option_id;
6772 votedfor_str = vOpt->name;
6776 reply("CSMSG_VOTE_INVALID");
6780 if (!cData->vote_start) {
6781 reply("CSMSG_VOTE_NOT_STARTED");
6783 reply("CSMSG_VOTE_QUESTION",cData->vote);
6785 unsigned int voteid = 0;
6788 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6789 struct vote_option *vOpt = iter_data(it);
6791 reply("CSMSG_VOTE_OPTION",voteid,vOpt->name,vOpt->voted);
6793 if(argc > 1 && cData->vote_start && votedfor_str) {
6794 reply("CSMSG_VOTE_DONE",votedfor_str);
6799 static CHANSERV_FUNC(cmd_startvote)
6801 struct chanData *cData = channel->channel_info;
6802 struct userData *target;
6803 struct handle_info *hi;
6804 if (!cData) return 0;
6805 hi = user->handle_info;
6806 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6808 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6811 if(target->access < 300) {
6812 reply("CSMSG_NO_ACCESS");
6816 reply("CSMSG_NO_VOTE");
6819 if(cData->vote_start) {
6820 reply("CSMSG_STARTVOTE_RUNNING");
6823 if(dict_size(cData->vote_options) < 2) {
6824 reply("CSMSG_VOTE_NEED_OPTIONS");
6827 cData->vote_start = 1;
6828 char response[MAXLEN];
6829 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_TOP"), user->nick);
6830 irc_privmsg(cmd->parent->bot, channel->name, response);
6831 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_QUESTION"), cData->vote);
6832 irc_privmsg(cmd->parent->bot, channel->name, response);
6833 unsigned int voteid = 0;
6835 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6836 struct vote_option *vOpt = iter_data(it);
6838 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_OPTION"), voteid, vOpt->name);
6839 irc_privmsg(cmd->parent->bot, channel->name, response);
6841 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_ACCESS"), cData->lvlOpts[lvlVote]); //Todo
6842 irc_privmsg(cmd->parent->bot, channel->name, response);
6843 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_HOWTO")); //Todo
6844 irc_privmsg(cmd->parent->bot, channel->name, response);
6848 static CHANSERV_FUNC(cmd_endvote)
6850 struct chanData *cData = channel->channel_info;
6851 struct userData *target;
6852 struct handle_info *hi;
6853 if (!cData) return 0;
6854 hi = user->handle_info;
6855 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6857 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6860 if(target->access < 300) {
6861 reply("CSMSG_NO_ACCESS");
6865 reply("CSMSG_NO_VOTE");
6868 if(!cData->vote_start) {
6869 reply("CSMSG_ENDVOTE_STOPPED");
6872 cData->vote_start = 0;
6873 reply("CSMSG_ENDVOTE_DONE");
6877 static CHANSERV_FUNC(cmd_voteresults)
6879 struct chanData *cData = channel->channel_info;
6880 struct userData *target;
6881 struct handle_info *hi;
6882 if (!cData) return 0;
6884 reply("CSMSG_NO_VOTE");
6887 if (argc > 1 && !irccasecmp(argv[1], "*")) {
6888 hi = user->handle_info;
6889 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6891 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6894 if(target->access < 300) {
6895 reply("CSMSG_NO_ACCESS");
6898 char response[MAXLEN];
6899 sprintf(response, user_find_message(user, "CSMSG_VOTERES_QUESTION"), cData->vote);
6900 irc_privmsg(cmd->parent->bot, channel->name, response);
6901 unsigned int voteid = 0;
6903 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6904 struct vote_option *vOpt = iter_data(it);
6906 sprintf(response, user_find_message(user, "CSMSG_VOTERES_OPTION"), voteid, vOpt->name, vOpt->voted);
6907 irc_privmsg(cmd->parent->bot, channel->name, response);
6910 reply("CSMSG_VOTE_QUESTION",cData->vote);
6911 unsigned int voteid = 0;
6913 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6914 struct vote_option *vOpt = iter_data(it);
6916 reply("CSMSG_VOTE_OPTION",voteid,vOpt->name,vOpt->voted);
6923 chanserv_refresh_topics(UNUSED_ARG(void *data))
6925 unsigned int refresh_num = (now - self->link_time) / chanserv_conf.refresh_period;
6926 struct chanData *cData;
6929 for(cData = channelList; cData; cData = cData->next)
6931 if(IsSuspended(cData))
6933 opt = cData->chOpts[chTopicRefresh];
6936 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
6939 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
6940 cData->last_refresh = refresh_num;
6942 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
6945 static CHANSERV_FUNC(cmd_unf)
6949 char response[MAXLEN];
6950 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
6951 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6952 irc_privmsg(cmd->parent->bot, channel->name, response);
6955 reply("CSMSG_UNF_RESPONSE");
6959 static CHANSERV_FUNC(cmd_ping)
6963 char response[MAXLEN];
6964 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
6965 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6966 irc_privmsg(cmd->parent->bot, channel->name, response);
6969 reply("CSMSG_PING_RESPONSE");
6973 static CHANSERV_FUNC(cmd_wut)
6977 char response[MAXLEN];
6978 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
6979 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6980 irc_privmsg(cmd->parent->bot, channel->name, response);
6983 reply("CSMSG_WUT_RESPONSE");
6987 static CHANSERV_FUNC(cmd_8ball)
6989 unsigned int i, j, accum;
6994 for(i=1; i<argc; i++)
6995 for(j=0; argv[i][j]; j++)
6996 accum = (accum << 5) - accum + toupper(argv[i][j]);
6997 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
7000 char response[MAXLEN];
7001 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
7002 irc_privmsg(cmd->parent->bot, channel->name, response);
7005 send_message_type(4, user, cmd->parent->bot, "%s", resp);
7009 static CHANSERV_FUNC(cmd_d)
7011 unsigned long sides, count, modifier, ii, total;
7012 char response[MAXLEN], *sep;
7016 if((count = strtoul(argv[1], &sep, 10)) < 1)
7026 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
7027 && (sides = strtoul(sep+1, &sep, 10)) > 1)
7031 else if((sep[0] == '-') && isdigit(sep[1]))
7032 modifier = strtoul(sep, NULL, 10);
7033 else if((sep[0] == '+') && isdigit(sep[1]))
7034 modifier = strtoul(sep+1, NULL, 10);
7041 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
7046 reply("CSMSG_BAD_DICE_COUNT", count, 10);
7049 for(total = ii = 0; ii < count; ++ii)
7050 total += (rand() % sides) + 1;
7053 if((count > 1) || modifier)
7055 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
7056 sprintf(response, fmt, total, count, sides, modifier);
7060 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
7061 sprintf(response, fmt, total, sides);
7064 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
7066 send_message_type(4, user, cmd->parent->bot, "%s", response);
7070 static CHANSERV_FUNC(cmd_huggle)
7072 /* CTCP must be via PRIVMSG, never notice */
7074 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
7076 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
7081 chanserv_adjust_limit(void *data)
7083 struct mod_chanmode change;
7084 struct chanData *cData = data;
7085 struct chanNode *channel = cData->channel;
7088 if(IsSuspended(cData))
7091 cData->limitAdjusted = now;
7092 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
7093 if(cData->modes.modes_set & MODE_LIMIT)
7095 if(limit > cData->modes.new_limit)
7096 limit = cData->modes.new_limit;
7097 else if(limit == cData->modes.new_limit)
7101 mod_chanmode_init(&change);
7102 change.modes_set = MODE_LIMIT;
7103 change.new_limit = limit;
7104 mod_chanmode_announce(chanserv, channel, &change);
7108 handle_new_channel(struct chanNode *channel)
7110 struct chanData *cData;
7112 if(!(cData = channel->channel_info))
7115 if(cData->modes.modes_set || cData->modes.modes_clear)
7116 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
7118 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
7119 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
7122 void handle_new_channel_created(char *chan, struct userNode *user) {
7123 if(user->handle_info && chanserv_conf.new_channel_authed) {
7124 send_target_message(5, chan, chanserv, "%s", chanserv_conf.new_channel_authed);
7125 } else if(!user->handle_info && chanserv_conf.new_channel_unauthed) {
7126 send_target_message(5, chan, chanserv, "%s", chanserv_conf.new_channel_unauthed);
7128 if(chanserv_conf.new_channel_msg)
7129 send_target_message(5, chan, chanserv, "%s", chanserv_conf.new_channel_msg);
7132 /* Welcome to my worst nightmare. Warning: Read (or modify)
7133 the code below at your own risk. */
7135 handle_join(struct modeNode *mNode)
7137 struct mod_chanmode change;
7138 struct userNode *user = mNode->user;
7139 struct chanNode *channel = mNode->channel;
7140 struct chanData *cData;
7141 struct userData *uData = NULL;
7142 struct banData *bData;
7143 struct handle_info *handle;
7144 unsigned int modes = 0, info = 0;
7148 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
7151 cData = channel->channel_info;
7152 if(channel->members.used > cData->max) {
7153 cData->max = channel->members.used;
7154 cData->max_time = now;
7157 for(i = 0; i < channel->invited.used; i++)
7159 if(channel->invited.list[i] == user) {
7160 userList_remove(&channel->invited, user);
7164 /* Check for bans. If they're joining through a ban, one of two
7166 * 1: Join during a netburst, by riding the break. Kick them
7167 * unless they have ops or voice in the channel.
7168 * 2: They're allowed to join through the ban (an invite in
7169 * ircu2.10, or a +e on Hybrid, or something).
7170 * If they're not joining through a ban, and the banlist is not
7171 * full, see if they're on the banlist for the channel. If so,
7174 if(user->uplink->burst && !mNode->modes)
7177 for(ii = 0; ii < channel->banlist.used; ii++)
7179 if(user_matches_glob(user, channel->banlist.list[ii]->ban, MATCH_USENICK))
7181 /* Riding a netburst. Naughty. */
7182 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
7188 mod_chanmode_init(&change);
7190 if(channel->banlist.used < MAXBANS)
7192 /* Not joining through a ban. */
7193 for(bData = cData->bans;
7194 bData && !user_matches_glob(user, bData->mask, MATCH_USENICK);
7195 bData = bData->next);
7199 char kick_reason[MAXLEN];
7200 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
7202 bData->triggered = now;
7203 if(bData != cData->bans)
7205 /* Shuffle the ban to the head of the list. */
7207 bData->next->prev = bData->prev;
7209 bData->prev->next = bData->next;
7212 bData->next = cData->bans;
7215 cData->bans->prev = bData;
7216 cData->bans = bData;
7219 change.args[0].mode = MODE_BAN;
7220 change.args[0].u.hostmask = bData->mask;
7221 mod_chanmode_announce(chanserv, channel, &change);
7222 KickChannelUser(user, channel, chanserv, kick_reason);
7227 /* ChanServ will not modify the limits in join-flooded channels,
7228 or when there are enough slots left below the limit. */
7229 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
7230 && !channel->join_flooded
7231 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
7233 /* The user count has begun "bumping" into the channel limit,
7234 so set a timer to raise the limit a bit. Any previous
7235 timers are removed so three incoming users within the delay
7236 results in one limit change, not three. */
7238 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7239 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7242 if(channel->join_flooded)
7244 /* don't automatically give ops or voice during a join flood */
7246 else if(cData->lvlOpts[lvlGiveOps] == 0)
7247 modes |= MODE_CHANOP;
7248 else if(cData->lvlOpts[lvlGiveVoice] == 0)
7249 modes |= MODE_VOICE;
7251 greeting = cData->greeting;
7252 if(user->handle_info)
7254 handle = user->handle_info;
7256 if(IsHelper(user) && !IsHelping(user))
7259 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7261 if(channel == chanserv_conf.support_channels.list[ii])
7263 HANDLE_SET_FLAG(user->handle_info, HELPING);
7269 uData = GetTrueChannelAccess(cData, handle);
7270 if(uData && !IsUserSuspended(uData))
7272 /* Ops and above were handled by the above case. */
7273 if(IsUserAutoOp(uData))
7275 if(uData->access >= cData->lvlOpts[lvlGiveOps])
7276 modes |= MODE_CHANOP;
7277 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
7278 modes |= MODE_VOICE;
7280 if(uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
7281 cData->visited = now;
7282 if(cData->user_greeting)
7283 greeting = cData->user_greeting;
7285 && (uData->access >= cData->lvlOpts[lvlUserInfo])
7286 && ((now - uData->seen) >= chanserv_conf.info_delay)
7294 /* If user joining normally (not during burst), apply op or voice,
7295 * and send greeting/userinfo as appropriate.
7297 if(!user->uplink->burst)
7301 if(modes & MODE_CHANOP)
7302 modes &= ~MODE_VOICE;
7303 change.args[0].mode = modes;
7304 change.args[0].u.member = mNode;
7305 mod_chanmode_announce(chanserv, channel, &change);
7308 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
7309 if(uData && info && (modes || !(channel->modes & MODE_DELAYJOINS)))
7310 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
7316 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
7318 struct mod_chanmode change;
7319 struct userData *channel;
7320 unsigned int ii, jj;
7322 if(!user->handle_info)
7325 mod_chanmode_init(&change);
7327 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
7329 struct chanNode *cn;
7330 struct modeNode *mn;
7331 if(IsUserSuspended(channel)
7332 || IsSuspended(channel->channel)
7333 || !(cn = channel->channel->channel))
7336 mn = GetUserMode(cn, user);
7339 if(!IsUserSuspended(channel)
7340 && IsUserAutoInvite(channel)
7341 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
7343 && !user->uplink->burst)
7344 irc_invite(chanserv, user, cn);
7348 if(channel->access >= UL_PRESENT && !HANDLE_FLAGGED(channel->handle, BOT))
7349 channel->channel->visited = now;
7351 if(IsUserAutoOp(channel))
7353 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
7354 change.args[0].mode = MODE_CHANOP;
7355 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
7356 change.args[0].mode = MODE_VOICE;
7358 change.args[0].mode = 0;
7359 change.args[0].u.member = mn;
7360 if(change.args[0].mode)
7361 mod_chanmode_announce(chanserv, cn, &change);
7364 channel->seen = now;
7365 channel->present = 1;
7368 for(ii = 0; ii < user->channels.used; ++ii)
7370 struct chanNode *chan = user->channels.list[ii]->channel;
7371 struct banData *ban;
7373 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
7374 || !chan->channel_info
7375 || IsSuspended(chan->channel_info))
7377 for(jj = 0; jj < chan->banlist.used; ++jj)
7378 if(user_matches_glob(user, chan->banlist.list[jj]->ban, MATCH_USENICK))
7380 if(jj < chan->banlist.used)
7382 for(ban = chan->channel_info->bans; ban; ban = ban->next)
7384 char kick_reason[MAXLEN];
7385 if(!user_matches_glob(user, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
7387 change.args[0].mode = MODE_BAN;
7388 change.args[0].u.hostmask = ban->mask;
7389 mod_chanmode_announce(chanserv, chan, &change);
7390 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
7391 KickChannelUser(user, chan, chanserv, kick_reason);
7392 ban->triggered = now;
7397 if(IsSupportHelper(user))
7399 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7401 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
7403 HANDLE_SET_FLAG(user->handle_info, HELPING);
7411 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
7413 struct chanData *cData;
7414 struct userData *uData;
7416 cData = mn->channel->channel_info;
7417 if(!cData || IsSuspended(cData) || IsLocal(mn->user))
7420 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
7422 /* Allow for a bit of padding so that the limit doesn't
7423 track the user count exactly, which could get annoying. */
7424 if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
7426 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7427 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7431 if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
7433 scan_user_presence(uData, mn->user);
7435 if (uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
7436 cData->visited = now;
7439 if(IsHelping(mn->user) && IsSupportHelper(mn->user))
7442 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii) {
7443 struct chanNode *channel;
7444 struct userNode *exclude;
7445 /* When looking at the channel that is being /part'ed, we
7446 * have to skip over the client that is leaving. For
7447 * other channels, we must not do that.
7449 channel = chanserv_conf.support_channels.list[ii];
7450 exclude = (channel == mn->channel) ? mn->user : NULL;
7451 if(find_handle_in_channel(channel, mn->user->handle_info, exclude))
7454 if(ii == chanserv_conf.support_channels.used)
7455 HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
7460 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
7462 struct userData *uData;
7464 if(!channel->channel_info || !kicker || IsService(kicker)
7465 || (kicker == victim) || IsSuspended(channel->channel_info)
7466 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
7469 if(protect_user(victim, kicker, channel->channel_info))
7471 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED_2");
7472 KickChannelUser(kicker, channel, chanserv, reason);
7475 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
7480 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
7482 struct chanData *cData;
7484 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
7487 cData = channel->channel_info;
7488 if(bad_topic(channel, user, channel->topic))
7490 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
7491 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
7492 SetChannelTopic(channel, chanserv, old_topic, 1);
7493 else if(cData->topic)
7494 SetChannelTopic(channel, chanserv, cData->topic, 1);
7497 /* With topicsnarf, grab the topic and save it as the default topic. */
7498 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
7501 cData->topic = strdup(channel->topic);
7507 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
7509 struct mod_chanmode *bounce = NULL;
7510 unsigned int bnc, ii;
7513 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
7516 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
7517 && mode_lock_violated(&channel->channel_info->modes, change))
7519 char correct[MAXLEN];
7520 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
7521 mod_chanmode_format(&channel->channel_info->modes, correct);
7522 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
7524 for(ii = bnc = 0; ii < change->argc; ++ii)
7526 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
7528 const struct userNode *victim = change->args[ii].u.member->user;
7529 if(!protect_user(victim, user, channel->channel_info))
7532 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7535 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
7536 bounce->args[bnc].u.member = GetUserMode(channel, user);
7537 if(bounce->args[bnc].u.member)
7541 bounce->args[bnc].mode = MODE_CHANOP;
7542 bounce->args[bnc].u.member = change->args[ii].u.member;
7544 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
7546 else if(change->args[ii].mode & MODE_CHANOP)
7548 const struct userNode *victim = change->args[ii].u.member->user;
7549 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
7552 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7553 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
7554 bounce->args[bnc].u.member = change->args[ii].u.member;
7557 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
7559 const char *ban = change->args[ii].u.hostmask;
7560 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
7563 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7564 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
7565 bounce->args[bnc].u.hostmask = strdup(ban);
7567 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
7572 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
7573 mod_chanmode_announce(chanserv, channel, bounce);
7574 for(ii = 0; ii < change->argc; ++ii)
7575 if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
7576 free((char*)bounce->args[ii].u.hostmask);
7577 mod_chanmode_free(bounce);
7582 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
7584 struct chanNode *channel;
7585 struct banData *bData;
7586 struct mod_chanmode change;
7587 unsigned int ii, jj;
7588 char kick_reason[MAXLEN];
7590 mod_chanmode_init(&change);
7592 change.args[0].mode = MODE_BAN;
7593 for(ii = 0; ii < user->channels.used; ++ii)
7595 channel = user->channels.list[ii]->channel;
7596 /* Need not check for bans if they're opped or voiced. */
7597 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
7599 /* Need not check for bans unless channel registration is active. */
7600 if(!channel->channel_info || IsSuspended(channel->channel_info))
7602 /* Look for a matching ban already on the channel. */
7603 for(jj = 0; jj < channel->banlist.used; ++jj)
7604 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
7606 /* Need not act if we found one. */
7607 if(jj < channel->banlist.used)
7609 /* Look for a matching ban in this channel. */
7610 for(bData = channel->channel_info->bans; bData; bData = bData->next)
7612 if(!user_matches_glob(user, bData->mask, MATCH_USENICK | MATCH_VISIBLE))
7614 change.args[0].u.hostmask = bData->mask;
7615 mod_chanmode_announce(chanserv, channel, &change);
7616 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
7617 KickChannelUser(user, channel, chanserv, kick_reason);
7618 bData->triggered = now;
7619 break; /* we don't need to check any more bans in the channel */
7624 static void handle_rename(struct handle_info *handle, const char *old_handle)
7626 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
7630 dict_remove2(handle_dnrs, old_handle, 1);
7631 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
7632 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
7637 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
7639 struct userNode *h_user;
7641 if(handle->channels)
7643 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
7644 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
7646 while(handle->channels)
7647 del_channel_user(handle->channels, 1);
7652 handle_server_link(UNUSED_ARG(struct server *server))
7654 struct chanData *cData;
7656 for(cData = channelList; cData; cData = cData->next)
7658 if(!IsSuspended(cData))
7659 cData->may_opchan = 1;
7660 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
7661 && !cData->channel->join_flooded
7662 && ((cData->channel->limit - cData->channel->members.used)
7663 < chanserv_conf.adjust_threshold))
7665 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7666 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7672 chanserv_conf_read(void)
7676 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
7677 struct mod_chanmode *change;
7678 struct string_list *strlist;
7679 struct chanNode *chan;
7682 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
7684 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
7687 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7688 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7689 chanserv_conf.support_channels.used = 0;
7690 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
7692 for(ii = 0; ii < strlist->used; ++ii)
7694 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7697 chan = AddChannel(strlist->list[ii], now, str2, NULL);
7699 channelList_append(&chanserv_conf.support_channels, chan);
7702 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
7705 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7708 chan = AddChannel(str, now, str2, NULL);
7710 channelList_append(&chanserv_conf.support_channels, chan);
7712 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
7713 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
7714 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
7715 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
7716 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
7717 chanserv_conf.greeting_length = str ? atoi(str) : 200;
7718 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
7719 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
7720 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
7721 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
7722 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
7723 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
7724 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
7725 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
7726 str = database_get_data(conf_node, KEY_DNR_EXPIRE_FREQ, RECDB_QSTRING);
7727 chanserv_conf.dnr_expire_frequency = str ? ParseInterval(str) : 3600;
7728 str = database_get_data(conf_node, KEY_INVITED_INTERVAL, RECDB_QSTRING);
7729 chanserv_conf.invited_timeout = str ? ParseInterval(str) : 600*2;
7730 str = database_get_data(conf_node, KEY_REVOKE_MODE_A, RECDB_QSTRING);
7731 chanserv_conf.revoke_mode_a = str ? atoi(str) : 1;
7732 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
7733 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
7734 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
7735 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
7736 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
7737 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
7738 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
7739 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
7740 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
7742 NickChange(chanserv, str, 0);
7743 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
7744 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
7745 str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
7746 chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
7747 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
7748 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
7749 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
7750 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
7751 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
7752 chanserv_conf.max_owned = str ? atoi(str) : 5;
7753 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
7754 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
7755 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
7756 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
7757 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
7758 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
7759 str = database_get_data(conf_node, KEY_NEW_CHANNEL_AUTHED, RECDB_QSTRING);
7760 chanserv_conf.new_channel_authed = (str && *str) ? str : NULL;
7761 str = database_get_data(conf_node, KEY_NEW_CHANNEL_UNAUTHED, RECDB_QSTRING);
7762 chanserv_conf.new_channel_unauthed = (str && *str) ? str : NULL;
7763 str = database_get_data(conf_node, KEY_NEW_CHANNEL_MSG, RECDB_QSTRING);
7764 chanserv_conf.new_channel_msg = (str && *str) ? str : NULL;
7765 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
7768 safestrncpy(mode_line, str, sizeof(mode_line));
7769 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
7770 if((change = mod_chanmode_parse(NULL, NULL, modes, ii, MCP_KEY_FREE|MCP_NO_APASS, 0))
7771 && (change->argc < 2))
7773 chanserv_conf.default_modes = *change;
7774 mod_chanmode_free(change);
7776 free_string_list(chanserv_conf.set_shows);
7777 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
7779 strlist = string_list_copy(strlist);
7782 static const char *list[] = {
7783 /* free form text */
7784 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
7785 /* options based on user level */
7786 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
7787 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
7788 /* multiple choice options */
7789 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
7790 /* binary options */
7791 "DynLimit", "NoDelete", "expire", "Vote",
7795 strlist = alloc_string_list(ArrayLength(list)-1);
7796 for(ii=0; list[ii]; ii++)
7797 string_list_append(strlist, strdup(list[ii]));
7799 chanserv_conf.set_shows = strlist;
7800 /* We don't look things up now, in case the list refers to options
7801 * defined by modules initialized after this point. Just mark the
7802 * function list as invalid, so it will be initialized.
7804 set_shows_list.used = 0;
7805 free_string_list(chanserv_conf.eightball);
7806 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
7809 strlist = string_list_copy(strlist);
7813 strlist = alloc_string_list(4);
7814 string_list_append(strlist, strdup("Yes."));
7815 string_list_append(strlist, strdup("No."));
7816 string_list_append(strlist, strdup("Maybe so."));
7818 chanserv_conf.eightball = strlist;
7819 free_string_list(chanserv_conf.old_ban_names);
7820 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
7822 strlist = string_list_copy(strlist);
7824 strlist = alloc_string_list(2);
7825 chanserv_conf.old_ban_names = strlist;
7826 str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
7827 off_channel = str ? atoi(str) : 0;
7831 chanserv_note_type_read(const char *key, struct record_data *rd)
7834 struct note_type *ntype;
7837 if(!(obj = GET_RECORD_OBJECT(rd)))
7839 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
7842 if(!(ntype = chanserv_create_note_type(key)))
7844 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
7848 /* Figure out set access */
7849 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
7851 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7852 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
7854 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
7856 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
7857 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
7859 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
7861 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
7865 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
7866 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7867 ntype->set_access.min_opserv = 0;
7870 /* Figure out visibility */
7871 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
7872 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7873 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
7874 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7875 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
7876 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
7877 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
7878 ntype->visible_type = NOTE_VIS_ALL;
7880 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7882 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
7883 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
7887 vote_option_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7889 struct vote_option *vOpt;
7892 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7894 log_module(CS_LOG, LOG_ERROR, "Invalid vote option in %s.", chan->channel->name);
7898 vOpt = calloc(1, sizeof(*vOpt));
7899 vOpt->name = strdup(database_get_data(rd->d.object, KEY_VOTE_OPTION_NAME, RECDB_QSTRING));
7900 str = database_get_data(rd->d.object, KEY_VOTE_OPTION_VOTED, RECDB_QSTRING);
7901 vOpt->voted = str ? atoi(str) : 0;
7902 vOpt->option_id = str ? atoi(key) : 0;
7903 vOpt->option_str = strdup(key);
7904 dict_insert(chan->vote_options,vOpt->option_str,vOpt);
7908 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7910 struct handle_info *handle;
7911 struct userData *uData;
7912 char *seen, *inf, *flags, *voted, *votefor;
7913 unsigned long last_seen;
7914 unsigned short access_level;
7916 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7918 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
7922 access_level = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
7923 if(access_level > UL_OWNER)
7925 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
7929 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
7930 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
7931 last_seen = seen ? strtoul(seen, NULL, 0) : now;
7932 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
7933 voted = database_get_data(rd->d.object, KEY_VOTE_VOTED, RECDB_QSTRING);
7934 votefor = database_get_data(rd->d.object, KEY_VOTE_VOTEDFOR, RECDB_QSTRING);
7935 handle = get_handle_info(key);
7938 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
7942 uData = add_channel_user(chan, handle, access_level, last_seen, inf);
7943 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
7945 uData->voted = voted ? strtoul(voted, NULL, 0) : 0;
7946 uData->votefor = votefor ? strtoul(votefor, NULL, 0) : 0;
7954 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7956 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
7957 unsigned long set_time, triggered_time, expires_time;
7959 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7961 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
7965 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
7966 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
7967 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
7968 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
7969 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
7970 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
7971 if (!reason || !owner)
7974 set_time = set ? strtoul(set, NULL, 0) : now;
7975 triggered_time = triggered ? strtoul(triggered, NULL, 0) : 0;
7977 expires_time = strtoul(s_expires, NULL, 0);
7979 expires_time = set_time + atoi(s_duration);
7983 if(!reason || (expires_time && (expires_time < now)))
7986 add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
7989 static struct suspended *
7990 chanserv_read_suspended(dict_t obj)
7992 struct suspended *suspended = calloc(1, sizeof(*suspended));
7996 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
7997 suspended->expires = str ? strtoul(str, NULL, 0) : 0;
7998 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
7999 suspended->revoked = str ? strtoul(str, NULL, 0) : 0;
8000 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
8001 suspended->issued = str ? strtoul(str, NULL, 0) : 0;
8002 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
8003 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
8004 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
8005 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
8009 static struct giveownership *
8010 chanserv_read_giveownership(dict_t obj)
8012 struct giveownership *giveownership = calloc(1, sizeof(*giveownership));
8016 str = database_get_data(obj, KEY_STAFF_ISSUER, RECDB_QSTRING);
8017 giveownership->staff_issuer = str ? strdup(str) : NULL;
8019 giveownership->old_owner = strdup(database_get_data(obj, KEY_OLD_OWNER, RECDB_QSTRING));
8021 giveownership->target = strdup(database_get_data(obj, KEY_TARGET, RECDB_QSTRING));
8022 giveownership->target_access = atoi(database_get_data(obj, KEY_TARGET_ACCESS, RECDB_QSTRING));
8024 str = database_get_data(obj, KEY_REASON, RECDB_QSTRING);
8025 giveownership->reason = str ? strdup(str) : NULL;
8026 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
8027 giveownership->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
8029 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
8030 giveownership->previous = previous ? chanserv_read_giveownership(previous) : NULL;
8031 return giveownership;
8035 chanserv_channel_read(const char *key, struct record_data *hir)
8037 struct suspended *suspended;
8038 struct giveownership *giveownership;
8039 struct mod_chanmode *modes;
8040 struct chanNode *cNode;
8041 struct chanData *cData;
8042 struct dict *channel, *obj;
8043 char *str, *argv[10];
8047 channel = hir->d.object;
8049 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
8052 cNode = AddChannel(key, now, NULL, NULL);
8055 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
8058 cData = register_channel(cNode, str);
8061 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
8065 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
8067 enum levelOption lvlOpt;
8068 enum charOption chOpt;
8070 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
8071 cData->flags = atoi(str);
8073 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8075 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
8077 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
8078 else if(levelOptions[lvlOpt].old_flag)
8080 if(cData->flags & levelOptions[lvlOpt].old_flag)
8081 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
8083 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
8087 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8089 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
8091 cData->chOpts[chOpt] = str[0];
8094 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
8096 enum levelOption lvlOpt;
8097 enum charOption chOpt;
8100 cData->flags = base64toint(str, 5);
8101 count = strlen(str += 5);
8102 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8105 if(levelOptions[lvlOpt].old_flag)
8107 if(cData->flags & levelOptions[lvlOpt].old_flag)
8108 lvl = levelOptions[lvlOpt].flag_value;
8110 lvl = levelOptions[lvlOpt].default_value;
8112 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
8114 case 'c': lvl = UL_COOWNER; break;
8115 case 'm': lvl = UL_MASTER; break;
8116 case 'n': lvl = UL_OWNER+1; break;
8117 case 'o': lvl = UL_OP; break;
8118 case 'p': lvl = UL_PEON; break;
8119 case 'w': lvl = UL_OWNER; break;
8120 default: lvl = 0; break;
8122 cData->lvlOpts[lvlOpt] = lvl;
8124 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8125 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
8128 if((str = database_get_data(hir->d.object, KEY_EXPIRE, RECDB_QSTRING)))
8130 cData->expiry = atoi(str);
8131 if(cData->expiry > 0) {
8132 if(cData->expiry > now) {
8133 timeq_add(cData->expiry, chanserv_expire_channel, cData);
8135 timeq_add(1, chanserv_expire_channel, cData);
8142 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
8144 suspended = chanserv_read_suspended(obj);
8145 cData->suspended = suspended;
8146 suspended->cData = cData;
8147 /* We could use suspended->expires and suspended->revoked to
8148 * set the CHANNEL_SUSPENDED flag, but we don't. */
8150 else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
8152 suspended = calloc(1, sizeof(*suspended));
8153 suspended->issued = 0;
8154 suspended->revoked = 0;
8155 suspended->suspender = strdup(str);
8156 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
8157 suspended->expires = str ? atoi(str) : 0;
8158 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
8159 suspended->reason = strdup(str ? str : "No reason");
8160 suspended->previous = NULL;
8161 cData->suspended = suspended;
8162 suspended->cData = cData;
8166 cData->flags &= ~CHANNEL_SUSPENDED;
8167 suspended = NULL; /* to squelch a warning */
8170 if(IsSuspended(cData)) {
8171 if(suspended->expires > now)
8172 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
8173 else if(suspended->expires)
8174 cData->flags &= ~CHANNEL_SUSPENDED;
8177 if((obj = database_get_data(hir->d.object, KEY_GIVEOWNERSHIP, RECDB_OBJECT)))
8179 giveownership = chanserv_read_giveownership(obj);
8180 cData->giveownership = giveownership;
8183 if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
8184 struct mod_chanmode change;
8185 mod_chanmode_init(&change);
8187 change.args[0].mode = MODE_CHANOP;
8188 change.args[0].u.member = AddChannelUser(chanserv, cNode);
8189 mod_chanmode_announce(chanserv, cNode, &change);
8192 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
8193 cData->registered = str ? strtoul(str, NULL, 0) : now;
8194 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
8195 cData->visited = str ? strtoul(str, NULL, 0) : now;
8196 str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
8197 cData->ownerTransfer = str ? strtoul(str, NULL, 0) : 0;
8198 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
8199 cData->max = str ? atoi(str) : 0;
8200 str = database_get_data(channel, KEY_MAX_TIME, RECDB_QSTRING);
8201 cData->max_time = str ? atoi(str) : 0;
8202 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
8203 cData->greeting = str ? strdup(str) : NULL;
8204 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
8205 cData->user_greeting = str ? strdup(str) : NULL;
8206 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
8207 cData->topic_mask = str ? strdup(str) : NULL;
8208 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
8209 cData->topic = str ? strdup(str) : NULL;
8211 str = database_get_data(channel, KEY_VOTE, RECDB_QSTRING);
8213 cData->vote = str ? strdup(str) : NULL;
8214 dict_delete(cData->vote_options);
8215 cData->vote_options = dict_new();
8216 dict_set_free_data(cData->vote_options, free_vote_options);
8217 str = database_get_data(channel, KEY_VOTE_START, RECDB_QSTRING);
8218 cData->vote_start = str ? atoi(str) : 0;
8219 obj = database_get_data(channel, KEY_VOTE_OPTIONS, RECDB_OBJECT);
8220 for(it = dict_first(obj); it; it = iter_next(it)) {
8221 vote_option_read_helper(iter_key(it), iter_data(it), cData);
8225 if(!IsSuspended(cData)
8226 && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
8227 && (argc = split_line(str, 0, ArrayLength(argv), argv))
8228 && (modes = mod_chanmode_parse(cNode, NULL, argv, argc, MCP_KEY_FREE|MCP_NO_APASS, 0))) {
8229 cData->modes = *modes;
8231 cData->modes.modes_set |= MODE_REGISTERED;
8232 if(cData->modes.argc > 1)
8233 cData->modes.argc = 1;
8234 mod_chanmode_announce(chanserv, cNode, &cData->modes);
8235 mod_chanmode_free(modes);
8238 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
8239 for(it = dict_first(obj); it; it = iter_next(it))
8240 user_read_helper(iter_key(it), iter_data(it), cData);
8242 if(!cData->users && !IsProtected(cData))
8244 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
8245 unregister_channel(cData, "has empty user list.");
8249 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
8250 for(it = dict_first(obj); it; it = iter_next(it))
8251 ban_read_helper(iter_key(it), iter_data(it), cData);
8253 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
8254 for(it = dict_first(obj); it; it = iter_next(it))
8256 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
8257 struct record_data *rd = iter_data(it);
8258 const char *note, *setter;
8260 if(rd->type != RECDB_OBJECT)
8262 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
8266 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
8268 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
8270 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
8274 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
8275 if(!setter) setter = "<unknown>";
8276 chanserv_add_channel_note(cData, ntype, setter, note);
8284 chanserv_dnr_read(const char *key, struct record_data *hir)
8286 const char *setter, *reason, *str;
8287 struct do_not_register *dnr;
8288 unsigned long expiry;
8290 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
8293 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
8296 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
8299 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
8302 str = database_get_data(hir->d.object, KEY_EXPIRES, RECDB_QSTRING);
8303 expiry = str ? strtoul(str, NULL, 0) : 0;
8304 if(expiry && expiry <= now)
8306 dnr = chanserv_add_dnr(key, setter, expiry, reason);
8309 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
8311 dnr->set = atoi(str);
8317 chanserv_saxdb_read(struct dict *database)
8319 struct dict *section;
8322 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
8323 for(it = dict_first(section); it; it = iter_next(it))
8324 chanserv_note_type_read(iter_key(it), iter_data(it));
8326 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
8327 for(it = dict_first(section); it; it = iter_next(it))
8328 chanserv_channel_read(iter_key(it), iter_data(it));
8330 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
8331 for(it = dict_first(section); it; it = iter_next(it))
8332 chanserv_dnr_read(iter_key(it), iter_data(it));
8338 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
8340 int high_present = 0;
8341 saxdb_start_record(ctx, KEY_USERS, 1);
8342 for(; uData; uData = uData->next)
8344 if((uData->access >= UL_PRESENT) && uData->present && !HANDLE_FLAGGED(uData->handle, BOT))
8346 saxdb_start_record(ctx, uData->handle->handle, 0);
8347 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
8348 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
8350 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
8351 if(uData->channel->vote && uData->voted)
8352 saxdb_write_int(ctx, KEY_VOTE_VOTED, uData->voted);
8353 if(uData->channel->vote && uData->votefor)
8354 saxdb_write_int(ctx, KEY_VOTE_VOTEDFOR, uData->votefor);
8356 saxdb_write_string(ctx, KEY_INFO, uData->info);
8357 saxdb_end_record(ctx);
8359 saxdb_end_record(ctx);
8360 return high_present;
8364 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
8368 saxdb_start_record(ctx, KEY_BANS, 1);
8369 for(; bData; bData = bData->next)
8371 saxdb_start_record(ctx, bData->mask, 0);
8372 saxdb_write_int(ctx, KEY_SET, bData->set);
8373 if(bData->triggered)
8374 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
8376 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
8378 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
8380 saxdb_write_string(ctx, KEY_REASON, bData->reason);
8381 saxdb_end_record(ctx);
8383 saxdb_end_record(ctx);
8387 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
8389 saxdb_start_record(ctx, name, 0);
8390 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
8391 saxdb_write_string(ctx, KEY_REASON, susp->reason);
8393 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
8395 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
8397 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
8399 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
8400 saxdb_end_record(ctx);
8404 chanserv_write_giveownership(struct saxdb_context *ctx, const char *name, struct giveownership *giveownership)
8406 saxdb_start_record(ctx, name, 0);
8407 if(giveownership->staff_issuer)
8408 saxdb_write_string(ctx, KEY_STAFF_ISSUER, giveownership->staff_issuer);
8409 if(giveownership->old_owner)
8410 saxdb_write_string(ctx, KEY_OLD_OWNER, giveownership->old_owner);
8411 if(giveownership->target)
8412 saxdb_write_string(ctx, KEY_TARGET, giveownership->target);
8413 if(giveownership->target_access)
8414 saxdb_write_int(ctx, KEY_TARGET_ACCESS, giveownership->target_access);
8415 if(giveownership->reason)
8416 saxdb_write_string(ctx, KEY_REASON, giveownership->reason);
8417 if(giveownership->issued)
8418 saxdb_write_int(ctx, KEY_ISSUED, giveownership->issued);
8419 if(giveownership->previous)
8420 chanserv_write_giveownership(ctx, KEY_PREVIOUS, giveownership->previous);
8421 saxdb_end_record(ctx);
8425 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
8429 enum levelOption lvlOpt;
8430 enum charOption chOpt;
8433 saxdb_start_record(ctx, channel->channel->name, 1);
8435 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
8436 saxdb_write_int(ctx, KEY_MAX, channel->max);
8437 saxdb_write_int(ctx, KEY_MAX_TIME, channel->max_time);
8439 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
8440 if(channel->registrar)
8441 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
8442 if(channel->greeting)
8443 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
8444 if(channel->user_greeting)
8445 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
8446 if(channel->topic_mask)
8447 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
8448 if(channel->suspended)
8449 chanserv_write_suspended(ctx, "suspended", channel->suspended);
8450 if(channel->giveownership)
8451 chanserv_write_giveownership(ctx, "giveownership", channel->giveownership);
8453 saxdb_write_int(ctx, KEY_EXPIRE, channel->expiry);
8456 saxdb_write_string(ctx, KEY_VOTE, channel->vote);
8457 if(channel->vote_start)
8458 saxdb_write_int(ctx, KEY_VOTE_START, channel->vote_start);
8459 if (dict_size(channel->vote_options)) {
8460 saxdb_start_record(ctx, KEY_VOTE_OPTIONS, 1);
8461 for (it = dict_first(channel->vote_options); it; it = iter_next(it)) {
8462 struct vote_option *vOpt = iter_data(it);
8464 sprintf(str,"%i",vOpt->option_id);
8465 saxdb_start_record(ctx, str, 0);
8467 saxdb_write_int(ctx, KEY_VOTE_OPTION_VOTED, vOpt->voted);
8469 saxdb_write_string(ctx, KEY_VOTE_OPTION_NAME, vOpt->name);
8470 saxdb_end_record(ctx);
8472 saxdb_end_record(ctx);
8476 saxdb_start_record(ctx, KEY_OPTIONS, 0);
8477 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
8478 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8479 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
8480 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8482 buf[0] = channel->chOpts[chOpt];
8484 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
8486 saxdb_end_record(ctx);
8488 if(channel->modes.modes_set || channel->modes.modes_clear)
8490 mod_chanmode_format(&channel->modes, buf);
8491 saxdb_write_string(ctx, KEY_MODES, buf);
8494 high_present = chanserv_write_users(ctx, channel->users);
8495 chanserv_write_bans(ctx, channel->bans);
8497 if(dict_size(channel->notes))
8501 saxdb_start_record(ctx, KEY_NOTES, 1);
8502 for(it = dict_first(channel->notes); it; it = iter_next(it))
8504 struct note *note = iter_data(it);
8505 saxdb_start_record(ctx, iter_key(it), 0);
8506 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
8507 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
8508 saxdb_end_record(ctx);
8510 saxdb_end_record(ctx);
8513 if(channel->ownerTransfer)
8514 saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
8515 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
8516 saxdb_end_record(ctx);
8520 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
8524 saxdb_start_record(ctx, ntype->name, 0);
8525 switch(ntype->set_access_type)
8527 case NOTE_SET_CHANNEL_ACCESS:
8528 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
8530 case NOTE_SET_CHANNEL_SETTER:
8531 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
8533 case NOTE_SET_PRIVILEGED: default:
8534 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
8537 switch(ntype->visible_type)
8539 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
8540 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
8541 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
8543 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
8544 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
8545 saxdb_end_record(ctx);
8549 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
8551 struct do_not_register *dnr;
8552 dict_iterator_t it, next;
8554 for(it = dict_first(dnrs); it; it = next)
8556 next = iter_next(it);
8557 dnr = iter_data(it);
8558 if(dnr->expires && dnr->expires <= now)
8560 dict_remove(dnrs, iter_key(it));
8563 saxdb_start_record(ctx, dnr->chan_name, 0);
8565 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
8567 saxdb_write_int(ctx, KEY_EXPIRES, dnr->expires);
8568 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
8569 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
8570 saxdb_end_record(ctx);
8575 chanserv_saxdb_write(struct saxdb_context *ctx)
8578 struct chanData *channel;
8581 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
8582 for(it = dict_first(note_types); it; it = iter_next(it))
8583 chanserv_write_note_type(ctx, iter_data(it));
8584 saxdb_end_record(ctx);
8587 saxdb_start_record(ctx, KEY_DNR, 1);
8588 write_dnrs_helper(ctx, handle_dnrs);
8589 write_dnrs_helper(ctx, plain_dnrs);
8590 write_dnrs_helper(ctx, mask_dnrs);
8591 saxdb_end_record(ctx);
8594 saxdb_start_record(ctx, KEY_CHANNELS, 1);
8595 for(channel = channelList; channel; channel = channel->next)
8596 chanserv_write_channel(ctx, channel);
8597 saxdb_end_record(ctx);
8603 chanserv_db_cleanup(void) {
8605 unreg_part_func(handle_part);
8607 unregister_channel(channelList, "terminating.");
8608 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
8609 UnlockChannel(chanserv_conf.support_channels.list[ii]);
8610 free(chanserv_conf.support_channels.list);
8611 dict_delete(handle_dnrs);
8612 dict_delete(plain_dnrs);
8613 dict_delete(mask_dnrs);
8614 dict_delete(note_types);
8615 free_string_list(chanserv_conf.eightball);
8616 free_string_list(chanserv_conf.old_ban_names);
8617 free_string_list(chanserv_conf.set_shows);
8618 free(set_shows_list.list);
8619 free(uset_shows_list.list);
8622 struct userData *helper = helperList;
8623 helperList = helperList->next;
8628 #if defined(GCC_VARMACROS)
8629 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ARGS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ARGS)
8630 #elif defined(C99_VARMACROS)
8631 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, __VA_ARGS__)
8633 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
8634 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
8637 init_chanserv(const char *nick)
8639 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
8640 conf_register_reload(chanserv_conf_read);
8644 reg_server_link_func(handle_server_link);
8645 reg_new_channel_func(handle_new_channel);
8646 reg_join_func(handle_join);
8647 reg_part_func(handle_part);
8648 reg_kick_func(handle_kick);
8649 reg_topic_func(handle_topic);
8650 reg_mode_change_func(handle_mode);
8651 reg_nick_change_func(handle_nick_change);
8652 reg_auth_func(handle_auth);
8655 reg_handle_rename_func(handle_rename);
8656 reg_unreg_func(handle_unreg);
8658 handle_dnrs = dict_new();
8659 dict_set_free_data(handle_dnrs, free);
8660 plain_dnrs = dict_new();
8661 dict_set_free_data(plain_dnrs, free);
8662 mask_dnrs = dict_new();
8663 dict_set_free_data(mask_dnrs, free);
8665 reg_svccmd_unbind_func(handle_svccmd_unbind);
8666 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
8667 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
8668 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
8669 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
8670 DEFINE_COMMAND(dnrsearch, 3, 0, "template", "noregister", NULL);
8671 modcmd_register(chanserv_module, "dnrsearch print", NULL, 0, 0, NULL);
8672 modcmd_register(chanserv_module, "dnrsearch remove", NULL, 0, 0, NULL);
8673 modcmd_register(chanserv_module, "dnrsearch count", NULL, 0, 0, NULL);
8674 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
8675 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
8676 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
8677 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
8678 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
8680 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
8681 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
8683 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8684 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8685 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8686 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8687 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
8689 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
8690 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
8691 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
8692 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8693 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8695 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8696 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
8697 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8698 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
8700 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
8701 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
8702 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8703 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8704 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
8705 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8706 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8707 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8709 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8710 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8711 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8712 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
8713 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
8714 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
8715 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
8716 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
8717 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8718 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
8719 DEFINE_COMMAND(invitemeall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8720 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
8721 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
8722 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8723 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8725 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
8726 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8727 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8728 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8729 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
8731 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
8732 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
8734 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
8735 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8736 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8737 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8738 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8739 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8740 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8741 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8742 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8743 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8744 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8746 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
8747 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
8749 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
8750 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
8751 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
8752 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
8754 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
8755 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
8756 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
8757 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
8758 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
8760 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8761 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8762 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8763 DEFINE_COMMAND(8ball, 2, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8764 DEFINE_COMMAND(d, 2, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8765 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8767 DEFINE_COMMAND(addvote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8768 DEFINE_COMMAND(delvote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8769 DEFINE_COMMAND(addoption, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8770 DEFINE_COMMAND(deloption, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8771 DEFINE_COMMAND(vote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8772 DEFINE_COMMAND(startvote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8773 DEFINE_COMMAND(endvote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8774 DEFINE_COMMAND(voteresults, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8776 DEFINE_COMMAND(opme, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
8778 /* Channel options */
8779 DEFINE_CHANNEL_OPTION(defaulttopic);
8780 DEFINE_CHANNEL_OPTION(topicmask);
8781 DEFINE_CHANNEL_OPTION(greeting);
8782 DEFINE_CHANNEL_OPTION(usergreeting);
8783 DEFINE_CHANNEL_OPTION(modes);
8784 DEFINE_CHANNEL_OPTION(enfops);
8785 DEFINE_CHANNEL_OPTION(giveops);
8786 DEFINE_CHANNEL_OPTION(protect);
8787 DEFINE_CHANNEL_OPTION(enfmodes);
8788 DEFINE_CHANNEL_OPTION(enftopic);
8789 DEFINE_CHANNEL_OPTION(pubcmd);
8790 DEFINE_CHANNEL_OPTION(givevoice);
8791 DEFINE_CHANNEL_OPTION(userinfo);
8792 DEFINE_CHANNEL_OPTION(dynlimit);
8793 DEFINE_CHANNEL_OPTION(topicsnarf);
8794 DEFINE_CHANNEL_OPTION(vote);
8795 DEFINE_CHANNEL_OPTION(nodelete);
8796 DEFINE_CHANNEL_OPTION(toys);
8797 DEFINE_CHANNEL_OPTION(setters);
8798 DEFINE_CHANNEL_OPTION(topicrefresh);
8799 DEFINE_CHANNEL_OPTION(ctcpusers);
8800 DEFINE_CHANNEL_OPTION(ctcpreaction);
8801 DEFINE_CHANNEL_OPTION(inviteme);
8802 DEFINE_CHANNEL_OPTION(unreviewed);
8803 modcmd_register(chanserv_module, "set expire", chan_opt_expire, 1, 0, "flags", "+helping", NULL);
8804 modcmd_register(chanserv_module, "set unreviewed on", NULL, 0, 0, "flags", "+helping", NULL);
8805 modcmd_register(chanserv_module, "set unreviewed off", NULL, 0, 0, "flags", "+oper", NULL);
8807 DEFINE_CHANNEL_OPTION(offchannel);
8808 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
8810 /* Alias set topic to set defaulttopic for compatibility. */
8811 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
8814 DEFINE_USER_OPTION(noautoop);
8815 DEFINE_USER_OPTION(autoinvite);
8816 DEFINE_USER_OPTION(info);
8818 /* Alias uset autovoice to uset autoop. */
8819 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
8821 note_types = dict_new();
8822 dict_set_free_data(note_types, chanserv_deref_note_type);
8825 const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
8826 chanserv = AddLocalUser(nick, nick, NULL, "Channel Services", modes);
8827 service_register(chanserv)->trigger = '!';
8828 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
8830 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
8832 if(chanserv_conf.channel_expire_frequency)
8833 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
8835 if(chanserv_conf.dnr_expire_frequency)
8836 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
8838 if(chanserv_conf.refresh_period)
8840 unsigned long next_refresh;
8841 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
8842 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
8845 reg_exit_func(chanserv_db_cleanup);
8846 message_register_table(msgtab);