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_CHANNEL_OWNERSHIP_HISTORY", "Ownership transfer history for %s" },
436 { "CSMSG_CHANNEL_OWNERSHIP_STAFF_REASON", " from %s to %s (%d access) by %s on %s (Reason: %s)" },
437 { "CSMSG_CHANNEL_OWNERSHIP_STAFF", " from %s to %s (%d access) by %s on %s" },
438 { "CSMSG_CHANNEL_OWNERSHIP_NORMAL", " from %s to %s (%d access) on %s" },
440 { "CSMSG_PEEK_INFO", "$b%s$b Status:" },
441 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
442 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
443 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d (%d ops, %d voices, %d regulars)" },
444 { "CSMSG_PEEK_OPS", "$bOps:$b" },
445 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
447 /* Network information */
448 { "CSMSG_NETWORK_INFO", "Network Information:" },
449 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
450 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
451 { "CSMSG_NETWORK_BANS", "$bTotal Ban Count: $b%i" },
452 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
453 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
454 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
455 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
456 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
459 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
460 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
461 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
463 /* Channel searches */
464 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
465 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
466 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
467 { "CSMSG_CHANNEL_SEARCH_RESULTS", "The following channels were found:" },
469 /* Channel configuration */
470 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
471 { "CSMSG_INVALID_CFLAG", "$b%s$b is not a recognized channel flag." },
472 { "CSMSG_CHANNEL_OPTIONS", "Channel Options:" },
473 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
476 { "CSMSG_USER_OPTIONS", "User Options:" },
477 { "CSMSG_USER_PROTECTED_2", "That user is protected." },
480 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
481 { "CSMSG_PING_RESPONSE", "Pong!" },
482 { "CSMSG_WUT_RESPONSE", "wut" },
483 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
484 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
485 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
486 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
487 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
488 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
489 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
492 { "CSMSG_ADDVOTE_DONE", "Vote added. Use $baddoption$b to add at least 2 vote options and then $bstartvote$b to start the voting." },
493 { "CSMSG_ADDVOTE_FULL", "There is already a vote in this channel. Use $bdelvote$b to delete it." },
494 { "CSMSG_DELVOTE_DONE", "Vote deleted." },
495 { "CSMSG_NO_VOTE", "There is no vote in this channel." },
496 { "CSMSG_ADDOPTION_DONE", "Vote option added with id %i (%i - %i)." },
497 { "CSMSG_DELOPTION_DONE", "Vote option deleted." },
498 { "CSMSG_DELOPTION_NONE", "Vote option does not exist." },
499 { "CSMSG_VOTE_NEED_OPTIONS", "There must be at least 2 options in a vote." },
500 { "CSMSG_VOTE_NOT_STARTED", "The vote is not started. Use $bstartvote$b to allow voting." },
501 { "CSMSG_VOTE_QUESTION", "Question: %s" },
502 { "CSMSG_VOTE_OPTION", "$b%i$b: %s ($b%i$b votes)" },
503 { "CSMSG_VOTERES_QUESTION", "Question: %s" },
504 { "CSMSG_VOTERES_OPTION", "
\ 2%i
\ 2: %s (
\ 2%i
\ 2 votes)" },
505 { "CSMSG_STARTVOTE_TOP", "
\ 2%s
\ 2 has started a voting:" },
506 { "CSMSG_STARTVOTE_QUESTION", "
\ 2Question:
\ 2 %s" },
507 { "CSMSG_STARTVOTE_OPTION", "
\ 2%i:
\ 2 %s" },
508 { "CSMSG_STARTVOTE_ACCESS", "All channel users with at least
\ 2%i
\ 2 access can vote." },
509 { "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." },
510 { "CSMSG_STARTVOTE_RUNNING", "The vote is already running." },
511 { "CSMSG_VOTE_VOTED", "You have already voted." },
512 { "CSMSG_VOTE_INVALID", "Vote option does not exist." },
513 { "CSMSG_VOTE_DONE", "You voted for $b%s$b." },
514 { "CSMSG_ENDVOTE_DONE", "The vote has been finished." },
515 { "CSMSG_ENDVOTE_STOPPED", "The vote is not running." },
518 { "CSMSG_EVENT_SEARCH_RESULTS", "The following channel events were found:" },
522 /* eject_user and unban_user flags */
523 #define ACTION_KICK 0x0001
524 #define ACTION_BAN 0x0002
525 #define ACTION_ADD_BAN 0x0004
526 #define ACTION_ADD_TIMED_BAN 0x0008
527 #define ACTION_UNBAN 0x0010
528 #define ACTION_DEL_BAN 0x0020
530 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
531 #define MODELEN 40 + KEYLEN
535 #define CSFUNC_ARGS user, channel, argc, argv, cmd
537 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
538 #define CHANSERV_SYNTAX() svccmd_send_help(user, chanserv, cmd)
539 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
540 reply("MSG_MISSING_PARAMS", argv[0]); \
544 DECLARE_LIST(dnrList, struct do_not_register *);
545 DEFINE_LIST(dnrList, struct do_not_register *)
547 static int eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action);
549 struct userNode *chanserv;
552 static dict_t plain_dnrs, mask_dnrs, handle_dnrs;
553 static struct log_type *CS_LOG;
557 struct channelList support_channels;
558 struct mod_chanmode default_modes;
560 unsigned long db_backup_frequency;
561 unsigned long channel_expire_frequency;
562 unsigned long dnr_expire_frequency;
564 unsigned long invited_timeout;
566 unsigned long info_delay;
567 unsigned long adjust_delay;
568 unsigned long channel_expire_delay;
569 unsigned int nodelete_level;
571 unsigned int adjust_threshold;
572 int join_flood_threshold;
574 unsigned int greeting_length;
575 unsigned int refresh_period;
576 unsigned int giveownership_period;
578 unsigned int max_owned;
579 unsigned int max_chan_users;
580 unsigned int max_chan_bans;
581 unsigned int max_userinfo_length;
583 unsigned int revoke_mode_a;
585 struct string_list *set_shows;
586 struct string_list *eightball;
587 struct string_list *old_ban_names;
589 const char *ctcp_short_ban_duration;
590 const char *ctcp_long_ban_duration;
592 const char *irc_operator_epithet;
593 const char *network_helper_epithet;
594 const char *support_helper_epithet;
596 const char *new_channel_authed;
597 const char *new_channel_unauthed;
598 const char *new_channel_msg;
603 struct userNode *user;
604 struct userNode *bot;
605 struct chanNode *channel;
607 unsigned short lowest;
608 unsigned short highest;
609 struct userData **users;
610 struct helpfile_table table;
615 struct userNode *user;
616 struct chanNode *chan;
619 enum note_access_type
621 NOTE_SET_CHANNEL_ACCESS,
622 NOTE_SET_CHANNEL_SETTER,
626 enum note_visible_type
629 NOTE_VIS_CHANNEL_USERS,
635 enum note_access_type set_access_type;
637 unsigned int min_opserv;
638 unsigned short min_ulevel;
640 enum note_visible_type visible_type;
641 unsigned int max_length;
648 struct note_type *type;
649 char setter[NICKSERV_HANDLE_LEN+1];
653 static unsigned int registered_channels;
654 static unsigned int banCount;
656 static const struct {
659 unsigned short level;
662 { "peon", "Peon", UL_PEON, '+' },
663 { "op", "Op", UL_OP, '@' },
664 { "master", "Master", UL_MASTER, '%' },
665 { "coowner", "Coowner", UL_COOWNER, '*' },
666 { "owner", "Owner", UL_OWNER, '!' },
667 { "helper", "BUG:", UL_HELPER, 'X' }
670 static const struct {
673 unsigned short default_value;
674 unsigned int old_idx;
675 unsigned int old_flag;
676 unsigned short flag_value;
678 { "CSMSG_SET_GIVE_VOICE", "givevoice", 100, ~0, CHANNEL_VOICE_ALL, 0 },
679 { "CSMSG_SET_GIVE_OPS", "giveops", 200, 2, 0, 0 },
680 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
681 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
682 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
683 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
684 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
685 { "CSMSG_SET_CTCPUSERS", "ctcpusers", 0, 9, 0, 0 },
686 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES, 1 },
687 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE, 200 },
688 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF, 1 },
689 { "CSMSG_SET_VOTE", "vote", 100, ~0, 0, 0 }
692 struct charOptionValues {
695 } protectValues[] = {
696 { 'a', "CSMSG_PROTECT_ALL" },
697 { 'e', "CSMSG_PROTECT_EQUAL" },
698 { 'l', "CSMSG_PROTECT_LOWER" },
699 { 'n', "CSMSG_PROTECT_NONE" }
701 { 'd', "CSMSG_TOYS_DISABLED" },
702 { 'n', "CSMSG_TOYS_PRIVATE" },
703 { 'p', "CSMSG_TOYS_PUBLIC" }
704 }, topicRefreshValues[] = {
705 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
706 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
707 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
708 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
709 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
710 }, ctcpReactionValues[] = {
711 { 'k', "CSMSG_CTCPREACTION_KICK" },
712 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
713 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
714 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
717 static const struct {
721 unsigned int old_idx;
723 struct charOptionValues *values;
725 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues), protectValues },
726 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues), toysValues },
727 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues), topicRefreshValues },
728 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 't', 10, ArrayLength(ctcpReactionValues), ctcpReactionValues }
731 struct userData *helperList;
732 struct chanData *channelList;
733 static struct module *chanserv_module;
734 static unsigned int userCount;
736 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
737 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
738 static void unregister_channel(struct chanData *channel, const char *reason);
741 user_level_from_name(const char *name, unsigned short clamp_level)
743 unsigned int level = 0, ii;
745 level = strtoul(name, NULL, 10);
746 else for(ii = 0; (ii < ArrayLength(accessLevels)) && !level; ++ii)
747 if(!irccasecmp(name, accessLevels[ii].name))
748 level = accessLevels[ii].level;
749 if(level > clamp_level)
755 parse_level_range(unsigned short *minl, unsigned short *maxl, const char *arg)
758 *minl = strtoul(arg, &sep, 10);
766 *maxl = strtoul(sep+1, &sep, 10);
774 _GetChannelUser(struct chanData *channel, struct handle_info *handle, int override, int allow_suspended)
776 struct userData *uData, **head;
778 if(!channel || !handle)
781 if(override && HANDLE_FLAGGED(handle, HELPING)
782 && ((handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel)))
784 for(uData = helperList;
785 uData && uData->handle != handle;
786 uData = uData->next);
790 uData = calloc(1, sizeof(struct userData));
791 uData->handle = handle;
793 uData->access = UL_HELPER;
799 uData->next = helperList;
801 helperList->prev = uData;
809 for(uData = channel->users; uData; uData = uData->next)
810 if((uData->handle == handle) && (allow_suspended || !IsUserSuspended(uData)))
813 head = &(channel->users);
816 if(uData && (uData != *head))
818 /* Shuffle the user to the head of whatever list he was in. */
820 uData->next->prev = uData->prev;
822 uData->prev->next = uData->next;
828 (**head).prev = uData;
835 /* Returns non-zero if user has at least the minimum access.
836 * exempt_owner is set when handling !set, so the owner can set things
839 int check_user_level(struct chanNode *channel, struct userNode *user, enum levelOption opt, int allow_override, int exempt_owner)
841 struct userData *uData;
842 struct chanData *cData = channel->channel_info;
843 unsigned short minimum = cData->lvlOpts[opt];
846 uData = _GetChannelUser(cData, user->handle_info, allow_override, 0);
849 if(minimum <= uData->access)
851 if((minimum > UL_OWNER) && (uData->access == UL_OWNER) && exempt_owner)
856 /* Scan for other users authenticated to the same handle
857 still in the channel. If so, keep them listed as present.
859 user is optional, if not null, it skips checking that userNode
860 (for the handle_part function) */
862 scan_user_presence(struct userData *uData, struct userNode *user)
866 if(IsSuspended(uData->channel)
867 || IsUserSuspended(uData)
868 || !(mn = find_handle_in_channel(uData->channel->channel, uData->handle, user)))
880 chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, const char *text, UNUSED_ARG(struct userNode *bot), UNUSED_ARG(unsigned int is_notice))
882 unsigned int eflags, argc;
884 static char *bad_ctcp_reason = "CTCPs to this channel are forbidden.";
886 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
887 if(!channel->channel_info
888 || IsSuspended(channel->channel_info)
890 || !ircncasecmp(text, "ACTION ", 7))
892 /* Figure out the minimum level needed to CTCP the channel */
893 if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
895 /* We need to enforce against them; do so. */
897 argv[0] = (char*)text;
898 argv[1] = user->nick;
900 if(GetUserMode(channel, user))
901 eflags |= ACTION_KICK;
902 switch(channel->channel_info->chOpts[chCTCPReaction]) {
903 default: case 'k': /* just do the kick */ break;
905 eflags |= ACTION_BAN;
908 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
909 argv[argc++] = (char*)chanserv_conf.ctcp_short_ban_duration;
912 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
913 argv[argc++] = (char*)chanserv_conf.ctcp_long_ban_duration;
916 argv[argc++] = bad_ctcp_reason;
917 eject_user(chanserv, channel, argc, argv, NULL, eflags);
921 chanserv_create_note_type(const char *name)
923 struct note_type *ntype = calloc(1, sizeof(*ntype) + strlen(name));
924 strcpy(ntype->name, name);
926 dict_insert(note_types, ntype->name, ntype);
931 free_vote_options(void *data)
933 struct vote_option *vOpt = data;
935 free(vOpt->option_str);
940 chanserv_deref_note_type(void *data)
942 struct note_type *ntype = data;
944 if(--ntype->refs > 0)
950 chanserv_flush_note_type(struct note_type *ntype)
952 struct chanData *cData;
953 for(cData = channelList; cData; cData = cData->next)
954 dict_remove(cData->notes, ntype->name);
958 chanserv_truncate_notes(struct note_type *ntype)
960 struct chanData *cData;
962 unsigned int size = sizeof(*note) + ntype->max_length;
964 for(cData = channelList; cData; cData = cData->next) {
965 note = dict_find(cData->notes, ntype->name, NULL);
968 if(strlen(note->note) <= ntype->max_length)
970 dict_remove2(cData->notes, ntype->name, 1);
971 note = realloc(note, size);
972 note->note[ntype->max_length] = 0;
973 dict_insert(cData->notes, ntype->name, note);
977 static int note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user);
980 chanserv_add_channel_note(struct chanData *channel, struct note_type *type, const char *setter, const char *text)
983 unsigned int len = strlen(text);
985 if(len > type->max_length) len = type->max_length;
986 note = calloc(1, sizeof(*note) + len);
988 strncpy(note->setter, setter, sizeof(note->setter)-1);
989 memcpy(note->note, text, len);
991 dict_insert(channel->notes, type->name, note);
997 chanserv_free_note(void *data)
999 struct note *note = data;
1001 chanserv_deref_note_type(note->type);
1002 assert(note->type->refs > 0); /* must use delnote to remove the type */
1006 static MODCMD_FUNC(cmd_createnote) {
1007 struct note_type *ntype;
1008 unsigned int arg = 1, existed = 0, max_length;
1010 if((ntype = dict_find(note_types, argv[1], NULL)))
1013 ntype = chanserv_create_note_type(argv[arg]);
1014 if(!irccasecmp(argv[++arg], "privileged"))
1017 ntype->set_access_type = NOTE_SET_PRIVILEGED;
1018 ntype->set_access.min_opserv = strtoul(argv[arg], NULL, 0);
1020 else if(!irccasecmp(argv[arg], "channel"))
1022 unsigned short ulvl = user_level_from_name(argv[++arg], UL_OWNER);
1025 reply("CSMSG_INVALID_ACCESS", argv[arg]);
1028 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
1029 ntype->set_access.min_ulevel = ulvl;
1031 else if(!irccasecmp(argv[arg], "setter"))
1033 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
1037 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
1041 if(!irccasecmp(argv[++arg], "privileged"))
1042 ntype->visible_type = NOTE_VIS_PRIVILEGED;
1043 else if(!irccasecmp(argv[arg], "channel_users"))
1044 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
1045 else if(!irccasecmp(argv[arg], "all"))
1046 ntype->visible_type = NOTE_VIS_ALL;
1048 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
1052 if((arg+1) >= argc) {
1053 reply("MSG_MISSING_PARAMS", argv[0]);
1056 max_length = strtoul(argv[++arg], NULL, 0);
1057 if(max_length < 20 || max_length > 450)
1059 reply("CSMSG_BAD_MAX_LENGTH", argv[arg]);
1062 if(existed && (max_length < ntype->max_length))
1064 ntype->max_length = max_length;
1065 chanserv_truncate_notes(ntype);
1067 ntype->max_length = max_length;
1070 reply("CSMSG_NOTE_MODIFIED", ntype->name);
1072 reply("CSMSG_NOTE_CREATED", ntype->name);
1077 dict_remove(note_types, ntype->name);
1081 static MODCMD_FUNC(cmd_removenote) {
1082 struct note_type *ntype;
1085 ntype = dict_find(note_types, argv[1], NULL);
1086 force = (argc > 2) && !irccasecmp(argv[2], "force");
1089 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
1096 reply("CSMSG_NOTE_TYPE_USED", ntype->name);
1099 chanserv_flush_note_type(ntype);
1101 dict_remove(note_types, argv[1]);
1102 reply("CSMSG_NOTE_DELETED", argv[1]);
1107 chanserv_expire_channel(void *data)
1109 struct chanData *channel = data;
1110 char reason[MAXLEN];
1111 sprintf(reason, "channel expired.");
1112 channel->expiry = 0;
1113 spamserv_cs_unregister(NULL, channel->channel, expire, NULL);
1114 unregister_channel(channel, reason);
1117 static MODCMD_FUNC(chan_opt_expire)
1119 struct chanData *cData = channel->channel_info;
1120 unsigned long value = cData->expiry;
1124 if((!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
1126 reply("MSG_SETTING_PRIVILEGED", argv[0]);
1129 unsigned long expiry,duration;
1131 /* The two directions can have different ACLs. */
1132 if(!strcmp(argv[1], "0"))
1134 else if((duration = ParseInterval(argv[1])))
1135 expiry = now + duration;
1138 reply("MSG_INVALID_DURATION", argv[1]);
1142 if (expiry != value)
1146 timeq_del(value, chanserv_expire_channel, cData, 0);
1149 cData->expiry = value;
1152 timeq_add(expiry, chanserv_expire_channel, cData);
1157 if(cData->expiry > now) {
1158 char expirestr[INTERVALLEN];
1159 reply("CSMSG_SET_EXPIRE", intervalString(expirestr, cData->expiry - now, user->handle_info));
1161 reply("CSMSG_SET_EXPIRE_OFF");
1166 mode_lock_violated(const struct mod_chanmode *orig, const struct mod_chanmode *change)
1170 if(orig->modes_set & change->modes_clear)
1172 if(orig->modes_clear & change->modes_set)
1174 if((orig->modes_set & MODE_KEY) && (change->modes_set & MODE_KEY)
1175 && strcmp(orig->new_key, change->new_key))
1177 if((orig->modes_set & MODE_LIMIT) && (change->modes_set & MODE_LIMIT)
1178 && (orig->new_limit != change->new_limit))
1183 static char max_length_text[MAXLEN+1][16];
1185 static struct helpfile_expansion
1186 chanserv_expand_variable(const char *variable)
1188 struct helpfile_expansion exp;
1190 if(!irccasecmp(variable, "notes"))
1193 exp.type = HF_TABLE;
1194 exp.value.table.length = 1;
1195 exp.value.table.width = 3;
1196 exp.value.table.flags = 0;
1197 exp.value.table.contents = calloc(dict_size(note_types)+1, sizeof(char**));
1198 exp.value.table.contents[0] = calloc(exp.value.table.width, sizeof(char*));
1199 exp.value.table.contents[0][0] = "Note Type";
1200 exp.value.table.contents[0][1] = "Visibility";
1201 exp.value.table.contents[0][2] = "Max Length";
1202 for(it=dict_first(note_types); it; it=iter_next(it))
1204 struct note_type *ntype = iter_data(it);
1207 if(!note_type_visible_to_user(NULL, ntype, message_dest)) continue;
1208 row = exp.value.table.length++;
1209 exp.value.table.contents[row] = calloc(exp.value.table.width, sizeof(char*));
1210 exp.value.table.contents[row][0] = ntype->name;
1211 exp.value.table.contents[row][1] = (ntype->visible_type == NOTE_VIS_ALL) ? "all" :
1212 (ntype->visible_type == NOTE_VIS_CHANNEL_USERS) ? "chan users" :
1214 if(!max_length_text[ntype->max_length][0])
1215 snprintf(max_length_text[ntype->max_length], sizeof(max_length_text[ntype->max_length]), "%u", ntype->max_length);
1216 exp.value.table.contents[row][2] = max_length_text[ntype->max_length];
1221 exp.type = HF_STRING;
1222 exp.value.str = NULL;
1226 static struct chanData*
1227 register_channel(struct chanNode *cNode, char *registrar)
1229 struct chanData *channel;
1230 enum levelOption lvlOpt;
1231 enum charOption chOpt;
1233 channel = calloc(1, sizeof(struct chanData));
1235 channel->notes = dict_new();
1236 dict_set_free_data(channel->notes, chanserv_free_note);
1238 channel->registrar = strdup(registrar);
1239 channel->registered = now;
1240 channel->visited = now;
1241 channel->limitAdjusted = now;
1242 channel->ownerTransfer = now;
1243 channel->flags = CHANNEL_DEFAULT_FLAGS;
1244 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
1245 channel->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
1246 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
1247 channel->chOpts[chOpt] = charOptions[chOpt].default_value;
1249 channel->prev = NULL;
1250 channel->next = channelList;
1253 channelList->prev = channel;
1254 channelList = channel;
1255 registered_channels++;
1257 channel->channel = cNode;
1259 cNode->channel_info = channel;
1261 channel->vote = NULL;
1266 static struct userData*
1267 add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned short access_level, unsigned long seen, const char *info)
1269 struct userData *ud;
1271 if(access_level > UL_OWNER)
1274 ud = calloc(1, sizeof(*ud));
1275 ud->channel = channel;
1276 ud->handle = handle;
1278 ud->access = access_level;
1279 ud->info = info ? strdup(info) : NULL;
1282 ud->next = channel->users;
1284 channel->users->prev = ud;
1285 channel->users = ud;
1287 channel->userCount++;
1291 ud->u_next = ud->handle->channels;
1293 ud->u_next->u_prev = ud;
1294 ud->handle->channels = ud;
1300 del_channel_user(struct userData *user, int do_gc)
1302 struct chanData *channel = user->channel;
1304 channel->userCount--;
1308 user->prev->next = user->next;
1310 channel->users = user->next;
1312 user->next->prev = user->prev;
1315 user->u_prev->u_next = user->u_next;
1317 user->handle->channels = user->u_next;
1319 user->u_next->u_prev = user->u_prev;
1323 if(do_gc && !channel->users && !IsProtected(channel)) {
1324 spamserv_cs_unregister(NULL, channel->channel, lost_all_users, NULL);
1325 unregister_channel(channel, "lost all users.");
1329 static void expire_ban(void *data);
1332 add_channel_ban(struct chanData *channel, const char *mask, char *owner, unsigned long set, unsigned long triggered, unsigned long expires, char *reason)
1335 unsigned int ii, l1, l2;
1340 bd = malloc(sizeof(struct banData));
1342 bd->channel = channel;
1344 bd->triggered = triggered;
1345 bd->expires = expires;
1347 for(ii = 0; ii < chanserv_conf.old_ban_names->used; ++ii)
1349 extern const char *hidden_host_suffix;
1350 const char *old_name = chanserv_conf.old_ban_names->list[ii];
1354 l2 = strlen(old_name);
1357 if(irccasecmp(mask + l1 - l2, old_name))
1359 new_mask = alloca(MAXLEN);
1360 sprintf(new_mask, "%.*s%s", (int)(l1-l2), mask, hidden_host_suffix);
1363 safestrncpy(bd->mask, mask, sizeof(bd->mask));
1365 safestrncpy(bd->owner, owner, sizeof(bd->owner));
1366 bd->reason = strdup(reason);
1369 timeq_add(expires, expire_ban, bd);
1372 bd->next = channel->bans;
1374 channel->bans->prev = bd;
1376 channel->banCount++;
1383 del_channel_ban(struct banData *ban)
1385 ban->channel->banCount--;
1389 ban->prev->next = ban->next;
1391 ban->channel->bans = ban->next;
1394 ban->next->prev = ban->prev;
1397 timeq_del(0, expire_ban, ban, TIMEQ_IGNORE_WHEN);
1406 expire_ban(void *data)
1408 struct banData *bd = data;
1409 if(!IsSuspended(bd->channel))
1411 struct banList bans;
1412 struct mod_chanmode change;
1414 bans = bd->channel->channel->banlist;
1415 mod_chanmode_init(&change);
1416 for(ii=0; ii<bans.used; ii++)
1418 if(!strcmp(bans.list[ii]->ban, bd->mask))
1421 change.args[0].mode = MODE_REMOVE|MODE_BAN;
1422 change.args[0].u.hostmask = bd->mask;
1423 mod_chanmode_announce(chanserv, bd->channel->channel, &change);
1429 del_channel_ban(bd);
1432 static void chanserv_expire_suspension(void *data);
1435 unregister_channel(struct chanData *channel, const char *reason)
1437 struct mod_chanmode change;
1438 char msgbuf[MAXLEN];
1440 /* After channel unregistration, the following must be cleaned
1442 - Channel information.
1445 - Channel suspension data.
1446 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1452 timeq_del(0, NULL, channel, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
1454 if(off_channel > 0 || chanserv_conf.revoke_mode_a) {
1455 mod_chanmode_init(&change);
1457 change.modes_clear |= MODE_REGISTERED;
1458 if(chanserv_conf.revoke_mode_a)
1459 change.modes_clear |= MODE_ACCESS;
1460 mod_chanmode_announce(chanserv, channel->channel, &change);
1463 while(channel->users)
1464 del_channel_user(channel->users, 0);
1466 while(channel->bans)
1467 del_channel_ban(channel->bans);
1469 free(channel->topic);
1470 free(channel->registrar);
1471 free(channel->greeting);
1472 free(channel->user_greeting);
1473 free(channel->topic_mask);
1476 channel->prev->next = channel->next;
1478 channelList = channel->next;
1481 channel->next->prev = channel->prev;
1483 if(channel->suspended)
1485 struct chanNode *cNode = channel->channel;
1486 struct suspended *suspended, *next_suspended;
1488 for(suspended = channel->suspended; suspended; suspended = next_suspended)
1490 next_suspended = suspended->previous;
1491 free(suspended->suspender);
1492 free(suspended->reason);
1493 if(suspended->expires)
1494 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
1499 cNode->channel_info = NULL;
1502 timeq_del(channel->expiry, chanserv_expire_channel, channel, 0);
1503 channel->channel->channel_info = NULL;
1505 dict_delete(channel->notes);
1506 sprintf(msgbuf, "%s %s", channel->channel->name, reason);
1507 if(!IsSuspended(channel))
1508 DelChannelUser(chanserv, channel->channel, msgbuf, 0);
1509 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, msgbuf);
1510 UnlockChannel(channel->channel);
1512 registered_channels--;
1516 expire_channels(void *data)
1518 struct chanData *channel, *next;
1519 struct userData *user;
1520 char delay[INTERVALLEN], reason[INTERVALLEN + 64];
1522 intervalString(delay, chanserv_conf.channel_expire_delay, NULL);
1523 sprintf(reason, "Channel registration automatically expired after %s of disuse.", delay);
1525 for(channel = channelList; channel; channel = next)
1527 next = channel->next;
1529 /* See if the channel can be expired. */
1530 if(((now - channel->visited) <= chanserv_conf.channel_expire_delay)
1531 || IsProtected(channel))
1534 /* Make sure there are no high-ranking users still in the channel. */
1535 for(user=channel->users; user; user=user->next)
1536 if(user->present && (user->access >= UL_PRESENT) && !HANDLE_FLAGGED(user->handle, BOT))
1541 /* Unregister the channel */
1542 log_module(CS_LOG, LOG_INFO, "(%s) Channel registration expired.", channel->channel->name);
1543 spamserv_cs_unregister(NULL, channel->channel, expire, NULL);
1544 unregister_channel(channel, "registration expired.");
1547 if(chanserv_conf.channel_expire_frequency && !data)
1548 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
1552 expire_dnrs(UNUSED_ARG(void *data))
1554 dict_iterator_t it, next;
1555 struct do_not_register *dnr;
1557 for(it = dict_first(handle_dnrs); it; it = next)
1559 dnr = iter_data(it);
1560 next = iter_next(it);
1561 if(dnr->expires && dnr->expires <= now)
1562 dict_remove(handle_dnrs, dnr->chan_name + 1);
1564 for(it = dict_first(plain_dnrs); it; it = next)
1566 dnr = iter_data(it);
1567 next = iter_next(it);
1568 if(dnr->expires && dnr->expires <= now)
1569 dict_remove(plain_dnrs, dnr->chan_name + 1);
1571 for(it = dict_first(mask_dnrs); it; it = next)
1573 dnr = iter_data(it);
1574 next = iter_next(it);
1575 if(dnr->expires && dnr->expires <= now)
1576 dict_remove(mask_dnrs, dnr->chan_name + 1);
1579 if(chanserv_conf.dnr_expire_frequency)
1580 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
1584 protect_user(const struct userNode *victim, const struct userNode *aggressor, struct chanData *channel)
1586 char protect = channel->chOpts[chProtect];
1587 struct userData *cs_victim, *cs_aggressor;
1589 /* Don't protect if no one is to be protected, someone is attacking
1590 himself, or if the aggressor is an IRC Operator. */
1591 if(protect == 'n' || victim == aggressor || IsOper(aggressor))
1594 /* Don't protect if the victim isn't authenticated (because they
1595 can't be a channel user), unless we are to protect non-users
1597 cs_victim = GetChannelAccess(channel, victim->handle_info);
1598 if(protect != 'a' && !cs_victim)
1601 /* Protect if the aggressor isn't a user because at this point,
1602 the aggressor can only be less than or equal to the victim. */
1603 cs_aggressor = GetChannelAccess(channel, aggressor->handle_info);
1607 /* If the aggressor was a user, then the victim can't be helped. */
1614 if(cs_victim->access > cs_aggressor->access)
1619 if(cs_victim->access >= cs_aggressor->access)
1628 validate_op(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1630 struct chanData *cData = channel->channel_info;
1631 struct userData *cs_victim;
1633 if((!(cs_victim = GetChannelUser(cData, victim->handle_info))
1634 || (cs_victim->access < cData->lvlOpts[lvlGiveOps]))
1635 && !check_user_level(channel, user, lvlEnfOps, 0, 0))
1637 send_message(user, chanserv, "CSMSG_OPBY_LOCKED");
1645 validate_deop(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1647 if(IsService(victim))
1649 send_message(user, chanserv, "MSG_SERVICE_IMMUNE", victim->nick);
1653 if(protect_user(victim, user, channel->channel_info))
1655 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
1662 static struct do_not_register *
1663 chanserv_add_dnr(const char *chan_name, const char *setter, unsigned long expires, const char *reason)
1665 struct do_not_register *dnr = calloc(1, sizeof(*dnr)+strlen(reason));
1666 safestrncpy(dnr->chan_name, chan_name, sizeof(dnr->chan_name));
1667 safestrncpy(dnr->setter, setter, sizeof(dnr->setter));
1668 strcpy(dnr->reason, reason);
1670 dnr->expires = expires;
1671 if(dnr->chan_name[0] == '*')
1672 dict_insert(handle_dnrs, dnr->chan_name+1, dnr);
1673 else if(strpbrk(dnr->chan_name, "*?"))
1674 dict_insert(mask_dnrs, dnr->chan_name, dnr);
1676 dict_insert(plain_dnrs, dnr->chan_name, dnr);
1680 static struct dnrList
1681 chanserv_find_dnrs(const char *chan_name, const char *handle, unsigned int max)
1683 struct dnrList list;
1684 dict_iterator_t it, next;
1685 struct do_not_register *dnr;
1687 dnrList_init(&list);
1689 if(handle && (dnr = dict_find(handle_dnrs, handle, NULL)))
1691 if(dnr->expires && dnr->expires <= now)
1692 dict_remove(handle_dnrs, handle);
1693 else if(list.used < max)
1694 dnrList_append(&list, dnr);
1697 if(chan_name && (dnr = dict_find(plain_dnrs, chan_name, NULL)))
1699 if(dnr->expires && dnr->expires <= now)
1700 dict_remove(plain_dnrs, chan_name);
1701 else if(list.used < max)
1702 dnrList_append(&list, dnr);
1707 for(it = dict_first(mask_dnrs); it && list.used < max; it = next)
1709 next = iter_next(it);
1710 if(!match_ircglob(chan_name, iter_key(it)))
1712 dnr = iter_data(it);
1713 if(dnr->expires && dnr->expires <= now)
1714 dict_remove(mask_dnrs, iter_key(it));
1716 dnrList_append(&list, dnr);
1723 static int dnr_print_func(struct do_not_register *dnr, void *extra)
1725 struct userNode *user;
1726 char buf1[INTERVALLEN];
1727 char buf2[INTERVALLEN];
1734 strftime(buf1, sizeof(buf1), "%d %b %Y", localtime(&feh));
1739 strftime(buf2, sizeof(buf2), "%d %b %Y", localtime(&feh));
1740 send_message(user, chanserv, "CSMSG_DNR_INFO_SET_EXPIRES", dnr->chan_name, buf1, dnr->setter, buf2, dnr->reason);
1744 send_message(user, chanserv, "CSMSG_DNR_INFO_SET", dnr->chan_name, buf1, dnr->setter, dnr->reason);
1747 send_message(user, chanserv, "CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1752 chanserv_show_dnrs(struct userNode *user, struct svccmd *cmd, const char *chan_name, const char *handle)
1754 struct dnrList list;
1757 list = chanserv_find_dnrs(chan_name, handle, UINT_MAX);
1758 for(ii = 0; (ii < list.used) && (ii < 10); ++ii)
1759 dnr_print_func(list.list[ii], user);
1761 reply("CSMSG_MORE_DNRS", list.used - ii);
1766 struct do_not_register *
1767 chanserv_is_dnr(const char *chan_name, struct handle_info *handle)
1769 struct dnrList list;
1770 struct do_not_register *dnr;
1772 list = chanserv_find_dnrs(chan_name, handle ? handle->handle : NULL, 1);
1773 dnr = list.used ? list.list[0] : NULL;
1778 static unsigned int send_dnrs(struct userNode *user, dict_t dict)
1780 struct do_not_register *dnr;
1781 dict_iterator_t it, next;
1782 unsigned int matches = 0;
1784 for(it = dict_first(dict); it; it = next)
1786 dnr = iter_data(it);
1787 next = iter_next(it);
1788 if(dnr->expires && dnr->expires <= now)
1790 dict_remove(dict, iter_key(it));
1793 dnr_print_func(dnr, user);
1800 static CHANSERV_FUNC(cmd_noregister)
1804 unsigned long expiry, duration;
1805 unsigned int matches;
1809 reply("CSMSG_DNR_SEARCH_RESULTS");
1810 matches = send_dnrs(user, handle_dnrs);
1811 matches += send_dnrs(user, plain_dnrs);
1812 matches += send_dnrs(user, mask_dnrs);
1814 reply("MSG_MATCH_COUNT", matches);
1816 reply("MSG_NO_MATCHES");
1822 if(!IsChannelName(target) && (*target != '*'))
1824 reply("CSMSG_NOT_DNR", target);
1832 reply("MSG_INVALID_DURATION", argv[2]);
1836 if(!strcmp(argv[2], "0"))
1838 else if((duration = ParseInterval(argv[2])))
1839 expiry = now + duration;
1842 reply("MSG_INVALID_DURATION", argv[2]);
1846 reason = unsplit_string(argv + 3, argc - 3, NULL);
1847 if((*target == '*') && !get_handle_info(target + 1))
1849 reply("MSG_HANDLE_UNKNOWN", target + 1);
1852 chanserv_add_dnr(target, user->handle_info->handle, expiry, reason);
1853 reply("CSMSG_NOREGISTER_CHANNEL", target);
1857 reply("CSMSG_DNR_SEARCH_RESULTS");
1859 matches = chanserv_show_dnrs(user, cmd, NULL, target + 1);
1861 matches = chanserv_show_dnrs(user, cmd, target, NULL);
1863 reply("MSG_NO_MATCHES");
1867 static CHANSERV_FUNC(cmd_allowregister)
1869 const char *chan_name = argv[1];
1871 if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
1872 || dict_remove(plain_dnrs, chan_name)
1873 || dict_remove(mask_dnrs, chan_name))
1875 reply("CSMSG_DNR_REMOVED", chan_name);
1878 reply("CSMSG_NO_SUCH_DNR", chan_name);
1883 struct userNode *source;
1887 unsigned long min_set, max_set;
1888 unsigned long min_expires, max_expires;
1893 dnr_search_matches(const struct do_not_register *dnr, const struct dnr_search *search)
1895 return !((dnr->set < search->min_set)
1896 || (dnr->set > search->max_set)
1897 || (dnr->expires < search->min_expires)
1898 || (search->max_expires
1899 && ((dnr->expires == 0)
1900 || (dnr->expires > search->max_expires)))
1901 || (search->chan_mask
1902 && !match_ircglob(dnr->chan_name, search->chan_mask))
1903 || (search->setter_mask
1904 && !match_ircglob(dnr->setter, search->setter_mask))
1905 || (search->reason_mask
1906 && !match_ircglob(dnr->reason, search->reason_mask)));
1909 static struct dnr_search *
1910 dnr_search_create(struct userNode *user, struct svccmd *cmd, unsigned int argc, char *argv[])
1912 struct dnr_search *discrim;
1915 discrim = calloc(1, sizeof(*discrim));
1916 discrim->source = user;
1917 discrim->chan_mask = NULL;
1918 discrim->setter_mask = NULL;
1919 discrim->reason_mask = NULL;
1920 discrim->max_set = INT_MAX;
1921 discrim->limit = 50;
1923 for(ii=0; ii<argc; ++ii)
1927 reply("MSG_MISSING_PARAMS", argv[ii]);
1930 else if(0 == irccasecmp(argv[ii], "channel"))
1932 discrim->chan_mask = argv[++ii];
1934 else if(0 == irccasecmp(argv[ii], "setter"))
1936 discrim->setter_mask = argv[++ii];
1938 else if(0 == irccasecmp(argv[ii], "reason"))
1940 discrim->reason_mask = argv[++ii];
1942 else if(0 == irccasecmp(argv[ii], "limit"))
1944 discrim->limit = strtoul(argv[++ii], NULL, 0);
1946 else if(0 == irccasecmp(argv[ii], "set"))
1948 const char *cmp = argv[++ii];
1951 discrim->min_set = now - ParseInterval(cmp + 2);
1953 discrim->min_set = now - (ParseInterval(cmp + 1) - 1);
1954 } else if(cmp[0] == '=') {
1955 discrim->min_set = discrim->max_set = now - ParseInterval(cmp + 1);
1956 } else if(cmp[0] == '>') {
1958 discrim->max_set = now - ParseInterval(cmp + 2);
1960 discrim->max_set = now - (ParseInterval(cmp + 1) - 1);
1962 discrim->max_set = now - (ParseInterval(cmp) - 1);
1965 else if(0 == irccasecmp(argv[ii], "expires"))
1967 const char *cmp = argv[++ii];
1970 discrim->max_expires = now + ParseInterval(cmp + 2);
1972 discrim->max_expires = now + (ParseInterval(cmp + 1) - 1);
1973 } else if(cmp[0] == '=') {
1974 discrim->min_expires = discrim->max_expires = now + ParseInterval(cmp + 1);
1975 } else if(cmp[0] == '>') {
1977 discrim->min_expires = now + ParseInterval(cmp + 2);
1979 discrim->min_expires = now + (ParseInterval(cmp + 1) - 1);
1981 discrim->min_expires = now + (ParseInterval(cmp) - 1);
1986 reply("MSG_INVALID_CRITERIA", argv[ii]);
1997 typedef int (*dnr_search_func)(struct do_not_register *match, void *extra);
2000 dnr_search(struct dnr_search *discrim, dnr_search_func dsf, void *data)
2002 struct do_not_register *dnr;
2003 dict_iterator_t next;
2008 /* Initialize local variables. */
2011 if(discrim->chan_mask)
2013 int shift = (discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*') ? 2 : 0;
2014 if('\0' == discrim->chan_mask[shift + strcspn(discrim->chan_mask+shift, "*?")])
2018 if(target_fixed && discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*')
2020 /* Check against account-based DNRs. */
2021 dnr = dict_find(handle_dnrs, discrim->chan_mask + 2, NULL);
2022 if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
2025 else if(target_fixed)
2027 /* Check against channel-based DNRs. */
2028 dnr = dict_find(plain_dnrs, discrim->chan_mask, NULL);
2029 if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
2034 /* Exhaustively search account DNRs. */
2035 for(it = dict_first(handle_dnrs); it; it = next)
2037 next = iter_next(it);
2038 dnr = iter_data(it);
2039 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
2043 /* Do the same for channel DNRs. */
2044 for(it = dict_first(plain_dnrs); it; it = next)
2046 next = iter_next(it);
2047 dnr = iter_data(it);
2048 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
2052 /* Do the same for wildcarded channel DNRs. */
2053 for(it = dict_first(mask_dnrs); it; it = next)
2055 next = iter_next(it);
2056 dnr = iter_data(it);
2057 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
2065 dnr_remove_func(struct do_not_register *match, void *extra)
2067 struct userNode *user;
2070 chan_name = alloca(strlen(match->chan_name) + 1);
2071 strcpy(chan_name, match->chan_name);
2073 if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
2074 || dict_remove(plain_dnrs, chan_name)
2075 || dict_remove(mask_dnrs, chan_name))
2077 send_message(user, chanserv, "CSMSG_DNR_REMOVED", chan_name);
2083 dnr_count_func(struct do_not_register *match, void *extra)
2085 return 0; (void)match; (void)extra;
2088 static MODCMD_FUNC(cmd_dnrsearch)
2090 struct dnr_search *discrim;
2091 dnr_search_func action;
2092 struct svccmd *subcmd;
2093 unsigned int matches;
2096 sprintf(buf, "dnrsearch %s", argv[1]);
2097 subcmd = dict_find(cmd->parent->commands, buf, NULL);
2100 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
2103 if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
2105 if(!irccasecmp(argv[1], "print"))
2106 action = dnr_print_func;
2107 else if(!irccasecmp(argv[1], "remove"))
2108 action = dnr_remove_func;
2109 else if(!irccasecmp(argv[1], "count"))
2110 action = dnr_count_func;
2113 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
2117 discrim = dnr_search_create(user, cmd, argc-2, argv+2);
2121 if(action == dnr_print_func)
2122 reply("CSMSG_DNR_SEARCH_RESULTS");
2123 matches = dnr_search(discrim, action, user);
2125 reply("MSG_MATCH_COUNT", matches);
2127 reply("MSG_NO_MATCHES");
2133 chanserv_get_owned_count(struct handle_info *hi)
2135 struct userData *cList;
2138 for(owned=0, cList=hi->channels; cList; cList=cList->u_next)
2139 if(cList->access == UL_OWNER)
2144 static CHANSERV_FUNC(cmd_register)
2146 struct handle_info *handle;
2147 struct chanData *cData;
2148 struct modeNode *mn;
2149 char reason[MAXLEN];
2151 unsigned int new_channel, force=0;
2152 struct do_not_register *dnr;
2156 if(channel->channel_info)
2158 reply("CSMSG_ALREADY_REGGED", channel->name);
2162 if(channel->bad_channel)
2164 reply("CSMSG_ILLEGAL_CHANNEL", channel->name);
2169 && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
2171 reply("CSMSG_MUST_BE_OPPED", channel->name);
2176 chan_name = channel->name;
2180 if((argc < 2) || !IsChannelName(argv[1]))
2182 reply("MSG_NOT_CHANNEL_NAME");
2186 if(opserv_bad_channel(argv[1]))
2188 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2193 chan_name = argv[1];
2196 if(argc >= (new_channel+2))
2198 if(!IsHelping(user))
2200 reply("CSMSG_PROXY_FORBIDDEN");
2204 if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
2206 force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
2207 dnr = chanserv_is_dnr(chan_name, handle);
2211 handle = user->handle_info;
2212 dnr = chanserv_is_dnr(chan_name, handle);
2216 if(!IsHelping(user))
2217 reply("CSMSG_DNR_CHANNEL", chan_name);
2219 chanserv_show_dnrs(user, cmd, chan_name, handle->handle);
2223 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2225 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
2230 channel = AddChannel(argv[1], now, NULL, NULL);
2232 cData = register_channel(channel, user->handle_info->handle);
2233 scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL), NULL);
2234 cData->modes = chanserv_conf.default_modes;
2236 cData->modes.modes_set |= MODE_REGISTERED;
2237 if (IsOffChannel(cData))
2239 mod_chanmode_announce(chanserv, channel, &cData->modes);
2243 struct mod_chanmode *change = mod_chanmode_dup(&cData->modes, 1);
2244 change->args[change->argc].mode = MODE_CHANOP;
2245 change->args[change->argc].u.member = AddChannelUser(chanserv, channel);
2247 mod_chanmode_announce(chanserv, channel, change);
2248 mod_chanmode_free(change);
2251 /* Initialize the channel's max user record. */
2252 cData->max = channel->members.used;
2253 cData->max_time = 0;
2255 if(handle != user->handle_info)
2256 reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
2258 reply("CSMSG_REG_SUCCESS", channel->name);
2260 sprintf(reason, "%s registered to %s by %s.", channel->name, handle->handle, user->handle_info->handle);
2261 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2266 make_confirmation_string(struct userData *uData)
2268 static char strbuf[16];
2273 for(src = uData->handle->handle; *src; )
2274 accum = accum * 31 + toupper(*src++);
2276 for(src = uData->channel->channel->name; *src; )
2277 accum = accum * 31 + toupper(*src++);
2278 sprintf(strbuf, "%08x", accum);
2282 static CHANSERV_FUNC(cmd_unregister)
2285 char reason[MAXLEN];
2286 struct chanData *cData;
2287 struct userData *uData;
2289 cData = channel->channel_info;
2292 reply("CSMSG_NOT_REGISTERED", channel->name);
2296 uData = GetChannelUser(cData, user->handle_info);
2297 if(!uData || (uData->access < UL_OWNER))
2299 reply("CSMSG_NO_ACCESS");
2303 if(IsProtected(cData) && !IsOper(user))
2305 reply("CSMSG_UNREG_NODELETE", channel->name);
2309 if(!IsHelping(user))
2311 const char *confirm_string;
2312 if(IsSuspended(cData))
2314 reply("CSMSG_CHAN_SUSPENDED", channel->name, cData->suspended->reason);
2317 confirm_string = make_confirmation_string(uData);
2318 if((argc < 2) || strcmp(argv[1], confirm_string))
2320 reply("CSMSG_CONFIRM_UNREG", confirm_string);
2325 sprintf(reason, "unregistered by %s.", user->handle_info->handle);
2326 name = strdup(channel->name);
2327 unregister_channel(cData, reason);
2328 spamserv_cs_unregister(user, channel, manually, "unregistered");
2329 reply("CSMSG_UNREG_SUCCESS", name);
2335 ss_cs_join_channel(struct chanNode *channel, int spamserv_join)
2337 extern struct userNode *spamserv;
2338 struct mod_chanmode *change;
2340 if(spamserv && spamserv_join && get_chanInfo(channel->name))
2342 change = mod_chanmode_alloc(2);
2344 change->args[0].mode = MODE_CHANOP;
2345 change->args[0].u.member = AddChannelUser(chanserv, channel);
2346 change->args[1].mode = MODE_CHANOP;
2347 change->args[1].u.member = AddChannelUser(spamserv, channel);
2351 change = mod_chanmode_alloc(1);
2353 change->args[0].mode = MODE_CHANOP;
2354 change->args[0].u.member = AddChannelUser(chanserv, channel);
2357 mod_chanmode_announce(chanserv, channel, change);
2358 mod_chanmode_free(change);
2361 static CHANSERV_FUNC(cmd_move)
2363 struct mod_chanmode change;
2364 struct chanNode *target;
2365 struct modeNode *mn;
2366 struct userData *uData;
2367 char reason[MAXLEN];
2368 struct do_not_register *dnr;
2369 int chanserv_join = 0, spamserv_join;
2373 if(IsProtected(channel->channel_info))
2375 reply("CSMSG_MOVE_NODELETE", channel->name);
2379 if(!IsChannelName(argv[1]))
2381 reply("MSG_NOT_CHANNEL_NAME");
2385 if(opserv_bad_channel(argv[1]))
2387 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2391 if(!IsHelping(user) || (argc < 3) || irccasecmp(argv[2], "force"))
2393 for(uData = channel->channel_info->users; uData; uData = uData->next)
2395 if((uData->access == UL_OWNER) && (dnr = chanserv_is_dnr(argv[1], uData->handle)))
2397 if(!IsHelping(user))
2398 reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]);
2400 chanserv_show_dnrs(user, cmd, argv[1], uData->handle->handle);
2406 mod_chanmode_init(&change);
2407 if(!(target = GetChannel(argv[1])))
2409 target = AddChannel(argv[1], now, NULL, NULL);
2410 if(!IsSuspended(channel->channel_info))
2413 else if(target->channel_info)
2415 reply("CSMSG_ALREADY_REGGED", target->name);
2418 else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
2419 && !IsHelping(user))
2421 reply("CSMSG_MUST_BE_OPPED", target->name);
2424 else if(!IsSuspended(channel->channel_info))
2429 /* Clear MODE_REGISTERED from old channel, add it to new. */
2431 change.modes_clear = MODE_REGISTERED;
2432 mod_chanmode_announce(chanserv, channel, &change);
2433 change.modes_clear = 0;
2434 change.modes_set = MODE_REGISTERED;
2435 mod_chanmode_announce(chanserv, target, &change);
2438 /* Move the channel_info to the target channel; it
2439 shouldn't be necessary to clear timeq callbacks
2440 for the old channel. */
2441 target->channel_info = channel->channel_info;
2442 target->channel_info->channel = target;
2443 channel->channel_info = NULL;
2445 /* Check whether users are present in the new channel. */
2446 for(uData = target->channel_info->users; uData; uData = uData->next)
2447 scan_user_presence(uData, NULL);
2449 spamserv_join = spamserv_cs_move_merge(user, channel, target, 1);
2452 ss_cs_join_channel(target, spamserv_join);
2454 sprintf(reason, "%s moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
2455 if(!IsSuspended(target->channel_info))
2457 char reason2[MAXLEN];
2458 sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
2459 DelChannelUser(chanserv, channel, reason2, 0);
2461 UnlockChannel(channel);
2462 LockChannel(target);
2463 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2464 reply("CSMSG_MOVE_SUCCESS", target->name);
2469 merge_users(struct chanData *source, struct chanData *target)
2471 struct userData *suData, *tuData, *next;
2477 /* Insert the source's users into the scratch area. */
2478 for(suData = source->users; suData; suData = suData->next)
2479 dict_insert(merge, suData->handle->handle, suData);
2481 /* Iterate through the target's users, looking for
2482 users common to both channels. The lower access is
2483 removed from either the scratch area or target user
2485 for(tuData = target->users; tuData; tuData = next)
2487 struct userData *choice;
2489 next = tuData->next;
2491 /* If a source user exists with the same handle as a target
2492 channel's user, resolve the conflict by removing one. */
2493 suData = dict_find(merge, tuData->handle->handle, NULL);
2497 /* Pick the data we want to keep. */
2498 /* If the access is the same, use the later seen time. */
2499 if(suData->access == tuData->access)
2500 choice = (suData->seen > tuData->seen) ? suData : tuData;
2501 else /* Otherwise, keep the higher access level. */
2502 choice = (suData->access > tuData->access) ? suData : tuData;
2503 /* Use the later seen time. */
2504 if(suData->seen < tuData->seen)
2505 suData->seen = tuData->seen;
2507 tuData->seen = suData->seen;
2509 /* Remove the user that wasn't picked. */
2510 if(choice == tuData)
2512 dict_remove(merge, suData->handle->handle);
2513 del_channel_user(suData, 0);
2516 del_channel_user(tuData, 0);
2519 /* Move the remaining users to the target channel. */
2520 for(it = dict_first(merge); it; it = iter_next(it))
2522 suData = iter_data(it);
2524 /* Insert the user into the target channel's linked list. */
2525 suData->prev = NULL;
2526 suData->next = target->users;
2527 suData->channel = target;
2530 target->users->prev = suData;
2531 target->users = suData;
2533 /* Update the user counts for the target channel; the
2534 source counts are left alone. */
2535 target->userCount++;
2537 /* Check whether the user is in the target channel. */
2538 scan_user_presence(suData, NULL);
2541 /* Possible to assert (source->users == NULL) here. */
2542 source->users = NULL;
2547 merge_bans(struct chanData *source, struct chanData *target)
2549 struct banData *sbData, *tbData, *sNext, *tNext, *tFront;
2551 /* Hold on to the original head of the target ban list
2552 to avoid comparing source bans with source bans. */
2553 tFront = target->bans;
2555 /* Perform a totally expensive O(n*m) merge, ick. */
2556 for(sbData = source->bans; sbData; sbData = sNext)
2558 /* Flag to track whether the ban's been moved
2559 to the destination yet. */
2562 /* Possible to assert (sbData->prev == NULL) here. */
2563 sNext = sbData->next;
2565 for(tbData = tFront; tbData; tbData = tNext)
2567 tNext = tbData->next;
2569 /* Perform two comparisons between each source
2570 and target ban, conflicts are resolved by
2571 keeping the broader ban and copying the later
2572 expiration and triggered time. */
2573 if(match_ircglobs(tbData->mask, sbData->mask))
2575 /* There is a broader ban in the target channel that
2576 overrides one in the source channel; remove the
2577 source ban and break. */
2578 if(sbData->expires > tbData->expires)
2579 tbData->expires = sbData->expires;
2580 if(sbData->triggered > tbData->triggered)
2581 tbData->triggered = sbData->triggered;
2582 del_channel_ban(sbData);
2585 else if(match_ircglobs(sbData->mask, tbData->mask))
2587 /* There is a broader ban in the source channel that
2588 overrides one in the target channel; remove the
2589 target ban, fall through and move the source over. */
2590 if(tbData->expires > sbData->expires)
2591 sbData->expires = tbData->expires;
2592 if(tbData->triggered > sbData->triggered)
2593 sbData->triggered = tbData->triggered;
2594 if(tbData == tFront)
2596 del_channel_ban(tbData);
2599 /* Source bans can override multiple target bans, so
2600 we allow a source to run through this loop multiple
2601 times, but we can only move it once. */
2606 /* Remove the source ban from the source ban list. */
2608 sbData->next->prev = sbData->prev;
2610 /* Modify the source ban's associated channel. */
2611 sbData->channel = target;
2613 /* Insert the ban into the target channel's linked list. */
2614 sbData->prev = NULL;
2615 sbData->next = target->bans;
2618 target->bans->prev = sbData;
2619 target->bans = sbData;
2621 /* Update the user counts for the target channel. */
2626 /* Possible to assert (source->bans == NULL) here. */
2627 source->bans = NULL;
2631 merge_data(struct chanData *source, struct chanData *target)
2633 /* Use more recent visited and owner-transfer time; use older
2634 * registered time. Bitwise or may_opchan. Use higher max.
2635 * Do not touch last_refresh, ban count or user counts.
2637 if(source->visited > target->visited)
2638 target->visited = source->visited;
2639 if(source->registered < target->registered)
2640 target->registered = source->registered;
2641 if(source->ownerTransfer > target->ownerTransfer)
2642 target->ownerTransfer = source->ownerTransfer;
2643 if(source->may_opchan)
2644 target->may_opchan = 1;
2645 if(source->max > target->max) {
2646 target->max = source->max;
2647 target->max_time = source->max_time;
2652 merge_channel(struct chanData *source, struct chanData *target)
2654 merge_users(source, target);
2655 merge_bans(source, target);
2656 merge_data(source, target);
2659 static CHANSERV_FUNC(cmd_merge)
2661 struct userData *target_user;
2662 struct chanNode *target;
2663 char reason[MAXLEN];
2667 /* Make sure the target channel exists and is registered to the user
2668 performing the command. */
2669 if(!(target = GetChannel(argv[1])))
2671 reply("MSG_INVALID_CHANNEL");
2675 if(!target->channel_info)
2677 reply("CSMSG_NOT_REGISTERED", target->name);
2681 if(IsProtected(channel->channel_info))
2683 reply("CSMSG_MERGE_NODELETE");
2687 if(IsSuspended(target->channel_info))
2689 reply("CSMSG_MERGE_SUSPENDED");
2693 if(channel == target)
2695 reply("CSMSG_MERGE_SELF");
2699 target_user = GetChannelUser(target->channel_info, user->handle_info);
2700 if(!target_user || (target_user->access < UL_OWNER))
2702 reply("CSMSG_MERGE_NOT_OWNER");
2706 /* Merge the channel structures and associated data. */
2707 merge_channel(channel->channel_info, target->channel_info);
2708 spamserv_cs_move_merge(user, channel, target, 0);
2709 sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
2710 unregister_channel(channel->channel_info, reason);
2711 reply("CSMSG_MERGE_SUCCESS", target->name);
2715 static CHANSERV_FUNC(cmd_opchan)
2717 struct mod_chanmode change;
2718 if(!IsHelping(user) && !channel->channel_info->may_opchan)
2720 reply("CSMSG_ALREADY_OPCHANNED", channel->name);
2723 channel->channel_info->may_opchan = 0;
2724 mod_chanmode_init(&change);
2726 change.args[0].mode = MODE_CHANOP;
2727 change.args[0].u.member = GetUserMode(channel, chanserv);
2728 if(!change.args[0].u.member)
2730 reply("CSMSG_OUT_OF_CHANNEL", channel->name);
2733 mod_chanmode_announce(chanserv, channel, &change);
2734 reply("CSMSG_OPCHAN_DONE", channel->name);
2738 static CHANSERV_FUNC(cmd_adduser)
2740 struct userData *actee;
2741 struct userData *actor, *real_actor;
2742 struct handle_info *handle;
2743 unsigned short access_level, override = 0;
2747 if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
2749 reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
2753 access_level = user_level_from_name(argv[2], UL_OWNER);
2756 reply("CSMSG_INVALID_ACCESS", argv[2]);
2760 actor = GetChannelUser(channel->channel_info, user->handle_info);
2761 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2763 if(actor->access <= access_level)
2765 reply("CSMSG_NO_BUMP_ACCESS");
2769 /* Trying to add someone with equal/more access? */
2770 if (!real_actor || real_actor->access <= access_level)
2771 override = CMD_LOG_OVERRIDE;
2773 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2776 if((actee = GetTrueChannelAccess(channel->channel_info, handle)))
2778 reply("CSMSG_USER_EXISTS", handle->handle, channel->name, actee->access);
2782 actee = add_channel_user(channel->channel_info, handle, access_level, 0, NULL);
2783 scan_user_presence(actee, NULL);
2784 reply("CSMSG_ADDED_USER", handle->handle, channel->name, access_level);
2785 return 1 | override;
2788 static CHANSERV_FUNC(cmd_clvl)
2790 struct handle_info *handle;
2791 struct userData *victim;
2792 struct userData *actor, *real_actor;
2793 unsigned short new_access, override = 0;
2794 int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
2798 actor = GetChannelUser(channel->channel_info, user->handle_info);
2799 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2801 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2804 if(handle == user->handle_info && !privileged)
2806 reply("CSMSG_NO_SELF_CLVL");
2810 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2812 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2816 if(actor->access <= victim->access && !privileged)
2818 reply("MSG_USER_OUTRANKED", handle->handle);
2822 new_access = user_level_from_name(argv[2], UL_OWNER);
2826 reply("CSMSG_INVALID_ACCESS", argv[2]);
2830 if(new_access >= actor->access && !privileged)
2832 reply("CSMSG_NO_BUMP_ACCESS");
2836 /* Trying to clvl a equal/higher user? */
2837 if(!real_actor || (real_actor->access <= victim->access && handle != user->handle_info))
2838 override = CMD_LOG_OVERRIDE;
2839 /* Trying to clvl someone to equal/higher access? */
2840 if(!real_actor || new_access >= real_actor->access)
2841 override = CMD_LOG_OVERRIDE;
2842 /* Helpers clvling themselves get caught by the "clvl someone to equal/higher access" check.
2843 * If they lower their own access it's not a big problem.
2846 victim->access = new_access;
2847 reply("CSMSG_CHANGED_ACCESS", handle->handle, new_access, channel->name);
2848 return 1 | override;
2851 static CHANSERV_FUNC(cmd_deluser)
2853 struct handle_info *handle;
2854 struct userData *victim;
2855 struct userData *actor, *real_actor;
2856 unsigned short access_level, override = 0;
2861 actor = GetChannelUser(channel->channel_info, user->handle_info);
2862 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2864 if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
2867 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2869 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2875 access_level = user_level_from_name(argv[1], UL_OWNER);
2878 reply("CSMSG_INVALID_ACCESS", argv[1]);
2881 if(access_level != victim->access)
2883 reply("CSMSG_INCORRECT_ACCESS", handle->handle, victim->access, argv[1]);
2889 access_level = victim->access;
2892 if((actor->access <= victim->access) && !IsHelping(user))
2894 reply("MSG_USER_OUTRANKED", victim->handle->handle);
2898 /* If people delete themselves it is an override, but they
2899 * could've used deleteme so we don't log it as an override
2901 if(!real_actor || (real_actor->access <= victim->access && real_actor != victim))
2902 override = CMD_LOG_OVERRIDE;
2904 chan_name = strdup(channel->name);
2905 del_channel_user(victim, 1);
2906 reply("CSMSG_DELETED_USER", handle->handle, access_level, chan_name);
2908 return 1 | override;
2912 cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
2914 struct userData *actor, *real_actor, *uData, *next;
2915 unsigned int override = 0;
2917 actor = GetChannelUser(channel->channel_info, user->handle_info);
2918 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2920 if(min_access > max_access)
2922 reply("CSMSG_BAD_RANGE", min_access, max_access);
2926 if(actor->access <= max_access)
2928 reply("CSMSG_NO_ACCESS");
2932 if(!real_actor || real_actor->access <= max_access)
2933 override = CMD_LOG_OVERRIDE;
2935 for(uData = channel->channel_info->users; uData; uData = next)
2939 if((uData->access >= min_access)
2940 && (uData->access <= max_access)
2941 && match_ircglob(uData->handle->handle, mask))
2942 del_channel_user(uData, 1);
2945 reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
2946 return 1 | override;
2949 static CHANSERV_FUNC(cmd_mdelowner)
2951 return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
2954 static CHANSERV_FUNC(cmd_mdelcoowner)
2956 return cmd_mdel_user(user, channel, UL_COOWNER, UL_COOWNER, argv[1], cmd);
2959 static CHANSERV_FUNC(cmd_mdelmaster)
2961 return cmd_mdel_user(user, channel, UL_MASTER, UL_MASTER, argv[1], cmd);
2964 static CHANSERV_FUNC(cmd_mdelop)
2966 return cmd_mdel_user(user, channel, UL_OP, UL_OP, argv[1], cmd);
2969 static CHANSERV_FUNC(cmd_mdelpeon)
2971 return cmd_mdel_user(user, channel, UL_PEON, UL_PEON, argv[1], cmd);
2975 cmd_trim_bans(struct userNode *user, struct chanNode *channel, unsigned long duration)
2977 struct banData *bData, *next;
2978 char interval[INTERVALLEN];
2980 unsigned long limit;
2983 limit = now - duration;
2984 for(bData = channel->channel_info->bans; bData; bData = next)
2988 if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
2991 del_channel_ban(bData);
2995 intervalString(interval, duration, user->handle_info);
2996 send_message(user, chanserv, "CSMSG_TRIMMED_BANS", count, channel->name, interval);
3001 cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration, int vacation)
3003 struct userData *actor, *uData, *next;
3004 char interval[INTERVALLEN];
3006 unsigned long limit;
3008 actor = GetChannelUser(channel->channel_info, user->handle_info);
3009 if(min_access > max_access)
3011 send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
3015 if(!actor || actor->access <= max_access)
3017 send_message(user, chanserv, "CSMSG_NO_ACCESS");
3022 limit = now - duration;
3023 for(uData = channel->channel_info->users; uData; uData = next)
3027 if((uData->seen > limit)
3029 || (HANDLE_FLAGGED(uData->handle, FROZEN) && !vacation))
3032 if(((uData->access >= min_access) && (uData->access <= max_access))
3033 || (!max_access && (uData->access < actor->access)))
3035 del_channel_user(uData, 1);
3043 max_access = (actor->access > UL_OWNER) ? UL_OWNER : (actor->access - 1);
3045 send_message(user, chanserv, "CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration, user->handle_info));
3049 static CHANSERV_FUNC(cmd_trim)
3051 unsigned long duration;
3052 unsigned short min_level, max_level;
3057 vacation = argc > 3 && !strcmp(argv[3], "vacation");
3058 duration = ParseInterval(argv[2]);
3061 reply("CSMSG_CANNOT_TRIM");
3065 if(!irccasecmp(argv[1], "bans"))
3067 cmd_trim_bans(user, channel, duration);
3070 else if(!irccasecmp(argv[1], "users"))
3072 cmd_trim_users(user, channel, 0, 0, duration, vacation);
3075 else if(parse_level_range(&min_level, &max_level, argv[1]))
3077 cmd_trim_users(user, channel, min_level, max_level, duration, vacation);
3080 else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
3082 cmd_trim_users(user, channel, min_level, min_level, duration, vacation);
3087 reply("CSMSG_INVALID_TRIM", argv[1]);
3092 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
3093 to the user. cmd_all takes advantage of this. */
3094 static CHANSERV_FUNC(cmd_up)
3096 struct mod_chanmode change;
3097 struct userData *uData;
3100 mod_chanmode_init(&change);
3102 change.args[0].u.member = GetUserMode(channel, user);
3103 if(!change.args[0].u.member)
3106 reply("MSG_CHANNEL_ABSENT", channel->name);
3110 uData = GetChannelAccess(channel->channel_info, user->handle_info);
3114 reply("CSMSG_GODMODE_UP", argv[0]);
3117 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveOps])
3119 change.args[0].mode = MODE_CHANOP;
3120 errmsg = "CSMSG_ALREADY_OPPED";
3122 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveVoice])
3124 change.args[0].mode = MODE_VOICE;
3125 errmsg = "CSMSG_ALREADY_VOICED";
3130 reply("CSMSG_NO_ACCESS");
3133 change.args[0].mode &= ~change.args[0].u.member->modes;
3134 if(!change.args[0].mode)
3137 reply(errmsg, channel->name);
3140 modcmd_chanmode_announce(&change);
3144 static CHANSERV_FUNC(cmd_down)
3146 struct mod_chanmode change;
3148 mod_chanmode_init(&change);
3150 change.args[0].u.member = GetUserMode(channel, user);
3151 if(!change.args[0].u.member)
3154 reply("MSG_CHANNEL_ABSENT", channel->name);
3158 if(!change.args[0].u.member->modes)
3161 reply("CSMSG_ALREADY_DOWN", channel->name);
3165 change.args[0].mode = MODE_REMOVE | change.args[0].u.member->modes;
3166 modcmd_chanmode_announce(&change);
3170 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)
3172 struct userData *cList;
3174 for(cList = user->handle_info->channels; cList; cList = cList->u_next)
3176 if(IsSuspended(cList->channel)
3177 || IsUserSuspended(cList)
3178 || !GetUserMode(cList->channel->channel, user))
3181 mcmd(user, cList->channel->channel, 0, NULL, cmd);
3187 static CHANSERV_FUNC(cmd_upall)
3189 return cmd_all(CSFUNC_ARGS, cmd_up);
3192 static CHANSERV_FUNC(cmd_downall)
3194 return cmd_all(CSFUNC_ARGS, cmd_down);
3197 typedef int validate_func_t(struct userNode *user, struct chanNode *channel, struct userNode *victim);
3198 typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
3201 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)
3203 unsigned int ii, valid;
3204 struct userNode *victim;
3205 struct mod_chanmode *change;
3207 change = mod_chanmode_alloc(argc - 1);
3209 for(ii=valid=0; ++ii < argc; )
3211 if(!(victim = GetUserH(argv[ii])))
3213 change->args[valid].mode = mode;
3214 change->args[valid].u.member = GetUserMode(channel, victim);
3215 if(!change->args[valid].u.member)
3217 if(validate && !validate(user, channel, victim))
3222 change->argc = valid;
3223 if(valid < (argc-1))
3224 reply("CSMSG_PROCESS_FAILED");
3227 modcmd_chanmode_announce(change);
3228 reply(action, channel->name);
3230 mod_chanmode_free(change);
3234 static CHANSERV_FUNC(cmd_op)
3236 return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
3239 static CHANSERV_FUNC(cmd_deop)
3241 return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
3244 static CHANSERV_FUNC(cmd_voice)
3246 return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
3249 static CHANSERV_FUNC(cmd_devoice)
3251 return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
3254 static CHANSERV_FUNC(cmd_opme)
3256 struct mod_chanmode change;
3259 mod_chanmode_init(&change);
3261 change.args[0].u.member = GetUserMode(channel, user);
3262 if(!change.args[0].u.member)
3265 reply("MSG_CHANNEL_ABSENT", channel->name);
3269 struct devnull_class *devnull;
3270 if(user->handle_info->devnull && (devnull = devnull_get(user->handle_info->devnull)) && (devnull->modes & DEVNULL_MODE_OPME))
3272 change.args[0].mode = MODE_CHANOP;
3273 errmsg = "CSMSG_ALREADY_OPPED";
3278 reply("CSMSG_NO_ACCESS");
3281 change.args[0].mode &= ~change.args[0].u.member->modes;
3282 if(!change.args[0].mode)
3285 reply(errmsg, channel->name);
3288 modcmd_chanmode_announce(&change);
3293 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, unsigned int *victimCount, struct modeNode **victims)
3299 for(ii=0; ii<channel->members.used; ii++)
3301 struct modeNode *mn = channel->members.list[ii];
3303 if(IsService(mn->user))
3306 if(!user_matches_glob(mn->user, ban, MATCH_USENICK | MATCH_VISIBLE))
3309 if(protect_user(mn->user, user, channel->channel_info))
3313 victims[(*victimCount)++] = mn;
3319 eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3321 struct userNode *victim;
3322 struct modeNode **victims;
3323 unsigned int offset, n, victimCount, duration = 0;
3324 char *reason = "Bye.", *ban, *name;
3325 char interval[INTERVALLEN];
3327 offset = (action & ACTION_ADD_TIMED_BAN) ? 3 : 2;
3328 REQUIRE_PARAMS(offset);
3329 if(argc > offset && IsNetServ(user))
3331 if(*argv[offset] == '$') {
3332 struct userNode *hib;
3333 const char *accountnameb = argv[offset] + 1;
3334 if(!(hib = GetUserH(accountnameb)))
3336 reply("MSG_HANDLE_UNKNOWN", accountnameb);
3345 reason = unsplit_string(argv + offset, argc - offset, NULL);
3346 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
3348 /* Truncate the reason to a length of TOPICLEN, as
3349 the ircd does; however, leave room for an ellipsis
3350 and the kicker's nick. */
3351 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
3355 if((victim = GetUserH(argv[1])))
3357 victims = alloca(sizeof(victims[0]));
3358 victims[0] = GetUserMode(channel, victim);
3359 /* XXX: The comparison with ACTION_KICK is just because all
3360 * other actions can work on users outside the channel, and we
3361 * want to allow those (e.g. unbans) in that case. If we add
3362 * some other ejection action for in-channel users, change
3364 victimCount = victims[0] ? 1 : 0;
3366 if(IsService(victim))
3368 reply("MSG_SERVICE_IMMUNE", victim->nick);
3372 if((action == ACTION_KICK) && !victimCount)
3374 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
3378 if(protect_user(victim, user, channel->channel_info))
3380 reply("CSMSG_USER_PROTECTED", victim->nick);
3384 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
3385 name = victim->nick;
3387 else if(!is_ircmask(argv[1]) && (*argv[1] == '*'))
3389 struct handle_info *hi;
3390 extern const char *titlehost_suffix;
3391 char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
3392 const char *accountname = argv[1] + 1;
3394 if(!(hi = get_handle_info(accountname)))
3396 reply("MSG_HANDLE_UNKNOWN", accountname);
3400 snprintf(banmask, sizeof(banmask), "*!*@%s.*.%s", hi->handle, titlehost_suffix);
3401 victims = alloca(sizeof(victims[0]) * channel->members.used);
3403 if(bad_channel_ban(channel, user, banmask, &victimCount, victims))
3405 reply("CSMSG_MASK_PROTECTED", banmask);
3409 if((action == ACTION_KICK) && (victimCount == 0))
3411 reply("CSMSG_NO_MATCHING_USERS", channel->name, banmask);
3415 name = ban = strdup(banmask);
3419 if(!is_ircmask(argv[1]))
3421 reply("MSG_NICK_UNKNOWN", argv[1]);
3425 victims = alloca(sizeof(victims[0]) * channel->members.used);
3427 if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
3429 reply("CSMSG_MASK_PROTECTED", argv[1]);
3433 if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
3435 reply("CSMSG_LAME_MASK", argv[1]);
3439 if((action == ACTION_KICK) && (victimCount == 0))
3441 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
3445 name = ban = strdup(argv[1]);
3448 /* Truncate the ban in place if necessary; we must ensure
3449 that 'ban' is a valid ban mask before sanitizing it. */
3450 sanitize_ircmask(ban);
3452 if(action & ACTION_ADD_BAN)
3454 struct banData *bData, *next;
3456 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans)
3458 reply("CSMSG_MAXIMUM_BANS", chanserv_conf.max_chan_bans);
3463 if(action & ACTION_ADD_TIMED_BAN)
3465 duration = ParseInterval(argv[2]);
3469 reply("CSMSG_DURATION_TOO_LOW");
3473 else if(duration > (86400 * 365 * 2))
3475 reply("CSMSG_DURATION_TOO_HIGH");
3481 for(bData = channel->channel_info->bans; bData; bData = next)
3483 if(match_ircglobs(bData->mask, ban))
3485 int exact = !irccasecmp(bData->mask, ban);
3487 /* The ban is redundant; there is already a ban
3488 with the same effect in place. */
3492 free(bData->reason);
3493 bData->reason = strdup(reason);
3494 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
3496 reply("CSMSG_REASON_CHANGE", ban);
3500 if(exact && bData->expires)
3504 /* If the ban matches an existing one exactly,
3505 extend the expiration time if the provided
3506 duration is longer. */
3507 if(duration && (now + duration > bData->expires))
3509 bData->expires = now + duration;
3520 /* Delete the expiration timeq entry and
3521 requeue if necessary. */
3522 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
3525 timeq_add(bData->expires, expire_ban, bData);
3529 /* automated kickban */
3532 reply("CSMSG_BAN_EXTENDED", ban, intervalString(interval, duration, user->handle_info));
3534 reply("CSMSG_BAN_ADDED", name, channel->name);
3540 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3547 if(match_ircglobs(ban, bData->mask))
3549 /* The ban we are adding makes previously existing
3550 bans redundant; silently remove them. */
3551 del_channel_ban(bData);
3555 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);
3557 name = ban = strdup(bData->mask);
3561 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
3563 extern const char *hidden_host_suffix;
3564 const char *old_name = chanserv_conf.old_ban_names->list[n];
3566 unsigned int l1, l2;
3569 l2 = strlen(old_name);
3572 if(irccasecmp(ban + l1 - l2, old_name))
3574 new_mask = malloc(MAXLEN);
3575 sprintf(new_mask, "%.*s%s", (int)(l1-l2), ban, hidden_host_suffix);
3577 name = ban = new_mask;
3582 if(action & ACTION_BAN)
3584 unsigned int exists;
3585 struct mod_chanmode *change;
3587 if(channel->banlist.used >= MAXBANS)
3590 reply("CSMSG_BANLIST_FULL", channel->name);
3595 exists = ChannelBanExists(channel, ban);
3596 change = mod_chanmode_alloc(victimCount + 1);
3597 for(n = 0; n < victimCount; ++n)
3599 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_VOICE;
3600 change->args[n].u.member = victims[n];
3604 change->args[n].mode = MODE_BAN;
3605 change->args[n++].u.hostmask = ban;
3609 modcmd_chanmode_announce(change);
3611 mod_chanmode_announce(chanserv, channel, change);
3612 mod_chanmode_free(change);
3614 if(exists && (action == ACTION_BAN))
3617 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3623 if(action & ACTION_KICK)
3625 char kick_reason[MAXLEN];
3626 sprintf(kick_reason, "(%s) %s", user->nick, reason);
3628 for(n = 0; n < victimCount; n++)
3629 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
3634 /* No response, since it was automated. */
3636 else if(action & ACTION_ADD_BAN)
3639 reply("CSMSG_TIMED_BAN_ADDED", name, channel->name, intervalString(interval, duration, user->handle_info));
3641 reply("CSMSG_BAN_ADDED", name, channel->name);
3643 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
3644 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
3645 else if(action & ACTION_BAN)
3646 reply("CSMSG_BAN_DONE", name, channel->name);
3647 else if(action & ACTION_KICK && victimCount)
3648 reply("CSMSG_KICK_DONE", name, channel->name);
3654 static CHANSERV_FUNC(cmd_kickban)
3656 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
3659 static CHANSERV_FUNC(cmd_kick)
3661 return eject_user(CSFUNC_ARGS, ACTION_KICK);
3664 static CHANSERV_FUNC(cmd_ban)
3666 return eject_user(CSFUNC_ARGS, ACTION_BAN);
3669 static CHANSERV_FUNC(cmd_addban)
3671 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN);
3674 static CHANSERV_FUNC(cmd_addtimedban)
3676 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN);
3679 struct mod_chanmode *
3680 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
3682 struct mod_chanmode *change;
3683 unsigned char *match;
3684 unsigned int ii, count;
3686 match = alloca(bans->used);
3689 for(ii = count = 0; ii < bans->used; ++ii)
3691 match[ii] = user_matches_glob(actee, bans->list[ii]->ban,
3692 MATCH_USENICK | MATCH_VISIBLE);
3699 for(ii = count = 0; ii < bans->used; ++ii)
3701 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
3708 change = mod_chanmode_alloc(count);
3709 for(ii = count = 0; ii < bans->used; ++ii)
3713 change->args[count].mode = MODE_REMOVE | MODE_BAN;
3714 change->args[count++].u.hostmask = strdup(bans->list[ii]->ban);
3716 assert(count == change->argc);
3721 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3723 struct userNode *actee;
3729 /* may want to allow a comma delimited list of users... */
3730 if(!(actee = GetUserH(argv[1])))
3732 if(!is_ircmask(argv[1]) && *argv[1] == '*')
3734 char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
3735 const char *accountname = argv[1] + 1;
3737 snprintf(banmask, sizeof(banmask), "*!*@%s.*", accountname);
3738 mask = strdup(banmask);
3740 else if(!is_ircmask(argv[1]))
3742 reply("MSG_NICK_UNKNOWN", argv[1]);
3747 mask = strdup(argv[1]);
3751 /* We don't sanitize the mask here because ircu
3753 if(action & ACTION_UNBAN)
3755 struct mod_chanmode *change;
3756 change = find_matching_bans(&channel->banlist, actee, mask);
3761 modcmd_chanmode_announce(change);
3762 for(ii = 0; ii < change->argc; ++ii)
3763 free((char*)change->args[ii].u.hostmask);
3764 mod_chanmode_free(change);
3769 if(action & ACTION_DEL_BAN)
3771 struct banData *ban, *next;
3773 ban = channel->channel_info->bans;
3777 for( ; ban && !user_matches_glob(actee, ban->mask,
3778 MATCH_USENICK | MATCH_VISIBLE);
3781 for( ; ban && !match_ircglobs(mask, ban->mask);
3786 del_channel_ban(ban);
3793 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3795 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3801 static CHANSERV_FUNC(cmd_unban)
3803 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3806 static CHANSERV_FUNC(cmd_delban)
3808 /* it doesn't necessarily have to remove the channel ban - may want
3809 to make that an option. */
3810 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_BAN);
3813 static CHANSERV_FUNC(cmd_unbanme)
3815 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3816 long flags = ACTION_UNBAN;
3818 /* remove permanent bans if the user has the proper access. */
3819 if(uData->access >= UL_MASTER)
3820 flags |= ACTION_DEL_BAN;
3822 argv[1] = user->nick;
3823 return unban_user(user, channel, 2, argv, cmd, flags);
3826 static CHANSERV_FUNC(cmd_unbanall)
3828 struct mod_chanmode *change;
3831 if(!channel->banlist.used)
3833 reply("CSMSG_NO_BANS", channel->name);
3837 change = mod_chanmode_alloc(channel->banlist.used);
3838 for(ii=0; ii<channel->banlist.used; ii++)
3840 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3841 change->args[ii].u.hostmask = strdup(channel->banlist.list[ii]->ban);
3843 modcmd_chanmode_announce(change);
3844 for(ii = 0; ii < change->argc; ++ii)
3845 free((char*)change->args[ii].u.hostmask);
3846 mod_chanmode_free(change);
3847 reply("CSMSG_BANS_REMOVED", channel->name);
3851 static CHANSERV_FUNC(cmd_open)
3853 struct mod_chanmode *change;
3856 change = find_matching_bans(&channel->banlist, user, NULL);
3858 change = mod_chanmode_alloc(0);
3859 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3860 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3861 && channel->channel_info->modes.modes_set)
3862 change->modes_clear &= ~channel->channel_info->modes.modes_set;
3863 modcmd_chanmode_announce(change);
3864 reply("CSMSG_CHANNEL_OPENED", channel->name);
3865 for(ii = 0; ii < change->argc; ++ii)
3866 free((char*)change->args[ii].u.hostmask);
3867 mod_chanmode_free(change);
3871 static CHANSERV_FUNC(cmd_myaccess)
3873 static struct string_buffer sbuf;
3874 struct handle_info *target_handle;
3875 struct userData *uData;
3880 target_handle = user->handle_info;
3881 else if(!IsStaff(user))
3883 reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
3886 else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3889 if(!oper_outranks(user, target_handle))
3892 if(!target_handle->channels)
3894 reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
3898 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3899 for(uData = target_handle->channels; uData; uData = uData->u_next)
3901 struct chanData *cData = uData->channel;
3903 unsigned int base_len;
3905 if(uData->access > UL_OWNER)
3907 if(uData->access == UL_OWNER)
3910 if(IsProtected(cData)
3911 && (target_handle != user->handle_info)
3912 && !GetTrueChannelAccess(cData, user->handle_info)
3913 && !IsNetworkHelper(user))
3916 string_buffer_append_printf(&sbuf, "[%s (%d,", cData->channel->name, uData->access);
3917 base_len = sbuf.used;
3918 if(IsUserSuspended(uData))
3919 string_buffer_append(&sbuf, 's');
3920 if(IsUserAutoOp(uData))
3922 if(uData->access >= cData->lvlOpts[lvlGiveOps])
3923 string_buffer_append(&sbuf, 'o');
3924 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
3925 string_buffer_append(&sbuf, 'v');
3927 if(IsUserAutoInvite(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe]))
3928 string_buffer_append(&sbuf, 'i');
3929 if(sbuf.used==base_len)
3932 string_buffer_append_printf(&sbuf, ")] %s", uData->info);
3934 string_buffer_append_string(&sbuf, ")]");
3935 string_buffer_append(&sbuf, '\0');
3936 send_message_type(4, user, cmd->parent->bot, "%s", sbuf.list);
3940 reply("CSMSG_MYACCESS_COUNT_1", target_handle->handle, ccount, ocount);
3942 reply("CSMSG_MYACCESS_COUNT", target_handle->handle, ccount, ocount);
3948 static CHANSERV_FUNC(cmd_access)
3950 struct userNode *target;
3951 struct handle_info *target_handle;
3952 struct userData *uData;
3954 char prefix[MAXLEN];
3959 target_handle = target->handle_info;
3961 else if((target = GetUserH(argv[1])))
3963 target_handle = target->handle_info;
3965 else if(argv[1][0] == '*')
3967 if(!(target_handle = get_handle_info(argv[1]+1)))
3969 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3975 reply("MSG_NICK_UNKNOWN", argv[1]);
3979 assert(target || target_handle);
3981 if(target == chanserv)
3983 reply("CSMSG_IS_CHANSERV");
3991 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
3996 reply("MSG_USER_AUTHENTICATE", target->nick);
3999 reply("MSG_AUTHENTICATE");
4005 const char *epithet = NULL, *type = NULL;
4008 epithet = chanserv_conf.irc_operator_epithet;
4009 type = user_find_message(user, "CSMSG_OPERATOR_TITLE");
4011 else if(IsNetworkHelper(target))
4013 epithet = chanserv_conf.network_helper_epithet;
4014 type = user_find_message(user, "CSMSG_UC_H_TITLE");
4016 else if(IsSupportHelper(target))
4018 epithet = chanserv_conf.support_helper_epithet;
4019 type = user_find_message(user, "CSMSG_LC_H_TITLE");
4023 if(target_handle->epithet)
4024 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
4026 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
4028 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
4032 sprintf(prefix, "%s", target_handle->handle);
4035 if(!channel->channel_info)
4037 reply("CSMSG_NOT_REGISTERED", channel->name);
4041 helping = HANDLE_FLAGGED(target_handle, HELPING)
4042 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
4043 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
4045 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
4046 /* To prevent possible information leaks, only show infolines
4047 * if the requestor is in the channel or it's their own
4049 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
4051 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
4053 /* Likewise, only say it's suspended if the user has active
4054 * access in that channel or it's their own entry. */
4055 if(IsUserSuspended(uData)
4056 && (GetChannelUser(channel->channel_info, user->handle_info)
4057 || (user->handle_info == uData->handle)))
4059 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
4064 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
4071 def_list(struct listData *list)
4075 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
4077 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
4078 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4079 if(list->table.length == 1)
4081 msg = user_find_message(list->user, "MSG_NONE");
4082 send_message_type(4, list->user, list->bot, " %s", msg);
4087 userData_access_comp(const void *arg_a, const void *arg_b)
4089 const struct userData *a = *(struct userData**)arg_a;
4090 const struct userData *b = *(struct userData**)arg_b;
4092 if(a->access != b->access)
4093 res = b->access - a->access;
4095 res = irccasecmp(a->handle->handle, b->handle->handle);
4100 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
4102 void (*send_list)(struct listData *);
4103 struct userData *uData;
4104 struct listData lData;
4105 unsigned int matches;
4109 lData.bot = cmd->parent->bot;
4110 lData.channel = channel;
4111 lData.lowest = lowest;
4112 lData.highest = highest;
4113 lData.search = (argc > 1) ? argv[1] : NULL;
4114 send_list = def_list;
4116 if(user->handle_info)
4118 switch(user->handle_info->userlist_style)
4120 case HI_STYLE_DEF: send_list = def_list; break;
4121 case HI_STYLE_ZOOT: send_list = def_list; break;
4125 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
4127 for(uData = channel->channel_info->users; uData; uData = uData->next)
4129 if((uData->access < lowest)
4130 || (uData->access > highest)
4131 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
4133 lData.users[matches++] = uData;
4135 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
4137 lData.table.length = matches+1;
4138 lData.table.width = 4;
4139 lData.table.flags = TABLE_NO_FREE;
4140 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
4141 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
4142 lData.table.contents[0] = ary;
4145 ary[2] = "Last Seen";
4147 for(matches = 1; matches < lData.table.length; ++matches)
4149 char seen[INTERVALLEN];
4151 uData = lData.users[matches-1];
4152 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
4153 lData.table.contents[matches] = ary;
4154 ary[0] = strtab(uData->access);
4155 ary[1] = uData->handle->handle;
4158 else if(!uData->seen)
4161 ary[2] = intervalString(seen, now - uData->seen, user->handle_info);
4162 ary[2] = strdup(ary[2]);
4163 if(IsUserSuspended(uData))
4164 ary[3] = "Suspended";
4165 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
4166 ary[3] = "Vacation";
4167 else if(HANDLE_FLAGGED(uData->handle, BOT))
4173 for(matches = 1; matches < lData.table.length; ++matches)
4175 free((char*)lData.table.contents[matches][2]);
4176 free(lData.table.contents[matches]);
4178 free(lData.table.contents[0]);
4179 free(lData.table.contents);
4180 reply("CSMSG_TOTAL_USERS",(lData.table.length - 1),channel->name);
4184 static CHANSERV_FUNC(cmd_users)
4186 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
4189 static CHANSERV_FUNC(cmd_wlist)
4191 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
4194 static CHANSERV_FUNC(cmd_clist)
4196 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
4199 static CHANSERV_FUNC(cmd_mlist)
4201 return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
4204 static CHANSERV_FUNC(cmd_olist)
4206 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
4209 static CHANSERV_FUNC(cmd_plist)
4211 return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
4214 static CHANSERV_FUNC(cmd_bans)
4216 struct userNode *search_u = NULL;
4217 struct helpfile_table tbl;
4218 unsigned int matches = 0, timed = 0, search_wilds = 0, ii;
4219 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
4220 const char *msg_never, *triggered, *expires;
4221 struct banData *ban, **bans;
4225 else if(strchr(search = argv[1], '!'))
4228 search_wilds = search[strcspn(search, "?*")];
4230 else if(!(search_u = GetUserH(search)))
4231 reply("MSG_NICK_UNKNOWN", search);
4233 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
4235 for(ban = channel->channel_info->bans; ban; ban = ban->next)
4239 if(!user_matches_glob(search_u, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
4244 if(search_wilds ? !match_ircglobs(search, ban->mask) : !match_ircglob(search, ban->mask))
4247 bans[matches++] = ban;
4252 tbl.length = matches + 1;
4253 tbl.width = 4 + timed;
4255 tbl.flags = TABLE_NO_FREE;
4256 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
4257 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4258 tbl.contents[0][0] = "Mask";
4259 tbl.contents[0][1] = "Set By";
4260 tbl.contents[0][2] = "Triggered";
4263 tbl.contents[0][3] = "Expires";
4264 tbl.contents[0][4] = "Reason";
4267 tbl.contents[0][3] = "Reason";
4270 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4272 free(tbl.contents[0]);
4277 msg_never = user_find_message(user, "MSG_NEVER");
4278 for(ii = 0; ii < matches; )
4284 else if(ban->expires)
4285 expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
4287 expires = msg_never;
4290 triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
4292 triggered = msg_never;
4294 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4295 tbl.contents[ii][0] = ban->mask;
4296 tbl.contents[ii][1] = ban->owner;
4297 tbl.contents[ii][2] = strdup(triggered);
4300 tbl.contents[ii][3] = strdup(expires);
4301 tbl.contents[ii][4] = ban->reason;
4304 tbl.contents[ii][3] = ban->reason;
4306 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4307 reply("MSG_MATCH_COUNT", matches);
4308 for(ii = 1; ii < tbl.length; ++ii)
4310 free((char*)tbl.contents[ii][2]);
4312 free((char*)tbl.contents[ii][3]);
4313 free(tbl.contents[ii]);
4315 free(tbl.contents[0]);
4321 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
4323 struct chanData *cData = channel->channel_info;
4324 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
4326 if(cData->topic_mask)
4327 return !match_ircglob(new_topic, cData->topic_mask);
4328 else if(cData->topic)
4329 return irccasecmp(new_topic, cData->topic);
4334 static CHANSERV_FUNC(cmd_topic)
4336 struct chanData *cData;
4339 cData = channel->channel_info;
4344 SetChannelTopic(channel, chanserv, cData->topic, 1);
4345 reply("CSMSG_TOPIC_SET", cData->topic);
4349 reply("CSMSG_NO_TOPIC", channel->name);
4353 topic = unsplit_string(argv + 1, argc - 1, NULL);
4354 /* If they say "!topic *", use an empty topic. */
4355 if((topic[0] == '*') && (topic[1] == 0))
4357 if(bad_topic(channel, user, topic))
4359 char *topic_mask = cData->topic_mask;
4362 char new_topic[TOPICLEN+1], tchar;
4363 int pos=0, starpos=-1, dpos=0, len;
4365 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
4372 len = strlen(topic);
4373 if((dpos + len) > TOPICLEN)
4374 len = TOPICLEN + 1 - dpos;
4375 memcpy(new_topic+dpos, topic, len);
4379 case '\\': tchar = topic_mask[pos++]; /* and fall through */
4380 default: new_topic[dpos++] = tchar; break;
4383 if((dpos > TOPICLEN) || tchar)
4386 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
4387 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
4390 new_topic[dpos] = 0;
4391 SetChannelTopic(channel, chanserv, new_topic, 1);
4393 reply("CSMSG_TOPIC_LOCKED", channel->name);
4398 SetChannelTopic(channel, chanserv, topic, 1);
4400 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
4402 /* Grab the topic and save it as the default topic. */
4404 cData->topic = strdup(channel->topic);
4410 static CHANSERV_FUNC(cmd_mode)
4412 struct userData *uData;
4413 struct mod_chanmode *change;
4419 change = &channel->channel_info->modes;
4420 if(change->modes_set || change->modes_clear) {
4421 modcmd_chanmode_announce(change);
4422 reply("CSMSG_DEFAULTED_MODES", channel->name);
4424 reply("CSMSG_NO_MODES", channel->name);
4428 uData = GetChannelUser(channel->channel_info, user->handle_info);
4430 base_oplevel = MAXOPLEVEL;
4431 else if (uData->access >= UL_OWNER)
4434 base_oplevel = 1 + UL_OWNER - uData->access;
4435 change = mod_chanmode_parse(channel, user, argv+1, argc-1, MCP_KEY_FREE|MCP_IGN_REGISTERED|MCP_NO_APASS, base_oplevel);
4438 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
4442 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
4443 && mode_lock_violated(&channel->channel_info->modes, change))
4446 mod_chanmode_format(&channel->channel_info->modes, modes);
4447 reply("CSMSG_MODE_LOCKED", modes, channel->name);
4451 modcmd_chanmode_announce(change);
4452 mod_chanmode_format(change, fmt);
4453 mod_chanmode_free(change);
4454 reply("CSMSG_MODES_SET", fmt);
4459 chanserv_del_invite_mark(void *data)
4461 struct ChanUser *chanuser = data;
4462 struct chanNode *channel = chanuser->chan;
4464 if(!channel) return;
4465 for(i = 0; i < channel->invited.used; i++)
4467 if(channel->invited.list[i] == chanuser->user) {
4468 userList_remove(&channel->invited, chanuser->user);
4474 static CHANSERV_FUNC(cmd_invite)
4476 struct userNode *invite;
4477 struct ChanUser *chanuser;
4482 if(!(invite = GetUserH(argv[1])))
4484 reply("MSG_NICK_UNKNOWN", argv[1]);
4491 if(GetUserMode(channel, invite))
4493 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
4497 for(i = 0; i < channel->invited.used; i++)
4499 if(channel->invited.list[i] == invite) {
4500 reply("CSMSG_ALREADY_INVITED", invite->nick, channel->name);
4509 char *reason = unsplit_string(argv + 2, argc - 2, NULL);
4510 send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
4513 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
4515 irc_invite(chanserv, invite, channel);
4517 reply("CSMSG_INVITED_USER", argv[1], channel->name);
4519 userList_append(&channel->invited, invite);
4520 chanuser = calloc(1, sizeof(*chanuser));
4521 chanuser->user=invite;
4522 chanuser->chan=channel;
4523 timeq_add(now + chanserv_conf.invited_timeout, chanserv_del_invite_mark, chanuser);
4528 static CHANSERV_FUNC(cmd_inviteme)
4530 if(GetUserMode(channel, user))
4532 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
4535 if(channel->channel_info
4536 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
4538 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
4541 irc_invite(cmd->parent->bot, user, channel);
4545 static CHANSERV_FUNC(cmd_invitemeall)
4547 struct handle_info *target = user->handle_info;
4548 struct userData *uData;
4550 if(!target->channels)
4552 reply("CSMSG_SQUAT_ACCESS", target->handle);
4556 for(uData = target->channels; uData; uData = uData->u_next)
4558 struct chanData *cData = uData->channel;
4559 if(uData->access >= cData->lvlOpts[lvlInviteMe])
4561 irc_invite(cmd->parent->bot, user, cData->channel);
4568 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
4571 char buf1[INTERVALLEN], buf2[INTERVALLEN];
4573 /* We display things based on two dimensions:
4574 * - Issue time: present or absent
4575 * - Expiration: revoked, expired, expires in future, or indefinite expiration
4576 * (in order of precedence, so something both expired and revoked
4577 * only counts as revoked)
4579 combo = (suspended->issued ? 4 : 0)
4580 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
4582 case 0: /* no issue time, indefinite expiration */
4583 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
4585 case 1: /* no issue time, expires in future */
4586 intervalString(buf1, suspended->expires-now, user->handle_info);
4587 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
4589 case 2: /* no issue time, expired */
4590 intervalString(buf1, now-suspended->expires, user->handle_info);
4591 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
4593 case 3: /* no issue time, revoked */
4594 intervalString(buf1, now-suspended->revoked, user->handle_info);
4595 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
4597 case 4: /* issue time set, indefinite expiration */
4598 intervalString(buf1, now-suspended->issued, user->handle_info);
4599 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
4601 case 5: /* issue time set, expires in future */
4602 intervalString(buf1, now-suspended->issued, user->handle_info);
4603 intervalString(buf2, suspended->expires-now, user->handle_info);
4604 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
4606 case 6: /* issue time set, expired */
4607 intervalString(buf1, now-suspended->issued, user->handle_info);
4608 intervalString(buf2, now-suspended->expires, user->handle_info);
4609 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
4611 case 7: /* issue time set, revoked */
4612 intervalString(buf1, now-suspended->issued, user->handle_info);
4613 intervalString(buf2, now-suspended->revoked, user->handle_info);
4614 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
4617 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
4623 show_giveownership_info(struct svccmd *cmd, struct userNode *user, struct giveownership *giveownership)
4626 const char *fmt = "%a %b %d %H:%M %Y";
4627 strftime(buf, sizeof(buf), fmt, localtime(&giveownership->issued));
4629 if(giveownership->staff_issuer)
4631 if(giveownership->reason)
4632 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF_REASON", giveownership->old_owner,
4633 giveownership->target, giveownership->target_access,
4634 giveownership->staff_issuer, buf, giveownership->reason);
4636 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF", giveownership->old_owner,
4637 giveownership->target, giveownership->target_access,
4638 giveownership->staff_issuer, buf);
4642 reply("CSMSG_CHANNEL_OWNERSHIP_NORMAL", giveownership->old_owner, giveownership->target, giveownership->target_access, buf);
4646 static CHANSERV_FUNC(cmd_info)
4648 char modes[MAXLEN], buffer[INTERVALLEN];
4649 struct userData *uData, *owner;
4650 struct chanData *cData;
4651 struct do_not_register *dnr;
4656 cData = channel->channel_info;
4657 reply("CSMSG_CHANNEL_INFO", channel->name);
4659 uData = GetChannelUser(cData, user->handle_info);
4660 if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
4662 mod_chanmode_format(&cData->modes, modes);
4663 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
4664 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
4667 for(it = dict_first(cData->notes); it; it = iter_next(it))
4671 note = iter_data(it);
4672 if(!note_type_visible_to_user(cData, note->type, user))
4675 padding = PADLEN - 1 - strlen(iter_key(it));
4676 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
4679 if(cData->max_time) {
4680 reply("CSMSG_CHANNEL_MAX_TIME", cData->max, intervalString(buffer, now - cData->max_time, user->handle_info));
4682 reply("CSMSG_CHANNEL_MAX", cData->max);
4684 for(owner = cData->users; owner; owner = owner->next)
4685 if(owner->access == UL_OWNER)
4686 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
4687 reply("CSMSG_CHANNEL_USERS", cData->userCount);
4688 reply("CSMSG_CHANNEL_BANS", cData->banCount);
4689 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
4691 privileged = IsStaff(user);
4693 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
4694 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
4695 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
4697 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
4698 chanserv_show_dnrs(user, cmd, channel->name, NULL);
4700 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
4702 struct suspended *suspended;
4703 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
4704 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
4705 show_suspension_info(cmd, user, suspended);
4707 else if(IsSuspended(cData))
4709 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
4710 show_suspension_info(cmd, user, cData->suspended);
4713 if(cData->giveownership && ((uData && (uData->access >= UL_COOWNER)) || IsStaff(user)))
4715 struct giveownership *giveownership;
4716 reply("CSMSG_CHANNEL_OWNERSHIP_HISTORY", channel->name);
4717 for(giveownership = cData->giveownership; giveownership; giveownership = giveownership->previous)
4718 show_giveownership_info(cmd, user, giveownership);
4723 static CHANSERV_FUNC(cmd_netinfo)
4725 extern unsigned long boot_time;
4726 extern unsigned long burst_length;
4727 char interval[INTERVALLEN];
4729 reply("CSMSG_NETWORK_INFO");
4730 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
4731 reply("CSMSG_NETWORK_USERS", dict_size(clients));
4732 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
4733 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
4734 reply("CSMSG_NETWORK_BANS", banCount);
4735 reply("CSMSG_NETWORK_CHANUSERS", userCount);
4736 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
4737 reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
4742 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
4744 struct helpfile_table table;
4746 struct userNode *user;
4751 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4752 table.contents = alloca(list->used*sizeof(*table.contents));
4753 for(nn=0; nn<list->used; nn++)
4755 user = list->list[nn];
4756 if(user->modes & skip_flags)
4762 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
4765 nick = alloca(strlen(user->nick)+3);
4766 sprintf(nick, "(%s)", user->nick);
4770 table.contents[table.length][0] = nick;
4773 table_send(chanserv, to->nick, 0, NULL, table);
4776 static CHANSERV_FUNC(cmd_ircops)
4778 reply("CSMSG_STAFF_OPERS");
4779 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
4783 static CHANSERV_FUNC(cmd_helpers)
4785 reply("CSMSG_STAFF_HELPERS");
4786 send_staff_list(user, &curr_helpers, FLAGS_OPER);
4790 static CHANSERV_FUNC(cmd_staff)
4792 reply("CSMSG_NETWORK_STAFF");
4793 cmd_ircops(CSFUNC_ARGS);
4794 cmd_helpers(CSFUNC_ARGS);
4798 static CHANSERV_FUNC(cmd_peek)
4800 struct modeNode *mn;
4801 char modes[MODELEN];
4803 struct helpfile_table table;
4804 int opcount = 0, voicecount = 0, srvcount = 0;
4806 irc_make_chanmode(channel, modes);
4808 reply("CSMSG_PEEK_INFO", channel->name);
4809 reply("CSMSG_PEEK_TOPIC", channel->topic);
4810 reply("CSMSG_PEEK_MODES", modes);
4814 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4815 table.contents = alloca(channel->members.used*sizeof(*table.contents));
4816 for(n = 0; n < channel->members.used; n++)
4818 mn = channel->members.list[n];
4819 if(IsLocal(mn->user))
4821 else if(mn->modes & MODE_CHANOP)
4823 else if(mn->modes & MODE_VOICE)
4826 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
4828 table.contents[table.length] = alloca(sizeof(**table.contents));
4829 table.contents[table.length][0] = mn->user->nick;
4833 reply("CSMSG_PEEK_USERS", channel->members.used, opcount, voicecount,
4834 (channel->members.used - opcount - voicecount - srvcount));
4838 reply("CSMSG_PEEK_OPS");
4839 table_send(chanserv, user->nick, 0, NULL, table);
4842 reply("CSMSG_PEEK_NO_OPS");
4846 static MODCMD_FUNC(cmd_wipeinfo)
4848 struct handle_info *victim;
4849 struct userData *ud, *actor, *real_actor;
4850 unsigned int override = 0;
4853 actor = GetChannelUser(channel->channel_info, user->handle_info);
4854 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
4855 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4857 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4859 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4862 if((ud->access >= actor->access) && (ud != actor))
4864 reply("MSG_USER_OUTRANKED", victim->handle);
4867 if((ud != real_actor) && (!real_actor || (ud->access >= real_actor->access)))
4868 override = CMD_LOG_OVERRIDE;
4872 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4873 return 1 | override;
4876 static CHANSERV_FUNC(cmd_resync)
4878 struct mod_chanmode *changes;
4879 struct chanData *cData = channel->channel_info;
4880 unsigned int ii, used;
4882 changes = mod_chanmode_alloc(channel->members.used * 2);
4883 for(ii = used = 0; ii < channel->members.used; ++ii)
4885 struct modeNode *mn = channel->members.list[ii];
4886 struct userData *uData;
4888 if(IsService(mn->user))
4891 uData = GetChannelAccess(cData, mn->user->handle_info);
4892 if(!cData->lvlOpts[lvlGiveOps]
4893 || (uData && uData->access >= cData->lvlOpts[lvlGiveOps]))
4895 if(!(mn->modes & MODE_CHANOP))
4897 if(!uData || IsUserAutoOp(uData))
4899 changes->args[used].mode = MODE_CHANOP;
4900 changes->args[used++].u.member = mn;
4901 if(!(mn->modes & MODE_VOICE))
4903 changes->args[used].mode = MODE_VOICE;
4904 changes->args[used++].u.member = mn;
4909 else if(!cData->lvlOpts[lvlGiveVoice]
4910 || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice]))
4912 if(mn->modes & MODE_CHANOP)
4914 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4915 changes->args[used++].u.member = mn;
4917 if(!(mn->modes & MODE_VOICE) && (!uData || IsUserAutoOp(uData)))
4919 changes->args[used].mode = MODE_VOICE;
4920 changes->args[used++].u.member = mn;
4927 changes->args[used].mode = MODE_REMOVE | mn->modes;
4928 changes->args[used++].u.member = mn;
4932 changes->argc = used;
4933 modcmd_chanmode_announce(changes);
4934 mod_chanmode_free(changes);
4935 reply("CSMSG_RESYNCED_USERS", channel->name);
4939 static CHANSERV_FUNC(cmd_seen)
4941 struct userData *uData;
4942 struct handle_info *handle;
4943 char seen[INTERVALLEN];
4947 if(!irccasecmp(argv[1], chanserv->nick))
4949 reply("CSMSG_IS_CHANSERV");
4953 if(!(handle = get_handle_info(argv[1])))
4955 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4959 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4961 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4966 reply("CSMSG_USER_PRESENT", handle->handle);
4967 else if(uData->seen)
4968 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
4970 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4972 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4973 reply("CSMSG_USER_VACATION", handle->handle);
4978 static MODCMD_FUNC(cmd_names)
4980 struct userNode *targ;
4981 struct userData *targData;
4982 unsigned int ii, pos;
4985 for(ii=pos=0; ii<channel->members.used; ++ii)
4987 targ = channel->members.list[ii]->user;
4988 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4991 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
4994 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4998 if(IsUserSuspended(targData))
5000 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
5003 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
5004 reply("CSMSG_END_NAMES", channel->name);
5009 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
5011 switch(ntype->visible_type)
5013 case NOTE_VIS_ALL: return 1;
5014 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
5015 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
5020 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
5022 struct userData *uData;
5024 switch(ntype->set_access_type)
5026 case NOTE_SET_CHANNEL_ACCESS:
5027 if(!user->handle_info)
5029 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
5031 return uData->access >= ntype->set_access.min_ulevel;
5032 case NOTE_SET_CHANNEL_SETTER:
5033 return check_user_level(channel, user, lvlSetters, 1, 0);
5034 case NOTE_SET_PRIVILEGED: default:
5035 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
5039 static CHANSERV_FUNC(cmd_note)
5041 struct chanData *cData;
5043 struct note_type *ntype;
5045 cData = channel->channel_info;
5048 reply("CSMSG_NOT_REGISTERED", channel->name);
5052 /* If no arguments, show all visible notes for the channel. */
5058 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
5060 note = iter_data(it);
5061 if(!note_type_visible_to_user(cData, note->type, user))
5064 reply("CSMSG_NOTELIST_HEADER", channel->name);
5065 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
5068 reply("CSMSG_NOTELIST_END", channel->name);
5070 reply("CSMSG_NOTELIST_EMPTY", channel->name);
5072 /* If one argument, show the named note. */
5075 if((note = dict_find(cData->notes, argv[1], NULL))
5076 && note_type_visible_to_user(cData, note->type, user))
5078 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
5080 else if((ntype = dict_find(note_types, argv[1], NULL))
5081 && note_type_visible_to_user(NULL, ntype, user))
5083 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
5088 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
5092 /* Assume they're trying to set a note. */
5096 ntype = dict_find(note_types, argv[1], NULL);
5099 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
5102 else if(note_type_settable_by_user(channel, ntype, user))
5104 note_text = unsplit_string(argv+2, argc-2, NULL);
5105 if((note = dict_find(cData->notes, argv[1], NULL)))
5106 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
5107 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
5108 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
5110 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
5112 /* The note is viewable to staff only, so return 0
5113 to keep the invocation from getting logged (or
5114 regular users can see it in !events). */
5120 reply("CSMSG_NO_ACCESS");
5127 static CHANSERV_FUNC(cmd_delnote)
5132 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
5133 || !note_type_settable_by_user(channel, note->type, user))
5135 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
5138 dict_remove(channel->channel_info->notes, note->type->name);
5139 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
5143 static CHANSERV_FUNC(cmd_events)
5145 struct logSearch discrim;
5146 struct logReport report;
5147 unsigned int matches, limit;
5149 limit = (argc > 1) ? atoi(argv[1]) : 10;
5150 if(limit < 1 || limit > 200)
5153 memset(&discrim, 0, sizeof(discrim));
5154 discrim.masks.bot = chanserv;
5155 discrim.masks.channel_name = channel->name;
5157 discrim.masks.command = argv[2];
5158 discrim.limit = limit;
5159 discrim.max_time = INT_MAX;
5160 discrim.severities = 1 << LOG_COMMAND;
5161 report.reporter = chanserv;
5163 reply("CSMSG_EVENT_SEARCH_RESULTS");
5164 matches = log_entry_search(&discrim, log_report_entry, &report);
5166 reply("MSG_MATCH_COUNT", matches);
5168 reply("MSG_NO_MATCHES");
5172 static CHANSERV_FUNC(cmd_say)
5178 msg = unsplit_string(argv + 1, argc - 1, NULL);
5179 send_channel_message(channel, cmd->parent->bot, "%s", msg);
5181 else if(*argv[1] == '*' && argv[1][1] != '\0')
5183 struct handle_info *hi;
5184 struct userNode *authed;
5187 msg = unsplit_string(argv + 2, argc - 2, NULL);
5189 if (!(hi = get_handle_info(argv[1] + 1)))
5191 reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
5195 for (authed = hi->users; authed; authed = authed->next_authed)
5196 send_target_message(5, authed->nick, cmd->parent->bot, "%s", msg);
5198 else if(GetUserH(argv[1]))
5201 msg = unsplit_string(argv + 2, argc - 2, NULL);
5202 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
5206 reply("MSG_NOT_TARGET_NAME");
5212 static CHANSERV_FUNC(cmd_emote)
5218 /* CTCP is so annoying. */
5219 msg = unsplit_string(argv + 1, argc - 1, NULL);
5220 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
5222 else if(*argv[1] == '*' && argv[1][1] != '\0')
5224 struct handle_info *hi;
5225 struct userNode *authed;
5228 msg = unsplit_string(argv + 2, argc - 2, NULL);
5230 if (!(hi = get_handle_info(argv[1] + 1)))
5232 reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
5236 for (authed = hi->users; authed; authed = authed->next_authed)
5237 send_target_message(5, authed->nick, cmd->parent->bot, "\001ACTION %s\001", msg);
5239 else if(GetUserH(argv[1]))
5241 msg = unsplit_string(argv + 2, argc - 2, NULL);
5242 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
5246 reply("MSG_NOT_TARGET_NAME");
5252 struct channelList *
5253 chanserv_support_channels(void)
5255 return &chanserv_conf.support_channels;
5258 static CHANSERV_FUNC(cmd_expire)
5260 int channel_count = registered_channels;
5261 expire_channels(chanserv);
5262 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
5267 chanserv_expire_suspension(void *data)
5269 struct suspended *suspended = data;
5270 struct chanNode *channel;
5273 /* Update the channel registration data structure. */
5274 if(!suspended->expires || (now < suspended->expires))
5275 suspended->revoked = now;
5276 channel = suspended->cData->channel;
5277 suspended->cData->channel = channel;
5278 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
5280 /* If appropriate, re-join ChanServ to the channel. */
5281 if(!IsOffChannel(suspended->cData))
5283 spamserv_cs_suspend(channel, 0, 0, NULL);
5284 ss_cs_join_channel(channel, 1);
5287 /* Mark everyone currently in the channel as present. */
5288 for(ii = 0; ii < channel->members.used; ++ii)
5290 struct userData *uData = GetChannelAccess(suspended->cData, channel->members.list[ii]->user->handle_info);
5299 static CHANSERV_FUNC(cmd_csuspend)
5301 struct suspended *suspended;
5302 char reason[MAXLEN];
5303 unsigned long expiry, duration;
5304 struct userData *uData;
5308 if(IsProtected(channel->channel_info))
5310 reply("CSMSG_SUSPEND_NODELETE", channel->name);
5314 if(argv[1][0] == '!')
5316 else if(IsSuspended(channel->channel_info))
5318 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
5319 show_suspension_info(cmd, user, channel->channel_info->suspended);
5323 if(!strcmp(argv[1], "0"))
5325 else if((duration = ParseInterval(argv[1])))
5326 expiry = now + duration;
5329 reply("MSG_INVALID_DURATION", argv[1]);
5333 unsplit_string(argv + 2, argc - 2, reason);
5335 suspended = calloc(1, sizeof(*suspended));
5336 suspended->revoked = 0;
5337 suspended->issued = now;
5338 suspended->suspender = strdup(user->handle_info->handle);
5339 suspended->expires = expiry;
5340 suspended->reason = strdup(reason);
5341 suspended->cData = channel->channel_info;
5342 suspended->previous = suspended->cData->suspended;
5343 suspended->cData->suspended = suspended;
5345 if(suspended->expires)
5346 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
5348 if(IsSuspended(channel->channel_info))
5350 suspended->previous->revoked = now;
5351 if(suspended->previous->expires)
5352 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
5353 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
5354 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5358 /* Mark all users in channel as absent. */
5359 for(uData = channel->channel_info->users; uData; uData = uData->next)
5368 /* Mark the channel as suspended, then part. */
5369 channel->channel_info->flags |= CHANNEL_SUSPENDED;
5370 spamserv_cs_suspend(channel, expiry, 1, suspended->reason);
5371 DelChannelUser(chanserv, channel, suspended->reason, 0);
5372 reply("CSMSG_SUSPENDED", channel->name);
5373 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
5374 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5379 static CHANSERV_FUNC(cmd_cunsuspend)
5381 struct suspended *suspended;
5382 char message[MAXLEN];
5384 if(!IsSuspended(channel->channel_info))
5386 reply("CSMSG_NOT_SUSPENDED", channel->name);
5390 suspended = channel->channel_info->suspended;
5392 /* Expire the suspension and join ChanServ to the channel. */
5393 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
5394 chanserv_expire_suspension(suspended);
5395 reply("CSMSG_UNSUSPENDED", channel->name);
5396 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
5397 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
5401 typedef struct chanservSearch
5406 unsigned long unvisited;
5407 unsigned long registered;
5409 unsigned long flags;
5413 typedef void (*channel_search_func)(struct chanData *channel, void *data);
5416 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
5421 search = malloc(sizeof(struct chanservSearch));
5422 memset(search, 0, sizeof(*search));
5425 for(i = 0; i < argc; i++)
5427 /* Assume all criteria require arguments. */
5430 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
5434 if(!irccasecmp(argv[i], "name"))
5435 search->name = argv[++i];
5436 else if(!irccasecmp(argv[i], "registrar"))
5437 search->registrar = argv[++i];
5438 else if(!irccasecmp(argv[i], "unvisited"))
5439 search->unvisited = ParseInterval(argv[++i]);
5440 else if(!irccasecmp(argv[i], "registered"))
5441 search->registered = ParseInterval(argv[++i]);
5442 else if(!irccasecmp(argv[i], "flags"))
5445 if(!irccasecmp(argv[i], "nodelete"))
5446 search->flags |= CHANNEL_NODELETE;
5447 else if(!irccasecmp(argv[i], "suspended"))
5448 search->flags |= CHANNEL_SUSPENDED;
5449 else if(!irccasecmp(argv[i], "unreviewed"))
5450 search->flags |= CHANNEL_UNREVIEWED;
5453 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
5457 else if(!irccasecmp(argv[i], "limit"))
5458 search->limit = strtoul(argv[++i], NULL, 10);
5461 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
5466 if(search->name && !strcmp(search->name, "*"))
5468 if(search->registrar && !strcmp(search->registrar, "*"))
5469 search->registrar = 0;
5478 chanserv_channel_match(struct chanData *channel, search_t search)
5480 const char *name = channel->channel->name;
5481 if((search->name && !match_ircglob(name, search->name)) ||
5482 (search->registrar && !channel->registrar) ||
5483 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
5484 (search->unvisited && (now - channel->visited) < search->unvisited) ||
5485 (search->registered && (now - channel->registered) > search->registered) ||
5486 (search->flags && ((search->flags & channel->flags) != search->flags)))
5493 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
5495 struct chanData *channel;
5496 unsigned int matches = 0;
5498 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
5500 if(!chanserv_channel_match(channel, search))
5510 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
5515 search_print(struct chanData *channel, void *data)
5517 send_message_type(4, data, chanserv, "%s", channel->channel->name);
5520 static CHANSERV_FUNC(cmd_search)
5523 unsigned int matches;
5524 channel_search_func action;
5528 if(!irccasecmp(argv[1], "count"))
5529 action = search_count;
5530 else if(!irccasecmp(argv[1], "print"))
5531 action = search_print;
5534 reply("CSMSG_ACTION_INVALID", argv[1]);
5538 search = chanserv_search_create(user, argc - 2, argv + 2);
5542 if(action == search_count)
5543 search->limit = INT_MAX;
5545 if(action == search_print)
5546 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
5548 matches = chanserv_channel_search(search, action, user);
5551 reply("MSG_MATCH_COUNT", matches);
5553 reply("MSG_NO_MATCHES");
5559 static CHANSERV_FUNC(cmd_unvisited)
5561 struct chanData *cData;
5562 unsigned long interval = chanserv_conf.channel_expire_delay;
5563 char buffer[INTERVALLEN];
5564 unsigned int limit = 25, matches = 0;
5568 interval = ParseInterval(argv[1]);
5570 limit = atoi(argv[2]);
5573 intervalString(buffer, interval, user->handle_info);
5574 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
5576 for(cData = channelList; cData && matches < limit; cData = cData->next)
5578 if((now - cData->visited) < interval)
5581 intervalString(buffer, now - cData->visited, user->handle_info);
5582 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
5589 static MODCMD_FUNC(chan_opt_defaulttopic)
5595 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5597 reply("CSMSG_TOPIC_LOCKED", channel->name);
5601 topic = unsplit_string(argv+1, argc-1, NULL);
5603 free(channel->channel_info->topic);
5604 if(topic[0] == '*' && topic[1] == 0)
5606 topic = channel->channel_info->topic = NULL;
5610 topic = channel->channel_info->topic = strdup(topic);
5611 if(channel->channel_info->topic_mask
5612 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
5613 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5615 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
5618 if(channel->channel_info->topic)
5619 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
5621 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
5625 static MODCMD_FUNC(chan_opt_topicmask)
5629 struct chanData *cData = channel->channel_info;
5632 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5634 reply("CSMSG_TOPIC_LOCKED", channel->name);
5638 mask = unsplit_string(argv+1, argc-1, NULL);
5640 if(cData->topic_mask)
5641 free(cData->topic_mask);
5642 if(mask[0] == '*' && mask[1] == 0)
5644 cData->topic_mask = 0;
5648 cData->topic_mask = strdup(mask);
5650 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
5651 else if(!match_ircglob(cData->topic, cData->topic_mask))
5652 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5656 if(channel->channel_info->topic_mask)
5657 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
5659 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
5663 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
5667 char *greeting = unsplit_string(argv+1, argc-1, NULL);
5671 if(greeting[0] == '*' && greeting[1] == 0)
5675 unsigned int length = strlen(greeting);
5676 if(length > chanserv_conf.greeting_length)
5678 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
5681 *data = strdup(greeting);
5690 reply(name, user_find_message(user, "MSG_NONE"));
5694 static MODCMD_FUNC(chan_opt_greeting)
5696 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
5699 static MODCMD_FUNC(chan_opt_usergreeting)
5701 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
5704 static MODCMD_FUNC(chan_opt_modes)
5706 struct mod_chanmode *new_modes;
5711 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
5713 reply("CSMSG_NO_ACCESS");
5716 if(argv[1][0] == '*' && argv[1][1] == 0)
5718 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
5720 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)))
5722 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5725 else if(new_modes->argc > 1)
5727 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5728 mod_chanmode_free(new_modes);
5733 channel->channel_info->modes = *new_modes;
5734 modcmd_chanmode_announce(new_modes);
5735 mod_chanmode_free(new_modes);
5739 mod_chanmode_format(&channel->channel_info->modes, modes);
5741 reply("CSMSG_SET_MODES", modes);
5743 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
5747 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5749 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5751 struct chanData *cData = channel->channel_info;
5756 /* Set flag according to value. */
5757 if(enabled_string(argv[1]))
5759 cData->flags |= mask;
5762 else if(disabled_string(argv[1]))
5764 cData->flags &= ~mask;
5769 reply("MSG_INVALID_BINARY", argv[1]);
5775 /* Find current option value. */
5776 value = (cData->flags & mask) ? 1 : 0;
5780 reply(name, user_find_message(user, "MSG_ON"));
5782 reply(name, user_find_message(user, "MSG_OFF"));
5786 static MODCMD_FUNC(chan_opt_nodelete)
5788 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
5790 reply("MSG_SETTING_PRIVILEGED", argv[0]);
5794 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
5797 static MODCMD_FUNC(chan_opt_dynlimit)
5799 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
5802 static MODCMD_FUNC(chan_opt_offchannel)
5804 struct chanData *cData = channel->channel_info;
5809 /* Set flag according to value. */
5810 if(enabled_string(argv[1]))
5812 if(!IsOffChannel(cData))
5813 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
5814 cData->flags |= CHANNEL_OFFCHANNEL;
5817 else if(disabled_string(argv[1]))
5819 if(IsOffChannel(cData))
5821 struct mod_chanmode change;
5822 mod_chanmode_init(&change);
5824 change.args[0].mode = MODE_CHANOP;
5825 change.args[0].u.member = AddChannelUser(chanserv, channel);
5826 mod_chanmode_announce(chanserv, channel, &change);
5828 cData->flags &= ~CHANNEL_OFFCHANNEL;
5833 reply("MSG_INVALID_BINARY", argv[1]);
5839 /* Find current option value. */
5840 value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
5844 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
5846 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
5850 static MODCMD_FUNC(chan_opt_unreviewed)
5852 struct chanData *cData = channel->channel_info;
5853 int value = (cData->flags & CHANNEL_UNREVIEWED) ? 1 : 0;
5859 /* The two directions can have different ACLs. */
5860 if(enabled_string(argv[1]))
5862 else if(disabled_string(argv[1]))
5866 reply("MSG_INVALID_BINARY", argv[1]);
5870 if (new_value != value)
5872 struct svccmd *subcmd;
5873 char subcmd_name[32];
5875 snprintf(subcmd_name, sizeof(subcmd_name), "%s %s", argv[0], (new_value ? "on" : "off"));
5876 subcmd = dict_find(cmd->parent->commands, subcmd_name, NULL);
5879 reply("MSG_COMMAND_DISABLED", subcmd_name);
5882 else if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
5886 cData->flags |= CHANNEL_UNREVIEWED;
5889 free(cData->registrar);
5890 cData->registrar = strdup(user->handle_info->handle);
5891 cData->flags &= ~CHANNEL_UNREVIEWED;
5898 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_ON"));
5900 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_OFF"));
5904 static MODCMD_FUNC(chan_opt_defaults)
5906 struct userData *uData;
5907 struct chanData *cData;
5908 const char *confirm;
5909 enum levelOption lvlOpt;
5910 enum charOption chOpt;
5912 cData = channel->channel_info;
5913 uData = GetChannelUser(cData, user->handle_info);
5914 if(!uData || (uData->access < UL_OWNER))
5916 reply("CSMSG_OWNER_DEFAULTS", channel->name);
5919 confirm = make_confirmation_string(uData);
5920 if((argc < 2) || strcmp(argv[1], confirm))
5922 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
5925 cData->flags = (CHANNEL_DEFAULT_FLAGS & ~CHANNEL_PRESERVED_FLAGS)
5926 | (cData->flags & CHANNEL_PRESERVED_FLAGS);
5927 cData->modes = chanserv_conf.default_modes;
5928 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
5929 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
5930 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
5931 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
5932 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
5937 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5939 struct chanData *cData = channel->channel_info;
5940 struct userData *uData;
5941 unsigned short value;
5945 if(!check_user_level(channel, user, option, 1, 1))
5947 reply("CSMSG_CANNOT_SET");
5950 value = user_level_from_name(argv[1], UL_OWNER+1);
5951 if(!value && strcmp(argv[1], "0"))
5953 reply("CSMSG_INVALID_ACCESS", argv[1]);
5956 uData = GetChannelUser(cData, user->handle_info);
5957 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
5959 reply("CSMSG_BAD_SETLEVEL");
5965 if(value > cData->lvlOpts[lvlGiveOps])
5967 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
5972 if(value < cData->lvlOpts[lvlGiveVoice])
5974 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
5979 /* This test only applies to owners, since non-owners
5980 * trying to set an option to above their level get caught
5981 * by the CSMSG_BAD_SETLEVEL test above.
5983 if(value > uData->access)
5985 reply("CSMSG_BAD_SETTERS");
5992 cData->lvlOpts[option] = value;
5994 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
5998 static MODCMD_FUNC(chan_opt_enfops)
6000 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
6003 static MODCMD_FUNC(chan_opt_giveops)
6005 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
6008 static MODCMD_FUNC(chan_opt_enfmodes)
6010 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
6013 static MODCMD_FUNC(chan_opt_enftopic)
6015 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
6018 static MODCMD_FUNC(chan_opt_pubcmd)
6020 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
6023 static MODCMD_FUNC(chan_opt_setters)
6025 return channel_level_option(lvlSetters, CSFUNC_ARGS);
6028 static MODCMD_FUNC(chan_opt_ctcpusers)
6030 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
6033 static MODCMD_FUNC(chan_opt_userinfo)
6035 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
6038 static MODCMD_FUNC(chan_opt_givevoice)
6040 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
6043 static MODCMD_FUNC(chan_opt_topicsnarf)
6045 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
6048 static MODCMD_FUNC(chan_opt_vote)
6050 return channel_level_option(lvlVote, CSFUNC_ARGS);
6053 static MODCMD_FUNC(chan_opt_inviteme)
6055 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
6059 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
6061 struct chanData *cData = channel->channel_info;
6062 int count = charOptions[option].count, idx;
6066 idx = atoi(argv[1]);
6068 if(!isdigit(argv[1][0]) || (idx < 0) || (idx >= count))
6070 reply("CSMSG_INVALID_NUMERIC", idx);
6071 /* Show possible values. */
6072 for(idx = 0; idx < count; idx++)
6073 reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
6077 cData->chOpts[option] = charOptions[option].values[idx].value;
6081 /* Find current option value. */
6084 (idx < count) && (cData->chOpts[option] != charOptions[option].values[idx].value);
6088 /* Somehow, the option value is corrupt; reset it to the default. */
6089 cData->chOpts[option] = charOptions[option].default_value;
6094 reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
6098 static MODCMD_FUNC(chan_opt_protect)
6100 return channel_multiple_option(chProtect, CSFUNC_ARGS);
6103 static MODCMD_FUNC(chan_opt_toys)
6105 return channel_multiple_option(chToys, CSFUNC_ARGS);
6108 static MODCMD_FUNC(chan_opt_ctcpreaction)
6110 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
6113 static MODCMD_FUNC(chan_opt_topicrefresh)
6115 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
6118 static struct svccmd_list set_shows_list;
6121 handle_svccmd_unbind(struct svccmd *target) {
6123 for(ii=0; ii<set_shows_list.used; ++ii)
6124 if(target == set_shows_list.list[ii])
6125 set_shows_list.used = 0;
6128 static CHANSERV_FUNC(cmd_set)
6130 struct svccmd *subcmd;
6134 /* Check if we need to (re-)initialize set_shows_list. */
6135 if(!set_shows_list.used)
6137 if(!set_shows_list.size)
6139 set_shows_list.size = chanserv_conf.set_shows->used;
6140 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
6142 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
6144 const char *name = chanserv_conf.set_shows->list[ii];
6145 sprintf(buf, "%s %s", argv[0], name);
6146 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6149 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
6152 svccmd_list_append(&set_shows_list, subcmd);
6158 reply("CSMSG_CHANNEL_OPTIONS");
6159 for(ii = 0; ii < set_shows_list.used; ii++)
6161 subcmd = set_shows_list.list[ii];
6162 subcmd->command->func(user, channel, 1, argv+1, subcmd);
6167 sprintf(buf, "%s %s", argv[0], argv[1]);
6168 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6171 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
6174 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
6176 reply("CSMSG_NO_ACCESS");
6182 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
6186 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
6188 struct userData *uData;
6190 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6193 reply("CSMSG_NOT_USER", channel->name);
6199 /* Just show current option value. */
6201 else if(enabled_string(argv[1]))
6203 uData->flags |= mask;
6205 else if(disabled_string(argv[1]))
6207 uData->flags &= ~mask;
6211 reply("MSG_INVALID_BINARY", argv[1]);
6215 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
6219 static MODCMD_FUNC(user_opt_noautoop)
6221 struct userData *uData;
6223 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6226 reply("CSMSG_NOT_USER", channel->name);
6229 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
6230 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
6232 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
6235 static MODCMD_FUNC(user_opt_autoinvite)
6237 if((argc > 1) && !check_user_level(channel, user, lvlInviteMe, 1, 0))
6239 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
6241 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
6244 static MODCMD_FUNC(user_opt_info)
6246 struct userData *uData;
6249 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6253 /* If they got past the command restrictions (which require access)
6254 * but fail this test, we have some fool with security override on.
6256 reply("CSMSG_NOT_USER", channel->name);
6263 infoline = unsplit_string(argv + 1, argc - 1, NULL);
6264 if(strlen(infoline) > chanserv_conf.max_userinfo_length)
6266 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
6269 bp = strcspn(infoline, "\001");
6272 reply("CSMSG_BAD_INFOLINE", infoline[bp]);
6277 if(infoline[0] == '*' && infoline[1] == 0)
6280 uData->info = strdup(infoline);
6283 reply("CSMSG_USET_INFO", uData->info);
6285 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
6289 struct svccmd_list uset_shows_list;
6291 static CHANSERV_FUNC(cmd_uset)
6293 struct svccmd *subcmd;
6297 /* Check if we need to (re-)initialize uset_shows_list. */
6298 if(!uset_shows_list.used)
6302 "NoAutoOp", "AutoInvite", "Info"
6305 if(!uset_shows_list.size)
6307 uset_shows_list.size = ArrayLength(options);
6308 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
6310 for(ii = 0; ii < ArrayLength(options); ii++)
6312 const char *name = options[ii];
6313 sprintf(buf, "%s %s", argv[0], name);
6314 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6317 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
6320 svccmd_list_append(&uset_shows_list, subcmd);
6326 /* Do this so options are presented in a consistent order. */
6327 reply("CSMSG_USER_OPTIONS");
6328 for(ii = 0; ii < uset_shows_list.used; ii++)
6329 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
6333 sprintf(buf, "%s %s", argv[0], argv[1]);
6334 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6337 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
6341 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
6344 static CHANSERV_FUNC(cmd_giveownership)
6346 struct handle_info *new_owner_hi;
6347 struct userData *new_owner;
6348 struct userData *curr_user;
6349 struct userData *invoker;
6350 struct chanData *cData = channel->channel_info;
6351 struct do_not_register *dnr;
6352 const char *confirm;
6353 struct giveownership *giveownership;
6354 unsigned int force, override;
6355 unsigned short co_access, new_owner_old_access;
6356 char reason[MAXLEN], transfer_reason[MAXLEN];
6359 curr_user = GetChannelAccess(cData, user->handle_info);
6360 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
6362 struct userData *uData = _GetChannelUser(channel->channel_info, user->handle_info, 1, 0);
6363 override = ((cmd->effective_flags & MODCMD_REQUIRE_CHANUSER)
6364 && (uData->access > 500)
6365 && (!(uData = _GetChannelUser(channel->channel_info, user->handle_info, 0, 0))
6366 || uData->access < 500));
6368 if(!curr_user || (curr_user->access != UL_OWNER))
6370 struct userData *owner = NULL;
6371 for(curr_user = channel->channel_info->users;
6373 curr_user = curr_user->next)
6375 if(curr_user->access != UL_OWNER)
6379 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
6386 else if(!force && (now < cData->ownerTransfer + chanserv_conf.giveownership_period))
6388 char delay[INTERVALLEN];
6389 intervalString(delay, cData->ownerTransfer + chanserv_conf.giveownership_period - now, user->handle_info);
6390 reply("CSMSG_TRANSFER_WAIT", delay, channel->name);
6393 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
6395 if(new_owner_hi == user->handle_info)
6397 reply("CSMSG_NO_TRANSFER_SELF");
6400 new_owner = GetChannelAccess(cData, new_owner_hi);
6405 new_owner = add_channel_user(cData, new_owner_hi, UL_OWNER - 1, 0, NULL);
6409 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
6413 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
6415 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
6418 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
6419 if(!IsHelping(user))
6420 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
6422 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi->handle);
6425 invoker = GetChannelUser(cData, user->handle_info);
6426 if(invoker->access <= UL_OWNER)
6428 confirm = make_confirmation_string(curr_user);
6429 if((argc < 3) || strcmp(argv[2], confirm))
6431 reply("CSMSG_CONFIRM_GIVEOWNERSHIP", new_owner_hi->handle, confirm);
6435 new_owner_old_access = new_owner->access;
6436 if(new_owner->access >= UL_COOWNER)
6437 co_access = new_owner->access;
6439 co_access = UL_COOWNER;
6440 new_owner->access = UL_OWNER;
6442 curr_user->access = co_access;
6443 cData->ownerTransfer = now;
6444 giveownership = calloc(1, sizeof(*giveownership));
6445 giveownership->issued = now;
6446 giveownership->old_owner = curr_user->handle->handle;
6447 giveownership->target = new_owner_hi->handle;
6448 giveownership->target_access = new_owner_old_access;
6451 if(argc > (2 + force))
6453 unsplit_string(argv + 2 + force, argc - 2 - force, transfer_reason);
6454 giveownership->reason = strdup(transfer_reason);
6456 giveownership->staff_issuer = strdup(user->handle_info->handle);
6459 giveownership->previous = channel->channel_info->giveownership;
6460 channel->channel_info->giveownership = giveownership;
6461 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
6462 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
6463 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
6467 static CHANSERV_FUNC(cmd_suspend)
6469 struct handle_info *hi;
6470 struct userData *actor, *real_actor, *target;
6471 unsigned int override = 0;
6474 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6475 actor = GetChannelUser(channel->channel_info, user->handle_info);
6476 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
6477 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6479 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6482 if(target->access >= actor->access)
6484 reply("MSG_USER_OUTRANKED", hi->handle);
6487 if(target->flags & USER_SUSPENDED)
6489 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
6494 target->present = 0;
6497 if(!real_actor || target->access >= real_actor->access)
6498 override = CMD_LOG_OVERRIDE;
6499 target->flags |= USER_SUSPENDED;
6500 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
6501 return 1 | override;
6504 static CHANSERV_FUNC(cmd_unsuspend)
6506 struct handle_info *hi;
6507 struct userData *actor, *real_actor, *target;
6508 unsigned int override = 0;
6511 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6512 actor = GetChannelUser(channel->channel_info, user->handle_info);
6513 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
6514 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6516 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6519 if(target->access >= actor->access)
6521 reply("MSG_USER_OUTRANKED", hi->handle);
6524 if(!(target->flags & USER_SUSPENDED))
6526 reply("CSMSG_NOT_SUSPENDED", hi->handle);
6529 if(!real_actor || target->access >= real_actor->access)
6530 override = CMD_LOG_OVERRIDE;
6531 target->flags &= ~USER_SUSPENDED;
6532 scan_user_presence(target, NULL);
6533 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
6534 return 1 | override;
6537 static MODCMD_FUNC(cmd_deleteme)
6539 struct handle_info *hi;
6540 struct userData *target;
6541 const char *confirm_string;
6542 unsigned short access_level;
6545 hi = user->handle_info;
6546 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6548 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6551 if(target->access == UL_OWNER)
6553 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
6556 confirm_string = make_confirmation_string(target);
6557 if((argc < 2) || strcmp(argv[1], confirm_string))
6559 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
6562 access_level = target->access;
6563 channel_name = strdup(channel->name);
6564 del_channel_user(target, 1);
6565 reply("CSMSG_DELETED_YOU", access_level, channel_name);
6570 static CHANSERV_FUNC(cmd_addvote)
6572 struct chanData *cData = channel->channel_info;
6573 struct userData *uData, *target;
6574 struct handle_info *hi;
6575 if (!cData) return 0;
6577 hi = user->handle_info;
6578 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6580 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6583 if(target->access < 300) {
6584 reply("CSMSG_NO_ACCESS");
6588 reply("CSMSG_ADDVOTE_FULL");
6592 msg = unsplit_string(argv + 1, argc - 1, NULL);
6593 cData->vote = strdup(msg);
6594 cData->vote_start=0;
6595 dict_delete(cData->vote_options);
6596 cData->vote_options = dict_new();
6597 dict_set_free_data(cData->vote_options, free_vote_options);
6598 for(uData = channel->channel_info->users; uData; uData = uData->next)
6603 reply("CSMSG_ADDVOTE_DONE");
6607 static CHANSERV_FUNC(cmd_delvote)
6609 struct chanData *cData = channel->channel_info;
6610 struct userData *target;
6611 struct handle_info *hi;
6612 if (!cData) return 0;
6613 hi = user->handle_info;
6614 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6616 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6619 if(target->access < 300) {
6620 reply("CSMSG_NO_ACCESS");
6624 reply("CSMSG_NO_VOTE");
6629 reply("CSMSG_DELVOTE_DONE");
6633 static CHANSERV_FUNC(cmd_addoption)
6635 struct chanData *cData = channel->channel_info;
6636 struct userData *target;
6637 struct handle_info *hi;
6638 if (!cData) return 0;
6640 hi = user->handle_info;
6641 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6643 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6646 if(target->access < 300) {
6647 reply("CSMSG_NO_ACCESS");
6651 reply("CSMSG_NO_VOTE");
6657 msg = unsplit_string(argv + 1, argc - 1, NULL);
6660 unsigned int lastid = 1;
6661 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6662 struct vote_option *cvOpt = iter_data(it);
6663 if(cvOpt->option_id > lastid)
6664 lastid = cvOpt->option_id;
6666 struct vote_option *vOpt;
6667 vOpt = calloc(1, sizeof(*vOpt));
6668 vOpt->name = strdup(msg);
6669 vOpt->option_id = (lastid + 1);
6671 sprintf(str,"%i",(lastid + 1));
6672 vOpt->option_str = strdup(str);
6674 dict_insert(cData->vote_options,vOpt->option_str,vOpt);
6676 reply("CSMSG_ADDOPTION_DONE",dict_size(cData->vote_options),lastid,(lastid + 1));
6680 static CHANSERV_FUNC(cmd_deloption)
6682 struct chanData *cData = channel->channel_info;
6683 struct userData *uData, *target;
6684 struct handle_info *hi;
6685 if (!cData) return 0;
6687 hi = user->handle_info;
6688 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6690 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6693 if(target->access < 300) {
6694 reply("CSMSG_NO_ACCESS");
6698 reply("CSMSG_NO_VOTE");
6701 if(cData->vote_start) {
6702 if(dict_size(cData->vote_options) < 3) {
6703 reply("CSMSG_VOTE_NEED_OPTIONS");
6708 int find_id = atoi(argv[1]);
6710 unsigned int found = 0;
6713 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6715 if (find_id == ii) {
6716 struct vote_option *vOpt = iter_data(it);
6717 found = vOpt->option_id;
6719 sprintf(str,"%i",vOpt->option_id);
6720 dict_remove(cData->vote_options, str);
6725 for(uData = channel->channel_info->users; uData; uData = uData->next) {
6726 if(uData->votefor == found) {
6731 reply("CSMSG_DELOPTION_DONE");
6734 reply("CSMSG_DELOPTION_NONE");
6739 static CHANSERV_FUNC(cmd_vote)
6741 struct chanData *cData = channel->channel_info;
6742 struct userData *target;
6743 struct handle_info *hi;
6744 unsigned int votedfor = 0;
6745 char *votedfor_str = NULL;
6747 if (!cData || !cData->vote) {
6748 reply("CSMSG_NO_VOTE");
6751 if(argc > 1 && cData->vote_start) {
6752 hi = user->handle_info;
6753 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6755 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6758 if(!check_user_level(channel, user, lvlVote, 1, 0)) {
6759 reply("CSMSG_NO_ACCESS");
6763 reply("CSMSG_VOTE_VOTED");
6766 int find_id = atoi(argv[1]);
6769 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6771 if (find_id == ii) {
6772 struct vote_option *vOpt = iter_data(it);
6775 target->votefor = vOpt->option_id;
6776 votedfor = vOpt->option_id;
6777 votedfor_str = vOpt->name;
6781 reply("CSMSG_VOTE_INVALID");
6785 if (!cData->vote_start) {
6786 reply("CSMSG_VOTE_NOT_STARTED");
6788 reply("CSMSG_VOTE_QUESTION",cData->vote);
6790 unsigned int voteid = 0;
6793 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6794 struct vote_option *vOpt = iter_data(it);
6796 reply("CSMSG_VOTE_OPTION",voteid,vOpt->name,vOpt->voted);
6798 if(argc > 1 && cData->vote_start && votedfor_str) {
6799 reply("CSMSG_VOTE_DONE",votedfor_str);
6804 static CHANSERV_FUNC(cmd_startvote)
6806 struct chanData *cData = channel->channel_info;
6807 struct userData *target;
6808 struct handle_info *hi;
6809 if (!cData) return 0;
6810 hi = user->handle_info;
6811 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6813 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6816 if(target->access < 300) {
6817 reply("CSMSG_NO_ACCESS");
6821 reply("CSMSG_NO_VOTE");
6824 if(cData->vote_start) {
6825 reply("CSMSG_STARTVOTE_RUNNING");
6828 if(dict_size(cData->vote_options) < 2) {
6829 reply("CSMSG_VOTE_NEED_OPTIONS");
6832 cData->vote_start = 1;
6833 char response[MAXLEN];
6834 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_TOP"), user->nick);
6835 irc_privmsg(cmd->parent->bot, channel->name, response);
6836 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_QUESTION"), cData->vote);
6837 irc_privmsg(cmd->parent->bot, channel->name, response);
6838 unsigned int voteid = 0;
6840 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6841 struct vote_option *vOpt = iter_data(it);
6843 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_OPTION"), voteid, vOpt->name);
6844 irc_privmsg(cmd->parent->bot, channel->name, response);
6846 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_ACCESS"), cData->lvlOpts[lvlVote]); //Todo
6847 irc_privmsg(cmd->parent->bot, channel->name, response);
6848 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_HOWTO")); //Todo
6849 irc_privmsg(cmd->parent->bot, channel->name, response);
6853 static CHANSERV_FUNC(cmd_endvote)
6855 struct chanData *cData = channel->channel_info;
6856 struct userData *target;
6857 struct handle_info *hi;
6858 if (!cData) return 0;
6859 hi = user->handle_info;
6860 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6862 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6865 if(target->access < 300) {
6866 reply("CSMSG_NO_ACCESS");
6870 reply("CSMSG_NO_VOTE");
6873 if(!cData->vote_start) {
6874 reply("CSMSG_ENDVOTE_STOPPED");
6877 cData->vote_start = 0;
6878 reply("CSMSG_ENDVOTE_DONE");
6882 static CHANSERV_FUNC(cmd_voteresults)
6884 struct chanData *cData = channel->channel_info;
6885 struct userData *target;
6886 struct handle_info *hi;
6887 if (!cData) return 0;
6889 reply("CSMSG_NO_VOTE");
6892 if (argc > 1 && !irccasecmp(argv[1], "*")) {
6893 hi = user->handle_info;
6894 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6896 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6899 if(target->access < 300) {
6900 reply("CSMSG_NO_ACCESS");
6903 char response[MAXLEN];
6904 sprintf(response, user_find_message(user, "CSMSG_VOTERES_QUESTION"), cData->vote);
6905 irc_privmsg(cmd->parent->bot, channel->name, response);
6906 unsigned int voteid = 0;
6908 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6909 struct vote_option *vOpt = iter_data(it);
6911 sprintf(response, user_find_message(user, "CSMSG_VOTERES_OPTION"), voteid, vOpt->name, vOpt->voted);
6912 irc_privmsg(cmd->parent->bot, channel->name, response);
6915 reply("CSMSG_VOTE_QUESTION",cData->vote);
6916 unsigned int voteid = 0;
6918 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6919 struct vote_option *vOpt = iter_data(it);
6921 reply("CSMSG_VOTE_OPTION",voteid,vOpt->name,vOpt->voted);
6928 chanserv_refresh_topics(UNUSED_ARG(void *data))
6930 unsigned int refresh_num = (now - self->link_time) / chanserv_conf.refresh_period;
6931 struct chanData *cData;
6934 for(cData = channelList; cData; cData = cData->next)
6936 if(IsSuspended(cData))
6938 opt = cData->chOpts[chTopicRefresh];
6941 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
6944 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
6945 cData->last_refresh = refresh_num;
6947 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
6950 static CHANSERV_FUNC(cmd_unf)
6954 char response[MAXLEN];
6955 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
6956 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6957 irc_privmsg(cmd->parent->bot, channel->name, response);
6960 reply("CSMSG_UNF_RESPONSE");
6964 static CHANSERV_FUNC(cmd_ping)
6968 char response[MAXLEN];
6969 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
6970 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6971 irc_privmsg(cmd->parent->bot, channel->name, response);
6974 reply("CSMSG_PING_RESPONSE");
6978 static CHANSERV_FUNC(cmd_wut)
6982 char response[MAXLEN];
6983 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
6984 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6985 irc_privmsg(cmd->parent->bot, channel->name, response);
6988 reply("CSMSG_WUT_RESPONSE");
6992 static CHANSERV_FUNC(cmd_8ball)
6994 unsigned int i, j, accum;
6999 for(i=1; i<argc; i++)
7000 for(j=0; argv[i][j]; j++)
7001 accum = (accum << 5) - accum + toupper(argv[i][j]);
7002 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
7005 char response[MAXLEN];
7006 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
7007 irc_privmsg(cmd->parent->bot, channel->name, response);
7010 send_message_type(4, user, cmd->parent->bot, "%s", resp);
7014 static CHANSERV_FUNC(cmd_d)
7016 unsigned long sides, count, modifier, ii, total;
7017 char response[MAXLEN], *sep;
7021 if((count = strtoul(argv[1], &sep, 10)) < 1)
7031 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
7032 && (sides = strtoul(sep+1, &sep, 10)) > 1)
7036 else if((sep[0] == '-') && isdigit(sep[1]))
7037 modifier = strtoul(sep, NULL, 10);
7038 else if((sep[0] == '+') && isdigit(sep[1]))
7039 modifier = strtoul(sep+1, NULL, 10);
7046 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
7051 reply("CSMSG_BAD_DICE_COUNT", count, 10);
7054 for(total = ii = 0; ii < count; ++ii)
7055 total += (rand() % sides) + 1;
7058 if((count > 1) || modifier)
7060 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
7061 sprintf(response, fmt, total, count, sides, modifier);
7065 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
7066 sprintf(response, fmt, total, sides);
7069 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
7071 send_message_type(4, user, cmd->parent->bot, "%s", response);
7075 static CHANSERV_FUNC(cmd_huggle)
7077 /* CTCP must be via PRIVMSG, never notice */
7079 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
7081 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
7086 chanserv_adjust_limit(void *data)
7088 struct mod_chanmode change;
7089 struct chanData *cData = data;
7090 struct chanNode *channel = cData->channel;
7093 if(IsSuspended(cData))
7096 cData->limitAdjusted = now;
7097 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
7098 if(cData->modes.modes_set & MODE_LIMIT)
7100 if(limit > cData->modes.new_limit)
7101 limit = cData->modes.new_limit;
7102 else if(limit == cData->modes.new_limit)
7106 mod_chanmode_init(&change);
7107 change.modes_set = MODE_LIMIT;
7108 change.new_limit = limit;
7109 mod_chanmode_announce(chanserv, channel, &change);
7113 handle_new_channel(struct chanNode *channel)
7115 struct chanData *cData;
7117 if(!(cData = channel->channel_info))
7120 if(cData->modes.modes_set || cData->modes.modes_clear)
7121 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
7123 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
7124 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
7127 void handle_new_channel_created(char *chan, struct userNode *user) {
7128 if(user->handle_info && chanserv_conf.new_channel_authed) {
7129 send_target_message(5, chan, chanserv, "%s", chanserv_conf.new_channel_authed);
7130 } else if(!user->handle_info && chanserv_conf.new_channel_unauthed) {
7131 send_target_message(5, chan, chanserv, "%s", chanserv_conf.new_channel_unauthed);
7133 if(chanserv_conf.new_channel_msg)
7134 send_target_message(5, chan, chanserv, "%s", chanserv_conf.new_channel_msg);
7137 /* Welcome to my worst nightmare. Warning: Read (or modify)
7138 the code below at your own risk. */
7140 handle_join(struct modeNode *mNode)
7142 struct mod_chanmode change;
7143 struct userNode *user = mNode->user;
7144 struct chanNode *channel = mNode->channel;
7145 struct chanData *cData;
7146 struct userData *uData = NULL;
7147 struct banData *bData;
7148 struct handle_info *handle;
7149 unsigned int modes = 0, info = 0;
7153 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
7156 cData = channel->channel_info;
7157 if(channel->members.used > cData->max) {
7158 cData->max = channel->members.used;
7159 cData->max_time = now;
7162 for(i = 0; i < channel->invited.used; i++)
7164 if(channel->invited.list[i] == user) {
7165 userList_remove(&channel->invited, user);
7169 /* Check for bans. If they're joining through a ban, one of two
7171 * 1: Join during a netburst, by riding the break. Kick them
7172 * unless they have ops or voice in the channel.
7173 * 2: They're allowed to join through the ban (an invite in
7174 * ircu2.10, or a +e on Hybrid, or something).
7175 * If they're not joining through a ban, and the banlist is not
7176 * full, see if they're on the banlist for the channel. If so,
7179 if(user->uplink->burst && !mNode->modes)
7182 for(ii = 0; ii < channel->banlist.used; ii++)
7184 if(user_matches_glob(user, channel->banlist.list[ii]->ban, MATCH_USENICK))
7186 /* Riding a netburst. Naughty. */
7187 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
7193 mod_chanmode_init(&change);
7195 if(channel->banlist.used < MAXBANS)
7197 /* Not joining through a ban. */
7198 for(bData = cData->bans;
7199 bData && !user_matches_glob(user, bData->mask, MATCH_USENICK);
7200 bData = bData->next);
7204 char kick_reason[MAXLEN];
7205 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
7207 bData->triggered = now;
7208 if(bData != cData->bans)
7210 /* Shuffle the ban to the head of the list. */
7212 bData->next->prev = bData->prev;
7214 bData->prev->next = bData->next;
7217 bData->next = cData->bans;
7220 cData->bans->prev = bData;
7221 cData->bans = bData;
7224 change.args[0].mode = MODE_BAN;
7225 change.args[0].u.hostmask = bData->mask;
7226 mod_chanmode_announce(chanserv, channel, &change);
7227 KickChannelUser(user, channel, chanserv, kick_reason);
7232 /* ChanServ will not modify the limits in join-flooded channels,
7233 or when there are enough slots left below the limit. */
7234 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
7235 && !channel->join_flooded
7236 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
7238 /* The user count has begun "bumping" into the channel limit,
7239 so set a timer to raise the limit a bit. Any previous
7240 timers are removed so three incoming users within the delay
7241 results in one limit change, not three. */
7243 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7244 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7247 if(channel->join_flooded)
7249 /* don't automatically give ops or voice during a join flood */
7251 else if(cData->lvlOpts[lvlGiveOps] == 0)
7252 modes |= MODE_CHANOP;
7253 else if(cData->lvlOpts[lvlGiveVoice] == 0)
7254 modes |= MODE_VOICE;
7256 greeting = cData->greeting;
7257 if(user->handle_info)
7259 handle = user->handle_info;
7261 if(IsHelper(user) && !IsHelping(user))
7264 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7266 if(channel == chanserv_conf.support_channels.list[ii])
7268 HANDLE_SET_FLAG(user->handle_info, HELPING);
7274 uData = GetTrueChannelAccess(cData, handle);
7275 if(uData && !IsUserSuspended(uData))
7277 /* Ops and above were handled by the above case. */
7278 if(IsUserAutoOp(uData))
7280 if(uData->access >= cData->lvlOpts[lvlGiveOps])
7281 modes |= MODE_CHANOP;
7282 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
7283 modes |= MODE_VOICE;
7285 if(uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
7286 cData->visited = now;
7287 if(cData->user_greeting)
7288 greeting = cData->user_greeting;
7290 && (uData->access >= cData->lvlOpts[lvlUserInfo])
7291 && ((now - uData->seen) >= chanserv_conf.info_delay)
7299 /* If user joining normally (not during burst), apply op or voice,
7300 * and send greeting/userinfo as appropriate.
7302 if(!user->uplink->burst)
7306 if(modes & MODE_CHANOP)
7307 modes &= ~MODE_VOICE;
7308 change.args[0].mode = modes;
7309 change.args[0].u.member = mNode;
7310 mod_chanmode_announce(chanserv, channel, &change);
7313 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
7314 if(uData && info && (modes || !(channel->modes & MODE_DELAYJOINS)))
7315 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
7321 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
7323 struct mod_chanmode change;
7324 struct userData *channel;
7325 unsigned int ii, jj;
7327 if(!user->handle_info)
7330 mod_chanmode_init(&change);
7332 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
7334 struct chanNode *cn;
7335 struct modeNode *mn;
7336 if(IsUserSuspended(channel)
7337 || IsSuspended(channel->channel)
7338 || !(cn = channel->channel->channel))
7341 mn = GetUserMode(cn, user);
7344 if(!IsUserSuspended(channel)
7345 && IsUserAutoInvite(channel)
7346 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
7348 && !user->uplink->burst)
7349 irc_invite(chanserv, user, cn);
7353 if(channel->access >= UL_PRESENT && !HANDLE_FLAGGED(channel->handle, BOT))
7354 channel->channel->visited = now;
7356 if(IsUserAutoOp(channel))
7358 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
7359 change.args[0].mode = MODE_CHANOP;
7360 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
7361 change.args[0].mode = MODE_VOICE;
7363 change.args[0].mode = 0;
7364 change.args[0].u.member = mn;
7365 if(change.args[0].mode)
7366 mod_chanmode_announce(chanserv, cn, &change);
7369 channel->seen = now;
7370 channel->present = 1;
7373 for(ii = 0; ii < user->channels.used; ++ii)
7375 struct chanNode *chan = user->channels.list[ii]->channel;
7376 struct banData *ban;
7378 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
7379 || !chan->channel_info
7380 || IsSuspended(chan->channel_info))
7382 for(jj = 0; jj < chan->banlist.used; ++jj)
7383 if(user_matches_glob(user, chan->banlist.list[jj]->ban, MATCH_USENICK))
7385 if(jj < chan->banlist.used)
7387 for(ban = chan->channel_info->bans; ban; ban = ban->next)
7389 char kick_reason[MAXLEN];
7390 if(!user_matches_glob(user, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
7392 change.args[0].mode = MODE_BAN;
7393 change.args[0].u.hostmask = ban->mask;
7394 mod_chanmode_announce(chanserv, chan, &change);
7395 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
7396 KickChannelUser(user, chan, chanserv, kick_reason);
7397 ban->triggered = now;
7402 if(IsSupportHelper(user))
7404 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7406 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
7408 HANDLE_SET_FLAG(user->handle_info, HELPING);
7416 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
7418 struct chanData *cData;
7419 struct userData *uData;
7421 cData = mn->channel->channel_info;
7422 if(!cData || IsSuspended(cData) || IsLocal(mn->user))
7425 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
7427 /* Allow for a bit of padding so that the limit doesn't
7428 track the user count exactly, which could get annoying. */
7429 if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
7431 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7432 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7436 if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
7438 scan_user_presence(uData, mn->user);
7440 if (uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
7441 cData->visited = now;
7444 if(IsHelping(mn->user) && IsSupportHelper(mn->user))
7447 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii) {
7448 struct chanNode *channel;
7449 struct userNode *exclude;
7450 /* When looking at the channel that is being /part'ed, we
7451 * have to skip over the client that is leaving. For
7452 * other channels, we must not do that.
7454 channel = chanserv_conf.support_channels.list[ii];
7455 exclude = (channel == mn->channel) ? mn->user : NULL;
7456 if(find_handle_in_channel(channel, mn->user->handle_info, exclude))
7459 if(ii == chanserv_conf.support_channels.used)
7460 HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
7465 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
7467 struct userData *uData;
7469 if(!channel->channel_info || !kicker || IsService(kicker)
7470 || (kicker == victim) || IsSuspended(channel->channel_info)
7471 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
7474 if(protect_user(victim, kicker, channel->channel_info))
7476 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED_2");
7477 KickChannelUser(kicker, channel, chanserv, reason);
7480 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
7485 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
7487 struct chanData *cData;
7489 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
7492 cData = channel->channel_info;
7493 if(bad_topic(channel, user, channel->topic))
7495 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
7496 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
7497 SetChannelTopic(channel, chanserv, old_topic, 1);
7498 else if(cData->topic)
7499 SetChannelTopic(channel, chanserv, cData->topic, 1);
7502 /* With topicsnarf, grab the topic and save it as the default topic. */
7503 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
7506 cData->topic = strdup(channel->topic);
7512 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
7514 struct mod_chanmode *bounce = NULL;
7515 unsigned int bnc, ii;
7518 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
7521 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
7522 && mode_lock_violated(&channel->channel_info->modes, change))
7524 char correct[MAXLEN];
7525 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
7526 mod_chanmode_format(&channel->channel_info->modes, correct);
7527 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
7529 for(ii = bnc = 0; ii < change->argc; ++ii)
7531 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
7533 const struct userNode *victim = change->args[ii].u.member->user;
7534 if(!protect_user(victim, user, channel->channel_info))
7537 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7540 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
7541 bounce->args[bnc].u.member = GetUserMode(channel, user);
7542 if(bounce->args[bnc].u.member)
7546 bounce->args[bnc].mode = MODE_CHANOP;
7547 bounce->args[bnc].u.member = change->args[ii].u.member;
7549 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
7551 else if(change->args[ii].mode & MODE_CHANOP)
7553 const struct userNode *victim = change->args[ii].u.member->user;
7554 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
7557 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7558 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
7559 bounce->args[bnc].u.member = change->args[ii].u.member;
7562 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
7564 const char *ban = change->args[ii].u.hostmask;
7565 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
7568 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7569 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
7570 bounce->args[bnc].u.hostmask = strdup(ban);
7572 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
7577 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
7578 mod_chanmode_announce(chanserv, channel, bounce);
7579 for(ii = 0; ii < change->argc; ++ii)
7580 if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
7581 free((char*)bounce->args[ii].u.hostmask);
7582 mod_chanmode_free(bounce);
7587 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
7589 struct chanNode *channel;
7590 struct banData *bData;
7591 struct mod_chanmode change;
7592 unsigned int ii, jj;
7593 char kick_reason[MAXLEN];
7595 mod_chanmode_init(&change);
7597 change.args[0].mode = MODE_BAN;
7598 for(ii = 0; ii < user->channels.used; ++ii)
7600 channel = user->channels.list[ii]->channel;
7601 /* Need not check for bans if they're opped or voiced. */
7602 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
7604 /* Need not check for bans unless channel registration is active. */
7605 if(!channel->channel_info || IsSuspended(channel->channel_info))
7607 /* Look for a matching ban already on the channel. */
7608 for(jj = 0; jj < channel->banlist.used; ++jj)
7609 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
7611 /* Need not act if we found one. */
7612 if(jj < channel->banlist.used)
7614 /* Look for a matching ban in this channel. */
7615 for(bData = channel->channel_info->bans; bData; bData = bData->next)
7617 if(!user_matches_glob(user, bData->mask, MATCH_USENICK | MATCH_VISIBLE))
7619 change.args[0].u.hostmask = bData->mask;
7620 mod_chanmode_announce(chanserv, channel, &change);
7621 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
7622 KickChannelUser(user, channel, chanserv, kick_reason);
7623 bData->triggered = now;
7624 break; /* we don't need to check any more bans in the channel */
7629 static void handle_rename(struct handle_info *handle, const char *old_handle)
7631 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
7635 dict_remove2(handle_dnrs, old_handle, 1);
7636 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
7637 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
7642 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
7644 struct userNode *h_user;
7646 if(handle->channels)
7648 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
7649 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
7651 while(handle->channels)
7652 del_channel_user(handle->channels, 1);
7657 handle_server_link(UNUSED_ARG(struct server *server))
7659 struct chanData *cData;
7661 for(cData = channelList; cData; cData = cData->next)
7663 if(!IsSuspended(cData))
7664 cData->may_opchan = 1;
7665 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
7666 && !cData->channel->join_flooded
7667 && ((cData->channel->limit - cData->channel->members.used)
7668 < chanserv_conf.adjust_threshold))
7670 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7671 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7677 chanserv_conf_read(void)
7681 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
7682 struct mod_chanmode *change;
7683 struct string_list *strlist;
7684 struct chanNode *chan;
7687 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
7689 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
7692 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7693 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7694 chanserv_conf.support_channels.used = 0;
7695 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
7697 for(ii = 0; ii < strlist->used; ++ii)
7699 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7702 chan = AddChannel(strlist->list[ii], now, str2, NULL);
7704 channelList_append(&chanserv_conf.support_channels, chan);
7707 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
7710 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7713 chan = AddChannel(str, now, str2, NULL);
7715 channelList_append(&chanserv_conf.support_channels, chan);
7717 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
7718 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
7719 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
7720 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
7721 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
7722 chanserv_conf.greeting_length = str ? atoi(str) : 200;
7723 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
7724 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
7725 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
7726 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
7727 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
7728 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
7729 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
7730 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
7731 str = database_get_data(conf_node, KEY_DNR_EXPIRE_FREQ, RECDB_QSTRING);
7732 chanserv_conf.dnr_expire_frequency = str ? ParseInterval(str) : 3600;
7733 str = database_get_data(conf_node, KEY_INVITED_INTERVAL, RECDB_QSTRING);
7734 chanserv_conf.invited_timeout = str ? ParseInterval(str) : 600*2;
7735 str = database_get_data(conf_node, KEY_REVOKE_MODE_A, RECDB_QSTRING);
7736 chanserv_conf.revoke_mode_a = str ? atoi(str) : 1;
7737 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
7738 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
7739 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
7740 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
7741 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
7742 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
7743 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
7744 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
7745 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
7747 NickChange(chanserv, str, 0);
7748 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
7749 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
7750 str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
7751 chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
7752 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
7753 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
7754 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
7755 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
7756 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
7757 chanserv_conf.max_owned = str ? atoi(str) : 5;
7758 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
7759 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
7760 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
7761 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
7762 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
7763 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
7764 str = database_get_data(conf_node, KEY_NEW_CHANNEL_AUTHED, RECDB_QSTRING);
7765 chanserv_conf.new_channel_authed = (str && *str) ? str : NULL;
7766 str = database_get_data(conf_node, KEY_NEW_CHANNEL_UNAUTHED, RECDB_QSTRING);
7767 chanserv_conf.new_channel_unauthed = (str && *str) ? str : NULL;
7768 str = database_get_data(conf_node, KEY_NEW_CHANNEL_MSG, RECDB_QSTRING);
7769 chanserv_conf.new_channel_msg = (str && *str) ? str : NULL;
7770 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
7773 safestrncpy(mode_line, str, sizeof(mode_line));
7774 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
7775 if((change = mod_chanmode_parse(NULL, NULL, modes, ii, MCP_KEY_FREE|MCP_NO_APASS, 0))
7776 && (change->argc < 2))
7778 chanserv_conf.default_modes = *change;
7779 mod_chanmode_free(change);
7781 free_string_list(chanserv_conf.set_shows);
7782 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
7784 strlist = string_list_copy(strlist);
7787 static const char *list[] = {
7788 /* free form text */
7789 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
7790 /* options based on user level */
7791 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
7792 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
7793 /* multiple choice options */
7794 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
7795 /* binary options */
7796 "DynLimit", "NoDelete", "expire", "Vote",
7800 strlist = alloc_string_list(ArrayLength(list)-1);
7801 for(ii=0; list[ii]; ii++)
7802 string_list_append(strlist, strdup(list[ii]));
7804 chanserv_conf.set_shows = strlist;
7805 /* We don't look things up now, in case the list refers to options
7806 * defined by modules initialized after this point. Just mark the
7807 * function list as invalid, so it will be initialized.
7809 set_shows_list.used = 0;
7810 free_string_list(chanserv_conf.eightball);
7811 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
7814 strlist = string_list_copy(strlist);
7818 strlist = alloc_string_list(4);
7819 string_list_append(strlist, strdup("Yes."));
7820 string_list_append(strlist, strdup("No."));
7821 string_list_append(strlist, strdup("Maybe so."));
7823 chanserv_conf.eightball = strlist;
7824 free_string_list(chanserv_conf.old_ban_names);
7825 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
7827 strlist = string_list_copy(strlist);
7829 strlist = alloc_string_list(2);
7830 chanserv_conf.old_ban_names = strlist;
7831 str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
7832 off_channel = str ? atoi(str) : 0;
7836 chanserv_note_type_read(const char *key, struct record_data *rd)
7839 struct note_type *ntype;
7842 if(!(obj = GET_RECORD_OBJECT(rd)))
7844 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
7847 if(!(ntype = chanserv_create_note_type(key)))
7849 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
7853 /* Figure out set access */
7854 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
7856 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7857 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
7859 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
7861 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
7862 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
7864 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
7866 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
7870 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
7871 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7872 ntype->set_access.min_opserv = 0;
7875 /* Figure out visibility */
7876 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
7877 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7878 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
7879 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7880 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
7881 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
7882 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
7883 ntype->visible_type = NOTE_VIS_ALL;
7885 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7887 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
7888 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
7892 vote_option_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7894 struct vote_option *vOpt;
7897 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7899 log_module(CS_LOG, LOG_ERROR, "Invalid vote option in %s.", chan->channel->name);
7903 vOpt = calloc(1, sizeof(*vOpt));
7904 vOpt->name = strdup(database_get_data(rd->d.object, KEY_VOTE_OPTION_NAME, RECDB_QSTRING));
7905 str = database_get_data(rd->d.object, KEY_VOTE_OPTION_VOTED, RECDB_QSTRING);
7906 vOpt->voted = str ? atoi(str) : 0;
7907 vOpt->option_id = str ? atoi(key) : 0;
7908 vOpt->option_str = strdup(key);
7909 dict_insert(chan->vote_options,vOpt->option_str,vOpt);
7913 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7915 struct handle_info *handle;
7916 struct userData *uData;
7917 char *seen, *inf, *flags, *voted, *votefor;
7918 unsigned long last_seen;
7919 unsigned short access_level;
7921 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7923 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
7927 access_level = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
7928 if(access_level > UL_OWNER)
7930 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
7934 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
7935 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
7936 last_seen = seen ? strtoul(seen, NULL, 0) : now;
7937 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
7938 voted = database_get_data(rd->d.object, KEY_VOTE_VOTED, RECDB_QSTRING);
7939 votefor = database_get_data(rd->d.object, KEY_VOTE_VOTEDFOR, RECDB_QSTRING);
7940 handle = get_handle_info(key);
7943 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
7947 uData = add_channel_user(chan, handle, access_level, last_seen, inf);
7948 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
7950 uData->voted = voted ? strtoul(voted, NULL, 0) : 0;
7951 uData->votefor = votefor ? strtoul(votefor, NULL, 0) : 0;
7959 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7961 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
7962 unsigned long set_time, triggered_time, expires_time;
7964 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7966 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
7970 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
7971 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
7972 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
7973 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
7974 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
7975 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
7976 if (!reason || !owner)
7979 set_time = set ? strtoul(set, NULL, 0) : now;
7980 triggered_time = triggered ? strtoul(triggered, NULL, 0) : 0;
7982 expires_time = strtoul(s_expires, NULL, 0);
7984 expires_time = set_time + atoi(s_duration);
7988 if(!reason || (expires_time && (expires_time < now)))
7991 add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
7994 static struct suspended *
7995 chanserv_read_suspended(dict_t obj)
7997 struct suspended *suspended = calloc(1, sizeof(*suspended));
8001 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
8002 suspended->expires = str ? strtoul(str, NULL, 0) : 0;
8003 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
8004 suspended->revoked = str ? strtoul(str, NULL, 0) : 0;
8005 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
8006 suspended->issued = str ? strtoul(str, NULL, 0) : 0;
8007 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
8008 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
8009 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
8010 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
8014 static struct giveownership *
8015 chanserv_read_giveownership(dict_t obj)
8017 struct giveownership *giveownership = calloc(1, sizeof(*giveownership));
8021 str = database_get_data(obj, KEY_STAFF_ISSUER, RECDB_QSTRING);
8022 giveownership->staff_issuer = str ? strdup(str) : NULL;
8024 giveownership->old_owner = strdup(database_get_data(obj, KEY_OLD_OWNER, RECDB_QSTRING));
8026 giveownership->target = strdup(database_get_data(obj, KEY_TARGET, RECDB_QSTRING));
8027 giveownership->target_access = atoi(database_get_data(obj, KEY_TARGET_ACCESS, RECDB_QSTRING));
8029 str = database_get_data(obj, KEY_REASON, RECDB_QSTRING);
8030 giveownership->reason = str ? strdup(str) : NULL;
8031 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
8032 giveownership->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
8034 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
8035 giveownership->previous = previous ? chanserv_read_giveownership(previous) : NULL;
8036 return giveownership;
8040 chanserv_channel_read(const char *key, struct record_data *hir)
8042 struct suspended *suspended;
8043 struct giveownership *giveownership;
8044 struct mod_chanmode *modes;
8045 struct chanNode *cNode;
8046 struct chanData *cData;
8047 struct dict *channel, *obj;
8048 char *str, *argv[10];
8052 channel = hir->d.object;
8054 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
8057 cNode = AddChannel(key, now, NULL, NULL);
8060 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
8063 cData = register_channel(cNode, str);
8066 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
8070 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
8072 enum levelOption lvlOpt;
8073 enum charOption chOpt;
8075 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
8076 cData->flags = atoi(str);
8078 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8080 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
8082 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
8083 else if(levelOptions[lvlOpt].old_flag)
8085 if(cData->flags & levelOptions[lvlOpt].old_flag)
8086 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
8088 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
8092 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8094 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
8096 cData->chOpts[chOpt] = str[0];
8099 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
8101 enum levelOption lvlOpt;
8102 enum charOption chOpt;
8105 cData->flags = base64toint(str, 5);
8106 count = strlen(str += 5);
8107 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8110 if(levelOptions[lvlOpt].old_flag)
8112 if(cData->flags & levelOptions[lvlOpt].old_flag)
8113 lvl = levelOptions[lvlOpt].flag_value;
8115 lvl = levelOptions[lvlOpt].default_value;
8117 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
8119 case 'c': lvl = UL_COOWNER; break;
8120 case 'm': lvl = UL_MASTER; break;
8121 case 'n': lvl = UL_OWNER+1; break;
8122 case 'o': lvl = UL_OP; break;
8123 case 'p': lvl = UL_PEON; break;
8124 case 'w': lvl = UL_OWNER; break;
8125 default: lvl = 0; break;
8127 cData->lvlOpts[lvlOpt] = lvl;
8129 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8130 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
8133 if((str = database_get_data(hir->d.object, KEY_EXPIRE, RECDB_QSTRING)))
8135 cData->expiry = atoi(str);
8136 if(cData->expiry > 0) {
8137 if(cData->expiry > now) {
8138 timeq_add(cData->expiry, chanserv_expire_channel, cData);
8140 timeq_add(1, chanserv_expire_channel, cData);
8147 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
8149 suspended = chanserv_read_suspended(obj);
8150 cData->suspended = suspended;
8151 suspended->cData = cData;
8152 /* We could use suspended->expires and suspended->revoked to
8153 * set the CHANNEL_SUSPENDED flag, but we don't. */
8155 else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
8157 suspended = calloc(1, sizeof(*suspended));
8158 suspended->issued = 0;
8159 suspended->revoked = 0;
8160 suspended->suspender = strdup(str);
8161 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
8162 suspended->expires = str ? atoi(str) : 0;
8163 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
8164 suspended->reason = strdup(str ? str : "No reason");
8165 suspended->previous = NULL;
8166 cData->suspended = suspended;
8167 suspended->cData = cData;
8171 cData->flags &= ~CHANNEL_SUSPENDED;
8172 suspended = NULL; /* to squelch a warning */
8175 if(IsSuspended(cData)) {
8176 if(suspended->expires > now)
8177 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
8178 else if(suspended->expires)
8179 cData->flags &= ~CHANNEL_SUSPENDED;
8182 if((obj = database_get_data(hir->d.object, KEY_GIVEOWNERSHIP, RECDB_OBJECT)))
8184 giveownership = chanserv_read_giveownership(obj);
8185 cData->giveownership = giveownership;
8188 if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
8189 struct mod_chanmode change;
8190 mod_chanmode_init(&change);
8192 change.args[0].mode = MODE_CHANOP;
8193 change.args[0].u.member = AddChannelUser(chanserv, cNode);
8194 mod_chanmode_announce(chanserv, cNode, &change);
8197 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
8198 cData->registered = str ? strtoul(str, NULL, 0) : now;
8199 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
8200 cData->visited = str ? strtoul(str, NULL, 0) : now;
8201 str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
8202 cData->ownerTransfer = str ? strtoul(str, NULL, 0) : 0;
8203 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
8204 cData->max = str ? atoi(str) : 0;
8205 str = database_get_data(channel, KEY_MAX_TIME, RECDB_QSTRING);
8206 cData->max_time = str ? atoi(str) : 0;
8207 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
8208 cData->greeting = str ? strdup(str) : NULL;
8209 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
8210 cData->user_greeting = str ? strdup(str) : NULL;
8211 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
8212 cData->topic_mask = str ? strdup(str) : NULL;
8213 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
8214 cData->topic = str ? strdup(str) : NULL;
8216 str = database_get_data(channel, KEY_VOTE, RECDB_QSTRING);
8218 cData->vote = str ? strdup(str) : NULL;
8219 dict_delete(cData->vote_options);
8220 cData->vote_options = dict_new();
8221 dict_set_free_data(cData->vote_options, free_vote_options);
8222 str = database_get_data(channel, KEY_VOTE_START, RECDB_QSTRING);
8223 cData->vote_start = str ? atoi(str) : 0;
8224 obj = database_get_data(channel, KEY_VOTE_OPTIONS, RECDB_OBJECT);
8225 for(it = dict_first(obj); it; it = iter_next(it)) {
8226 vote_option_read_helper(iter_key(it), iter_data(it), cData);
8230 if(!IsSuspended(cData)
8231 && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
8232 && (argc = split_line(str, 0, ArrayLength(argv), argv))
8233 && (modes = mod_chanmode_parse(cNode, NULL, argv, argc, MCP_KEY_FREE|MCP_NO_APASS, 0))) {
8234 cData->modes = *modes;
8236 cData->modes.modes_set |= MODE_REGISTERED;
8237 if(cData->modes.argc > 1)
8238 cData->modes.argc = 1;
8239 mod_chanmode_announce(chanserv, cNode, &cData->modes);
8240 mod_chanmode_free(modes);
8243 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
8244 for(it = dict_first(obj); it; it = iter_next(it))
8245 user_read_helper(iter_key(it), iter_data(it), cData);
8247 if(!cData->users && !IsProtected(cData))
8249 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
8250 unregister_channel(cData, "has empty user list.");
8254 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
8255 for(it = dict_first(obj); it; it = iter_next(it))
8256 ban_read_helper(iter_key(it), iter_data(it), cData);
8258 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
8259 for(it = dict_first(obj); it; it = iter_next(it))
8261 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
8262 struct record_data *rd = iter_data(it);
8263 const char *note, *setter;
8265 if(rd->type != RECDB_OBJECT)
8267 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
8271 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
8273 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
8275 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
8279 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
8280 if(!setter) setter = "<unknown>";
8281 chanserv_add_channel_note(cData, ntype, setter, note);
8289 chanserv_dnr_read(const char *key, struct record_data *hir)
8291 const char *setter, *reason, *str;
8292 struct do_not_register *dnr;
8293 unsigned long expiry;
8295 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
8298 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
8301 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
8304 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
8307 str = database_get_data(hir->d.object, KEY_EXPIRES, RECDB_QSTRING);
8308 expiry = str ? strtoul(str, NULL, 0) : 0;
8309 if(expiry && expiry <= now)
8311 dnr = chanserv_add_dnr(key, setter, expiry, reason);
8314 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
8316 dnr->set = atoi(str);
8322 chanserv_saxdb_read(struct dict *database)
8324 struct dict *section;
8327 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
8328 for(it = dict_first(section); it; it = iter_next(it))
8329 chanserv_note_type_read(iter_key(it), iter_data(it));
8331 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
8332 for(it = dict_first(section); it; it = iter_next(it))
8333 chanserv_channel_read(iter_key(it), iter_data(it));
8335 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
8336 for(it = dict_first(section); it; it = iter_next(it))
8337 chanserv_dnr_read(iter_key(it), iter_data(it));
8343 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
8345 int high_present = 0;
8346 saxdb_start_record(ctx, KEY_USERS, 1);
8347 for(; uData; uData = uData->next)
8349 if((uData->access >= UL_PRESENT) && uData->present && !HANDLE_FLAGGED(uData->handle, BOT))
8351 saxdb_start_record(ctx, uData->handle->handle, 0);
8352 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
8353 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
8355 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
8356 if(uData->channel->vote && uData->voted)
8357 saxdb_write_int(ctx, KEY_VOTE_VOTED, uData->voted);
8358 if(uData->channel->vote && uData->votefor)
8359 saxdb_write_int(ctx, KEY_VOTE_VOTEDFOR, uData->votefor);
8361 saxdb_write_string(ctx, KEY_INFO, uData->info);
8362 saxdb_end_record(ctx);
8364 saxdb_end_record(ctx);
8365 return high_present;
8369 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
8373 saxdb_start_record(ctx, KEY_BANS, 1);
8374 for(; bData; bData = bData->next)
8376 saxdb_start_record(ctx, bData->mask, 0);
8377 saxdb_write_int(ctx, KEY_SET, bData->set);
8378 if(bData->triggered)
8379 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
8381 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
8383 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
8385 saxdb_write_string(ctx, KEY_REASON, bData->reason);
8386 saxdb_end_record(ctx);
8388 saxdb_end_record(ctx);
8392 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
8394 saxdb_start_record(ctx, name, 0);
8395 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
8396 saxdb_write_string(ctx, KEY_REASON, susp->reason);
8398 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
8400 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
8402 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
8404 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
8405 saxdb_end_record(ctx);
8409 chanserv_write_giveownership(struct saxdb_context *ctx, const char *name, struct giveownership *giveownership)
8411 saxdb_start_record(ctx, name, 0);
8412 if(giveownership->staff_issuer)
8413 saxdb_write_string(ctx, KEY_STAFF_ISSUER, giveownership->staff_issuer);
8414 if(giveownership->old_owner)
8415 saxdb_write_string(ctx, KEY_OLD_OWNER, giveownership->old_owner);
8416 if(giveownership->target)
8417 saxdb_write_string(ctx, KEY_TARGET, giveownership->target);
8418 if(giveownership->target_access)
8419 saxdb_write_int(ctx, KEY_TARGET_ACCESS, giveownership->target_access);
8420 if(giveownership->reason)
8421 saxdb_write_string(ctx, KEY_REASON, giveownership->reason);
8422 if(giveownership->issued)
8423 saxdb_write_int(ctx, KEY_ISSUED, giveownership->issued);
8424 if(giveownership->previous)
8425 chanserv_write_giveownership(ctx, KEY_PREVIOUS, giveownership->previous);
8426 saxdb_end_record(ctx);
8430 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
8434 enum levelOption lvlOpt;
8435 enum charOption chOpt;
8438 saxdb_start_record(ctx, channel->channel->name, 1);
8440 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
8441 saxdb_write_int(ctx, KEY_MAX, channel->max);
8442 saxdb_write_int(ctx, KEY_MAX_TIME, channel->max_time);
8444 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
8445 if(channel->registrar)
8446 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
8447 if(channel->greeting)
8448 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
8449 if(channel->user_greeting)
8450 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
8451 if(channel->topic_mask)
8452 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
8453 if(channel->suspended)
8454 chanserv_write_suspended(ctx, "suspended", channel->suspended);
8455 if(channel->giveownership)
8456 chanserv_write_giveownership(ctx, "giveownership", channel->giveownership);
8458 saxdb_write_int(ctx, KEY_EXPIRE, channel->expiry);
8461 saxdb_write_string(ctx, KEY_VOTE, channel->vote);
8462 if(channel->vote_start)
8463 saxdb_write_int(ctx, KEY_VOTE_START, channel->vote_start);
8464 if (dict_size(channel->vote_options)) {
8465 saxdb_start_record(ctx, KEY_VOTE_OPTIONS, 1);
8466 for (it = dict_first(channel->vote_options); it; it = iter_next(it)) {
8467 struct vote_option *vOpt = iter_data(it);
8469 sprintf(str,"%i",vOpt->option_id);
8470 saxdb_start_record(ctx, str, 0);
8472 saxdb_write_int(ctx, KEY_VOTE_OPTION_VOTED, vOpt->voted);
8474 saxdb_write_string(ctx, KEY_VOTE_OPTION_NAME, vOpt->name);
8475 saxdb_end_record(ctx);
8477 saxdb_end_record(ctx);
8481 saxdb_start_record(ctx, KEY_OPTIONS, 0);
8482 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
8483 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8484 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
8485 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8487 buf[0] = channel->chOpts[chOpt];
8489 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
8491 saxdb_end_record(ctx);
8493 if(channel->modes.modes_set || channel->modes.modes_clear)
8495 mod_chanmode_format(&channel->modes, buf);
8496 saxdb_write_string(ctx, KEY_MODES, buf);
8499 high_present = chanserv_write_users(ctx, channel->users);
8500 chanserv_write_bans(ctx, channel->bans);
8502 if(dict_size(channel->notes))
8506 saxdb_start_record(ctx, KEY_NOTES, 1);
8507 for(it = dict_first(channel->notes); it; it = iter_next(it))
8509 struct note *note = iter_data(it);
8510 saxdb_start_record(ctx, iter_key(it), 0);
8511 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
8512 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
8513 saxdb_end_record(ctx);
8515 saxdb_end_record(ctx);
8518 if(channel->ownerTransfer)
8519 saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
8520 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
8521 saxdb_end_record(ctx);
8525 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
8529 saxdb_start_record(ctx, ntype->name, 0);
8530 switch(ntype->set_access_type)
8532 case NOTE_SET_CHANNEL_ACCESS:
8533 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
8535 case NOTE_SET_CHANNEL_SETTER:
8536 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
8538 case NOTE_SET_PRIVILEGED: default:
8539 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
8542 switch(ntype->visible_type)
8544 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
8545 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
8546 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
8548 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
8549 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
8550 saxdb_end_record(ctx);
8554 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
8556 struct do_not_register *dnr;
8557 dict_iterator_t it, next;
8559 for(it = dict_first(dnrs); it; it = next)
8561 next = iter_next(it);
8562 dnr = iter_data(it);
8563 if(dnr->expires && dnr->expires <= now)
8565 dict_remove(dnrs, iter_key(it));
8568 saxdb_start_record(ctx, dnr->chan_name, 0);
8570 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
8572 saxdb_write_int(ctx, KEY_EXPIRES, dnr->expires);
8573 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
8574 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
8575 saxdb_end_record(ctx);
8580 chanserv_saxdb_write(struct saxdb_context *ctx)
8583 struct chanData *channel;
8586 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
8587 for(it = dict_first(note_types); it; it = iter_next(it))
8588 chanserv_write_note_type(ctx, iter_data(it));
8589 saxdb_end_record(ctx);
8592 saxdb_start_record(ctx, KEY_DNR, 1);
8593 write_dnrs_helper(ctx, handle_dnrs);
8594 write_dnrs_helper(ctx, plain_dnrs);
8595 write_dnrs_helper(ctx, mask_dnrs);
8596 saxdb_end_record(ctx);
8599 saxdb_start_record(ctx, KEY_CHANNELS, 1);
8600 for(channel = channelList; channel; channel = channel->next)
8601 chanserv_write_channel(ctx, channel);
8602 saxdb_end_record(ctx);
8608 chanserv_db_cleanup(void) {
8610 unreg_part_func(handle_part);
8612 unregister_channel(channelList, "terminating.");
8613 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
8614 UnlockChannel(chanserv_conf.support_channels.list[ii]);
8615 free(chanserv_conf.support_channels.list);
8616 dict_delete(handle_dnrs);
8617 dict_delete(plain_dnrs);
8618 dict_delete(mask_dnrs);
8619 dict_delete(note_types);
8620 free_string_list(chanserv_conf.eightball);
8621 free_string_list(chanserv_conf.old_ban_names);
8622 free_string_list(chanserv_conf.set_shows);
8623 free(set_shows_list.list);
8624 free(uset_shows_list.list);
8627 struct userData *helper = helperList;
8628 helperList = helperList->next;
8633 #if defined(GCC_VARMACROS)
8634 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ARGS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ARGS)
8635 #elif defined(C99_VARMACROS)
8636 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, __VA_ARGS__)
8638 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
8639 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
8642 init_chanserv(const char *nick)
8644 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
8645 conf_register_reload(chanserv_conf_read);
8649 reg_server_link_func(handle_server_link);
8650 reg_new_channel_func(handle_new_channel);
8651 reg_join_func(handle_join);
8652 reg_part_func(handle_part);
8653 reg_kick_func(handle_kick);
8654 reg_topic_func(handle_topic);
8655 reg_mode_change_func(handle_mode);
8656 reg_nick_change_func(handle_nick_change);
8657 reg_auth_func(handle_auth);
8660 reg_handle_rename_func(handle_rename);
8661 reg_unreg_func(handle_unreg);
8663 handle_dnrs = dict_new();
8664 dict_set_free_data(handle_dnrs, free);
8665 plain_dnrs = dict_new();
8666 dict_set_free_data(plain_dnrs, free);
8667 mask_dnrs = dict_new();
8668 dict_set_free_data(mask_dnrs, free);
8670 reg_svccmd_unbind_func(handle_svccmd_unbind);
8671 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
8672 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
8673 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
8674 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
8675 DEFINE_COMMAND(dnrsearch, 3, 0, "template", "noregister", NULL);
8676 modcmd_register(chanserv_module, "dnrsearch print", NULL, 0, 0, NULL);
8677 modcmd_register(chanserv_module, "dnrsearch remove", NULL, 0, 0, NULL);
8678 modcmd_register(chanserv_module, "dnrsearch count", NULL, 0, 0, NULL);
8679 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
8680 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
8681 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
8682 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
8683 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
8685 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
8686 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
8688 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8689 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8690 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8691 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8692 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
8694 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
8695 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
8696 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
8697 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8698 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8700 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8701 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
8702 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8703 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
8705 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
8706 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
8707 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8708 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8709 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
8710 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8711 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8712 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8714 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8715 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8716 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8717 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
8718 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
8719 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
8720 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
8721 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
8722 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8723 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
8724 DEFINE_COMMAND(invitemeall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8725 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
8726 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
8727 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8728 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8730 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
8731 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8732 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8733 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8734 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
8736 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
8737 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
8739 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
8740 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8741 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8742 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8743 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8744 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8745 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8746 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8747 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8748 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8749 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8751 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
8752 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
8754 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
8755 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
8756 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
8757 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
8759 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
8760 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
8761 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
8762 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
8763 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
8765 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8766 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8767 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8768 DEFINE_COMMAND(8ball, 2, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8769 DEFINE_COMMAND(d, 2, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8770 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8772 DEFINE_COMMAND(addvote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8773 DEFINE_COMMAND(delvote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8774 DEFINE_COMMAND(addoption, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8775 DEFINE_COMMAND(deloption, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8776 DEFINE_COMMAND(vote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8777 DEFINE_COMMAND(startvote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8778 DEFINE_COMMAND(endvote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8779 DEFINE_COMMAND(voteresults, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8781 DEFINE_COMMAND(opme, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
8783 /* Channel options */
8784 DEFINE_CHANNEL_OPTION(defaulttopic);
8785 DEFINE_CHANNEL_OPTION(topicmask);
8786 DEFINE_CHANNEL_OPTION(greeting);
8787 DEFINE_CHANNEL_OPTION(usergreeting);
8788 DEFINE_CHANNEL_OPTION(modes);
8789 DEFINE_CHANNEL_OPTION(enfops);
8790 DEFINE_CHANNEL_OPTION(giveops);
8791 DEFINE_CHANNEL_OPTION(protect);
8792 DEFINE_CHANNEL_OPTION(enfmodes);
8793 DEFINE_CHANNEL_OPTION(enftopic);
8794 DEFINE_CHANNEL_OPTION(pubcmd);
8795 DEFINE_CHANNEL_OPTION(givevoice);
8796 DEFINE_CHANNEL_OPTION(userinfo);
8797 DEFINE_CHANNEL_OPTION(dynlimit);
8798 DEFINE_CHANNEL_OPTION(topicsnarf);
8799 DEFINE_CHANNEL_OPTION(vote);
8800 DEFINE_CHANNEL_OPTION(nodelete);
8801 DEFINE_CHANNEL_OPTION(toys);
8802 DEFINE_CHANNEL_OPTION(setters);
8803 DEFINE_CHANNEL_OPTION(topicrefresh);
8804 DEFINE_CHANNEL_OPTION(ctcpusers);
8805 DEFINE_CHANNEL_OPTION(ctcpreaction);
8806 DEFINE_CHANNEL_OPTION(inviteme);
8807 DEFINE_CHANNEL_OPTION(unreviewed);
8808 modcmd_register(chanserv_module, "set expire", chan_opt_expire, 1, 0, "flags", "+helping", NULL);
8809 modcmd_register(chanserv_module, "set unreviewed on", NULL, 0, 0, "flags", "+helping", NULL);
8810 modcmd_register(chanserv_module, "set unreviewed off", NULL, 0, 0, "flags", "+oper", NULL);
8812 DEFINE_CHANNEL_OPTION(offchannel);
8813 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
8815 /* Alias set topic to set defaulttopic for compatibility. */
8816 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
8819 DEFINE_USER_OPTION(noautoop);
8820 DEFINE_USER_OPTION(autoinvite);
8821 DEFINE_USER_OPTION(info);
8823 /* Alias uset autovoice to uset autoop. */
8824 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
8826 note_types = dict_new();
8827 dict_set_free_data(note_types, chanserv_deref_note_type);
8830 const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
8831 chanserv = AddLocalUser(nick, nick, NULL, "Channel Services", modes);
8832 service_register(chanserv)->trigger = '!';
8833 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
8835 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
8837 if(chanserv_conf.channel_expire_frequency)
8838 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
8840 if(chanserv_conf.dnr_expire_frequency)
8841 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
8843 if(chanserv_conf.refresh_period)
8845 unsigned long next_refresh;
8846 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
8847 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
8850 reg_exit_func(chanserv_db_cleanup);
8851 message_register_table(msgtab);