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_NEW_CHANNEL_AUTHED "new_channel_authed_join"
62 #define KEY_NEW_CHANNEL_UNAUTHED "new_channel_unauthed_join"
63 #define KEY_NEW_CHANNEL_MSG "new_channel_message"
65 /* ChanServ database */
66 #define KEY_CHANNELS "channels"
67 #define KEY_NOTE_TYPES "note_types"
69 /* Note type parameters */
70 #define KEY_NOTE_OPSERV_ACCESS "opserv_access"
71 #define KEY_NOTE_CHANNEL_ACCESS "channel_access"
72 #define KEY_NOTE_SETTER_ACCESS "setter_access"
73 #define KEY_NOTE_VISIBILITY "visibility"
74 #define KEY_NOTE_VIS_PRIVILEGED "privileged"
75 #define KEY_NOTE_VIS_CHANNEL_USERS "channel_users"
76 #define KEY_NOTE_VIS_ALL "all"
77 #define KEY_NOTE_MAX_LENGTH "max_length"
78 #define KEY_NOTE_SETTER "setter"
79 #define KEY_NOTE_NOTE "note"
81 /* Do-not-register channels */
83 #define KEY_DNR_SET "set"
84 #define KEY_DNR_SETTER "setter"
85 #define KEY_DNR_REASON "reason"
88 #define KEY_REGISTERED "registered"
89 #define KEY_REGISTRAR "registrar"
90 #define KEY_SUSPENDED "suspended"
91 #define KEY_PREVIOUS "previous"
92 #define KEY_SUSPENDER "suspender"
93 #define KEY_ISSUED "issued"
94 #define KEY_REVOKED "revoked"
95 #define KEY_SUSPEND_EXPIRES "suspend_expires"
96 #define KEY_SUSPEND_REASON "suspend_reason"
97 #define KEY_VISITED "visited"
98 #define KEY_TOPIC "topic"
99 #define KEY_GREETING "greeting"
100 #define KEY_USER_GREETING "user_greeting"
101 #define KEY_MODES "modes"
102 #define KEY_FLAGS "flags"
103 #define KEY_OPTIONS "options"
104 #define KEY_USERS "users"
105 #define KEY_BANS "bans"
106 #define KEY_MAX "max"
107 #define KEY_MAX_TIME "max_time"
108 #define KEY_NOTES "notes"
109 #define KEY_TOPIC_MASK "topic_mask"
110 #define KEY_OWNER_TRANSFER "owner_transfer"
111 #define KEY_EXPIRE "expire"
114 #define KEY_LEVEL "level"
115 #define KEY_INFO "info"
116 #define KEY_SEEN "seen"
119 #define KEY_VOTE "vote"
120 #define KEY_VOTE_START "votestart"
121 #define KEY_VOTE_OPTIONS "voptions"
122 #define KEY_VOTE_OPTION_NAME "voptionname"
123 #define KEY_VOTE_VOTED "vvoted"
124 #define KEY_VOTE_VOTEDFOR "vvotefor"
125 #define KEY_VOTE_OPTION_ID "voptionid"
126 #define KEY_VOTE_OPTION_VOTED "voptionvoted"
129 #define KEY_OWNER "owner"
130 #define KEY_REASON "reason"
131 #define KEY_SET "set"
132 #define KEY_DURATION "duration"
133 #define KEY_EXPIRES "expires"
134 #define KEY_TRIGGERED "triggered"
136 #define CHANNEL_DEFAULT_FLAGS (CHANNEL_OFFCHANNEL | CHANNEL_UNREVIEWED)
137 #define CHANNEL_PRESERVED_FLAGS (CHANNEL_UNREVIEWED)
138 #define CHANNEL_DEFAULT_OPTIONS "lmoooanpcnat"
140 /* Administrative messages */
141 static const struct message_entry msgtab[] = {
142 { "CSMSG_CHANNELS_EXPIRED", "%i channels expired." },
144 /* Channel registration */
145 { "CSMSG_REG_SUCCESS", "You now have ownership of $b%s$b." },
146 { "CSMSG_PROXY_SUCCESS", "%s now has ownership of $b%s$b." },
147 { "CSMSG_ALREADY_REGGED", "$b%s$b is registered to someone else." },
148 { "CSMSG_MUST_BE_OPPED", "You must be a channel operator in $b%s$b to register it." },
149 { "CSMSG_PROXY_FORBIDDEN", "You may not register a channel for someone else." },
150 { "CSMSG_OWN_TOO_MANY", "%s already owns enough channels (at least %d); use FORCE to override." },
152 /* Do-not-register channels */
153 { "CSMSG_NOT_DNR", "$b%s$b is not a valid channel name or *account." },
154 { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
155 { "CSMSG_DNR_INFO", "$b%s$b is do-not-register (by $b%s$b): %s" },
156 { "CSMSG_DNR_INFO_SET", "$b%s$b is do-not-register (set %s by $b%s$b): %s" },
157 { "CSMSG_DNR_INFO_SET_EXPIRES", "$b%s$b is do-not-register (set %s by $b%s$b; expires %s): %s" },
158 { "CSMSG_MORE_DNRS", "%d more do-not-register entries skipped." },
159 { "CSMSG_DNR_CHANNEL", "Only network staff may register $b%s$b." },
160 { "CSMSG_DNR_CHANNEL_MOVE", "Only network staff may move $b%s$b." },
161 { "CSMSG_DNR_ACCOUNT", "Only network staff may register channels to $b%s$b." },
162 { "CSMSG_NOREGISTER_CHANNEL", "$b%s$b has been added to the do-not-register list." },
163 { "CSMSG_NO_SUCH_DNR", "$b%s$b is not in the do-not-register list." },
164 { "CSMSG_DNR_REMOVED", "$b%s$b has been removed from the do-not-register list." },
165 { "CSMSG_DNR_BAD_ACTION", "$b%s$b is not a recognized do-not-register action." },
166 { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
168 /* Channel unregistration */
169 { "CSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
170 { "CSMSG_UNREG_NODELETE", "$b%s$b is protected from unregistration." },
171 { "CSMSG_CHAN_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended (%s)." },
172 { "CSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must use 'unregister %s'." },
175 { "CSMSG_MOVE_SUCCESS", "Channel registration has been moved to $b%s$b." },
176 { "CSMSG_MOVE_NODELETE", "$b%s$b is protected from unregistration, and cannot be moved." },
178 /* Channel merging */
179 { "CSMSG_MERGE_SUCCESS", "Channel successfully merged into $b%s$b." },
180 { "CSMSG_MERGE_SELF", "Merging cannot be performed if the source and target channels are the same." },
181 { "CSMSG_MERGE_NODELETE", "You may not merge a channel that is marked NoDelete." },
182 { "CSMSG_MERGE_SUSPENDED", "Merging cannot be performed if the source or target channel is suspended." },
183 { "CSMSG_MERGE_NOT_OWNER", "You must be the owner of the target channel (or a helper) to merge into the channel." },
185 /* Handle unregistration */
186 { "CSMSG_HANDLE_UNREGISTERED", "As a result of your account unregistration, you have been deleted from all of your channels' userlists." },
189 { "CSMSG_NOT_USER", "You lack access to $b%s$b." },
190 { "CSMSG_NO_CHAN_USER", "%s lacks access to $b%s$b." },
191 { "CSMSG_NO_ACCESS", "You lack sufficient access to use this command." },
192 { "CSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$C$b." },
193 { "CSMSG_MAXIMUM_BANS", "This channel has reached the ban count limit of $b%d$b." },
194 { "CSMSG_MAXIMUM_USERS", "This channel has reached the user count limit of $b%d$b." },
195 { "CSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel, and cannot be registered." },
196 { "CSMSG_GODMODE_UP", "You may not use $b%s$b to op yourself unless you are on the user list. Use the $bop$b command instead." },
197 { "CSMSG_ALREADY_OPPED", "You are already opped in $b%s$b." },
198 { "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." },
199 { "CSMSG_ALREADY_DOWN", "You are not opped or voiced in $b%s$b." },
200 { "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." },
201 { "CSMSG_OUT_OF_CHANNEL", "For some reason I don't seem to be in $b%s$b." },
202 { "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." },
204 /* Removing yourself from a channel. */
205 { "CSMSG_NO_OWNER_DELETEME", "You cannot delete your owner access in $b%s$b." },
206 { "CSMSG_CONFIRM_DELETEME", "To really remove yourself, you must use 'deleteme %s'." },
207 { "CSMSG_DELETED_YOU", "Your $b%d$b access has been deleted from $b%s$b." },
209 /* User management */
210 { "CSMSG_ADDED_USER", "Added %s to the %s user list with access %d." },
211 { "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." },
212 { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be lower than maximum (%d)." },
213 { "CSMSG_DELETED_USERS", "Deleted accounts matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list." },
214 { "CSMSG_TRIMMED_USERS", "Trimmed $b%d users$b with access from %d to %d from the %s user list who were inactive for at least %s." },
215 { "CSMSG_INCORRECT_ACCESS", "%s has access $b%d$b, not %s." },
216 { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with access %d)." },
217 { "CSMSG_CANNOT_TRIM", "You must include a minimum inactivity duration of at least 60 seconds to trim." },
219 { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." },
220 { "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." },
221 { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
222 { "CSMSG_TRANSFER_WAIT", "You must wait %s before you can give ownership of $b%s$b to someone else." },
223 { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
224 { "CSMSG_CONFIRM_GIVEOWNERSHIP", "To really give ownership to $b%1$s$b, you must use 'giveownership *%1$s %2$s'." },
225 { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
228 { "CSMSG_BAN_ADDED", "Permanently banned $b%s$b from %s." },
229 { "CSMSG_TIMED_BAN_ADDED", "Banned $b%s$b from %s for %s." },
230 { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
231 { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
232 { "CSMSG_REASON_CHANGE", "Reason for ban $b%s$b changed." },
233 { "CSMSG_BAN_EXTENDED", "Extended ban for $b%s$b expires in %s." },
234 { "CSMSG_BAN_REMOVED", "Matching ban(s) for $b%s$b removed." },
235 { "CSMSG_TRIMMED_BANS", "Trimmed $b%d bans$b from the %s ban list that were inactive for at least %s." },
236 { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
237 { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
238 { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
239 { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
240 { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
241 { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
242 { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban found for $b%s$b." },
243 { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
245 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
247 /* Channel management */
248 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
249 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
250 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
252 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
253 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
254 { "CSMSG_TOPICMASK_CONFLICT1", "I do not know how to make that topic work with the current topic mask in $b%s$b, which is: %s" },
255 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic is at most %d characters and matches the topic mask pattern." },
256 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
257 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
258 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
260 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
261 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
262 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
263 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
264 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
265 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
266 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you must use 'set defaults %s'." },
267 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
268 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
269 { "CSMSG_BAD_GIVEVOICE", "You cannot change GiveVoice to above GiveOps (%d)." },
270 { "CSMSG_BAD_GIVEOPS", "You cannot change GiveOps to below GiveVoice (%d)." },
271 { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
272 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
273 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
274 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
275 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
276 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
277 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
278 { "CSMSG_SET_MODES", "$bModes $b %s" },
279 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
280 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s" },
281 { "CSMSG_SET_OFFCHANNEL", "$bOffChannel $b %s" },
282 { "CSMSG_SET_USERINFO", "$bUserInfo $b %d" },
283 { "CSMSG_SET_GIVE_VOICE", "$bGiveVoice $b %d" },
284 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" },
285 { "CSMSG_SET_VOTE", "$bVote $b %d" },
286 { "CSMSG_SET_INVITEME", "$bInviteMe $b %d" },
287 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d" },
288 { "CSMSG_SET_GIVE_OPS", "$bGiveOps $b %d" },
289 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d" },
290 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d" },
291 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d" },
292 { "CSMSG_SET_SETTERS", "$bSetters $b %d" },
293 { "CSMSG_SET_CTCPUSERS", "$bCTCPUsers $b %d" },
294 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
295 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
296 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
297 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
298 { "CSMSG_SET_UNREVIEWED", "$bUnreviewed $b %s" },
299 { "CSMSG_SET_EXPIRE", "$bExpire $b %s" },
300 { "CSMSG_SET_EXPIRE_OFF", "$bExpire $b off" },
301 { "CSMSG_USET_NOAUTOOP", "$bNoAutoOp $b %s" },
302 { "CSMSG_USET_NOAUTOVOICE", "$bNoAutoVoice $b %s" },
303 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
304 { "CSMSG_USET_INFO", "$bInfo $b %s" },
306 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
307 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
308 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
309 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
310 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
311 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
312 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
313 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
314 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
315 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
316 { "CSMSG_PROTECT_NONE", "No users will be protected." },
317 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
318 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
319 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
320 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
321 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
322 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
323 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
324 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
325 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
326 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
327 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
328 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
330 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
331 { "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" },
332 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s." },
333 { "CSMSG_ALREADY_PRESENT", "%s is already in $b%s$b." },
334 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
335 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s for $S to invite you." },
336 { "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
337 { "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
339 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
340 { "CSMSG_NO_BANS", "No channel bans found on $b%s$b." },
341 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
343 /* Channel userlist */
344 { "CSMSG_ACCESS_ALL_HEADER", "%s users from level %d to %d:" },
345 { "CSMSG_ACCESS_SEARCH_HEADER", "%s users from level %d to %d matching %s:" },
346 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
347 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%d$b in %s." },
348 { "CSMSG_TOTAL_USERS", "There are $b%d$b users in %s." },
350 /* Channel note list */
351 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
352 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
353 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
354 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
355 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
356 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
357 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
358 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
359 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
360 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
361 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
362 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
363 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
364 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
365 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
367 /* Channel [un]suspension */
368 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
369 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
370 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
371 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
372 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
373 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
374 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
376 /* Access information */
377 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
378 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
379 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
380 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
381 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
382 { "CSMSG_USER_HAS_ACCESS", "%s has access $b%d$b in %s." },
383 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
384 { "CSMSG_HELPER_HAS_ACCESS", "%s has access $b%d$b in %s and has $bsecurity override$b enabled." },
385 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
386 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
387 { "CSMSG_OPERATOR_TITLE", "IRC operator" },
388 { "CSMSG_UC_H_TITLE", "network helper" },
389 { "CSMSG_LC_H_TITLE", "support helper" },
390 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
391 { "CSMSG_MYACCESS_COUNT", "%s has access in $b%d$b channels and is owner of $b%d$b channel(s)." },
392 { "CSMSG_MYACCESS_COUNT_1", "%s has access in $b%d$b channel and is owner of $b%d$b channel(s)." },
395 /* Seen information */
396 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
397 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
398 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
399 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
401 /* Names information */
402 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
403 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
405 /* Channel information */
406 { "CSMSG_CHANNEL_INFO", "$b%s$b Information:" },
407 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
408 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
409 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
410 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
411 { "CSMSG_CHANNEL_MAX_TIME", "$bRecord Visitors: $b%i (%s ago.)" },
412 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
413 { "CSMSG_CHANNEL_BANS", "$bBan Count: $b%i" },
414 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
415 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
416 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
417 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
418 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
419 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
420 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
421 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
422 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
423 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
424 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
425 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
426 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
427 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
429 { "CSMSG_PEEK_INFO", "$b%s$b Status:" },
430 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
431 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
432 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d (%d ops, %d voices, %d regulars)" },
433 { "CSMSG_PEEK_OPS", "$bOps:$b" },
434 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
436 /* Network information */
437 { "CSMSG_NETWORK_INFO", "Network Information:" },
438 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
439 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
440 { "CSMSG_NETWORK_BANS", "$bTotal Ban Count: $b%i" },
441 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
442 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
443 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
444 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
445 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
448 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
449 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
450 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
452 /* Channel searches */
453 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
454 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
455 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
456 { "CSMSG_CHANNEL_SEARCH_RESULTS", "The following channels were found:" },
458 /* Channel configuration */
459 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
460 { "CSMSG_INVALID_CFLAG", "$b%s$b is not a recognized channel flag." },
461 { "CSMSG_CHANNEL_OPTIONS", "Channel Options:" },
462 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
465 { "CSMSG_USER_OPTIONS", "User Options:" },
466 { "CSMSG_USER_PROTECTED_2", "That user is protected." },
469 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
470 { "CSMSG_PING_RESPONSE", "Pong!" },
471 { "CSMSG_WUT_RESPONSE", "wut" },
472 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
473 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
474 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
475 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
476 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
477 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
478 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
481 { "CSMSG_ADDVOTE_DONE", "Vote added. Use $baddoption$b to add at least 2 vote options and then $bstartvote$b to start the voting." },
482 { "CSMSG_ADDVOTE_FULL", "There is already a vote in this channel. Use $bdelvote$b to delete it." },
483 { "CSMSG_DELVOTE_DONE", "Vote deleted." },
484 { "CSMSG_NO_VOTE", "There is no vote in this channel." },
485 { "CSMSG_ADDOPTION_DONE", "Vote option added with id %i (%i - %i)." },
486 { "CSMSG_DELOPTION_DONE", "Vote option deleted." },
487 { "CSMSG_DELOPTION_NONE", "Vote option does not exist." },
488 { "CSMSG_VOTE_NEED_OPTIONS", "There must be at least 2 options in a vote." },
489 { "CSMSG_VOTE_NOT_STARTED", "The vote is not started. Use $bstartvote$b to allow voting." },
490 { "CSMSG_VOTE_QUESTION", "Question: %s" },
491 { "CSMSG_VOTE_OPTION", "$b%i$b: %s ($b%i$b votes)" },
492 { "CSMSG_VOTERES_QUESTION", "Question: %s" },
493 { "CSMSG_VOTERES_OPTION", "
\ 2%i
\ 2: %s (
\ 2%i
\ 2 votes)" },
494 { "CSMSG_STARTVOTE_TOP", "
\ 2%s
\ 2 has started a voting:" },
495 { "CSMSG_STARTVOTE_QUESTION", "
\ 2Question:
\ 2 %s" },
496 { "CSMSG_STARTVOTE_OPTION", "
\ 2%i:
\ 2 %s" },
497 { "CSMSG_STARTVOTE_ACCESS", "All channel users with at least
\ 2%i
\ 2 access can vote." },
498 { "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." },
499 { "CSMSG_STARTVOTE_RUNNING", "The vote is already running." },
500 { "CSMSG_VOTE_VOTED", "You have already voted." },
501 { "CSMSG_VOTE_INVALID", "Vote option does not exist." },
502 { "CSMSG_VOTE_DONE", "You voted for $b%s$b." },
503 { "CSMSG_ENDVOTE_DONE", "The vote has been finished." },
504 { "CSMSG_ENDVOTE_STOPPED", "The vote is not running." },
507 { "CSMSG_EVENT_SEARCH_RESULTS", "The following channel events were found:" },
511 /* eject_user and unban_user flags */
512 #define ACTION_KICK 0x0001
513 #define ACTION_BAN 0x0002
514 #define ACTION_ADD_BAN 0x0004
515 #define ACTION_ADD_TIMED_BAN 0x0008
516 #define ACTION_UNBAN 0x0010
517 #define ACTION_DEL_BAN 0x0020
519 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
520 #define MODELEN 40 + KEYLEN
524 #define CSFUNC_ARGS user, channel, argc, argv, cmd
526 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
527 #define CHANSERV_SYNTAX() svccmd_send_help(user, chanserv, cmd)
528 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
529 reply("MSG_MISSING_PARAMS", argv[0]); \
533 DECLARE_LIST(dnrList, struct do_not_register *);
534 DEFINE_LIST(dnrList, struct do_not_register *)
536 static int eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action);
538 struct userNode *chanserv;
541 static dict_t plain_dnrs, mask_dnrs, handle_dnrs;
542 static struct log_type *CS_LOG;
546 struct channelList support_channels;
547 struct mod_chanmode default_modes;
549 unsigned long db_backup_frequency;
550 unsigned long channel_expire_frequency;
551 unsigned long dnr_expire_frequency;
553 unsigned long invited_timeout;
555 unsigned long info_delay;
556 unsigned long adjust_delay;
557 unsigned long channel_expire_delay;
558 unsigned int nodelete_level;
560 unsigned int adjust_threshold;
561 int join_flood_threshold;
563 unsigned int greeting_length;
564 unsigned int refresh_period;
565 unsigned int giveownership_period;
567 unsigned int max_owned;
568 unsigned int max_chan_users;
569 unsigned int max_chan_bans;
570 unsigned int max_userinfo_length;
572 struct string_list *set_shows;
573 struct string_list *eightball;
574 struct string_list *old_ban_names;
576 const char *ctcp_short_ban_duration;
577 const char *ctcp_long_ban_duration;
579 const char *irc_operator_epithet;
580 const char *network_helper_epithet;
581 const char *support_helper_epithet;
583 const char *new_channel_authed;
584 const char *new_channel_unauthed;
585 const char *new_channel_msg;
590 struct userNode *user;
591 struct userNode *bot;
592 struct chanNode *channel;
594 unsigned short lowest;
595 unsigned short highest;
596 struct userData **users;
597 struct helpfile_table table;
602 struct userNode *user;
603 struct chanNode *chan;
606 enum note_access_type
608 NOTE_SET_CHANNEL_ACCESS,
609 NOTE_SET_CHANNEL_SETTER,
613 enum note_visible_type
616 NOTE_VIS_CHANNEL_USERS,
622 enum note_access_type set_access_type;
624 unsigned int min_opserv;
625 unsigned short min_ulevel;
627 enum note_visible_type visible_type;
628 unsigned int max_length;
635 struct note_type *type;
636 char setter[NICKSERV_HANDLE_LEN+1];
640 static unsigned int registered_channels;
641 static unsigned int banCount;
643 static const struct {
646 unsigned short level;
649 { "peon", "Peon", UL_PEON, '+' },
650 { "op", "Op", UL_OP, '@' },
651 { "master", "Master", UL_MASTER, '%' },
652 { "coowner", "Coowner", UL_COOWNER, '*' },
653 { "owner", "Owner", UL_OWNER, '!' },
654 { "helper", "BUG:", UL_HELPER, 'X' }
657 static const struct {
660 unsigned short default_value;
661 unsigned int old_idx;
662 unsigned int old_flag;
663 unsigned short flag_value;
665 { "CSMSG_SET_GIVE_VOICE", "givevoice", 100, ~0, CHANNEL_VOICE_ALL, 0 },
666 { "CSMSG_SET_GIVE_OPS", "giveops", 200, 2, 0, 0 },
667 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
668 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
669 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
670 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
671 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
672 { "CSMSG_SET_CTCPUSERS", "ctcpusers", 0, 9, 0, 0 },
673 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES, 1 },
674 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE, 200 },
675 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF, 1 },
676 { "CSMSG_SET_VOTE", "vote", 100, ~0, 0, 0 }
679 struct charOptionValues {
682 } protectValues[] = {
683 { 'a', "CSMSG_PROTECT_ALL" },
684 { 'e', "CSMSG_PROTECT_EQUAL" },
685 { 'l', "CSMSG_PROTECT_LOWER" },
686 { 'n', "CSMSG_PROTECT_NONE" }
688 { 'd', "CSMSG_TOYS_DISABLED" },
689 { 'n', "CSMSG_TOYS_PRIVATE" },
690 { 'p', "CSMSG_TOYS_PUBLIC" }
691 }, topicRefreshValues[] = {
692 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
693 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
694 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
695 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
696 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
697 }, ctcpReactionValues[] = {
698 { 'k', "CSMSG_CTCPREACTION_KICK" },
699 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
700 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
701 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
704 static const struct {
708 unsigned int old_idx;
710 struct charOptionValues *values;
712 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues), protectValues },
713 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues), toysValues },
714 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues), topicRefreshValues },
715 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 't', 10, ArrayLength(ctcpReactionValues), ctcpReactionValues }
718 struct userData *helperList;
719 struct chanData *channelList;
720 static struct module *chanserv_module;
721 static unsigned int userCount;
723 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
724 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
725 static void unregister_channel(struct chanData *channel, const char *reason);
728 user_level_from_name(const char *name, unsigned short clamp_level)
730 unsigned int level = 0, ii;
732 level = strtoul(name, NULL, 10);
733 else for(ii = 0; (ii < ArrayLength(accessLevels)) && !level; ++ii)
734 if(!irccasecmp(name, accessLevels[ii].name))
735 level = accessLevels[ii].level;
736 if(level > clamp_level)
742 parse_level_range(unsigned short *minl, unsigned short *maxl, const char *arg)
745 *minl = strtoul(arg, &sep, 10);
753 *maxl = strtoul(sep+1, &sep, 10);
761 _GetChannelUser(struct chanData *channel, struct handle_info *handle, int override, int allow_suspended)
763 struct userData *uData, **head;
765 if(!channel || !handle)
768 if(override && HANDLE_FLAGGED(handle, HELPING)
769 && ((handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel)))
771 for(uData = helperList;
772 uData && uData->handle != handle;
773 uData = uData->next);
777 uData = calloc(1, sizeof(struct userData));
778 uData->handle = handle;
780 uData->access = UL_HELPER;
786 uData->next = helperList;
788 helperList->prev = uData;
796 for(uData = channel->users; uData; uData = uData->next)
797 if((uData->handle == handle) && (allow_suspended || !IsUserSuspended(uData)))
800 head = &(channel->users);
803 if(uData && (uData != *head))
805 /* Shuffle the user to the head of whatever list he was in. */
807 uData->next->prev = uData->prev;
809 uData->prev->next = uData->next;
815 (**head).prev = uData;
822 /* Returns non-zero if user has at least the minimum access.
823 * exempt_owner is set when handling !set, so the owner can set things
826 int check_user_level(struct chanNode *channel, struct userNode *user, enum levelOption opt, int allow_override, int exempt_owner)
828 struct userData *uData;
829 struct chanData *cData = channel->channel_info;
830 unsigned short minimum = cData->lvlOpts[opt];
833 uData = _GetChannelUser(cData, user->handle_info, allow_override, 0);
836 if(minimum <= uData->access)
838 if((minimum > UL_OWNER) && (uData->access == UL_OWNER) && exempt_owner)
843 /* Scan for other users authenticated to the same handle
844 still in the channel. If so, keep them listed as present.
846 user is optional, if not null, it skips checking that userNode
847 (for the handle_part function) */
849 scan_user_presence(struct userData *uData, struct userNode *user)
853 if(IsSuspended(uData->channel)
854 || IsUserSuspended(uData)
855 || !(mn = find_handle_in_channel(uData->channel->channel, uData->handle, user)))
867 chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, const char *text, UNUSED_ARG(struct userNode *bot), UNUSED_ARG(unsigned int is_notice))
869 unsigned int eflags, argc;
871 static char *bad_ctcp_reason = "CTCPs to this channel are forbidden.";
873 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
874 if(!channel->channel_info
875 || IsSuspended(channel->channel_info)
877 || !ircncasecmp(text, "ACTION ", 7))
879 /* Figure out the minimum level needed to CTCP the channel */
880 if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
882 /* We need to enforce against them; do so. */
884 argv[0] = (char*)text;
885 argv[1] = user->nick;
887 if(GetUserMode(channel, user))
888 eflags |= ACTION_KICK;
889 switch(channel->channel_info->chOpts[chCTCPReaction]) {
890 default: case 'k': /* just do the kick */ break;
892 eflags |= ACTION_BAN;
895 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
896 argv[argc++] = (char*)chanserv_conf.ctcp_short_ban_duration;
899 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
900 argv[argc++] = (char*)chanserv_conf.ctcp_long_ban_duration;
903 argv[argc++] = bad_ctcp_reason;
904 eject_user(chanserv, channel, argc, argv, NULL, eflags);
908 chanserv_create_note_type(const char *name)
910 struct note_type *ntype = calloc(1, sizeof(*ntype) + strlen(name));
911 strcpy(ntype->name, name);
913 dict_insert(note_types, ntype->name, ntype);
918 free_vote_options(void *data)
920 struct vote_option *vOpt = data;
922 free(vOpt->option_str);
927 chanserv_deref_note_type(void *data)
929 struct note_type *ntype = data;
931 if(--ntype->refs > 0)
937 chanserv_flush_note_type(struct note_type *ntype)
939 struct chanData *cData;
940 for(cData = channelList; cData; cData = cData->next)
941 dict_remove(cData->notes, ntype->name);
945 chanserv_truncate_notes(struct note_type *ntype)
947 struct chanData *cData;
949 unsigned int size = sizeof(*note) + ntype->max_length;
951 for(cData = channelList; cData; cData = cData->next) {
952 note = dict_find(cData->notes, ntype->name, NULL);
955 if(strlen(note->note) <= ntype->max_length)
957 dict_remove2(cData->notes, ntype->name, 1);
958 note = realloc(note, size);
959 note->note[ntype->max_length] = 0;
960 dict_insert(cData->notes, ntype->name, note);
964 static int note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user);
967 chanserv_add_channel_note(struct chanData *channel, struct note_type *type, const char *setter, const char *text)
970 unsigned int len = strlen(text);
972 if(len > type->max_length) len = type->max_length;
973 note = calloc(1, sizeof(*note) + len);
975 strncpy(note->setter, setter, sizeof(note->setter)-1);
976 memcpy(note->note, text, len);
978 dict_insert(channel->notes, type->name, note);
984 chanserv_free_note(void *data)
986 struct note *note = data;
988 chanserv_deref_note_type(note->type);
989 assert(note->type->refs > 0); /* must use delnote to remove the type */
993 static MODCMD_FUNC(cmd_createnote) {
994 struct note_type *ntype;
995 unsigned int arg = 1, existed = 0, max_length;
997 if((ntype = dict_find(note_types, argv[1], NULL)))
1000 ntype = chanserv_create_note_type(argv[arg]);
1001 if(!irccasecmp(argv[++arg], "privileged"))
1004 ntype->set_access_type = NOTE_SET_PRIVILEGED;
1005 ntype->set_access.min_opserv = strtoul(argv[arg], NULL, 0);
1007 else if(!irccasecmp(argv[arg], "channel"))
1009 unsigned short ulvl = user_level_from_name(argv[++arg], UL_OWNER);
1012 reply("CSMSG_INVALID_ACCESS", argv[arg]);
1015 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
1016 ntype->set_access.min_ulevel = ulvl;
1018 else if(!irccasecmp(argv[arg], "setter"))
1020 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
1024 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
1028 if(!irccasecmp(argv[++arg], "privileged"))
1029 ntype->visible_type = NOTE_VIS_PRIVILEGED;
1030 else if(!irccasecmp(argv[arg], "channel_users"))
1031 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
1032 else if(!irccasecmp(argv[arg], "all"))
1033 ntype->visible_type = NOTE_VIS_ALL;
1035 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
1039 if((arg+1) >= argc) {
1040 reply("MSG_MISSING_PARAMS", argv[0]);
1043 max_length = strtoul(argv[++arg], NULL, 0);
1044 if(max_length < 20 || max_length > 450)
1046 reply("CSMSG_BAD_MAX_LENGTH", argv[arg]);
1049 if(existed && (max_length < ntype->max_length))
1051 ntype->max_length = max_length;
1052 chanserv_truncate_notes(ntype);
1054 ntype->max_length = max_length;
1057 reply("CSMSG_NOTE_MODIFIED", ntype->name);
1059 reply("CSMSG_NOTE_CREATED", ntype->name);
1064 dict_remove(note_types, ntype->name);
1068 static MODCMD_FUNC(cmd_removenote) {
1069 struct note_type *ntype;
1072 ntype = dict_find(note_types, argv[1], NULL);
1073 force = (argc > 2) && !irccasecmp(argv[2], "force");
1076 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
1083 reply("CSMSG_NOTE_TYPE_USED", ntype->name);
1086 chanserv_flush_note_type(ntype);
1088 dict_remove(note_types, argv[1]);
1089 reply("CSMSG_NOTE_DELETED", argv[1]);
1094 chanserv_expire_channel(void *data)
1096 struct chanData *channel = data;
1097 char reason[MAXLEN];
1098 sprintf(reason, "channel expired.");
1099 channel->expiry = 0;
1100 spamserv_cs_unregister(NULL, channel->channel, expire, NULL);
1101 unregister_channel(channel, reason);
1104 static MODCMD_FUNC(chan_opt_expire)
1106 struct chanData *cData = channel->channel_info;
1107 unsigned long value = cData->expiry;
1111 if((!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
1113 reply("MSG_SETTING_PRIVILEGED", argv[0]);
1116 unsigned long expiry,duration;
1118 /* The two directions can have different ACLs. */
1119 if(!strcmp(argv[1], "0"))
1121 else if((duration = ParseInterval(argv[1])))
1122 expiry = now + duration;
1125 reply("MSG_INVALID_DURATION", argv[1]);
1129 if (expiry != value)
1133 timeq_del(value, chanserv_expire_channel, cData, 0);
1136 cData->expiry = value;
1139 timeq_add(expiry, chanserv_expire_channel, cData);
1144 if(cData->expiry > now) {
1145 char expirestr[INTERVALLEN];
1146 reply("CSMSG_SET_EXPIRE", intervalString(expirestr, cData->expiry - now, user->handle_info));
1148 reply("CSMSG_SET_EXPIRE_OFF");
1153 mode_lock_violated(const struct mod_chanmode *orig, const struct mod_chanmode *change)
1157 if(orig->modes_set & change->modes_clear)
1159 if(orig->modes_clear & change->modes_set)
1161 if((orig->modes_set & MODE_KEY) && (change->modes_set & MODE_KEY)
1162 && strcmp(orig->new_key, change->new_key))
1164 if((orig->modes_set & MODE_LIMIT) && (change->modes_set & MODE_LIMIT)
1165 && (orig->new_limit != change->new_limit))
1170 static char max_length_text[MAXLEN+1][16];
1172 static struct helpfile_expansion
1173 chanserv_expand_variable(const char *variable)
1175 struct helpfile_expansion exp;
1177 if(!irccasecmp(variable, "notes"))
1180 exp.type = HF_TABLE;
1181 exp.value.table.length = 1;
1182 exp.value.table.width = 3;
1183 exp.value.table.flags = 0;
1184 exp.value.table.contents = calloc(dict_size(note_types)+1, sizeof(char**));
1185 exp.value.table.contents[0] = calloc(exp.value.table.width, sizeof(char*));
1186 exp.value.table.contents[0][0] = "Note Type";
1187 exp.value.table.contents[0][1] = "Visibility";
1188 exp.value.table.contents[0][2] = "Max Length";
1189 for(it=dict_first(note_types); it; it=iter_next(it))
1191 struct note_type *ntype = iter_data(it);
1194 if(!note_type_visible_to_user(NULL, ntype, message_dest)) continue;
1195 row = exp.value.table.length++;
1196 exp.value.table.contents[row] = calloc(exp.value.table.width, sizeof(char*));
1197 exp.value.table.contents[row][0] = ntype->name;
1198 exp.value.table.contents[row][1] = (ntype->visible_type == NOTE_VIS_ALL) ? "all" :
1199 (ntype->visible_type == NOTE_VIS_CHANNEL_USERS) ? "chan users" :
1201 if(!max_length_text[ntype->max_length][0])
1202 snprintf(max_length_text[ntype->max_length], sizeof(max_length_text[ntype->max_length]), "%u", ntype->max_length);
1203 exp.value.table.contents[row][2] = max_length_text[ntype->max_length];
1208 exp.type = HF_STRING;
1209 exp.value.str = NULL;
1213 static struct chanData*
1214 register_channel(struct chanNode *cNode, char *registrar)
1216 struct chanData *channel;
1217 enum levelOption lvlOpt;
1218 enum charOption chOpt;
1220 channel = calloc(1, sizeof(struct chanData));
1222 channel->notes = dict_new();
1223 dict_set_free_data(channel->notes, chanserv_free_note);
1225 channel->registrar = strdup(registrar);
1226 channel->registered = now;
1227 channel->visited = now;
1228 channel->limitAdjusted = now;
1229 channel->ownerTransfer = now;
1230 channel->flags = CHANNEL_DEFAULT_FLAGS;
1231 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
1232 channel->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
1233 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
1234 channel->chOpts[chOpt] = charOptions[chOpt].default_value;
1236 channel->prev = NULL;
1237 channel->next = channelList;
1240 channelList->prev = channel;
1241 channelList = channel;
1242 registered_channels++;
1244 channel->channel = cNode;
1246 cNode->channel_info = channel;
1248 channel->vote = NULL;
1253 static struct userData*
1254 add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned short access_level, unsigned long seen, const char *info)
1256 struct userData *ud;
1258 if(access_level > UL_OWNER)
1261 ud = calloc(1, sizeof(*ud));
1262 ud->channel = channel;
1263 ud->handle = handle;
1265 ud->access = access_level;
1266 ud->info = info ? strdup(info) : NULL;
1269 ud->next = channel->users;
1271 channel->users->prev = ud;
1272 channel->users = ud;
1274 channel->userCount++;
1278 ud->u_next = ud->handle->channels;
1280 ud->u_next->u_prev = ud;
1281 ud->handle->channels = ud;
1287 del_channel_user(struct userData *user, int do_gc)
1289 struct chanData *channel = user->channel;
1291 channel->userCount--;
1295 user->prev->next = user->next;
1297 channel->users = user->next;
1299 user->next->prev = user->prev;
1302 user->u_prev->u_next = user->u_next;
1304 user->handle->channels = user->u_next;
1306 user->u_next->u_prev = user->u_prev;
1310 if(do_gc && !channel->users && !IsProtected(channel)) {
1311 spamserv_cs_unregister(NULL, channel->channel, lost_all_users, NULL);
1312 unregister_channel(channel, "lost all users.");
1316 static void expire_ban(void *data);
1319 add_channel_ban(struct chanData *channel, const char *mask, char *owner, unsigned long set, unsigned long triggered, unsigned long expires, char *reason)
1322 unsigned int ii, l1, l2;
1327 bd = malloc(sizeof(struct banData));
1329 bd->channel = channel;
1331 bd->triggered = triggered;
1332 bd->expires = expires;
1334 for(ii = 0; ii < chanserv_conf.old_ban_names->used; ++ii)
1336 extern const char *hidden_host_suffix;
1337 const char *old_name = chanserv_conf.old_ban_names->list[ii];
1341 l2 = strlen(old_name);
1344 if(irccasecmp(mask + l1 - l2, old_name))
1346 new_mask = alloca(MAXLEN);
1347 sprintf(new_mask, "%.*s%s", (int)(l1-l2), mask, hidden_host_suffix);
1350 safestrncpy(bd->mask, mask, sizeof(bd->mask));
1352 safestrncpy(bd->owner, owner, sizeof(bd->owner));
1353 bd->reason = strdup(reason);
1356 timeq_add(expires, expire_ban, bd);
1359 bd->next = channel->bans;
1361 channel->bans->prev = bd;
1363 channel->banCount++;
1370 del_channel_ban(struct banData *ban)
1372 ban->channel->banCount--;
1376 ban->prev->next = ban->next;
1378 ban->channel->bans = ban->next;
1381 ban->next->prev = ban->prev;
1384 timeq_del(0, expire_ban, ban, TIMEQ_IGNORE_WHEN);
1393 expire_ban(void *data)
1395 struct banData *bd = data;
1396 if(!IsSuspended(bd->channel))
1398 struct banList bans;
1399 struct mod_chanmode change;
1401 bans = bd->channel->channel->banlist;
1402 mod_chanmode_init(&change);
1403 for(ii=0; ii<bans.used; ii++)
1405 if(!strcmp(bans.list[ii]->ban, bd->mask))
1408 change.args[0].mode = MODE_REMOVE|MODE_BAN;
1409 change.args[0].u.hostmask = bd->mask;
1410 mod_chanmode_announce(chanserv, bd->channel->channel, &change);
1416 del_channel_ban(bd);
1419 static void chanserv_expire_suspension(void *data);
1422 unregister_channel(struct chanData *channel, const char *reason)
1424 struct mod_chanmode change;
1425 char msgbuf[MAXLEN];
1427 /* After channel unregistration, the following must be cleaned
1429 - Channel information.
1432 - Channel suspension data.
1433 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1439 timeq_del(0, NULL, channel, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
1443 mod_chanmode_init(&change);
1444 change.modes_clear |= MODE_REGISTERED;
1445 mod_chanmode_announce(chanserv, channel->channel, &change);
1448 while(channel->users)
1449 del_channel_user(channel->users, 0);
1451 while(channel->bans)
1452 del_channel_ban(channel->bans);
1454 free(channel->topic);
1455 free(channel->registrar);
1456 free(channel->greeting);
1457 free(channel->user_greeting);
1458 free(channel->topic_mask);
1461 channel->prev->next = channel->next;
1463 channelList = channel->next;
1466 channel->next->prev = channel->prev;
1468 if(channel->suspended)
1470 struct chanNode *cNode = channel->channel;
1471 struct suspended *suspended, *next_suspended;
1473 for(suspended = channel->suspended; suspended; suspended = next_suspended)
1475 next_suspended = suspended->previous;
1476 free(suspended->suspender);
1477 free(suspended->reason);
1478 if(suspended->expires)
1479 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
1484 cNode->channel_info = NULL;
1487 timeq_del(channel->expiry, chanserv_expire_channel, channel, 0);
1488 channel->channel->channel_info = NULL;
1490 dict_delete(channel->notes);
1491 sprintf(msgbuf, "%s %s", channel->channel->name, reason);
1492 if(!IsSuspended(channel))
1493 DelChannelUser(chanserv, channel->channel, msgbuf, 0);
1494 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, msgbuf);
1495 UnlockChannel(channel->channel);
1497 registered_channels--;
1501 expire_channels(UNUSED_ARG(void *data))
1503 struct chanData *channel, *next;
1504 struct userData *user;
1505 char delay[INTERVALLEN], reason[INTERVALLEN + 64];
1507 intervalString(delay, chanserv_conf.channel_expire_delay, NULL);
1508 sprintf(reason, "Channel registration automatically expired after %s of disuse.", delay);
1510 for(channel = channelList; channel; channel = next)
1512 next = channel->next;
1514 /* See if the channel can be expired. */
1515 if(((now - channel->visited) <= chanserv_conf.channel_expire_delay)
1516 || IsProtected(channel))
1519 /* Make sure there are no high-ranking users still in the channel. */
1520 for(user=channel->users; user; user=user->next)
1521 if(user->present && (user->access >= UL_PRESENT) && !HANDLE_FLAGGED(user->handle, BOT))
1526 /* Unregister the channel */
1527 log_module(CS_LOG, LOG_INFO, "(%s) Channel registration expired.", channel->channel->name);
1528 spamserv_cs_unregister(NULL, channel->channel, expire, NULL);
1529 unregister_channel(channel, "registration expired.");
1532 if(chanserv_conf.channel_expire_frequency)
1533 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
1537 expire_dnrs(UNUSED_ARG(void *data))
1539 dict_iterator_t it, next;
1540 struct do_not_register *dnr;
1542 for(it = dict_first(handle_dnrs); it; it = next)
1544 dnr = iter_data(it);
1545 next = iter_next(it);
1546 if(dnr->expires && dnr->expires <= now)
1547 dict_remove(handle_dnrs, dnr->chan_name + 1);
1549 for(it = dict_first(plain_dnrs); it; it = next)
1551 dnr = iter_data(it);
1552 next = iter_next(it);
1553 if(dnr->expires && dnr->expires <= now)
1554 dict_remove(plain_dnrs, dnr->chan_name + 1);
1556 for(it = dict_first(mask_dnrs); it; it = next)
1558 dnr = iter_data(it);
1559 next = iter_next(it);
1560 if(dnr->expires && dnr->expires <= now)
1561 dict_remove(mask_dnrs, dnr->chan_name + 1);
1564 if(chanserv_conf.dnr_expire_frequency)
1565 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
1569 protect_user(const struct userNode *victim, const struct userNode *aggressor, struct chanData *channel)
1571 char protect = channel->chOpts[chProtect];
1572 struct userData *cs_victim, *cs_aggressor;
1574 /* Don't protect if no one is to be protected, someone is attacking
1575 himself, or if the aggressor is an IRC Operator. */
1576 if(protect == 'n' || victim == aggressor || IsOper(aggressor))
1579 /* Don't protect if the victim isn't authenticated (because they
1580 can't be a channel user), unless we are to protect non-users
1582 cs_victim = GetChannelAccess(channel, victim->handle_info);
1583 if(protect != 'a' && !cs_victim)
1586 /* Protect if the aggressor isn't a user because at this point,
1587 the aggressor can only be less than or equal to the victim. */
1588 cs_aggressor = GetChannelAccess(channel, aggressor->handle_info);
1592 /* If the aggressor was a user, then the victim can't be helped. */
1599 if(cs_victim->access > cs_aggressor->access)
1604 if(cs_victim->access >= cs_aggressor->access)
1613 validate_op(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1615 struct chanData *cData = channel->channel_info;
1616 struct userData *cs_victim;
1618 if((!(cs_victim = GetChannelUser(cData, victim->handle_info))
1619 || (cs_victim->access < cData->lvlOpts[lvlGiveOps]))
1620 && !check_user_level(channel, user, lvlEnfOps, 0, 0))
1622 send_message(user, chanserv, "CSMSG_OPBY_LOCKED");
1630 validate_deop(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1632 if(IsService(victim))
1634 send_message(user, chanserv, "MSG_SERVICE_IMMUNE", victim->nick);
1638 if(protect_user(victim, user, channel->channel_info))
1640 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
1647 static struct do_not_register *
1648 chanserv_add_dnr(const char *chan_name, const char *setter, unsigned long expires, const char *reason)
1650 struct do_not_register *dnr = calloc(1, sizeof(*dnr)+strlen(reason));
1651 safestrncpy(dnr->chan_name, chan_name, sizeof(dnr->chan_name));
1652 safestrncpy(dnr->setter, setter, sizeof(dnr->setter));
1653 strcpy(dnr->reason, reason);
1655 dnr->expires = expires;
1656 if(dnr->chan_name[0] == '*')
1657 dict_insert(handle_dnrs, dnr->chan_name+1, dnr);
1658 else if(strpbrk(dnr->chan_name, "*?"))
1659 dict_insert(mask_dnrs, dnr->chan_name, dnr);
1661 dict_insert(plain_dnrs, dnr->chan_name, dnr);
1665 static struct dnrList
1666 chanserv_find_dnrs(const char *chan_name, const char *handle, unsigned int max)
1668 struct dnrList list;
1669 dict_iterator_t it, next;
1670 struct do_not_register *dnr;
1672 dnrList_init(&list);
1674 if(handle && (dnr = dict_find(handle_dnrs, handle, NULL)))
1676 if(dnr->expires && dnr->expires <= now)
1677 dict_remove(handle_dnrs, handle);
1678 else if(list.used < max)
1679 dnrList_append(&list, dnr);
1682 if(chan_name && (dnr = dict_find(plain_dnrs, chan_name, NULL)))
1684 if(dnr->expires && dnr->expires <= now)
1685 dict_remove(plain_dnrs, chan_name);
1686 else if(list.used < max)
1687 dnrList_append(&list, dnr);
1692 for(it = dict_first(mask_dnrs); it && list.used < max; it = next)
1694 next = iter_next(it);
1695 if(!match_ircglob(chan_name, iter_key(it)))
1697 dnr = iter_data(it);
1698 if(dnr->expires && dnr->expires <= now)
1699 dict_remove(mask_dnrs, iter_key(it));
1701 dnrList_append(&list, dnr);
1708 static int dnr_print_func(struct do_not_register *dnr, void *extra)
1710 struct userNode *user;
1711 char buf1[INTERVALLEN];
1712 char buf2[INTERVALLEN];
1719 strftime(buf1, sizeof(buf1), "%d %b %Y", localtime(&feh));
1724 strftime(buf2, sizeof(buf2), "%d %b %Y", localtime(&feh));
1725 send_message(user, chanserv, "CSMSG_DNR_INFO_SET_EXPIRES", dnr->chan_name, buf1, dnr->setter, buf2, dnr->reason);
1729 send_message(user, chanserv, "CSMSG_DNR_INFO_SET", dnr->chan_name, buf1, dnr->setter, dnr->reason);
1732 send_message(user, chanserv, "CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1737 chanserv_show_dnrs(struct userNode *user, struct svccmd *cmd, const char *chan_name, const char *handle)
1739 struct dnrList list;
1742 list = chanserv_find_dnrs(chan_name, handle, UINT_MAX);
1743 for(ii = 0; (ii < list.used) && (ii < 10); ++ii)
1744 dnr_print_func(list.list[ii], user);
1746 reply("CSMSG_MORE_DNRS", list.used - ii);
1751 struct do_not_register *
1752 chanserv_is_dnr(const char *chan_name, struct handle_info *handle)
1754 struct dnrList list;
1755 struct do_not_register *dnr;
1757 list = chanserv_find_dnrs(chan_name, handle ? handle->handle : NULL, 1);
1758 dnr = list.used ? list.list[0] : NULL;
1763 static unsigned int send_dnrs(struct userNode *user, dict_t dict)
1765 struct do_not_register *dnr;
1766 dict_iterator_t it, next;
1767 unsigned int matches = 0;
1769 for(it = dict_first(dict); it; it = next)
1771 dnr = iter_data(it);
1772 next = iter_next(it);
1773 if(dnr->expires && dnr->expires <= now)
1775 dict_remove(dict, iter_key(it));
1778 dnr_print_func(dnr, user);
1785 static CHANSERV_FUNC(cmd_noregister)
1789 unsigned long expiry, duration;
1790 unsigned int matches;
1794 reply("CSMSG_DNR_SEARCH_RESULTS");
1795 matches = send_dnrs(user, handle_dnrs);
1796 matches += send_dnrs(user, plain_dnrs);
1797 matches += send_dnrs(user, mask_dnrs);
1799 reply("MSG_MATCH_COUNT", matches);
1801 reply("MSG_NO_MATCHES");
1807 if(!IsChannelName(target) && (*target != '*'))
1809 reply("CSMSG_NOT_DNR", target);
1817 reply("MSG_INVALID_DURATION", argv[2]);
1821 if(!strcmp(argv[2], "0"))
1823 else if((duration = ParseInterval(argv[2])))
1824 expiry = now + duration;
1827 reply("MSG_INVALID_DURATION", argv[2]);
1831 reason = unsplit_string(argv + 3, argc - 3, NULL);
1832 if((*target == '*') && !get_handle_info(target + 1))
1834 reply("MSG_HANDLE_UNKNOWN", target + 1);
1837 chanserv_add_dnr(target, user->handle_info->handle, expiry, reason);
1838 reply("CSMSG_NOREGISTER_CHANNEL", target);
1842 reply("CSMSG_DNR_SEARCH_RESULTS");
1844 matches = chanserv_show_dnrs(user, cmd, NULL, target + 1);
1846 matches = chanserv_show_dnrs(user, cmd, target, NULL);
1848 reply("MSG_NO_MATCHES");
1852 static CHANSERV_FUNC(cmd_allowregister)
1854 const char *chan_name = argv[1];
1856 if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
1857 || dict_remove(plain_dnrs, chan_name)
1858 || dict_remove(mask_dnrs, chan_name))
1860 reply("CSMSG_DNR_REMOVED", chan_name);
1863 reply("CSMSG_NO_SUCH_DNR", chan_name);
1868 struct userNode *source;
1872 unsigned long min_set, max_set;
1873 unsigned long min_expires, max_expires;
1878 dnr_search_matches(const struct do_not_register *dnr, const struct dnr_search *search)
1880 return !((dnr->set < search->min_set)
1881 || (dnr->set > search->max_set)
1882 || (dnr->expires < search->min_expires)
1883 || (search->max_expires
1884 && ((dnr->expires == 0)
1885 || (dnr->expires > search->max_expires)))
1886 || (search->chan_mask
1887 && !match_ircglob(dnr->chan_name, search->chan_mask))
1888 || (search->setter_mask
1889 && !match_ircglob(dnr->setter, search->setter_mask))
1890 || (search->reason_mask
1891 && !match_ircglob(dnr->reason, search->reason_mask)));
1894 static struct dnr_search *
1895 dnr_search_create(struct userNode *user, struct svccmd *cmd, unsigned int argc, char *argv[])
1897 struct dnr_search *discrim;
1900 discrim = calloc(1, sizeof(*discrim));
1901 discrim->source = user;
1902 discrim->chan_mask = NULL;
1903 discrim->setter_mask = NULL;
1904 discrim->reason_mask = NULL;
1905 discrim->max_set = INT_MAX;
1906 discrim->limit = 50;
1908 for(ii=0; ii<argc; ++ii)
1912 reply("MSG_MISSING_PARAMS", argv[ii]);
1915 else if(0 == irccasecmp(argv[ii], "channel"))
1917 discrim->chan_mask = argv[++ii];
1919 else if(0 == irccasecmp(argv[ii], "setter"))
1921 discrim->setter_mask = argv[++ii];
1923 else if(0 == irccasecmp(argv[ii], "reason"))
1925 discrim->reason_mask = argv[++ii];
1927 else if(0 == irccasecmp(argv[ii], "limit"))
1929 discrim->limit = strtoul(argv[++ii], NULL, 0);
1931 else if(0 == irccasecmp(argv[ii], "set"))
1933 const char *cmp = argv[++ii];
1936 discrim->min_set = now - ParseInterval(cmp + 2);
1938 discrim->min_set = now - (ParseInterval(cmp + 1) - 1);
1939 } else if(cmp[0] == '=') {
1940 discrim->min_set = discrim->max_set = now - ParseInterval(cmp + 1);
1941 } else if(cmp[0] == '>') {
1943 discrim->max_set = now - ParseInterval(cmp + 2);
1945 discrim->max_set = now - (ParseInterval(cmp + 1) - 1);
1947 discrim->max_set = now - (ParseInterval(cmp) - 1);
1950 else if(0 == irccasecmp(argv[ii], "expires"))
1952 const char *cmp = argv[++ii];
1955 discrim->max_expires = now + ParseInterval(cmp + 2);
1957 discrim->max_expires = now + (ParseInterval(cmp + 1) - 1);
1958 } else if(cmp[0] == '=') {
1959 discrim->min_expires = discrim->max_expires = now + ParseInterval(cmp + 1);
1960 } else if(cmp[0] == '>') {
1962 discrim->min_expires = now + ParseInterval(cmp + 2);
1964 discrim->min_expires = now + (ParseInterval(cmp + 1) - 1);
1966 discrim->min_expires = now + (ParseInterval(cmp) - 1);
1971 reply("MSG_INVALID_CRITERIA", argv[ii]);
1982 typedef int (*dnr_search_func)(struct do_not_register *match, void *extra);
1985 dnr_search(struct dnr_search *discrim, dnr_search_func dsf, void *data)
1987 struct do_not_register *dnr;
1988 dict_iterator_t next;
1993 /* Initialize local variables. */
1996 if(discrim->chan_mask)
1998 int shift = (discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*') ? 2 : 0;
1999 if('\0' == discrim->chan_mask[shift + strcspn(discrim->chan_mask+shift, "*?")])
2003 if(target_fixed && discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*')
2005 /* Check against account-based DNRs. */
2006 dnr = dict_find(handle_dnrs, discrim->chan_mask + 2, NULL);
2007 if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
2010 else if(target_fixed)
2012 /* Check against channel-based DNRs. */
2013 dnr = dict_find(plain_dnrs, discrim->chan_mask, NULL);
2014 if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
2019 /* Exhaustively search account DNRs. */
2020 for(it = dict_first(handle_dnrs); it; it = next)
2022 next = iter_next(it);
2023 dnr = iter_data(it);
2024 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
2028 /* Do the same for channel DNRs. */
2029 for(it = dict_first(plain_dnrs); it; it = next)
2031 next = iter_next(it);
2032 dnr = iter_data(it);
2033 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
2037 /* Do the same for wildcarded channel DNRs. */
2038 for(it = dict_first(mask_dnrs); it; it = next)
2040 next = iter_next(it);
2041 dnr = iter_data(it);
2042 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
2050 dnr_remove_func(struct do_not_register *match, void *extra)
2052 struct userNode *user;
2055 chan_name = alloca(strlen(match->chan_name) + 1);
2056 strcpy(chan_name, match->chan_name);
2058 if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
2059 || dict_remove(plain_dnrs, chan_name)
2060 || dict_remove(mask_dnrs, chan_name))
2062 send_message(user, chanserv, "CSMSG_DNR_REMOVED", chan_name);
2068 dnr_count_func(struct do_not_register *match, void *extra)
2070 return 0; (void)match; (void)extra;
2073 static MODCMD_FUNC(cmd_dnrsearch)
2075 struct dnr_search *discrim;
2076 dnr_search_func action;
2077 struct svccmd *subcmd;
2078 unsigned int matches;
2081 sprintf(buf, "dnrsearch %s", argv[1]);
2082 subcmd = dict_find(cmd->parent->commands, buf, NULL);
2085 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
2088 if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
2090 if(!irccasecmp(argv[1], "print"))
2091 action = dnr_print_func;
2092 else if(!irccasecmp(argv[1], "remove"))
2093 action = dnr_remove_func;
2094 else if(!irccasecmp(argv[1], "count"))
2095 action = dnr_count_func;
2098 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
2102 discrim = dnr_search_create(user, cmd, argc-2, argv+2);
2106 if(action == dnr_print_func)
2107 reply("CSMSG_DNR_SEARCH_RESULTS");
2108 matches = dnr_search(discrim, action, user);
2110 reply("MSG_MATCH_COUNT", matches);
2112 reply("MSG_NO_MATCHES");
2118 chanserv_get_owned_count(struct handle_info *hi)
2120 struct userData *cList;
2123 for(owned=0, cList=hi->channels; cList; cList=cList->u_next)
2124 if(cList->access == UL_OWNER)
2129 static CHANSERV_FUNC(cmd_register)
2131 struct handle_info *handle;
2132 struct chanData *cData;
2133 struct modeNode *mn;
2134 char reason[MAXLEN];
2136 unsigned int new_channel, force=0;
2137 struct do_not_register *dnr;
2141 if(channel->channel_info)
2143 reply("CSMSG_ALREADY_REGGED", channel->name);
2147 if(channel->bad_channel)
2149 reply("CSMSG_ILLEGAL_CHANNEL", channel->name);
2154 && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
2156 reply("CSMSG_MUST_BE_OPPED", channel->name);
2161 chan_name = channel->name;
2165 if((argc < 2) || !IsChannelName(argv[1]))
2167 reply("MSG_NOT_CHANNEL_NAME");
2171 if(opserv_bad_channel(argv[1]))
2173 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2178 chan_name = argv[1];
2181 if(argc >= (new_channel+2))
2183 if(!IsHelping(user))
2185 reply("CSMSG_PROXY_FORBIDDEN");
2189 if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
2191 force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
2192 dnr = chanserv_is_dnr(chan_name, handle);
2196 handle = user->handle_info;
2197 dnr = chanserv_is_dnr(chan_name, handle);
2201 if(!IsHelping(user))
2202 reply("CSMSG_DNR_CHANNEL", chan_name);
2204 chanserv_show_dnrs(user, cmd, chan_name, handle->handle);
2208 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2210 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
2215 channel = AddChannel(argv[1], now, NULL, NULL);
2217 cData = register_channel(channel, user->handle_info->handle);
2218 scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL), NULL);
2219 cData->modes = chanserv_conf.default_modes;
2221 cData->modes.modes_set |= MODE_REGISTERED;
2222 if (IsOffChannel(cData))
2224 mod_chanmode_announce(chanserv, channel, &cData->modes);
2228 struct mod_chanmode *change = mod_chanmode_dup(&cData->modes, 1);
2229 change->args[change->argc].mode = MODE_CHANOP;
2230 change->args[change->argc].u.member = AddChannelUser(chanserv, channel);
2232 mod_chanmode_announce(chanserv, channel, change);
2233 mod_chanmode_free(change);
2236 /* Initialize the channel's max user record. */
2237 cData->max = channel->members.used;
2238 cData->max_time = 0;
2240 if(handle != user->handle_info)
2241 reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
2243 reply("CSMSG_REG_SUCCESS", channel->name);
2245 sprintf(reason, "%s registered to %s by %s.", channel->name, handle->handle, user->handle_info->handle);
2246 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2251 make_confirmation_string(struct userData *uData)
2253 static char strbuf[16];
2258 for(src = uData->handle->handle; *src; )
2259 accum = accum * 31 + toupper(*src++);
2261 for(src = uData->channel->channel->name; *src; )
2262 accum = accum * 31 + toupper(*src++);
2263 sprintf(strbuf, "%08x", accum);
2267 static CHANSERV_FUNC(cmd_unregister)
2270 char reason[MAXLEN];
2271 struct chanData *cData;
2272 struct userData *uData;
2274 cData = channel->channel_info;
2277 reply("CSMSG_NOT_REGISTERED", channel->name);
2281 uData = GetChannelUser(cData, user->handle_info);
2282 if(!uData || (uData->access < UL_OWNER))
2284 reply("CSMSG_NO_ACCESS");
2288 if(IsProtected(cData) && !IsOper(user))
2290 reply("CSMSG_UNREG_NODELETE", channel->name);
2294 if(!IsHelping(user))
2296 const char *confirm_string;
2297 if(IsSuspended(cData))
2299 reply("CSMSG_CHAN_SUSPENDED", channel->name, cData->suspended->reason);
2302 confirm_string = make_confirmation_string(uData);
2303 if((argc < 2) || strcmp(argv[1], confirm_string))
2305 reply("CSMSG_CONFIRM_UNREG", confirm_string);
2310 sprintf(reason, "unregistered by %s.", user->handle_info->handle);
2311 name = strdup(channel->name);
2312 unregister_channel(cData, reason);
2313 spamserv_cs_unregister(user, channel, manually, "unregistered");
2314 reply("CSMSG_UNREG_SUCCESS", name);
2320 ss_cs_join_channel(struct chanNode *channel, int spamserv_join)
2322 extern struct userNode *spamserv;
2323 struct mod_chanmode *change;
2325 if(spamserv && spamserv_join && get_chanInfo(channel->name))
2327 change = mod_chanmode_alloc(2);
2329 change->args[0].mode = MODE_CHANOP;
2330 change->args[0].u.member = AddChannelUser(chanserv, channel);
2331 change->args[1].mode = MODE_CHANOP;
2332 change->args[1].u.member = AddChannelUser(spamserv, channel);
2336 change = mod_chanmode_alloc(1);
2338 change->args[0].mode = MODE_CHANOP;
2339 change->args[0].u.member = AddChannelUser(chanserv, channel);
2342 mod_chanmode_announce(chanserv, channel, change);
2343 mod_chanmode_free(change);
2346 static CHANSERV_FUNC(cmd_move)
2348 struct mod_chanmode change;
2349 struct chanNode *target;
2350 struct modeNode *mn;
2351 struct userData *uData;
2352 char reason[MAXLEN];
2353 struct do_not_register *dnr;
2354 int chanserv_join = 0, spamserv_join;
2358 if(IsProtected(channel->channel_info))
2360 reply("CSMSG_MOVE_NODELETE", channel->name);
2364 if(!IsChannelName(argv[1]))
2366 reply("MSG_NOT_CHANNEL_NAME");
2370 if(opserv_bad_channel(argv[1]))
2372 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2376 if(!IsHelping(user) || (argc < 3) || irccasecmp(argv[2], "force"))
2378 for(uData = channel->channel_info->users; uData; uData = uData->next)
2380 if((uData->access == UL_OWNER) && (dnr = chanserv_is_dnr(argv[1], uData->handle)))
2382 if(!IsHelping(user))
2383 reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]);
2385 chanserv_show_dnrs(user, cmd, argv[1], uData->handle->handle);
2391 mod_chanmode_init(&change);
2392 if(!(target = GetChannel(argv[1])))
2394 target = AddChannel(argv[1], now, NULL, NULL);
2395 if(!IsSuspended(channel->channel_info))
2398 else if(target->channel_info)
2400 reply("CSMSG_ALREADY_REGGED", target->name);
2403 else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
2404 && !IsHelping(user))
2406 reply("CSMSG_MUST_BE_OPPED", target->name);
2409 else if(!IsSuspended(channel->channel_info))
2414 /* Clear MODE_REGISTERED from old channel, add it to new. */
2416 change.modes_clear = MODE_REGISTERED;
2417 mod_chanmode_announce(chanserv, channel, &change);
2418 change.modes_clear = 0;
2419 change.modes_set = MODE_REGISTERED;
2420 mod_chanmode_announce(chanserv, target, &change);
2423 /* Move the channel_info to the target channel; it
2424 shouldn't be necessary to clear timeq callbacks
2425 for the old channel. */
2426 target->channel_info = channel->channel_info;
2427 target->channel_info->channel = target;
2428 channel->channel_info = NULL;
2430 /* Check whether users are present in the new channel. */
2431 for(uData = target->channel_info->users; uData; uData = uData->next)
2432 scan_user_presence(uData, NULL);
2434 spamserv_join = spamserv_cs_move_merge(user, channel, target, 1);
2437 ss_cs_join_channel(target, spamserv_join);
2439 sprintf(reason, "%s moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
2440 if(!IsSuspended(target->channel_info))
2442 char reason2[MAXLEN];
2443 sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
2444 DelChannelUser(chanserv, channel, reason2, 0);
2446 UnlockChannel(channel);
2447 LockChannel(target);
2448 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2449 reply("CSMSG_MOVE_SUCCESS", target->name);
2454 merge_users(struct chanData *source, struct chanData *target)
2456 struct userData *suData, *tuData, *next;
2462 /* Insert the source's users into the scratch area. */
2463 for(suData = source->users; suData; suData = suData->next)
2464 dict_insert(merge, suData->handle->handle, suData);
2466 /* Iterate through the target's users, looking for
2467 users common to both channels. The lower access is
2468 removed from either the scratch area or target user
2470 for(tuData = target->users; tuData; tuData = next)
2472 struct userData *choice;
2474 next = tuData->next;
2476 /* If a source user exists with the same handle as a target
2477 channel's user, resolve the conflict by removing one. */
2478 suData = dict_find(merge, tuData->handle->handle, NULL);
2482 /* Pick the data we want to keep. */
2483 /* If the access is the same, use the later seen time. */
2484 if(suData->access == tuData->access)
2485 choice = (suData->seen > tuData->seen) ? suData : tuData;
2486 else /* Otherwise, keep the higher access level. */
2487 choice = (suData->access > tuData->access) ? suData : tuData;
2488 /* Use the later seen time. */
2489 if(suData->seen < tuData->seen)
2490 suData->seen = tuData->seen;
2492 tuData->seen = suData->seen;
2494 /* Remove the user that wasn't picked. */
2495 if(choice == tuData)
2497 dict_remove(merge, suData->handle->handle);
2498 del_channel_user(suData, 0);
2501 del_channel_user(tuData, 0);
2504 /* Move the remaining users to the target channel. */
2505 for(it = dict_first(merge); it; it = iter_next(it))
2507 suData = iter_data(it);
2509 /* Insert the user into the target channel's linked list. */
2510 suData->prev = NULL;
2511 suData->next = target->users;
2512 suData->channel = target;
2515 target->users->prev = suData;
2516 target->users = suData;
2518 /* Update the user counts for the target channel; the
2519 source counts are left alone. */
2520 target->userCount++;
2522 /* Check whether the user is in the target channel. */
2523 scan_user_presence(suData, NULL);
2526 /* Possible to assert (source->users == NULL) here. */
2527 source->users = NULL;
2532 merge_bans(struct chanData *source, struct chanData *target)
2534 struct banData *sbData, *tbData, *sNext, *tNext, *tFront;
2536 /* Hold on to the original head of the target ban list
2537 to avoid comparing source bans with source bans. */
2538 tFront = target->bans;
2540 /* Perform a totally expensive O(n*m) merge, ick. */
2541 for(sbData = source->bans; sbData; sbData = sNext)
2543 /* Flag to track whether the ban's been moved
2544 to the destination yet. */
2547 /* Possible to assert (sbData->prev == NULL) here. */
2548 sNext = sbData->next;
2550 for(tbData = tFront; tbData; tbData = tNext)
2552 tNext = tbData->next;
2554 /* Perform two comparisons between each source
2555 and target ban, conflicts are resolved by
2556 keeping the broader ban and copying the later
2557 expiration and triggered time. */
2558 if(match_ircglobs(tbData->mask, sbData->mask))
2560 /* There is a broader ban in the target channel that
2561 overrides one in the source channel; remove the
2562 source ban and break. */
2563 if(sbData->expires > tbData->expires)
2564 tbData->expires = sbData->expires;
2565 if(sbData->triggered > tbData->triggered)
2566 tbData->triggered = sbData->triggered;
2567 del_channel_ban(sbData);
2570 else if(match_ircglobs(sbData->mask, tbData->mask))
2572 /* There is a broader ban in the source channel that
2573 overrides one in the target channel; remove the
2574 target ban, fall through and move the source over. */
2575 if(tbData->expires > sbData->expires)
2576 sbData->expires = tbData->expires;
2577 if(tbData->triggered > sbData->triggered)
2578 sbData->triggered = tbData->triggered;
2579 if(tbData == tFront)
2581 del_channel_ban(tbData);
2584 /* Source bans can override multiple target bans, so
2585 we allow a source to run through this loop multiple
2586 times, but we can only move it once. */
2591 /* Remove the source ban from the source ban list. */
2593 sbData->next->prev = sbData->prev;
2595 /* Modify the source ban's associated channel. */
2596 sbData->channel = target;
2598 /* Insert the ban into the target channel's linked list. */
2599 sbData->prev = NULL;
2600 sbData->next = target->bans;
2603 target->bans->prev = sbData;
2604 target->bans = sbData;
2606 /* Update the user counts for the target channel. */
2611 /* Possible to assert (source->bans == NULL) here. */
2612 source->bans = NULL;
2616 merge_data(struct chanData *source, struct chanData *target)
2618 /* Use more recent visited and owner-transfer time; use older
2619 * registered time. Bitwise or may_opchan. Use higher max.
2620 * Do not touch last_refresh, ban count or user counts.
2622 if(source->visited > target->visited)
2623 target->visited = source->visited;
2624 if(source->registered < target->registered)
2625 target->registered = source->registered;
2626 if(source->ownerTransfer > target->ownerTransfer)
2627 target->ownerTransfer = source->ownerTransfer;
2628 if(source->may_opchan)
2629 target->may_opchan = 1;
2630 if(source->max > target->max) {
2631 target->max = source->max;
2632 target->max_time = source->max_time;
2637 merge_channel(struct chanData *source, struct chanData *target)
2639 merge_users(source, target);
2640 merge_bans(source, target);
2641 merge_data(source, target);
2644 static CHANSERV_FUNC(cmd_merge)
2646 struct userData *target_user;
2647 struct chanNode *target;
2648 char reason[MAXLEN];
2652 /* Make sure the target channel exists and is registered to the user
2653 performing the command. */
2654 if(!(target = GetChannel(argv[1])))
2656 reply("MSG_INVALID_CHANNEL");
2660 if(!target->channel_info)
2662 reply("CSMSG_NOT_REGISTERED", target->name);
2666 if(IsProtected(channel->channel_info))
2668 reply("CSMSG_MERGE_NODELETE");
2672 if(IsSuspended(target->channel_info))
2674 reply("CSMSG_MERGE_SUSPENDED");
2678 if(channel == target)
2680 reply("CSMSG_MERGE_SELF");
2684 target_user = GetChannelUser(target->channel_info, user->handle_info);
2685 if(!target_user || (target_user->access < UL_OWNER))
2687 reply("CSMSG_MERGE_NOT_OWNER");
2691 /* Merge the channel structures and associated data. */
2692 merge_channel(channel->channel_info, target->channel_info);
2693 spamserv_cs_move_merge(user, channel, target, 0);
2694 sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
2695 unregister_channel(channel->channel_info, reason);
2696 reply("CSMSG_MERGE_SUCCESS", target->name);
2700 static CHANSERV_FUNC(cmd_opchan)
2702 struct mod_chanmode change;
2703 if(!IsHelping(user) && !channel->channel_info->may_opchan)
2705 reply("CSMSG_ALREADY_OPCHANNED", channel->name);
2708 channel->channel_info->may_opchan = 0;
2709 mod_chanmode_init(&change);
2711 change.args[0].mode = MODE_CHANOP;
2712 change.args[0].u.member = GetUserMode(channel, chanserv);
2713 if(!change.args[0].u.member)
2715 reply("CSMSG_OUT_OF_CHANNEL", channel->name);
2718 mod_chanmode_announce(chanserv, channel, &change);
2719 reply("CSMSG_OPCHAN_DONE", channel->name);
2723 static CHANSERV_FUNC(cmd_adduser)
2725 struct userData *actee;
2726 struct userData *actor, *real_actor;
2727 struct handle_info *handle;
2728 unsigned short access_level, override = 0;
2732 if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
2734 reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
2738 access_level = user_level_from_name(argv[2], UL_OWNER);
2741 reply("CSMSG_INVALID_ACCESS", argv[2]);
2745 actor = GetChannelUser(channel->channel_info, user->handle_info);
2746 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2748 if(actor->access <= access_level)
2750 reply("CSMSG_NO_BUMP_ACCESS");
2754 /* Trying to add someone with equal/more access? */
2755 if (!real_actor || real_actor->access <= access_level)
2756 override = CMD_LOG_OVERRIDE;
2758 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2761 if((actee = GetTrueChannelAccess(channel->channel_info, handle)))
2763 reply("CSMSG_USER_EXISTS", handle->handle, channel->name, actee->access);
2767 actee = add_channel_user(channel->channel_info, handle, access_level, 0, NULL);
2768 scan_user_presence(actee, NULL);
2769 reply("CSMSG_ADDED_USER", handle->handle, channel->name, access_level);
2770 return 1 | override;
2773 static CHANSERV_FUNC(cmd_clvl)
2775 struct handle_info *handle;
2776 struct userData *victim;
2777 struct userData *actor, *real_actor;
2778 unsigned short new_access, override = 0;
2779 int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
2783 actor = GetChannelUser(channel->channel_info, user->handle_info);
2784 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2786 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2789 if(handle == user->handle_info && !privileged)
2791 reply("CSMSG_NO_SELF_CLVL");
2795 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2797 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2801 if(actor->access <= victim->access && !privileged)
2803 reply("MSG_USER_OUTRANKED", handle->handle);
2807 new_access = user_level_from_name(argv[2], UL_OWNER);
2811 reply("CSMSG_INVALID_ACCESS", argv[2]);
2815 if(new_access >= actor->access && !privileged)
2817 reply("CSMSG_NO_BUMP_ACCESS");
2821 /* Trying to clvl a equal/higher user? */
2822 if(!real_actor || (real_actor->access <= victim->access && handle != user->handle_info))
2823 override = CMD_LOG_OVERRIDE;
2824 /* Trying to clvl someone to equal/higher access? */
2825 if(!real_actor || new_access >= real_actor->access)
2826 override = CMD_LOG_OVERRIDE;
2827 /* Helpers clvling themselves get caught by the "clvl someone to equal/higher access" check.
2828 * If they lower their own access it's not a big problem.
2831 victim->access = new_access;
2832 reply("CSMSG_CHANGED_ACCESS", handle->handle, new_access, channel->name);
2833 return 1 | override;
2836 static CHANSERV_FUNC(cmd_deluser)
2838 struct handle_info *handle;
2839 struct userData *victim;
2840 struct userData *actor, *real_actor;
2841 unsigned short access_level, override = 0;
2846 actor = GetChannelUser(channel->channel_info, user->handle_info);
2847 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2849 if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
2852 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2854 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2860 access_level = user_level_from_name(argv[1], UL_OWNER);
2863 reply("CSMSG_INVALID_ACCESS", argv[1]);
2866 if(access_level != victim->access)
2868 reply("CSMSG_INCORRECT_ACCESS", handle->handle, victim->access, argv[1]);
2874 access_level = victim->access;
2877 if((actor->access <= victim->access) && !IsHelping(user))
2879 reply("MSG_USER_OUTRANKED", victim->handle->handle);
2883 /* If people delete themselves it is an override, but they
2884 * could've used deleteme so we don't log it as an override
2886 if(!real_actor || (real_actor->access <= victim->access && real_actor != victim))
2887 override = CMD_LOG_OVERRIDE;
2889 chan_name = strdup(channel->name);
2890 del_channel_user(victim, 1);
2891 reply("CSMSG_DELETED_USER", handle->handle, access_level, chan_name);
2893 return 1 | override;
2897 cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
2899 struct userData *actor, *real_actor, *uData, *next;
2900 unsigned int override = 0;
2902 actor = GetChannelUser(channel->channel_info, user->handle_info);
2903 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2905 if(min_access > max_access)
2907 reply("CSMSG_BAD_RANGE", min_access, max_access);
2911 if(actor->access <= max_access)
2913 reply("CSMSG_NO_ACCESS");
2917 if(!real_actor || real_actor->access <= max_access)
2918 override = CMD_LOG_OVERRIDE;
2920 for(uData = channel->channel_info->users; uData; uData = next)
2924 if((uData->access >= min_access)
2925 && (uData->access <= max_access)
2926 && match_ircglob(uData->handle->handle, mask))
2927 del_channel_user(uData, 1);
2930 reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
2931 return 1 | override;
2934 static CHANSERV_FUNC(cmd_mdelowner)
2936 return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
2939 static CHANSERV_FUNC(cmd_mdelcoowner)
2941 return cmd_mdel_user(user, channel, UL_COOWNER, UL_COOWNER, argv[1], cmd);
2944 static CHANSERV_FUNC(cmd_mdelmaster)
2946 return cmd_mdel_user(user, channel, UL_MASTER, UL_MASTER, argv[1], cmd);
2949 static CHANSERV_FUNC(cmd_mdelop)
2951 return cmd_mdel_user(user, channel, UL_OP, UL_OP, argv[1], cmd);
2954 static CHANSERV_FUNC(cmd_mdelpeon)
2956 return cmd_mdel_user(user, channel, UL_PEON, UL_PEON, argv[1], cmd);
2960 cmd_trim_bans(struct userNode *user, struct chanNode *channel, unsigned long duration)
2962 struct banData *bData, *next;
2963 char interval[INTERVALLEN];
2965 unsigned long limit;
2968 limit = now - duration;
2969 for(bData = channel->channel_info->bans; bData; bData = next)
2973 if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
2976 del_channel_ban(bData);
2980 intervalString(interval, duration, user->handle_info);
2981 send_message(user, chanserv, "CSMSG_TRIMMED_BANS", count, channel->name, interval);
2986 cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration, int vacation)
2988 struct userData *actor, *uData, *next;
2989 char interval[INTERVALLEN];
2991 unsigned long limit;
2993 actor = GetChannelAccess(channel->channel_info, user->handle_info);
2994 if(min_access > max_access)
2996 send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
3000 if(!actor || actor->access <= max_access)
3002 send_message(user, chanserv, "CSMSG_NO_ACCESS");
3007 limit = now - duration;
3008 for(uData = channel->channel_info->users; uData; uData = next)
3012 if((uData->seen > limit)
3014 || (HANDLE_FLAGGED(uData->handle, FROZEN) && !vacation))
3017 if(((uData->access >= min_access) && (uData->access <= max_access))
3018 || (!max_access && (uData->access < actor->access)))
3020 del_channel_user(uData, 1);
3028 max_access = (actor->access > UL_OWNER) ? UL_OWNER : (actor->access - 1);
3030 send_message(user, chanserv, "CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration, user->handle_info));
3034 static CHANSERV_FUNC(cmd_trim)
3036 unsigned long duration;
3037 unsigned short min_level, max_level;
3042 vacation = argc > 3 && !strcmp(argv[3], "vacation");
3043 duration = ParseInterval(argv[2]);
3046 reply("CSMSG_CANNOT_TRIM");
3050 if(!irccasecmp(argv[1], "bans"))
3052 cmd_trim_bans(user, channel, duration);
3055 else if(!irccasecmp(argv[1], "users"))
3057 cmd_trim_users(user, channel, 0, 0, duration, vacation);
3060 else if(parse_level_range(&min_level, &max_level, argv[1]))
3062 cmd_trim_users(user, channel, min_level, max_level, duration, vacation);
3065 else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
3067 cmd_trim_users(user, channel, min_level, min_level, duration, vacation);
3072 reply("CSMSG_INVALID_TRIM", argv[1]);
3077 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
3078 to the user. cmd_all takes advantage of this. */
3079 static CHANSERV_FUNC(cmd_up)
3081 struct mod_chanmode change;
3082 struct userData *uData;
3085 mod_chanmode_init(&change);
3087 change.args[0].u.member = GetUserMode(channel, user);
3088 if(!change.args[0].u.member)
3091 reply("MSG_CHANNEL_ABSENT", channel->name);
3095 uData = GetChannelAccess(channel->channel_info, user->handle_info);
3099 reply("CSMSG_GODMODE_UP", argv[0]);
3102 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveOps])
3104 change.args[0].mode = MODE_CHANOP;
3105 errmsg = "CSMSG_ALREADY_OPPED";
3107 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveVoice])
3109 change.args[0].mode = MODE_VOICE;
3110 errmsg = "CSMSG_ALREADY_VOICED";
3115 reply("CSMSG_NO_ACCESS");
3118 change.args[0].mode &= ~change.args[0].u.member->modes;
3119 if(!change.args[0].mode)
3122 reply(errmsg, channel->name);
3125 modcmd_chanmode_announce(&change);
3129 static CHANSERV_FUNC(cmd_down)
3131 struct mod_chanmode change;
3133 mod_chanmode_init(&change);
3135 change.args[0].u.member = GetUserMode(channel, user);
3136 if(!change.args[0].u.member)
3139 reply("MSG_CHANNEL_ABSENT", channel->name);
3143 if(!change.args[0].u.member->modes)
3146 reply("CSMSG_ALREADY_DOWN", channel->name);
3150 change.args[0].mode = MODE_REMOVE | change.args[0].u.member->modes;
3151 modcmd_chanmode_announce(&change);
3155 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)
3157 struct userData *cList;
3159 for(cList = user->handle_info->channels; cList; cList = cList->u_next)
3161 if(IsSuspended(cList->channel)
3162 || IsUserSuspended(cList)
3163 || !GetUserMode(cList->channel->channel, user))
3166 mcmd(user, cList->channel->channel, 0, NULL, cmd);
3172 static CHANSERV_FUNC(cmd_upall)
3174 return cmd_all(CSFUNC_ARGS, cmd_up);
3177 static CHANSERV_FUNC(cmd_downall)
3179 return cmd_all(CSFUNC_ARGS, cmd_down);
3182 typedef int validate_func_t(struct userNode *user, struct chanNode *channel, struct userNode *victim);
3183 typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
3186 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)
3188 unsigned int ii, valid;
3189 struct userNode *victim;
3190 struct mod_chanmode *change;
3192 change = mod_chanmode_alloc(argc - 1);
3194 for(ii=valid=0; ++ii < argc; )
3196 if(!(victim = GetUserH(argv[ii])))
3198 change->args[valid].mode = mode;
3199 change->args[valid].u.member = GetUserMode(channel, victim);
3200 if(!change->args[valid].u.member)
3202 if(validate && !validate(user, channel, victim))
3207 change->argc = valid;
3208 if(valid < (argc-1))
3209 reply("CSMSG_PROCESS_FAILED");
3212 modcmd_chanmode_announce(change);
3213 reply(action, channel->name);
3215 mod_chanmode_free(change);
3219 static CHANSERV_FUNC(cmd_op)
3221 return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
3224 static CHANSERV_FUNC(cmd_deop)
3226 return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
3229 static CHANSERV_FUNC(cmd_voice)
3231 return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
3234 static CHANSERV_FUNC(cmd_devoice)
3236 return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
3239 static CHANSERV_FUNC(cmd_opme)
3241 struct mod_chanmode change;
3244 mod_chanmode_init(&change);
3246 change.args[0].u.member = GetUserMode(channel, user);
3247 if(!change.args[0].u.member)
3250 reply("MSG_CHANNEL_ABSENT", channel->name);
3254 struct devnull_class *devnull;
3255 if(user->handle_info->devnull && (devnull = devnull_get(user->handle_info->devnull)) && (devnull->modes & DEVNULL_MODE_OPME))
3257 change.args[0].mode = MODE_CHANOP;
3258 errmsg = "CSMSG_ALREADY_OPPED";
3263 reply("CSMSG_NO_ACCESS");
3266 change.args[0].mode &= ~change.args[0].u.member->modes;
3267 if(!change.args[0].mode)
3270 reply(errmsg, channel->name);
3273 modcmd_chanmode_announce(&change);
3278 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, unsigned int *victimCount, struct modeNode **victims)
3284 for(ii=0; ii<channel->members.used; ii++)
3286 struct modeNode *mn = channel->members.list[ii];
3288 if(IsService(mn->user))
3291 if(!user_matches_glob(mn->user, ban, MATCH_USENICK | MATCH_VISIBLE))
3294 if(protect_user(mn->user, user, channel->channel_info))
3298 victims[(*victimCount)++] = mn;
3304 eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3306 struct userNode *victim;
3307 struct modeNode **victims;
3308 unsigned int offset, n, victimCount, duration = 0;
3309 char *reason = "Bye.", *ban, *name;
3310 char interval[INTERVALLEN];
3312 offset = (action & ACTION_ADD_TIMED_BAN) ? 3 : 2;
3313 REQUIRE_PARAMS(offset);
3314 if(argc > offset && IsNetServ(user))
3316 if(*argv[offset] == '$') {
3317 struct userNode *hib;
3318 const char *accountnameb = argv[offset] + 1;
3319 if(!(hib = GetUserH(accountnameb)))
3321 reply("MSG_HANDLE_UNKNOWN", accountnameb);
3330 reason = unsplit_string(argv + offset, argc - offset, NULL);
3331 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
3333 /* Truncate the reason to a length of TOPICLEN, as
3334 the ircd does; however, leave room for an ellipsis
3335 and the kicker's nick. */
3336 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
3340 if((victim = GetUserH(argv[1])))
3342 victims = alloca(sizeof(victims[0]));
3343 victims[0] = GetUserMode(channel, victim);
3344 /* XXX: The comparison with ACTION_KICK is just because all
3345 * other actions can work on users outside the channel, and we
3346 * want to allow those (e.g. unbans) in that case. If we add
3347 * some other ejection action for in-channel users, change
3349 victimCount = victims[0] ? 1 : 0;
3351 if(IsService(victim))
3353 reply("MSG_SERVICE_IMMUNE", victim->nick);
3357 if((action == ACTION_KICK) && !victimCount)
3359 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
3363 if(protect_user(victim, user, channel->channel_info))
3365 reply("CSMSG_USER_PROTECTED", victim->nick);
3369 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
3370 name = victim->nick;
3372 else if(!is_ircmask(argv[1]) && (*argv[1] == '*'))
3374 struct handle_info *hi;
3375 extern const char *titlehost_suffix;
3376 char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
3377 const char *accountname = argv[1] + 1;
3379 if(!(hi = get_handle_info(accountname)))
3381 reply("MSG_HANDLE_UNKNOWN", accountname);
3385 snprintf(banmask, sizeof(banmask), "*!*@%s.*.%s", hi->handle, titlehost_suffix);
3386 victims = alloca(sizeof(victims[0]) * channel->members.used);
3388 if(bad_channel_ban(channel, user, banmask, &victimCount, victims))
3390 reply("CSMSG_MASK_PROTECTED", banmask);
3394 if((action == ACTION_KICK) && (victimCount == 0))
3396 reply("CSMSG_NO_MATCHING_USERS", channel->name, banmask);
3400 name = ban = strdup(banmask);
3404 if(!is_ircmask(argv[1]))
3406 reply("MSG_NICK_UNKNOWN", argv[1]);
3410 victims = alloca(sizeof(victims[0]) * channel->members.used);
3412 if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
3414 reply("CSMSG_MASK_PROTECTED", argv[1]);
3418 if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
3420 reply("CSMSG_LAME_MASK", argv[1]);
3424 if((action == ACTION_KICK) && (victimCount == 0))
3426 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
3430 name = ban = strdup(argv[1]);
3433 /* Truncate the ban in place if necessary; we must ensure
3434 that 'ban' is a valid ban mask before sanitizing it. */
3435 sanitize_ircmask(ban);
3437 if(action & ACTION_ADD_BAN)
3439 struct banData *bData, *next;
3441 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans)
3443 reply("CSMSG_MAXIMUM_BANS", chanserv_conf.max_chan_bans);
3448 if(action & ACTION_ADD_TIMED_BAN)
3450 duration = ParseInterval(argv[2]);
3454 reply("CSMSG_DURATION_TOO_LOW");
3458 else if(duration > (86400 * 365 * 2))
3460 reply("CSMSG_DURATION_TOO_HIGH");
3466 for(bData = channel->channel_info->bans; bData; bData = next)
3468 if(match_ircglobs(bData->mask, ban))
3470 int exact = !irccasecmp(bData->mask, ban);
3472 /* The ban is redundant; there is already a ban
3473 with the same effect in place. */
3477 free(bData->reason);
3478 bData->reason = strdup(reason);
3479 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
3481 reply("CSMSG_REASON_CHANGE", ban);
3485 if(exact && bData->expires)
3489 /* If the ban matches an existing one exactly,
3490 extend the expiration time if the provided
3491 duration is longer. */
3492 if(duration && (now + duration > bData->expires))
3494 bData->expires = now + duration;
3505 /* Delete the expiration timeq entry and
3506 requeue if necessary. */
3507 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
3510 timeq_add(bData->expires, expire_ban, bData);
3514 /* automated kickban */
3517 reply("CSMSG_BAN_EXTENDED", ban, intervalString(interval, duration, user->handle_info));
3519 reply("CSMSG_BAN_ADDED", name, channel->name);
3525 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3532 if(match_ircglobs(ban, bData->mask))
3534 /* The ban we are adding makes previously existing
3535 bans redundant; silently remove them. */
3536 del_channel_ban(bData);
3540 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);
3542 name = ban = strdup(bData->mask);
3546 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
3548 extern const char *hidden_host_suffix;
3549 const char *old_name = chanserv_conf.old_ban_names->list[n];
3551 unsigned int l1, l2;
3554 l2 = strlen(old_name);
3557 if(irccasecmp(ban + l1 - l2, old_name))
3559 new_mask = malloc(MAXLEN);
3560 sprintf(new_mask, "%.*s%s", (int)(l1-l2), ban, hidden_host_suffix);
3562 name = ban = new_mask;
3567 if(action & ACTION_BAN)
3569 unsigned int exists;
3570 struct mod_chanmode *change;
3572 if(channel->banlist.used >= MAXBANS)
3575 reply("CSMSG_BANLIST_FULL", channel->name);
3580 exists = ChannelBanExists(channel, ban);
3581 change = mod_chanmode_alloc(victimCount + 1);
3582 for(n = 0; n < victimCount; ++n)
3584 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_VOICE;
3585 change->args[n].u.member = victims[n];
3589 change->args[n].mode = MODE_BAN;
3590 change->args[n++].u.hostmask = ban;
3594 modcmd_chanmode_announce(change);
3596 mod_chanmode_announce(chanserv, channel, change);
3597 mod_chanmode_free(change);
3599 if(exists && (action == ACTION_BAN))
3602 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3608 if(action & ACTION_KICK)
3610 char kick_reason[MAXLEN];
3611 sprintf(kick_reason, "(%s) %s", user->nick, reason);
3613 for(n = 0; n < victimCount; n++)
3614 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
3619 /* No response, since it was automated. */
3621 else if(action & ACTION_ADD_BAN)
3624 reply("CSMSG_TIMED_BAN_ADDED", name, channel->name, intervalString(interval, duration, user->handle_info));
3626 reply("CSMSG_BAN_ADDED", name, channel->name);
3628 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
3629 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
3630 else if(action & ACTION_BAN)
3631 reply("CSMSG_BAN_DONE", name, channel->name);
3632 else if(action & ACTION_KICK && victimCount)
3633 reply("CSMSG_KICK_DONE", name, channel->name);
3639 static CHANSERV_FUNC(cmd_kickban)
3641 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
3644 static CHANSERV_FUNC(cmd_kick)
3646 return eject_user(CSFUNC_ARGS, ACTION_KICK);
3649 static CHANSERV_FUNC(cmd_ban)
3651 return eject_user(CSFUNC_ARGS, ACTION_BAN);
3654 static CHANSERV_FUNC(cmd_addban)
3656 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN);
3659 static CHANSERV_FUNC(cmd_addtimedban)
3661 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN);
3664 struct mod_chanmode *
3665 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
3667 struct mod_chanmode *change;
3668 unsigned char *match;
3669 unsigned int ii, count;
3671 match = alloca(bans->used);
3674 for(ii = count = 0; ii < bans->used; ++ii)
3676 match[ii] = user_matches_glob(actee, bans->list[ii]->ban,
3677 MATCH_USENICK | MATCH_VISIBLE);
3684 for(ii = count = 0; ii < bans->used; ++ii)
3686 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
3693 change = mod_chanmode_alloc(count);
3694 for(ii = count = 0; ii < bans->used; ++ii)
3698 change->args[count].mode = MODE_REMOVE | MODE_BAN;
3699 change->args[count++].u.hostmask = strdup(bans->list[ii]->ban);
3701 assert(count == change->argc);
3706 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3708 struct userNode *actee;
3714 /* may want to allow a comma delimited list of users... */
3715 if(!(actee = GetUserH(argv[1])))
3717 if(!is_ircmask(argv[1]) && *argv[1] == '*')
3719 char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
3720 const char *accountname = argv[1] + 1;
3722 snprintf(banmask, sizeof(banmask), "*!*@%s.*", accountname);
3723 mask = strdup(banmask);
3725 else if(!is_ircmask(argv[1]))
3727 reply("MSG_NICK_UNKNOWN", argv[1]);
3732 mask = strdup(argv[1]);
3736 /* We don't sanitize the mask here because ircu
3738 if(action & ACTION_UNBAN)
3740 struct mod_chanmode *change;
3741 change = find_matching_bans(&channel->banlist, actee, mask);
3746 modcmd_chanmode_announce(change);
3747 for(ii = 0; ii < change->argc; ++ii)
3748 free((char*)change->args[ii].u.hostmask);
3749 mod_chanmode_free(change);
3754 if(action & ACTION_DEL_BAN)
3756 struct banData *ban, *next;
3758 ban = channel->channel_info->bans;
3762 for( ; ban && !user_matches_glob(actee, ban->mask,
3763 MATCH_USENICK | MATCH_VISIBLE);
3766 for( ; ban && !match_ircglobs(mask, ban->mask);
3771 del_channel_ban(ban);
3778 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3780 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3786 static CHANSERV_FUNC(cmd_unban)
3788 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3791 static CHANSERV_FUNC(cmd_delban)
3793 /* it doesn't necessarily have to remove the channel ban - may want
3794 to make that an option. */
3795 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_BAN);
3798 static CHANSERV_FUNC(cmd_unbanme)
3800 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3801 long flags = ACTION_UNBAN;
3803 /* remove permanent bans if the user has the proper access. */
3804 if(uData->access >= UL_MASTER)
3805 flags |= ACTION_DEL_BAN;
3807 argv[1] = user->nick;
3808 return unban_user(user, channel, 2, argv, cmd, flags);
3811 static CHANSERV_FUNC(cmd_unbanall)
3813 struct mod_chanmode *change;
3816 if(!channel->banlist.used)
3818 reply("CSMSG_NO_BANS", channel->name);
3822 change = mod_chanmode_alloc(channel->banlist.used);
3823 for(ii=0; ii<channel->banlist.used; ii++)
3825 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3826 change->args[ii].u.hostmask = strdup(channel->banlist.list[ii]->ban);
3828 modcmd_chanmode_announce(change);
3829 for(ii = 0; ii < change->argc; ++ii)
3830 free((char*)change->args[ii].u.hostmask);
3831 mod_chanmode_free(change);
3832 reply("CSMSG_BANS_REMOVED", channel->name);
3836 static CHANSERV_FUNC(cmd_open)
3838 struct mod_chanmode *change;
3841 change = find_matching_bans(&channel->banlist, user, NULL);
3843 change = mod_chanmode_alloc(0);
3844 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3845 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3846 && channel->channel_info->modes.modes_set)
3847 change->modes_clear &= ~channel->channel_info->modes.modes_set;
3848 modcmd_chanmode_announce(change);
3849 reply("CSMSG_CHANNEL_OPENED", channel->name);
3850 for(ii = 0; ii < change->argc; ++ii)
3851 free((char*)change->args[ii].u.hostmask);
3852 mod_chanmode_free(change);
3856 static CHANSERV_FUNC(cmd_myaccess)
3858 static struct string_buffer sbuf;
3859 struct handle_info *target_handle;
3860 struct userData *uData;
3865 target_handle = user->handle_info;
3866 else if(!IsStaff(user))
3868 reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
3871 else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3874 if(!oper_outranks(user, target_handle))
3877 if(!target_handle->channels)
3879 reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
3883 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3884 for(uData = target_handle->channels; uData; uData = uData->u_next)
3886 struct chanData *cData = uData->channel;
3889 if(uData->access > UL_OWNER)
3891 if(uData->access == UL_OWNER)
3894 if(IsProtected(cData)
3895 && (target_handle != user->handle_info)
3896 && !GetTrueChannelAccess(cData, user->handle_info)
3897 && !IsNetworkHelper(user))
3900 string_buffer_append_printf(&sbuf, "[%s (%d", cData->channel->name, uData->access);
3901 if(uData->flags != USER_AUTO_OP)
3902 string_buffer_append(&sbuf, ',');
3903 if(IsUserSuspended(uData))
3904 string_buffer_append(&sbuf, 's');
3905 if(IsUserAutoOp(uData))
3907 if(uData->access >= cData->lvlOpts[lvlGiveOps])
3908 string_buffer_append(&sbuf, 'o');
3909 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
3910 string_buffer_append(&sbuf, 'v');
3912 if(IsUserAutoInvite(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe]))
3913 string_buffer_append(&sbuf, 'i');
3915 string_buffer_append_printf(&sbuf, ")] %s", uData->info);
3917 string_buffer_append_string(&sbuf, ")]");
3918 string_buffer_append(&sbuf, '\0');
3919 send_message_type(4, user, cmd->parent->bot, "%s", sbuf.list);
3923 reply("CSMSG_MYACCESS_COUNT_1", target_handle->handle, ccount, ocount);
3925 reply("CSMSG_MYACCESS_COUNT", target_handle->handle, ccount, ocount);
3931 static CHANSERV_FUNC(cmd_access)
3933 struct userNode *target;
3934 struct handle_info *target_handle;
3935 struct userData *uData;
3937 char prefix[MAXLEN];
3942 target_handle = target->handle_info;
3944 else if((target = GetUserH(argv[1])))
3946 target_handle = target->handle_info;
3948 else if(argv[1][0] == '*')
3950 if(!(target_handle = get_handle_info(argv[1]+1)))
3952 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3958 reply("MSG_NICK_UNKNOWN", argv[1]);
3962 assert(target || target_handle);
3964 if(target == chanserv)
3966 reply("CSMSG_IS_CHANSERV");
3974 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
3979 reply("MSG_USER_AUTHENTICATE", target->nick);
3982 reply("MSG_AUTHENTICATE");
3988 const char *epithet = NULL, *type = NULL;
3991 epithet = chanserv_conf.irc_operator_epithet;
3992 type = user_find_message(user, "CSMSG_OPERATOR_TITLE");
3994 else if(IsNetworkHelper(target))
3996 epithet = chanserv_conf.network_helper_epithet;
3997 type = user_find_message(user, "CSMSG_UC_H_TITLE");
3999 else if(IsSupportHelper(target))
4001 epithet = chanserv_conf.support_helper_epithet;
4002 type = user_find_message(user, "CSMSG_LC_H_TITLE");
4006 if(target_handle->epithet)
4007 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
4009 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
4011 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
4015 sprintf(prefix, "%s", target_handle->handle);
4018 if(!channel->channel_info)
4020 reply("CSMSG_NOT_REGISTERED", channel->name);
4024 helping = HANDLE_FLAGGED(target_handle, HELPING)
4025 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
4026 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
4028 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
4029 /* To prevent possible information leaks, only show infolines
4030 * if the requestor is in the channel or it's their own
4032 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
4034 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
4036 /* Likewise, only say it's suspended if the user has active
4037 * access in that channel or it's their own entry. */
4038 if(IsUserSuspended(uData)
4039 && (GetChannelUser(channel->channel_info, user->handle_info)
4040 || (user->handle_info == uData->handle)))
4042 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
4047 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
4054 zoot_list(struct listData *list)
4056 struct userData *uData;
4057 unsigned int start, curr, highest, lowest;
4058 struct helpfile_table tmp_table;
4059 const char **temp, *msg;
4061 if(list->table.length == 1)
4064 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
4066 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
4067 msg = user_find_message(list->user, "MSG_NONE");
4068 send_message_type(4, list->user, list->bot, " %s", msg);
4070 tmp_table.width = list->table.width;
4071 tmp_table.flags = list->table.flags;
4072 list->table.contents[0][0] = " ";
4073 highest = list->highest;
4074 if(list->lowest != 0)
4075 lowest = list->lowest;
4076 else if(highest < 100)
4079 lowest = highest - 100;
4080 for(start = curr = 1; curr < list->table.length; )
4082 uData = list->users[curr-1];
4083 list->table.contents[curr++][0] = " ";
4084 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
4087 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, lowest, highest, list->search);
4089 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, lowest, highest);
4090 temp = list->table.contents[--start];
4091 list->table.contents[start] = list->table.contents[0];
4092 tmp_table.contents = list->table.contents + start;
4093 tmp_table.length = curr - start;
4094 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
4095 list->table.contents[start] = temp;
4097 highest = lowest - 1;
4098 lowest = (highest < 100) ? 0 : (highest - 99);
4104 def_list(struct listData *list)
4108 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
4110 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
4111 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4112 if(list->table.length == 1)
4114 msg = user_find_message(list->user, "MSG_NONE");
4115 send_message_type(4, list->user, list->bot, " %s", msg);
4120 userData_access_comp(const void *arg_a, const void *arg_b)
4122 const struct userData *a = *(struct userData**)arg_a;
4123 const struct userData *b = *(struct userData**)arg_b;
4125 if(a->access != b->access)
4126 res = b->access - a->access;
4128 res = irccasecmp(a->handle->handle, b->handle->handle);
4133 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
4135 void (*send_list)(struct listData *);
4136 struct userData *uData;
4137 struct listData lData;
4138 unsigned int matches;
4142 lData.bot = cmd->parent->bot;
4143 lData.channel = channel;
4144 lData.lowest = lowest;
4145 lData.highest = highest;
4146 lData.search = (argc > 1) ? argv[1] : NULL;
4147 send_list = def_list;
4148 (void)zoot_list; /* since it doesn't show user levels */
4150 if(user->handle_info)
4152 switch(user->handle_info->userlist_style)
4154 case HI_STYLE_DEF: send_list = def_list; break;
4155 case HI_STYLE_ZOOT: send_list = def_list; break;
4159 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
4161 for(uData = channel->channel_info->users; uData; uData = uData->next)
4163 if((uData->access < lowest)
4164 || (uData->access > highest)
4165 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
4167 lData.users[matches++] = uData;
4169 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
4171 lData.table.length = matches+1;
4172 lData.table.width = 4;
4173 lData.table.flags = TABLE_NO_FREE;
4174 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
4175 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
4176 lData.table.contents[0] = ary;
4179 ary[2] = "Last Seen";
4181 for(matches = 1; matches < lData.table.length; ++matches)
4183 char seen[INTERVALLEN];
4185 uData = lData.users[matches-1];
4186 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
4187 lData.table.contents[matches] = ary;
4188 ary[0] = strtab(uData->access);
4189 ary[1] = uData->handle->handle;
4192 else if(!uData->seen)
4195 ary[2] = intervalString(seen, now - uData->seen, user->handle_info);
4196 ary[2] = strdup(ary[2]);
4197 if(IsUserSuspended(uData))
4198 ary[3] = "Suspended";
4199 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
4200 ary[3] = "Vacation";
4201 else if(HANDLE_FLAGGED(uData->handle, BOT))
4207 for(matches = 1; matches < lData.table.length; ++matches)
4209 free((char*)lData.table.contents[matches][2]);
4210 free(lData.table.contents[matches]);
4212 free(lData.table.contents[0]);
4213 free(lData.table.contents);
4214 reply("CSMSG_TOTAL_USERS",(lData.table.length - 1),channel->name);
4218 static CHANSERV_FUNC(cmd_users)
4220 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
4223 static CHANSERV_FUNC(cmd_wlist)
4225 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
4228 static CHANSERV_FUNC(cmd_clist)
4230 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
4233 static CHANSERV_FUNC(cmd_mlist)
4235 return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
4238 static CHANSERV_FUNC(cmd_olist)
4240 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
4243 static CHANSERV_FUNC(cmd_plist)
4245 return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
4248 static CHANSERV_FUNC(cmd_bans)
4250 struct userNode *search_u = NULL;
4251 struct helpfile_table tbl;
4252 unsigned int matches = 0, timed = 0, search_wilds = 0, ii;
4253 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
4254 const char *msg_never, *triggered, *expires;
4255 struct banData *ban, **bans;
4259 else if(strchr(search = argv[1], '!'))
4262 search_wilds = search[strcspn(search, "?*")];
4264 else if(!(search_u = GetUserH(search)))
4265 reply("MSG_NICK_UNKNOWN", search);
4267 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
4269 for(ban = channel->channel_info->bans; ban; ban = ban->next)
4273 if(!user_matches_glob(search_u, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
4278 if(search_wilds ? !match_ircglobs(search, ban->mask) : !match_ircglob(search, ban->mask))
4281 bans[matches++] = ban;
4286 tbl.length = matches + 1;
4287 tbl.width = 4 + timed;
4289 tbl.flags = TABLE_NO_FREE;
4290 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
4291 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4292 tbl.contents[0][0] = "Mask";
4293 tbl.contents[0][1] = "Set By";
4294 tbl.contents[0][2] = "Triggered";
4297 tbl.contents[0][3] = "Expires";
4298 tbl.contents[0][4] = "Reason";
4301 tbl.contents[0][3] = "Reason";
4304 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4306 free(tbl.contents[0]);
4311 msg_never = user_find_message(user, "MSG_NEVER");
4312 for(ii = 0; ii < matches; )
4318 else if(ban->expires)
4319 expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
4321 expires = msg_never;
4324 triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
4326 triggered = msg_never;
4328 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4329 tbl.contents[ii][0] = ban->mask;
4330 tbl.contents[ii][1] = ban->owner;
4331 tbl.contents[ii][2] = strdup(triggered);
4334 tbl.contents[ii][3] = strdup(expires);
4335 tbl.contents[ii][4] = ban->reason;
4338 tbl.contents[ii][3] = ban->reason;
4340 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4341 reply("MSG_MATCH_COUNT", matches);
4342 for(ii = 1; ii < tbl.length; ++ii)
4344 free((char*)tbl.contents[ii][2]);
4346 free((char*)tbl.contents[ii][3]);
4347 free(tbl.contents[ii]);
4349 free(tbl.contents[0]);
4355 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
4357 struct chanData *cData = channel->channel_info;
4358 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
4360 if(cData->topic_mask)
4361 return !match_ircglob(new_topic, cData->topic_mask);
4362 else if(cData->topic)
4363 return irccasecmp(new_topic, cData->topic);
4368 static CHANSERV_FUNC(cmd_topic)
4370 struct chanData *cData;
4373 cData = channel->channel_info;
4378 SetChannelTopic(channel, chanserv, cData->topic, 1);
4379 reply("CSMSG_TOPIC_SET", cData->topic);
4383 reply("CSMSG_NO_TOPIC", channel->name);
4387 topic = unsplit_string(argv + 1, argc - 1, NULL);
4388 /* If they say "!topic *", use an empty topic. */
4389 if((topic[0] == '*') && (topic[1] == 0))
4391 if(bad_topic(channel, user, topic))
4393 char *topic_mask = cData->topic_mask;
4396 char new_topic[TOPICLEN+1], tchar;
4397 int pos=0, starpos=-1, dpos=0, len;
4399 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
4406 len = strlen(topic);
4407 if((dpos + len) > TOPICLEN)
4408 len = TOPICLEN + 1 - dpos;
4409 memcpy(new_topic+dpos, topic, len);
4413 case '\\': tchar = topic_mask[pos++]; /* and fall through */
4414 default: new_topic[dpos++] = tchar; break;
4417 if((dpos > TOPICLEN) || tchar)
4420 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
4421 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
4424 new_topic[dpos] = 0;
4425 SetChannelTopic(channel, chanserv, new_topic, 1);
4427 reply("CSMSG_TOPIC_LOCKED", channel->name);
4432 SetChannelTopic(channel, chanserv, topic, 1);
4434 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
4436 /* Grab the topic and save it as the default topic. */
4438 cData->topic = strdup(channel->topic);
4444 static CHANSERV_FUNC(cmd_mode)
4446 struct userData *uData;
4447 struct mod_chanmode *change;
4453 change = &channel->channel_info->modes;
4454 if(change->modes_set || change->modes_clear) {
4455 modcmd_chanmode_announce(change);
4456 reply("CSMSG_DEFAULTED_MODES", channel->name);
4458 reply("CSMSG_NO_MODES", channel->name);
4462 uData = GetChannelUser(channel->channel_info, user->handle_info);
4464 base_oplevel = MAXOPLEVEL;
4465 else if (uData->access >= UL_OWNER)
4468 base_oplevel = 1 + UL_OWNER - uData->access;
4469 change = mod_chanmode_parse(channel, user, argv+1, argc-1, MCP_KEY_FREE|MCP_IGN_REGISTERED|MCP_NO_APASS, base_oplevel);
4472 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
4476 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
4477 && mode_lock_violated(&channel->channel_info->modes, change))
4480 mod_chanmode_format(&channel->channel_info->modes, modes);
4481 reply("CSMSG_MODE_LOCKED", modes, channel->name);
4485 modcmd_chanmode_announce(change);
4486 mod_chanmode_format(change, fmt);
4487 mod_chanmode_free(change);
4488 reply("CSMSG_MODES_SET", fmt);
4493 chanserv_del_invite_mark(void *data)
4495 struct ChanUser *chanuser = data;
4496 struct chanNode *channel = chanuser->chan;
4498 if(!channel) return;
4499 for(i = 0; i < channel->invited.used; i++)
4501 if(channel->invited.list[i] == chanuser->user) {
4502 userList_remove(&channel->invited, chanuser->user);
4508 static CHANSERV_FUNC(cmd_invite)
4510 struct userData *uData;
4511 struct userNode *invite;
4512 struct ChanUser *chanuser;
4515 uData = GetChannelUser(channel->channel_info, user->handle_info);
4519 if(!(invite = GetUserH(argv[1])))
4521 reply("MSG_NICK_UNKNOWN", argv[1]);
4528 if(GetUserMode(channel, invite))
4530 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
4534 for(i = 0; i < channel->invited.used; i++)
4536 if(channel->invited.list[i] == invite) {
4537 reply("CSMSG_ALREADY_INVITED", invite->nick, channel->name);
4546 char *reason = unsplit_string(argv + 2, argc - 2, NULL);
4547 send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
4550 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
4552 irc_invite(chanserv, invite, channel);
4554 reply("CSMSG_INVITED_USER", argv[1], channel->name);
4556 userList_append(&channel->invited, invite);
4557 chanuser = calloc(1, sizeof(*chanuser));
4558 chanuser->user=invite;
4559 chanuser->chan=channel;
4560 timeq_add(now + chanserv_conf.invited_timeout, chanserv_del_invite_mark, chanuser);
4565 static CHANSERV_FUNC(cmd_inviteme)
4567 if(GetUserMode(channel, user))
4569 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
4572 if(channel->channel_info
4573 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
4575 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
4578 irc_invite(cmd->parent->bot, user, channel);
4582 static CHANSERV_FUNC(cmd_invitemeall)
4584 struct handle_info *target = user->handle_info;
4585 struct userData *uData;
4587 if(!target->channels)
4589 reply("CSMSG_SQUAT_ACCESS", target->handle);
4593 for(uData = target->channels; uData; uData = uData->u_next)
4595 struct chanData *cData = uData->channel;
4596 if(uData->access >= cData->lvlOpts[lvlInviteMe])
4598 irc_invite(cmd->parent->bot, user, cData->channel);
4605 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
4608 char buf1[INTERVALLEN], buf2[INTERVALLEN];
4610 /* We display things based on two dimensions:
4611 * - Issue time: present or absent
4612 * - Expiration: revoked, expired, expires in future, or indefinite expiration
4613 * (in order of precedence, so something both expired and revoked
4614 * only counts as revoked)
4616 combo = (suspended->issued ? 4 : 0)
4617 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
4619 case 0: /* no issue time, indefinite expiration */
4620 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
4622 case 1: /* no issue time, expires in future */
4623 intervalString(buf1, suspended->expires-now, user->handle_info);
4624 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
4626 case 2: /* no issue time, expired */
4627 intervalString(buf1, now-suspended->expires, user->handle_info);
4628 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
4630 case 3: /* no issue time, revoked */
4631 intervalString(buf1, now-suspended->revoked, user->handle_info);
4632 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
4634 case 4: /* issue time set, indefinite expiration */
4635 intervalString(buf1, now-suspended->issued, user->handle_info);
4636 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
4638 case 5: /* issue time set, expires in future */
4639 intervalString(buf1, now-suspended->issued, user->handle_info);
4640 intervalString(buf2, suspended->expires-now, user->handle_info);
4641 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
4643 case 6: /* issue time set, expired */
4644 intervalString(buf1, now-suspended->issued, user->handle_info);
4645 intervalString(buf2, now-suspended->expires, user->handle_info);
4646 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
4648 case 7: /* issue time set, revoked */
4649 intervalString(buf1, now-suspended->issued, user->handle_info);
4650 intervalString(buf2, now-suspended->revoked, user->handle_info);
4651 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
4654 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
4659 static CHANSERV_FUNC(cmd_info)
4661 char modes[MAXLEN], buffer[INTERVALLEN];
4662 struct userData *uData, *owner;
4663 struct chanData *cData;
4664 struct do_not_register *dnr;
4669 cData = channel->channel_info;
4670 reply("CSMSG_CHANNEL_INFO", channel->name);
4672 uData = GetChannelUser(cData, user->handle_info);
4673 if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
4675 mod_chanmode_format(&cData->modes, modes);
4676 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
4677 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
4680 for(it = dict_first(cData->notes); it; it = iter_next(it))
4684 note = iter_data(it);
4685 if(!note_type_visible_to_user(cData, note->type, user))
4688 padding = PADLEN - 1 - strlen(iter_key(it));
4689 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
4692 if(cData->max_time) {
4693 reply("CSMSG_CHANNEL_MAX_TIME", cData->max, intervalString(buffer, now - cData->max_time, user->handle_info));
4695 reply("CSMSG_CHANNEL_MAX", cData->max);
4697 for(owner = cData->users; owner; owner = owner->next)
4698 if(owner->access == UL_OWNER)
4699 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
4700 reply("CSMSG_CHANNEL_USERS", cData->userCount);
4701 reply("CSMSG_CHANNEL_BANS", cData->banCount);
4702 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
4704 privileged = IsStaff(user);
4706 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
4707 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
4708 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
4710 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
4711 chanserv_show_dnrs(user, cmd, channel->name, NULL);
4713 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
4715 struct suspended *suspended;
4716 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
4717 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
4718 show_suspension_info(cmd, user, suspended);
4720 else if(IsSuspended(cData))
4722 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
4723 show_suspension_info(cmd, user, cData->suspended);
4728 static CHANSERV_FUNC(cmd_netinfo)
4730 extern unsigned long boot_time;
4731 extern unsigned long burst_length;
4732 char interval[INTERVALLEN];
4734 reply("CSMSG_NETWORK_INFO");
4735 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
4736 reply("CSMSG_NETWORK_USERS", dict_size(clients));
4737 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
4738 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
4739 reply("CSMSG_NETWORK_BANS", banCount);
4740 reply("CSMSG_NETWORK_CHANUSERS", userCount);
4741 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
4742 reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
4747 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
4749 struct helpfile_table table;
4751 struct userNode *user;
4756 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4757 table.contents = alloca(list->used*sizeof(*table.contents));
4758 for(nn=0; nn<list->used; nn++)
4760 user = list->list[nn];
4761 if(user->modes & skip_flags)
4767 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
4770 nick = alloca(strlen(user->nick)+3);
4771 sprintf(nick, "(%s)", user->nick);
4775 table.contents[table.length][0] = nick;
4778 table_send(chanserv, to->nick, 0, NULL, table);
4781 static CHANSERV_FUNC(cmd_ircops)
4783 reply("CSMSG_STAFF_OPERS");
4784 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
4788 static CHANSERV_FUNC(cmd_helpers)
4790 reply("CSMSG_STAFF_HELPERS");
4791 send_staff_list(user, &curr_helpers, FLAGS_OPER);
4795 static CHANSERV_FUNC(cmd_staff)
4797 reply("CSMSG_NETWORK_STAFF");
4798 cmd_ircops(CSFUNC_ARGS);
4799 cmd_helpers(CSFUNC_ARGS);
4803 static CHANSERV_FUNC(cmd_peek)
4805 struct modeNode *mn;
4806 char modes[MODELEN];
4808 struct helpfile_table table;
4809 int opcount = 0, voicecount = 0, srvcount = 0;
4811 irc_make_chanmode(channel, modes);
4813 reply("CSMSG_PEEK_INFO", channel->name);
4814 reply("CSMSG_PEEK_TOPIC", channel->topic);
4815 reply("CSMSG_PEEK_MODES", modes);
4819 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4820 table.contents = alloca(channel->members.used*sizeof(*table.contents));
4821 for(n = 0; n < channel->members.used; n++)
4823 mn = channel->members.list[n];
4824 if(IsLocal(mn->user))
4826 else if(mn->modes & MODE_CHANOP)
4828 else if(mn->modes & MODE_VOICE)
4831 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
4833 table.contents[table.length] = alloca(sizeof(**table.contents));
4834 table.contents[table.length][0] = mn->user->nick;
4838 reply("CSMSG_PEEK_USERS", channel->members.used, opcount, voicecount,
4839 (channel->members.used - opcount - voicecount - srvcount));
4843 reply("CSMSG_PEEK_OPS");
4844 table_send(chanserv, user->nick, 0, NULL, table);
4847 reply("CSMSG_PEEK_NO_OPS");
4851 static MODCMD_FUNC(cmd_wipeinfo)
4853 struct handle_info *victim;
4854 struct userData *ud, *actor, *real_actor;
4855 unsigned int override = 0;
4858 actor = GetChannelUser(channel->channel_info, user->handle_info);
4859 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
4860 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4862 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4864 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4867 if((ud->access >= actor->access) && (ud != actor))
4869 reply("MSG_USER_OUTRANKED", victim->handle);
4872 if((ud != real_actor) && (!real_actor || (ud->access >= real_actor->access)))
4873 override = CMD_LOG_OVERRIDE;
4877 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4878 return 1 | override;
4881 static CHANSERV_FUNC(cmd_resync)
4883 struct mod_chanmode *changes;
4884 struct chanData *cData = channel->channel_info;
4885 unsigned int ii, used;
4887 changes = mod_chanmode_alloc(channel->members.used * 2);
4888 for(ii = used = 0; ii < channel->members.used; ++ii)
4890 struct modeNode *mn = channel->members.list[ii];
4891 struct userData *uData;
4893 if(IsService(mn->user))
4896 uData = GetChannelAccess(cData, mn->user->handle_info);
4897 if(!cData->lvlOpts[lvlGiveOps]
4898 || (uData && uData->access >= cData->lvlOpts[lvlGiveOps]))
4900 if(!(mn->modes & MODE_CHANOP))
4902 if(!uData || IsUserAutoOp(uData))
4904 changes->args[used].mode = MODE_CHANOP;
4905 changes->args[used++].u.member = mn;
4906 if(!(mn->modes & MODE_VOICE))
4908 changes->args[used].mode = MODE_VOICE;
4909 changes->args[used++].u.member = mn;
4914 else if(!cData->lvlOpts[lvlGiveVoice]
4915 || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice]))
4917 if(mn->modes & MODE_CHANOP)
4919 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4920 changes->args[used++].u.member = mn;
4922 if(!(mn->modes & MODE_VOICE) && (!uData || IsUserAutoOp(uData)))
4924 changes->args[used].mode = MODE_VOICE;
4925 changes->args[used++].u.member = mn;
4932 changes->args[used].mode = MODE_REMOVE | mn->modes;
4933 changes->args[used++].u.member = mn;
4937 changes->argc = used;
4938 modcmd_chanmode_announce(changes);
4939 mod_chanmode_free(changes);
4940 reply("CSMSG_RESYNCED_USERS", channel->name);
4944 static CHANSERV_FUNC(cmd_seen)
4946 struct userData *uData;
4947 struct handle_info *handle;
4948 char seen[INTERVALLEN];
4952 if(!irccasecmp(argv[1], chanserv->nick))
4954 reply("CSMSG_IS_CHANSERV");
4958 if(!(handle = get_handle_info(argv[1])))
4960 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4964 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4966 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4971 reply("CSMSG_USER_PRESENT", handle->handle);
4972 else if(uData->seen)
4973 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
4975 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4977 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4978 reply("CSMSG_USER_VACATION", handle->handle);
4983 static MODCMD_FUNC(cmd_names)
4985 struct userNode *targ;
4986 struct userData *targData;
4987 unsigned int ii, pos;
4990 for(ii=pos=0; ii<channel->members.used; ++ii)
4992 targ = channel->members.list[ii]->user;
4993 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4996 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
4999 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
5003 if(IsUserSuspended(targData))
5005 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
5008 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
5009 reply("CSMSG_END_NAMES", channel->name);
5014 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
5016 switch(ntype->visible_type)
5018 case NOTE_VIS_ALL: return 1;
5019 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
5020 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
5025 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
5027 struct userData *uData;
5029 switch(ntype->set_access_type)
5031 case NOTE_SET_CHANNEL_ACCESS:
5032 if(!user->handle_info)
5034 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
5036 return uData->access >= ntype->set_access.min_ulevel;
5037 case NOTE_SET_CHANNEL_SETTER:
5038 return check_user_level(channel, user, lvlSetters, 1, 0);
5039 case NOTE_SET_PRIVILEGED: default:
5040 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
5044 static CHANSERV_FUNC(cmd_note)
5046 struct chanData *cData;
5048 struct note_type *ntype;
5050 cData = channel->channel_info;
5053 reply("CSMSG_NOT_REGISTERED", channel->name);
5057 /* If no arguments, show all visible notes for the channel. */
5063 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
5065 note = iter_data(it);
5066 if(!note_type_visible_to_user(cData, note->type, user))
5069 reply("CSMSG_NOTELIST_HEADER", channel->name);
5070 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
5073 reply("CSMSG_NOTELIST_END", channel->name);
5075 reply("CSMSG_NOTELIST_EMPTY", channel->name);
5077 /* If one argument, show the named note. */
5080 if((note = dict_find(cData->notes, argv[1], NULL))
5081 && note_type_visible_to_user(cData, note->type, user))
5083 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
5085 else if((ntype = dict_find(note_types, argv[1], NULL))
5086 && note_type_visible_to_user(NULL, ntype, user))
5088 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
5093 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
5097 /* Assume they're trying to set a note. */
5101 ntype = dict_find(note_types, argv[1], NULL);
5104 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
5107 else if(note_type_settable_by_user(channel, ntype, user))
5109 note_text = unsplit_string(argv+2, argc-2, NULL);
5110 if((note = dict_find(cData->notes, argv[1], NULL)))
5111 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
5112 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
5113 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
5115 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
5117 /* The note is viewable to staff only, so return 0
5118 to keep the invocation from getting logged (or
5119 regular users can see it in !events). */
5125 reply("CSMSG_NO_ACCESS");
5132 static CHANSERV_FUNC(cmd_delnote)
5137 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
5138 || !note_type_settable_by_user(channel, note->type, user))
5140 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
5143 dict_remove(channel->channel_info->notes, note->type->name);
5144 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
5148 static CHANSERV_FUNC(cmd_events)
5150 struct logSearch discrim;
5151 struct logReport report;
5152 unsigned int matches, limit;
5154 limit = (argc > 1) ? atoi(argv[1]) : 10;
5155 if(limit < 1 || limit > 200)
5158 memset(&discrim, 0, sizeof(discrim));
5159 discrim.masks.bot = chanserv;
5160 discrim.masks.channel_name = channel->name;
5162 discrim.masks.command = argv[2];
5163 discrim.limit = limit;
5164 discrim.max_time = INT_MAX;
5165 discrim.severities = 1 << LOG_COMMAND;
5166 report.reporter = chanserv;
5168 reply("CSMSG_EVENT_SEARCH_RESULTS");
5169 matches = log_entry_search(&discrim, log_report_entry, &report);
5171 reply("MSG_MATCH_COUNT", matches);
5173 reply("MSG_NO_MATCHES");
5177 static CHANSERV_FUNC(cmd_say)
5183 msg = unsplit_string(argv + 1, argc - 1, NULL);
5184 send_channel_message(channel, cmd->parent->bot, "%s", msg);
5186 else if(*argv[1] == '*' && argv[1][1] != '\0')
5188 struct handle_info *hi;
5189 struct userNode *authed;
5192 msg = unsplit_string(argv + 2, argc - 2, NULL);
5194 if (!(hi = get_handle_info(argv[1] + 1)))
5196 reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
5200 for (authed = hi->users; authed; authed = authed->next_authed)
5201 send_target_message(5, authed->nick, cmd->parent->bot, "%s", msg);
5203 else if(GetUserH(argv[1]))
5206 msg = unsplit_string(argv + 2, argc - 2, NULL);
5207 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
5211 reply("MSG_NOT_TARGET_NAME");
5217 static CHANSERV_FUNC(cmd_emote)
5223 /* CTCP is so annoying. */
5224 msg = unsplit_string(argv + 1, argc - 1, NULL);
5225 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
5227 else if(*argv[1] == '*' && argv[1][1] != '\0')
5229 struct handle_info *hi;
5230 struct userNode *authed;
5233 msg = unsplit_string(argv + 2, argc - 2, NULL);
5235 if (!(hi = get_handle_info(argv[1] + 1)))
5237 reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
5241 for (authed = hi->users; authed; authed = authed->next_authed)
5242 send_target_message(5, authed->nick, cmd->parent->bot, "\001ACTION %s\001", msg);
5244 else if(GetUserH(argv[1]))
5246 msg = unsplit_string(argv + 2, argc - 2, NULL);
5247 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
5251 reply("MSG_NOT_TARGET_NAME");
5257 struct channelList *
5258 chanserv_support_channels(void)
5260 return &chanserv_conf.support_channels;
5263 static CHANSERV_FUNC(cmd_expire)
5265 int channel_count = registered_channels;
5266 expire_channels(NULL);
5267 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
5272 chanserv_expire_suspension(void *data)
5274 struct suspended *suspended = data;
5275 struct chanNode *channel;
5278 /* Update the channel registration data structure. */
5279 if(!suspended->expires || (now < suspended->expires))
5280 suspended->revoked = now;
5281 channel = suspended->cData->channel;
5282 suspended->cData->channel = channel;
5283 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
5285 /* If appropriate, re-join ChanServ to the channel. */
5286 if(!IsOffChannel(suspended->cData))
5288 spamserv_cs_suspend(channel, 0, 0, NULL);
5289 ss_cs_join_channel(channel, 1);
5292 /* Mark everyone currently in the channel as present. */
5293 for(ii = 0; ii < channel->members.used; ++ii)
5295 struct userData *uData = GetChannelAccess(suspended->cData, channel->members.list[ii]->user->handle_info);
5304 static CHANSERV_FUNC(cmd_csuspend)
5306 struct suspended *suspended;
5307 char reason[MAXLEN];
5308 unsigned long expiry, duration;
5309 struct userData *uData;
5313 if(IsProtected(channel->channel_info))
5315 reply("CSMSG_SUSPEND_NODELETE", channel->name);
5319 if(argv[1][0] == '!')
5321 else if(IsSuspended(channel->channel_info))
5323 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
5324 show_suspension_info(cmd, user, channel->channel_info->suspended);
5328 if(!strcmp(argv[1], "0"))
5330 else if((duration = ParseInterval(argv[1])))
5331 expiry = now + duration;
5334 reply("MSG_INVALID_DURATION", argv[1]);
5338 unsplit_string(argv + 2, argc - 2, reason);
5340 suspended = calloc(1, sizeof(*suspended));
5341 suspended->revoked = 0;
5342 suspended->issued = now;
5343 suspended->suspender = strdup(user->handle_info->handle);
5344 suspended->expires = expiry;
5345 suspended->reason = strdup(reason);
5346 suspended->cData = channel->channel_info;
5347 suspended->previous = suspended->cData->suspended;
5348 suspended->cData->suspended = suspended;
5350 if(suspended->expires)
5351 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
5353 if(IsSuspended(channel->channel_info))
5355 suspended->previous->revoked = now;
5356 if(suspended->previous->expires)
5357 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
5358 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
5359 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5363 /* Mark all users in channel as absent. */
5364 for(uData = channel->channel_info->users; uData; uData = uData->next)
5373 /* Mark the channel as suspended, then part. */
5374 channel->channel_info->flags |= CHANNEL_SUSPENDED;
5375 spamserv_cs_suspend(channel, expiry, 1, suspended->reason);
5376 DelChannelUser(chanserv, channel, suspended->reason, 0);
5377 reply("CSMSG_SUSPENDED", channel->name);
5378 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
5379 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5384 static CHANSERV_FUNC(cmd_cunsuspend)
5386 struct suspended *suspended;
5387 char message[MAXLEN];
5389 if(!IsSuspended(channel->channel_info))
5391 reply("CSMSG_NOT_SUSPENDED", channel->name);
5395 suspended = channel->channel_info->suspended;
5397 /* Expire the suspension and join ChanServ to the channel. */
5398 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
5399 chanserv_expire_suspension(suspended);
5400 reply("CSMSG_UNSUSPENDED", channel->name);
5401 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
5402 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
5406 typedef struct chanservSearch
5411 unsigned long unvisited;
5412 unsigned long registered;
5414 unsigned long flags;
5418 typedef void (*channel_search_func)(struct chanData *channel, void *data);
5421 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
5426 search = malloc(sizeof(struct chanservSearch));
5427 memset(search, 0, sizeof(*search));
5430 for(i = 0; i < argc; i++)
5432 /* Assume all criteria require arguments. */
5435 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
5439 if(!irccasecmp(argv[i], "name"))
5440 search->name = argv[++i];
5441 else if(!irccasecmp(argv[i], "registrar"))
5442 search->registrar = argv[++i];
5443 else if(!irccasecmp(argv[i], "unvisited"))
5444 search->unvisited = ParseInterval(argv[++i]);
5445 else if(!irccasecmp(argv[i], "registered"))
5446 search->registered = ParseInterval(argv[++i]);
5447 else if(!irccasecmp(argv[i], "flags"))
5450 if(!irccasecmp(argv[i], "nodelete"))
5451 search->flags |= CHANNEL_NODELETE;
5452 else if(!irccasecmp(argv[i], "suspended"))
5453 search->flags |= CHANNEL_SUSPENDED;
5454 else if(!irccasecmp(argv[i], "unreviewed"))
5455 search->flags |= CHANNEL_UNREVIEWED;
5458 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
5462 else if(!irccasecmp(argv[i], "limit"))
5463 search->limit = strtoul(argv[++i], NULL, 10);
5466 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
5471 if(search->name && !strcmp(search->name, "*"))
5473 if(search->registrar && !strcmp(search->registrar, "*"))
5474 search->registrar = 0;
5483 chanserv_channel_match(struct chanData *channel, search_t search)
5485 const char *name = channel->channel->name;
5486 if((search->name && !match_ircglob(name, search->name)) ||
5487 (search->registrar && !channel->registrar) ||
5488 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
5489 (search->unvisited && (now - channel->visited) < search->unvisited) ||
5490 (search->registered && (now - channel->registered) > search->registered) ||
5491 (search->flags && ((search->flags & channel->flags) != search->flags)))
5498 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
5500 struct chanData *channel;
5501 unsigned int matches = 0;
5503 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
5505 if(!chanserv_channel_match(channel, search))
5515 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
5520 search_print(struct chanData *channel, void *data)
5522 send_message_type(4, data, chanserv, "%s", channel->channel->name);
5525 static CHANSERV_FUNC(cmd_search)
5528 unsigned int matches;
5529 channel_search_func action;
5533 if(!irccasecmp(argv[1], "count"))
5534 action = search_count;
5535 else if(!irccasecmp(argv[1], "print"))
5536 action = search_print;
5539 reply("CSMSG_ACTION_INVALID", argv[1]);
5543 search = chanserv_search_create(user, argc - 2, argv + 2);
5547 if(action == search_count)
5548 search->limit = INT_MAX;
5550 if(action == search_print)
5551 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
5553 matches = chanserv_channel_search(search, action, user);
5556 reply("MSG_MATCH_COUNT", matches);
5558 reply("MSG_NO_MATCHES");
5564 static CHANSERV_FUNC(cmd_unvisited)
5566 struct chanData *cData;
5567 unsigned long interval = chanserv_conf.channel_expire_delay;
5568 char buffer[INTERVALLEN];
5569 unsigned int limit = 25, matches = 0;
5573 interval = ParseInterval(argv[1]);
5575 limit = atoi(argv[2]);
5578 intervalString(buffer, interval, user->handle_info);
5579 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
5581 for(cData = channelList; cData && matches < limit; cData = cData->next)
5583 if((now - cData->visited) < interval)
5586 intervalString(buffer, now - cData->visited, user->handle_info);
5587 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
5594 static MODCMD_FUNC(chan_opt_defaulttopic)
5600 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5602 reply("CSMSG_TOPIC_LOCKED", channel->name);
5606 topic = unsplit_string(argv+1, argc-1, NULL);
5608 free(channel->channel_info->topic);
5609 if(topic[0] == '*' && topic[1] == 0)
5611 topic = channel->channel_info->topic = NULL;
5615 topic = channel->channel_info->topic = strdup(topic);
5616 if(channel->channel_info->topic_mask
5617 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
5618 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5620 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
5623 if(channel->channel_info->topic)
5624 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
5626 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
5630 static MODCMD_FUNC(chan_opt_topicmask)
5634 struct chanData *cData = channel->channel_info;
5637 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5639 reply("CSMSG_TOPIC_LOCKED", channel->name);
5643 mask = unsplit_string(argv+1, argc-1, NULL);
5645 if(cData->topic_mask)
5646 free(cData->topic_mask);
5647 if(mask[0] == '*' && mask[1] == 0)
5649 cData->topic_mask = 0;
5653 cData->topic_mask = strdup(mask);
5655 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
5656 else if(!match_ircglob(cData->topic, cData->topic_mask))
5657 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5661 if(channel->channel_info->topic_mask)
5662 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
5664 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
5668 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
5672 char *greeting = unsplit_string(argv+1, argc-1, NULL);
5676 if(greeting[0] == '*' && greeting[1] == 0)
5680 unsigned int length = strlen(greeting);
5681 if(length > chanserv_conf.greeting_length)
5683 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
5686 *data = strdup(greeting);
5695 reply(name, user_find_message(user, "MSG_NONE"));
5699 static MODCMD_FUNC(chan_opt_greeting)
5701 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
5704 static MODCMD_FUNC(chan_opt_usergreeting)
5706 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
5709 static MODCMD_FUNC(chan_opt_modes)
5711 struct mod_chanmode *new_modes;
5716 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
5718 reply("CSMSG_NO_ACCESS");
5721 if(argv[1][0] == '*' && argv[1][1] == 0)
5723 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
5725 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)))
5727 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5730 else if(new_modes->argc > 1)
5732 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5733 mod_chanmode_free(new_modes);
5738 channel->channel_info->modes = *new_modes;
5739 modcmd_chanmode_announce(new_modes);
5740 mod_chanmode_free(new_modes);
5744 mod_chanmode_format(&channel->channel_info->modes, modes);
5746 reply("CSMSG_SET_MODES", modes);
5748 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
5752 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5754 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5756 struct chanData *cData = channel->channel_info;
5761 /* Set flag according to value. */
5762 if(enabled_string(argv[1]))
5764 cData->flags |= mask;
5767 else if(disabled_string(argv[1]))
5769 cData->flags &= ~mask;
5774 reply("MSG_INVALID_BINARY", argv[1]);
5780 /* Find current option value. */
5781 value = (cData->flags & mask) ? 1 : 0;
5785 reply(name, user_find_message(user, "MSG_ON"));
5787 reply(name, user_find_message(user, "MSG_OFF"));
5791 static MODCMD_FUNC(chan_opt_nodelete)
5793 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
5795 reply("MSG_SETTING_PRIVILEGED", argv[0]);
5799 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
5802 static MODCMD_FUNC(chan_opt_dynlimit)
5804 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
5807 static MODCMD_FUNC(chan_opt_offchannel)
5809 struct chanData *cData = channel->channel_info;
5814 /* Set flag according to value. */
5815 if(enabled_string(argv[1]))
5817 if(!IsOffChannel(cData))
5818 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
5819 cData->flags |= CHANNEL_OFFCHANNEL;
5822 else if(disabled_string(argv[1]))
5824 if(IsOffChannel(cData))
5826 struct mod_chanmode change;
5827 mod_chanmode_init(&change);
5829 change.args[0].mode = MODE_CHANOP;
5830 change.args[0].u.member = AddChannelUser(chanserv, channel);
5831 mod_chanmode_announce(chanserv, channel, &change);
5833 cData->flags &= ~CHANNEL_OFFCHANNEL;
5838 reply("MSG_INVALID_BINARY", argv[1]);
5844 /* Find current option value. */
5845 value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
5849 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
5851 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
5855 static MODCMD_FUNC(chan_opt_unreviewed)
5857 struct chanData *cData = channel->channel_info;
5858 int value = (cData->flags & CHANNEL_UNREVIEWED) ? 1 : 0;
5864 /* The two directions can have different ACLs. */
5865 if(enabled_string(argv[1]))
5867 else if(disabled_string(argv[1]))
5871 reply("MSG_INVALID_BINARY", argv[1]);
5875 if (new_value != value)
5877 struct svccmd *subcmd;
5878 char subcmd_name[32];
5880 snprintf(subcmd_name, sizeof(subcmd_name), "%s %s", argv[0], (new_value ? "on" : "off"));
5881 subcmd = dict_find(cmd->parent->commands, subcmd_name, NULL);
5884 reply("MSG_COMMAND_DISABLED", subcmd_name);
5887 else if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
5891 cData->flags |= CHANNEL_UNREVIEWED;
5894 free(cData->registrar);
5895 cData->registrar = strdup(user->handle_info->handle);
5896 cData->flags &= ~CHANNEL_UNREVIEWED;
5903 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_ON"));
5905 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_OFF"));
5909 static MODCMD_FUNC(chan_opt_defaults)
5911 struct userData *uData;
5912 struct chanData *cData;
5913 const char *confirm;
5914 enum levelOption lvlOpt;
5915 enum charOption chOpt;
5917 cData = channel->channel_info;
5918 uData = GetChannelUser(cData, user->handle_info);
5919 if(!uData || (uData->access < UL_OWNER))
5921 reply("CSMSG_OWNER_DEFAULTS", channel->name);
5924 confirm = make_confirmation_string(uData);
5925 if((argc < 2) || strcmp(argv[1], confirm))
5927 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
5930 cData->flags = (CHANNEL_DEFAULT_FLAGS & ~CHANNEL_PRESERVED_FLAGS)
5931 | (cData->flags & CHANNEL_PRESERVED_FLAGS);
5932 cData->modes = chanserv_conf.default_modes;
5933 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
5934 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
5935 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
5936 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
5937 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
5942 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5944 struct chanData *cData = channel->channel_info;
5945 struct userData *uData;
5946 unsigned short value;
5950 if(!check_user_level(channel, user, option, 1, 1))
5952 reply("CSMSG_CANNOT_SET");
5955 value = user_level_from_name(argv[1], UL_OWNER+1);
5956 if(!value && strcmp(argv[1], "0"))
5958 reply("CSMSG_INVALID_ACCESS", argv[1]);
5961 uData = GetChannelUser(cData, user->handle_info);
5962 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
5964 reply("CSMSG_BAD_SETLEVEL");
5970 if(value > cData->lvlOpts[lvlGiveOps])
5972 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
5977 if(value < cData->lvlOpts[lvlGiveVoice])
5979 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
5984 /* This test only applies to owners, since non-owners
5985 * trying to set an option to above their level get caught
5986 * by the CSMSG_BAD_SETLEVEL test above.
5988 if(value > uData->access)
5990 reply("CSMSG_BAD_SETTERS");
5997 cData->lvlOpts[option] = value;
5999 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
6003 static MODCMD_FUNC(chan_opt_enfops)
6005 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
6008 static MODCMD_FUNC(chan_opt_giveops)
6010 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
6013 static MODCMD_FUNC(chan_opt_enfmodes)
6015 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
6018 static MODCMD_FUNC(chan_opt_enftopic)
6020 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
6023 static MODCMD_FUNC(chan_opt_pubcmd)
6025 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
6028 static MODCMD_FUNC(chan_opt_setters)
6030 return channel_level_option(lvlSetters, CSFUNC_ARGS);
6033 static MODCMD_FUNC(chan_opt_ctcpusers)
6035 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
6038 static MODCMD_FUNC(chan_opt_userinfo)
6040 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
6043 static MODCMD_FUNC(chan_opt_givevoice)
6045 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
6048 static MODCMD_FUNC(chan_opt_topicsnarf)
6050 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
6053 static MODCMD_FUNC(chan_opt_vote)
6055 return channel_level_option(lvlVote, CSFUNC_ARGS);
6058 static MODCMD_FUNC(chan_opt_inviteme)
6060 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
6064 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
6066 struct chanData *cData = channel->channel_info;
6067 int count = charOptions[option].count, idx;
6071 idx = atoi(argv[1]);
6073 if(!isdigit(argv[1][0]) || (idx < 0) || (idx >= count))
6075 reply("CSMSG_INVALID_NUMERIC", idx);
6076 /* Show possible values. */
6077 for(idx = 0; idx < count; idx++)
6078 reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
6082 cData->chOpts[option] = charOptions[option].values[idx].value;
6086 /* Find current option value. */
6089 (idx < count) && (cData->chOpts[option] != charOptions[option].values[idx].value);
6093 /* Somehow, the option value is corrupt; reset it to the default. */
6094 cData->chOpts[option] = charOptions[option].default_value;
6099 reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
6103 static MODCMD_FUNC(chan_opt_protect)
6105 return channel_multiple_option(chProtect, CSFUNC_ARGS);
6108 static MODCMD_FUNC(chan_opt_toys)
6110 return channel_multiple_option(chToys, CSFUNC_ARGS);
6113 static MODCMD_FUNC(chan_opt_ctcpreaction)
6115 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
6118 static MODCMD_FUNC(chan_opt_topicrefresh)
6120 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
6123 static struct svccmd_list set_shows_list;
6126 handle_svccmd_unbind(struct svccmd *target) {
6128 for(ii=0; ii<set_shows_list.used; ++ii)
6129 if(target == set_shows_list.list[ii])
6130 set_shows_list.used = 0;
6133 static CHANSERV_FUNC(cmd_set)
6135 struct svccmd *subcmd;
6139 /* Check if we need to (re-)initialize set_shows_list. */
6140 if(!set_shows_list.used)
6142 if(!set_shows_list.size)
6144 set_shows_list.size = chanserv_conf.set_shows->used;
6145 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
6147 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
6149 const char *name = chanserv_conf.set_shows->list[ii];
6150 sprintf(buf, "%s %s", argv[0], name);
6151 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6154 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
6157 svccmd_list_append(&set_shows_list, subcmd);
6163 reply("CSMSG_CHANNEL_OPTIONS");
6164 for(ii = 0; ii < set_shows_list.used; ii++)
6166 subcmd = set_shows_list.list[ii];
6167 subcmd->command->func(user, channel, 1, argv+1, subcmd);
6172 sprintf(buf, "%s %s", argv[0], argv[1]);
6173 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6176 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
6179 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
6181 reply("CSMSG_NO_ACCESS");
6187 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
6191 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
6193 struct userData *uData;
6195 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6198 reply("CSMSG_NOT_USER", channel->name);
6204 /* Just show current option value. */
6206 else if(enabled_string(argv[1]))
6208 uData->flags |= mask;
6210 else if(disabled_string(argv[1]))
6212 uData->flags &= ~mask;
6216 reply("MSG_INVALID_BINARY", argv[1]);
6220 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
6224 static MODCMD_FUNC(user_opt_noautoop)
6226 struct userData *uData;
6228 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6231 reply("CSMSG_NOT_USER", channel->name);
6234 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
6235 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
6237 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
6240 static MODCMD_FUNC(user_opt_autoinvite)
6242 if((argc > 1) && !check_user_level(channel, user, lvlInviteMe, 1, 0))
6244 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
6246 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
6249 static MODCMD_FUNC(user_opt_info)
6251 struct userData *uData;
6254 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6258 /* If they got past the command restrictions (which require access)
6259 * but fail this test, we have some fool with security override on.
6261 reply("CSMSG_NOT_USER", channel->name);
6268 infoline = unsplit_string(argv + 1, argc - 1, NULL);
6269 if(strlen(infoline) > chanserv_conf.max_userinfo_length)
6271 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
6274 bp = strcspn(infoline, "\001");
6277 reply("CSMSG_BAD_INFOLINE", infoline[bp]);
6282 if(infoline[0] == '*' && infoline[1] == 0)
6285 uData->info = strdup(infoline);
6288 reply("CSMSG_USET_INFO", uData->info);
6290 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
6294 struct svccmd_list uset_shows_list;
6296 static CHANSERV_FUNC(cmd_uset)
6298 struct svccmd *subcmd;
6302 /* Check if we need to (re-)initialize uset_shows_list. */
6303 if(!uset_shows_list.used)
6307 "NoAutoOp", "AutoInvite", "Info"
6310 if(!uset_shows_list.size)
6312 uset_shows_list.size = ArrayLength(options);
6313 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
6315 for(ii = 0; ii < ArrayLength(options); ii++)
6317 const char *name = options[ii];
6318 sprintf(buf, "%s %s", argv[0], name);
6319 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6322 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
6325 svccmd_list_append(&uset_shows_list, subcmd);
6331 /* Do this so options are presented in a consistent order. */
6332 reply("CSMSG_USER_OPTIONS");
6333 for(ii = 0; ii < uset_shows_list.used; ii++)
6334 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
6338 sprintf(buf, "%s %s", argv[0], argv[1]);
6339 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6342 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
6346 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
6349 static CHANSERV_FUNC(cmd_giveownership)
6351 struct handle_info *new_owner_hi;
6352 struct userData *new_owner;
6353 struct userData *curr_user;
6354 struct userData *invoker;
6355 struct chanData *cData = channel->channel_info;
6356 struct do_not_register *dnr;
6357 const char *confirm;
6359 unsigned short co_access;
6360 char reason[MAXLEN];
6363 curr_user = GetChannelAccess(cData, user->handle_info);
6364 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
6365 if(!curr_user || (curr_user->access != UL_OWNER))
6367 struct userData *owner = NULL;
6368 for(curr_user = channel->channel_info->users;
6370 curr_user = curr_user->next)
6372 if(curr_user->access != UL_OWNER)
6376 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
6383 else if(!force && (now < cData->ownerTransfer + chanserv_conf.giveownership_period))
6385 char delay[INTERVALLEN];
6386 intervalString(delay, cData->ownerTransfer + chanserv_conf.giveownership_period - now, user->handle_info);
6387 reply("CSMSG_TRANSFER_WAIT", delay, channel->name);
6390 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
6392 if(new_owner_hi == user->handle_info)
6394 reply("CSMSG_NO_TRANSFER_SELF");
6397 new_owner = GetChannelAccess(cData, new_owner_hi);
6402 new_owner = add_channel_user(cData, new_owner_hi, UL_OWNER - 1, 0, NULL);
6406 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
6410 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
6412 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
6415 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
6416 if(!IsHelping(user))
6417 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
6419 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi->handle);
6422 invoker = GetChannelUser(cData, user->handle_info);
6423 if(invoker->access <= UL_OWNER)
6425 confirm = make_confirmation_string(curr_user);
6426 if((argc < 3) || strcmp(argv[2], confirm))
6428 reply("CSMSG_CONFIRM_GIVEOWNERSHIP", new_owner_hi->handle, confirm);
6432 if(new_owner->access >= UL_COOWNER)
6433 co_access = new_owner->access;
6435 co_access = UL_COOWNER;
6436 new_owner->access = UL_OWNER;
6438 curr_user->access = co_access;
6439 cData->ownerTransfer = now;
6440 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
6441 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
6442 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
6446 static CHANSERV_FUNC(cmd_suspend)
6448 struct handle_info *hi;
6449 struct userData *actor, *real_actor, *target;
6450 unsigned int override = 0;
6453 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6454 actor = GetChannelUser(channel->channel_info, user->handle_info);
6455 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
6456 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6458 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6461 if(target->access >= actor->access)
6463 reply("MSG_USER_OUTRANKED", hi->handle);
6466 if(target->flags & USER_SUSPENDED)
6468 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
6473 target->present = 0;
6476 if(!real_actor || target->access >= real_actor->access)
6477 override = CMD_LOG_OVERRIDE;
6478 target->flags |= USER_SUSPENDED;
6479 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
6480 return 1 | override;
6483 static CHANSERV_FUNC(cmd_unsuspend)
6485 struct handle_info *hi;
6486 struct userData *actor, *real_actor, *target;
6487 unsigned int override = 0;
6490 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6491 actor = GetChannelUser(channel->channel_info, user->handle_info);
6492 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
6493 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6495 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6498 if(target->access >= actor->access)
6500 reply("MSG_USER_OUTRANKED", hi->handle);
6503 if(!(target->flags & USER_SUSPENDED))
6505 reply("CSMSG_NOT_SUSPENDED", hi->handle);
6508 if(!real_actor || target->access >= real_actor->access)
6509 override = CMD_LOG_OVERRIDE;
6510 target->flags &= ~USER_SUSPENDED;
6511 scan_user_presence(target, NULL);
6512 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
6513 return 1 | override;
6516 static MODCMD_FUNC(cmd_deleteme)
6518 struct handle_info *hi;
6519 struct userData *target;
6520 const char *confirm_string;
6521 unsigned short access_level;
6524 hi = user->handle_info;
6525 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6527 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6530 if(target->access == UL_OWNER)
6532 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
6535 confirm_string = make_confirmation_string(target);
6536 if((argc < 2) || strcmp(argv[1], confirm_string))
6538 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
6541 access_level = target->access;
6542 channel_name = strdup(channel->name);
6543 del_channel_user(target, 1);
6544 reply("CSMSG_DELETED_YOU", access_level, channel_name);
6549 static CHANSERV_FUNC(cmd_addvote)
6551 struct chanData *cData = channel->channel_info;
6552 struct userData *uData, *target;
6553 struct handle_info *hi;
6554 if (!cData) return 0;
6556 hi = user->handle_info;
6557 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6559 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6562 if(target->access < 300) {
6563 reply("CSMSG_NO_ACCESS");
6567 reply("CSMSG_ADDVOTE_FULL");
6571 msg = unsplit_string(argv + 1, argc - 1, NULL);
6572 cData->vote = strdup(msg);
6573 cData->vote_start=0;
6574 dict_delete(cData->vote_options);
6575 cData->vote_options = dict_new();
6576 dict_set_free_data(cData->vote_options, free_vote_options);
6577 for(uData = channel->channel_info->users; uData; uData = uData->next)
6582 reply("CSMSG_ADDVOTE_DONE");
6586 static CHANSERV_FUNC(cmd_delvote)
6588 struct chanData *cData = channel->channel_info;
6589 struct userData *target;
6590 struct handle_info *hi;
6591 if (!cData) return 0;
6592 hi = user->handle_info;
6593 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6595 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6598 if(target->access < 300) {
6599 reply("CSMSG_NO_ACCESS");
6603 reply("CSMSG_NO_VOTE");
6608 reply("CSMSG_DELVOTE_DONE");
6612 static CHANSERV_FUNC(cmd_addoption)
6614 struct chanData *cData = channel->channel_info;
6615 struct userData *target;
6616 struct handle_info *hi;
6617 if (!cData) return 0;
6619 hi = user->handle_info;
6620 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6622 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6625 if(target->access < 300) {
6626 reply("CSMSG_NO_ACCESS");
6630 reply("CSMSG_NO_VOTE");
6636 msg = unsplit_string(argv + 1, argc - 1, NULL);
6639 unsigned int lastid = 1;
6640 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6641 struct vote_option *cvOpt = iter_data(it);
6642 if(cvOpt->option_id > lastid)
6643 lastid = cvOpt->option_id;
6645 struct vote_option *vOpt;
6646 vOpt = calloc(1, sizeof(*vOpt));
6647 vOpt->name = strdup(msg);
6648 vOpt->option_id = (lastid + 1);
6650 sprintf(str,"%i",(lastid + 1));
6651 vOpt->option_str = strdup(str);
6653 dict_insert(cData->vote_options,vOpt->option_str,vOpt);
6655 reply("CSMSG_ADDOPTION_DONE",dict_size(cData->vote_options),lastid,(lastid + 1));
6659 static CHANSERV_FUNC(cmd_deloption)
6661 struct chanData *cData = channel->channel_info;
6662 struct userData *uData, *target;
6663 struct handle_info *hi;
6664 if (!cData) return 0;
6666 hi = user->handle_info;
6667 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6669 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6672 if(target->access < 300) {
6673 reply("CSMSG_NO_ACCESS");
6677 reply("CSMSG_NO_VOTE");
6680 if(cData->vote_start) {
6681 if(dict_size(cData->vote_options) < 3) {
6682 reply("CSMSG_VOTE_NEED_OPTIONS");
6687 int find_id = atoi(argv[1]);
6689 unsigned int found = 0;
6692 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6694 if (find_id == ii) {
6695 struct vote_option *vOpt = iter_data(it);
6696 found = vOpt->option_id;
6698 sprintf(str,"%i",vOpt->option_id);
6699 dict_remove(cData->vote_options, str);
6704 for(uData = channel->channel_info->users; uData; uData = uData->next) {
6705 if(uData->votefor == found) {
6710 reply("CSMSG_DELOPTION_DONE");
6713 reply("CSMSG_DELOPTION_NONE");
6718 static CHANSERV_FUNC(cmd_vote)
6720 struct chanData *cData = channel->channel_info;
6721 struct userData *target;
6722 struct handle_info *hi;
6723 unsigned int votedfor = 0;
6724 char *votedfor_str = NULL;
6726 if (!cData || !cData->vote) {
6727 reply("CSMSG_NO_VOTE");
6730 if(argc > 1 && cData->vote_start) {
6731 hi = user->handle_info;
6732 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6734 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6737 if(!check_user_level(channel, user, lvlVote, 1, 0)) {
6738 reply("CSMSG_NO_ACCESS");
6742 reply("CSMSG_VOTE_VOTED");
6745 int find_id = atoi(argv[1]);
6748 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6750 if (find_id == ii) {
6751 struct vote_option *vOpt = iter_data(it);
6754 target->votefor = vOpt->option_id;
6755 votedfor = vOpt->option_id;
6756 votedfor_str = vOpt->name;
6760 reply("CSMSG_VOTE_INVALID");
6764 if (!cData->vote_start) {
6765 reply("CSMSG_VOTE_NOT_STARTED");
6767 reply("CSMSG_VOTE_QUESTION",cData->vote);
6769 unsigned int voteid = 0;
6772 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6773 struct vote_option *vOpt = iter_data(it);
6775 reply("CSMSG_VOTE_OPTION",voteid,vOpt->name,vOpt->voted);
6777 if(argc > 1 && cData->vote_start && votedfor_str) {
6778 reply("CSMSG_VOTE_DONE",votedfor_str);
6783 static CHANSERV_FUNC(cmd_startvote)
6785 struct chanData *cData = channel->channel_info;
6786 struct userData *target;
6787 struct handle_info *hi;
6788 if (!cData) return 0;
6789 hi = user->handle_info;
6790 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6792 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6795 if(target->access < 300) {
6796 reply("CSMSG_NO_ACCESS");
6800 reply("CSMSG_NO_VOTE");
6803 if(cData->vote_start) {
6804 reply("CSMSG_STARTVOTE_RUNNING");
6807 if(dict_size(cData->vote_options) < 2) {
6808 reply("CSMSG_VOTE_NEED_OPTIONS");
6811 cData->vote_start = 1;
6812 char response[MAXLEN];
6813 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_TOP"), user->nick);
6814 irc_privmsg(cmd->parent->bot, channel->name, response);
6815 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_QUESTION"), cData->vote);
6816 irc_privmsg(cmd->parent->bot, channel->name, response);
6817 unsigned int voteid = 0;
6819 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6820 struct vote_option *vOpt = iter_data(it);
6822 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_OPTION"), voteid, vOpt->name);
6823 irc_privmsg(cmd->parent->bot, channel->name, response);
6825 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_ACCESS"), cData->lvlOpts[lvlVote]); //Todo
6826 irc_privmsg(cmd->parent->bot, channel->name, response);
6827 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_HOWTO")); //Todo
6828 irc_privmsg(cmd->parent->bot, channel->name, response);
6832 static CHANSERV_FUNC(cmd_endvote)
6834 struct chanData *cData = channel->channel_info;
6835 struct userData *target;
6836 struct handle_info *hi;
6837 if (!cData) return 0;
6838 hi = user->handle_info;
6839 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6841 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6844 if(target->access < 300) {
6845 reply("CSMSG_NO_ACCESS");
6849 reply("CSMSG_NO_VOTE");
6852 if(!cData->vote_start) {
6853 reply("CSMSG_ENDVOTE_STOPPED");
6856 cData->vote_start = 0;
6857 reply("CSMSG_ENDVOTE_DONE");
6861 static CHANSERV_FUNC(cmd_voteresults)
6863 struct chanData *cData = channel->channel_info;
6864 struct userData *target;
6865 struct handle_info *hi;
6866 if (!cData) return 0;
6868 reply("CSMSG_NO_VOTE");
6871 if (argc > 1 && !irccasecmp(argv[1], "*")) {
6872 hi = user->handle_info;
6873 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6875 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6878 if(target->access < 300) {
6879 reply("CSMSG_NO_ACCESS");
6882 char response[MAXLEN];
6883 sprintf(response, user_find_message(user, "CSMSG_VOTERES_QUESTION"), cData->vote);
6884 irc_privmsg(cmd->parent->bot, channel->name, response);
6885 unsigned int voteid = 0;
6887 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6888 struct vote_option *vOpt = iter_data(it);
6890 sprintf(response, user_find_message(user, "CSMSG_VOTERES_OPTION"), voteid, vOpt->name, vOpt->voted);
6891 irc_privmsg(cmd->parent->bot, channel->name, response);
6894 reply("CSMSG_VOTE_QUESTION",cData->vote);
6895 unsigned int voteid = 0;
6897 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6898 struct vote_option *vOpt = iter_data(it);
6900 reply("CSMSG_VOTE_OPTION",voteid,vOpt->name,vOpt->voted);
6907 chanserv_refresh_topics(UNUSED_ARG(void *data))
6909 unsigned int refresh_num = (now - self->link_time) / chanserv_conf.refresh_period;
6910 struct chanData *cData;
6913 for(cData = channelList; cData; cData = cData->next)
6915 if(IsSuspended(cData))
6917 opt = cData->chOpts[chTopicRefresh];
6920 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
6923 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
6924 cData->last_refresh = refresh_num;
6926 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
6929 static CHANSERV_FUNC(cmd_unf)
6933 char response[MAXLEN];
6934 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
6935 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6936 irc_privmsg(cmd->parent->bot, channel->name, response);
6939 reply("CSMSG_UNF_RESPONSE");
6943 static CHANSERV_FUNC(cmd_ping)
6947 char response[MAXLEN];
6948 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
6949 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6950 irc_privmsg(cmd->parent->bot, channel->name, response);
6953 reply("CSMSG_PING_RESPONSE");
6957 static CHANSERV_FUNC(cmd_wut)
6961 char response[MAXLEN];
6962 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
6963 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6964 irc_privmsg(cmd->parent->bot, channel->name, response);
6967 reply("CSMSG_WUT_RESPONSE");
6971 static CHANSERV_FUNC(cmd_8ball)
6973 unsigned int i, j, accum;
6978 for(i=1; i<argc; i++)
6979 for(j=0; argv[i][j]; j++)
6980 accum = (accum << 5) - accum + toupper(argv[i][j]);
6981 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
6984 char response[MAXLEN];
6985 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
6986 irc_privmsg(cmd->parent->bot, channel->name, response);
6989 send_message_type(4, user, cmd->parent->bot, "%s", resp);
6993 static CHANSERV_FUNC(cmd_d)
6995 unsigned long sides, count, modifier, ii, total;
6996 char response[MAXLEN], *sep;
7000 if((count = strtoul(argv[1], &sep, 10)) < 1)
7010 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
7011 && (sides = strtoul(sep+1, &sep, 10)) > 1)
7015 else if((sep[0] == '-') && isdigit(sep[1]))
7016 modifier = strtoul(sep, NULL, 10);
7017 else if((sep[0] == '+') && isdigit(sep[1]))
7018 modifier = strtoul(sep+1, NULL, 10);
7025 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
7030 reply("CSMSG_BAD_DICE_COUNT", count, 10);
7033 for(total = ii = 0; ii < count; ++ii)
7034 total += (rand() % sides) + 1;
7037 if((count > 1) || modifier)
7039 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
7040 sprintf(response, fmt, total, count, sides, modifier);
7044 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
7045 sprintf(response, fmt, total, sides);
7048 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
7050 send_message_type(4, user, cmd->parent->bot, "%s", response);
7054 static CHANSERV_FUNC(cmd_huggle)
7056 /* CTCP must be via PRIVMSG, never notice */
7058 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
7060 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
7065 chanserv_adjust_limit(void *data)
7067 struct mod_chanmode change;
7068 struct chanData *cData = data;
7069 struct chanNode *channel = cData->channel;
7072 if(IsSuspended(cData))
7075 cData->limitAdjusted = now;
7076 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
7077 if(cData->modes.modes_set & MODE_LIMIT)
7079 if(limit > cData->modes.new_limit)
7080 limit = cData->modes.new_limit;
7081 else if(limit == cData->modes.new_limit)
7085 mod_chanmode_init(&change);
7086 change.modes_set = MODE_LIMIT;
7087 change.new_limit = limit;
7088 mod_chanmode_announce(chanserv, channel, &change);
7092 handle_new_channel(struct chanNode *channel)
7094 struct chanData *cData;
7096 if(!(cData = channel->channel_info))
7099 if(cData->modes.modes_set || cData->modes.modes_clear)
7100 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
7102 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
7103 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
7106 void handle_new_channel_created(char *chan, struct userNode *user) {
7107 if(user->handle_info && chanserv_conf.new_channel_authed) {
7108 send_target_message(5, chan, chanserv, "%s", chanserv_conf.new_channel_authed);
7109 } else if(!user->handle_info && chanserv_conf.new_channel_unauthed) {
7110 send_target_message(5, chan, chanserv, "%s", chanserv_conf.new_channel_unauthed);
7112 if(chanserv_conf.new_channel_msg)
7113 send_target_message(5, chan, chanserv, "%s", chanserv_conf.new_channel_msg);
7116 /* Welcome to my worst nightmare. Warning: Read (or modify)
7117 the code below at your own risk. */
7119 handle_join(struct modeNode *mNode)
7121 struct mod_chanmode change;
7122 struct userNode *user = mNode->user;
7123 struct chanNode *channel = mNode->channel;
7124 struct chanData *cData;
7125 struct userData *uData = NULL;
7126 struct banData *bData;
7127 struct handle_info *handle;
7128 unsigned int modes = 0, info = 0;
7132 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
7135 cData = channel->channel_info;
7136 if(channel->members.used > cData->max) {
7137 cData->max = channel->members.used;
7138 cData->max_time = now;
7141 for(i = 0; i < channel->invited.used; i++)
7143 if(channel->invited.list[i] == user) {
7144 userList_remove(&channel->invited, user);
7148 /* Check for bans. If they're joining through a ban, one of two
7150 * 1: Join during a netburst, by riding the break. Kick them
7151 * unless they have ops or voice in the channel.
7152 * 2: They're allowed to join through the ban (an invite in
7153 * ircu2.10, or a +e on Hybrid, or something).
7154 * If they're not joining through a ban, and the banlist is not
7155 * full, see if they're on the banlist for the channel. If so,
7158 if(user->uplink->burst && !mNode->modes)
7161 for(ii = 0; ii < channel->banlist.used; ii++)
7163 if(user_matches_glob(user, channel->banlist.list[ii]->ban, MATCH_USENICK))
7165 /* Riding a netburst. Naughty. */
7166 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
7172 mod_chanmode_init(&change);
7174 if(channel->banlist.used < MAXBANS)
7176 /* Not joining through a ban. */
7177 for(bData = cData->bans;
7178 bData && !user_matches_glob(user, bData->mask, MATCH_USENICK);
7179 bData = bData->next);
7183 char kick_reason[MAXLEN];
7184 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
7186 bData->triggered = now;
7187 if(bData != cData->bans)
7189 /* Shuffle the ban to the head of the list. */
7191 bData->next->prev = bData->prev;
7193 bData->prev->next = bData->next;
7196 bData->next = cData->bans;
7199 cData->bans->prev = bData;
7200 cData->bans = bData;
7203 change.args[0].mode = MODE_BAN;
7204 change.args[0].u.hostmask = bData->mask;
7205 mod_chanmode_announce(chanserv, channel, &change);
7206 KickChannelUser(user, channel, chanserv, kick_reason);
7211 /* ChanServ will not modify the limits in join-flooded channels,
7212 or when there are enough slots left below the limit. */
7213 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
7214 && !channel->join_flooded
7215 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
7217 /* The user count has begun "bumping" into the channel limit,
7218 so set a timer to raise the limit a bit. Any previous
7219 timers are removed so three incoming users within the delay
7220 results in one limit change, not three. */
7222 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7223 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7226 if(channel->join_flooded)
7228 /* don't automatically give ops or voice during a join flood */
7230 else if(cData->lvlOpts[lvlGiveOps] == 0)
7231 modes |= MODE_CHANOP;
7232 else if(cData->lvlOpts[lvlGiveVoice] == 0)
7233 modes |= MODE_VOICE;
7235 greeting = cData->greeting;
7236 if(user->handle_info)
7238 handle = user->handle_info;
7240 if(IsHelper(user) && !IsHelping(user))
7243 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7245 if(channel == chanserv_conf.support_channels.list[ii])
7247 HANDLE_SET_FLAG(user->handle_info, HELPING);
7253 uData = GetTrueChannelAccess(cData, handle);
7254 if(uData && !IsUserSuspended(uData))
7256 /* Ops and above were handled by the above case. */
7257 if(IsUserAutoOp(uData))
7259 if(uData->access >= cData->lvlOpts[lvlGiveOps])
7260 modes |= MODE_CHANOP;
7261 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
7262 modes |= MODE_VOICE;
7264 if(uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
7265 cData->visited = now;
7266 if(cData->user_greeting)
7267 greeting = cData->user_greeting;
7269 && (uData->access >= cData->lvlOpts[lvlUserInfo])
7270 && ((now - uData->seen) >= chanserv_conf.info_delay)
7278 /* If user joining normally (not during burst), apply op or voice,
7279 * and send greeting/userinfo as appropriate.
7281 if(!user->uplink->burst)
7285 if(modes & MODE_CHANOP)
7286 modes &= ~MODE_VOICE;
7287 change.args[0].mode = modes;
7288 change.args[0].u.member = mNode;
7289 mod_chanmode_announce(chanserv, channel, &change);
7292 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
7293 if(uData && info && (modes || !(channel->modes & MODE_DELAYJOINS)))
7294 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
7300 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
7302 struct mod_chanmode change;
7303 struct userData *channel;
7304 unsigned int ii, jj;
7306 if(!user->handle_info)
7309 mod_chanmode_init(&change);
7311 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
7313 struct chanNode *cn;
7314 struct modeNode *mn;
7315 if(IsUserSuspended(channel)
7316 || IsSuspended(channel->channel)
7317 || !(cn = channel->channel->channel))
7320 mn = GetUserMode(cn, user);
7323 if(!IsUserSuspended(channel)
7324 && IsUserAutoInvite(channel)
7325 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
7327 && !user->uplink->burst)
7328 irc_invite(chanserv, user, cn);
7332 if(channel->access >= UL_PRESENT && !HANDLE_FLAGGED(channel->handle, BOT))
7333 channel->channel->visited = now;
7335 if(IsUserAutoOp(channel))
7337 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
7338 change.args[0].mode = MODE_CHANOP;
7339 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
7340 change.args[0].mode = MODE_VOICE;
7342 change.args[0].mode = 0;
7343 change.args[0].u.member = mn;
7344 if(change.args[0].mode)
7345 mod_chanmode_announce(chanserv, cn, &change);
7348 channel->seen = now;
7349 channel->present = 1;
7352 for(ii = 0; ii < user->channels.used; ++ii)
7354 struct chanNode *chan = user->channels.list[ii]->channel;
7355 struct banData *ban;
7357 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
7358 || !chan->channel_info
7359 || IsSuspended(chan->channel_info))
7361 for(jj = 0; jj < chan->banlist.used; ++jj)
7362 if(user_matches_glob(user, chan->banlist.list[jj]->ban, MATCH_USENICK))
7364 if(jj < chan->banlist.used)
7366 for(ban = chan->channel_info->bans; ban; ban = ban->next)
7368 char kick_reason[MAXLEN];
7369 if(!user_matches_glob(user, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
7371 change.args[0].mode = MODE_BAN;
7372 change.args[0].u.hostmask = ban->mask;
7373 mod_chanmode_announce(chanserv, chan, &change);
7374 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
7375 KickChannelUser(user, chan, chanserv, kick_reason);
7376 ban->triggered = now;
7381 if(IsSupportHelper(user))
7383 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7385 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
7387 HANDLE_SET_FLAG(user->handle_info, HELPING);
7395 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
7397 struct chanData *cData;
7398 struct userData *uData;
7400 cData = mn->channel->channel_info;
7401 if(!cData || IsSuspended(cData) || IsLocal(mn->user))
7404 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
7406 /* Allow for a bit of padding so that the limit doesn't
7407 track the user count exactly, which could get annoying. */
7408 if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
7410 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7411 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7415 if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
7417 scan_user_presence(uData, mn->user);
7419 if (uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
7420 cData->visited = now;
7423 if(IsHelping(mn->user) && IsSupportHelper(mn->user))
7426 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii) {
7427 struct chanNode *channel;
7428 struct userNode *exclude;
7429 /* When looking at the channel that is being /part'ed, we
7430 * have to skip over the client that is leaving. For
7431 * other channels, we must not do that.
7433 channel = chanserv_conf.support_channels.list[ii];
7434 exclude = (channel == mn->channel) ? mn->user : NULL;
7435 if(find_handle_in_channel(channel, mn->user->handle_info, exclude))
7438 if(ii == chanserv_conf.support_channels.used)
7439 HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
7444 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
7446 struct userData *uData;
7448 if(!channel->channel_info || !kicker || IsService(kicker)
7449 || (kicker == victim) || IsSuspended(channel->channel_info)
7450 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
7453 if(protect_user(victim, kicker, channel->channel_info))
7455 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED_2");
7456 KickChannelUser(kicker, channel, chanserv, reason);
7459 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
7464 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
7466 struct chanData *cData;
7468 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
7471 cData = channel->channel_info;
7472 if(bad_topic(channel, user, channel->topic))
7474 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
7475 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
7476 SetChannelTopic(channel, chanserv, old_topic, 1);
7477 else if(cData->topic)
7478 SetChannelTopic(channel, chanserv, cData->topic, 1);
7481 /* With topicsnarf, grab the topic and save it as the default topic. */
7482 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
7485 cData->topic = strdup(channel->topic);
7491 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
7493 struct mod_chanmode *bounce = NULL;
7494 unsigned int bnc, ii;
7497 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
7500 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
7501 && mode_lock_violated(&channel->channel_info->modes, change))
7503 char correct[MAXLEN];
7504 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
7505 mod_chanmode_format(&channel->channel_info->modes, correct);
7506 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
7508 for(ii = bnc = 0; ii < change->argc; ++ii)
7510 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
7512 const struct userNode *victim = change->args[ii].u.member->user;
7513 if(!protect_user(victim, user, channel->channel_info))
7516 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7519 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
7520 bounce->args[bnc].u.member = GetUserMode(channel, user);
7521 if(bounce->args[bnc].u.member)
7525 bounce->args[bnc].mode = MODE_CHANOP;
7526 bounce->args[bnc].u.member = change->args[ii].u.member;
7528 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
7530 else if(change->args[ii].mode & MODE_CHANOP)
7532 const struct userNode *victim = change->args[ii].u.member->user;
7533 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
7536 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7537 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
7538 bounce->args[bnc].u.member = change->args[ii].u.member;
7541 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
7543 const char *ban = change->args[ii].u.hostmask;
7544 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
7547 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7548 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
7549 bounce->args[bnc].u.hostmask = strdup(ban);
7551 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
7556 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
7557 mod_chanmode_announce(chanserv, channel, bounce);
7558 for(ii = 0; ii < change->argc; ++ii)
7559 if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
7560 free((char*)bounce->args[ii].u.hostmask);
7561 mod_chanmode_free(bounce);
7566 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
7568 struct chanNode *channel;
7569 struct banData *bData;
7570 struct mod_chanmode change;
7571 unsigned int ii, jj;
7572 char kick_reason[MAXLEN];
7574 mod_chanmode_init(&change);
7576 change.args[0].mode = MODE_BAN;
7577 for(ii = 0; ii < user->channels.used; ++ii)
7579 channel = user->channels.list[ii]->channel;
7580 /* Need not check for bans if they're opped or voiced. */
7581 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
7583 /* Need not check for bans unless channel registration is active. */
7584 if(!channel->channel_info || IsSuspended(channel->channel_info))
7586 /* Look for a matching ban already on the channel. */
7587 for(jj = 0; jj < channel->banlist.used; ++jj)
7588 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
7590 /* Need not act if we found one. */
7591 if(jj < channel->banlist.used)
7593 /* Look for a matching ban in this channel. */
7594 for(bData = channel->channel_info->bans; bData; bData = bData->next)
7596 if(!user_matches_glob(user, bData->mask, MATCH_USENICK | MATCH_VISIBLE))
7598 change.args[0].u.hostmask = bData->mask;
7599 mod_chanmode_announce(chanserv, channel, &change);
7600 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
7601 KickChannelUser(user, channel, chanserv, kick_reason);
7602 bData->triggered = now;
7603 break; /* we don't need to check any more bans in the channel */
7608 static void handle_rename(struct handle_info *handle, const char *old_handle)
7610 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
7614 dict_remove2(handle_dnrs, old_handle, 1);
7615 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
7616 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
7621 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
7623 struct userNode *h_user;
7625 if(handle->channels)
7627 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
7628 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
7630 while(handle->channels)
7631 del_channel_user(handle->channels, 1);
7636 handle_server_link(UNUSED_ARG(struct server *server))
7638 struct chanData *cData;
7640 for(cData = channelList; cData; cData = cData->next)
7642 if(!IsSuspended(cData))
7643 cData->may_opchan = 1;
7644 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
7645 && !cData->channel->join_flooded
7646 && ((cData->channel->limit - cData->channel->members.used)
7647 < chanserv_conf.adjust_threshold))
7649 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7650 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7656 chanserv_conf_read(void)
7660 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
7661 struct mod_chanmode *change;
7662 struct string_list *strlist;
7663 struct chanNode *chan;
7666 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
7668 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
7671 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7672 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7673 chanserv_conf.support_channels.used = 0;
7674 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
7676 for(ii = 0; ii < strlist->used; ++ii)
7678 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7681 chan = AddChannel(strlist->list[ii], now, str2, NULL);
7683 channelList_append(&chanserv_conf.support_channels, chan);
7686 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
7689 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7692 chan = AddChannel(str, now, str2, NULL);
7694 channelList_append(&chanserv_conf.support_channels, chan);
7696 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
7697 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
7698 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
7699 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
7700 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
7701 chanserv_conf.greeting_length = str ? atoi(str) : 200;
7702 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
7703 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
7704 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
7705 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
7706 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
7707 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
7708 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
7709 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
7710 str = database_get_data(conf_node, KEY_DNR_EXPIRE_FREQ, RECDB_QSTRING);
7711 chanserv_conf.dnr_expire_frequency = str ? ParseInterval(str) : 3600;
7712 str = database_get_data(conf_node, KEY_INVITED_INTERVAL, RECDB_QSTRING);
7713 chanserv_conf.invited_timeout = str ? ParseInterval(str) : 600*2;
7714 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
7715 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
7716 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
7717 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
7718 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
7719 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
7720 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
7721 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
7722 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
7724 NickChange(chanserv, str, 0);
7725 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
7726 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
7727 str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
7728 chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
7729 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
7730 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
7731 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
7732 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
7733 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
7734 chanserv_conf.max_owned = str ? atoi(str) : 5;
7735 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
7736 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
7737 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
7738 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
7739 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
7740 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
7741 str = database_get_data(conf_node, KEY_NEW_CHANNEL_AUTHED, RECDB_QSTRING);
7742 chanserv_conf.new_channel_authed = str ? str : NULL;
7743 str = database_get_data(conf_node, KEY_NEW_CHANNEL_UNAUTHED, RECDB_QSTRING);
7744 chanserv_conf.new_channel_unauthed = str ? str : NULL;
7745 str = database_get_data(conf_node, KEY_NEW_CHANNEL_MSG, RECDB_QSTRING);
7746 chanserv_conf.new_channel_msg = str ? str : NULL;
7747 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
7750 safestrncpy(mode_line, str, sizeof(mode_line));
7751 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
7752 if((change = mod_chanmode_parse(NULL, NULL, modes, ii, MCP_KEY_FREE|MCP_NO_APASS, 0))
7753 && (change->argc < 2))
7755 chanserv_conf.default_modes = *change;
7756 mod_chanmode_free(change);
7758 free_string_list(chanserv_conf.set_shows);
7759 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
7761 strlist = string_list_copy(strlist);
7764 static const char *list[] = {
7765 /* free form text */
7766 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
7767 /* options based on user level */
7768 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
7769 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
7770 /* multiple choice options */
7771 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
7772 /* binary options */
7773 "DynLimit", "NoDelete", "expire", "Vote",
7777 strlist = alloc_string_list(ArrayLength(list)-1);
7778 for(ii=0; list[ii]; ii++)
7779 string_list_append(strlist, strdup(list[ii]));
7781 chanserv_conf.set_shows = strlist;
7782 /* We don't look things up now, in case the list refers to options
7783 * defined by modules initialized after this point. Just mark the
7784 * function list as invalid, so it will be initialized.
7786 set_shows_list.used = 0;
7787 free_string_list(chanserv_conf.eightball);
7788 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
7791 strlist = string_list_copy(strlist);
7795 strlist = alloc_string_list(4);
7796 string_list_append(strlist, strdup("Yes."));
7797 string_list_append(strlist, strdup("No."));
7798 string_list_append(strlist, strdup("Maybe so."));
7800 chanserv_conf.eightball = strlist;
7801 free_string_list(chanserv_conf.old_ban_names);
7802 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
7804 strlist = string_list_copy(strlist);
7806 strlist = alloc_string_list(2);
7807 chanserv_conf.old_ban_names = strlist;
7808 str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
7809 off_channel = str ? atoi(str) : 0;
7813 chanserv_note_type_read(const char *key, struct record_data *rd)
7816 struct note_type *ntype;
7819 if(!(obj = GET_RECORD_OBJECT(rd)))
7821 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
7824 if(!(ntype = chanserv_create_note_type(key)))
7826 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
7830 /* Figure out set access */
7831 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
7833 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7834 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
7836 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
7838 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
7839 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
7841 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
7843 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
7847 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
7848 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7849 ntype->set_access.min_opserv = 0;
7852 /* Figure out visibility */
7853 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
7854 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7855 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
7856 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7857 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
7858 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
7859 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
7860 ntype->visible_type = NOTE_VIS_ALL;
7862 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7864 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
7865 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
7869 vote_option_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7871 struct vote_option *vOpt;
7874 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7876 log_module(CS_LOG, LOG_ERROR, "Invalid vote option in %s.", chan->channel->name);
7880 vOpt = calloc(1, sizeof(*vOpt));
7881 vOpt->name = strdup(database_get_data(rd->d.object, KEY_VOTE_OPTION_NAME, RECDB_QSTRING));
7882 str = database_get_data(rd->d.object, KEY_VOTE_OPTION_VOTED, RECDB_QSTRING);
7883 vOpt->voted = str ? atoi(str) : 0;
7884 vOpt->option_id = str ? atoi(key) : 0;
7885 vOpt->option_str = strdup(key);
7886 dict_insert(chan->vote_options,vOpt->option_str,vOpt);
7890 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7892 struct handle_info *handle;
7893 struct userData *uData;
7894 char *seen, *inf, *flags, *voted, *votefor;
7895 unsigned long last_seen;
7896 unsigned short access_level;
7898 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7900 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
7904 access_level = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
7905 if(access_level > UL_OWNER)
7907 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
7911 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
7912 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
7913 last_seen = seen ? strtoul(seen, NULL, 0) : now;
7914 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
7915 voted = database_get_data(rd->d.object, KEY_VOTE_VOTED, RECDB_QSTRING);
7916 votefor = database_get_data(rd->d.object, KEY_VOTE_VOTEDFOR, RECDB_QSTRING);
7917 handle = get_handle_info(key);
7920 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
7924 uData = add_channel_user(chan, handle, access_level, last_seen, inf);
7925 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
7927 uData->voted = voted ? strtoul(voted, NULL, 0) : 0;
7928 uData->votefor = votefor ? strtoul(votefor, NULL, 0) : 0;
7936 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7938 struct banData *bData;
7939 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
7940 unsigned long set_time, triggered_time, expires_time;
7942 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7944 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
7948 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
7949 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
7950 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
7951 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
7952 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
7953 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
7954 if (!reason || !owner)
7957 set_time = set ? strtoul(set, NULL, 0) : now;
7958 triggered_time = triggered ? strtoul(triggered, NULL, 0) : 0;
7960 expires_time = strtoul(s_expires, NULL, 0);
7962 expires_time = set_time + atoi(s_duration);
7966 if(!reason || (expires_time && (expires_time < now)))
7969 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
7972 static struct suspended *
7973 chanserv_read_suspended(dict_t obj)
7975 struct suspended *suspended = calloc(1, sizeof(*suspended));
7979 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
7980 suspended->expires = str ? strtoul(str, NULL, 0) : 0;
7981 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
7982 suspended->revoked = str ? strtoul(str, NULL, 0) : 0;
7983 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
7984 suspended->issued = str ? strtoul(str, NULL, 0) : 0;
7985 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
7986 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
7987 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
7988 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
7993 chanserv_channel_read(const char *key, struct record_data *hir)
7995 struct suspended *suspended;
7996 struct mod_chanmode *modes;
7997 struct chanNode *cNode;
7998 struct chanData *cData;
7999 struct dict *channel, *obj;
8000 char *str, *argv[10];
8004 channel = hir->d.object;
8006 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
8009 cNode = AddChannel(key, now, NULL, NULL);
8012 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
8015 cData = register_channel(cNode, str);
8018 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
8022 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
8024 enum levelOption lvlOpt;
8025 enum charOption chOpt;
8027 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
8028 cData->flags = atoi(str);
8030 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8032 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
8034 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
8035 else if(levelOptions[lvlOpt].old_flag)
8037 if(cData->flags & levelOptions[lvlOpt].old_flag)
8038 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
8040 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
8044 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8046 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
8048 cData->chOpts[chOpt] = str[0];
8051 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
8053 enum levelOption lvlOpt;
8054 enum charOption chOpt;
8057 cData->flags = base64toint(str, 5);
8058 count = strlen(str += 5);
8059 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8062 if(levelOptions[lvlOpt].old_flag)
8064 if(cData->flags & levelOptions[lvlOpt].old_flag)
8065 lvl = levelOptions[lvlOpt].flag_value;
8067 lvl = levelOptions[lvlOpt].default_value;
8069 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
8071 case 'c': lvl = UL_COOWNER; break;
8072 case 'm': lvl = UL_MASTER; break;
8073 case 'n': lvl = UL_OWNER+1; break;
8074 case 'o': lvl = UL_OP; break;
8075 case 'p': lvl = UL_PEON; break;
8076 case 'w': lvl = UL_OWNER; break;
8077 default: lvl = 0; break;
8079 cData->lvlOpts[lvlOpt] = lvl;
8081 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8082 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
8085 if((str = database_get_data(hir->d.object, KEY_EXPIRE, RECDB_QSTRING)))
8087 cData->expiry = atoi(str);
8088 if(cData->expiry > 0) {
8089 if(cData->expiry > now) {
8090 timeq_add(cData->expiry, chanserv_expire_channel, cData);
8092 timeq_add(1, chanserv_expire_channel, cData);
8099 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
8101 suspended = chanserv_read_suspended(obj);
8102 cData->suspended = suspended;
8103 suspended->cData = cData;
8104 /* We could use suspended->expires and suspended->revoked to
8105 * set the CHANNEL_SUSPENDED flag, but we don't. */
8107 else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
8109 suspended = calloc(1, sizeof(*suspended));
8110 suspended->issued = 0;
8111 suspended->revoked = 0;
8112 suspended->suspender = strdup(str);
8113 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
8114 suspended->expires = str ? atoi(str) : 0;
8115 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
8116 suspended->reason = strdup(str ? str : "No reason");
8117 suspended->previous = NULL;
8118 cData->suspended = suspended;
8119 suspended->cData = cData;
8123 cData->flags &= ~CHANNEL_SUSPENDED;
8124 suspended = NULL; /* to squelch a warning */
8127 if(IsSuspended(cData)) {
8128 if(suspended->expires > now)
8129 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
8130 else if(suspended->expires)
8131 cData->flags &= ~CHANNEL_SUSPENDED;
8134 if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
8135 struct mod_chanmode change;
8136 mod_chanmode_init(&change);
8138 change.args[0].mode = MODE_CHANOP;
8139 change.args[0].u.member = AddChannelUser(chanserv, cNode);
8140 mod_chanmode_announce(chanserv, cNode, &change);
8143 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
8144 cData->registered = str ? strtoul(str, NULL, 0) : now;
8145 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
8146 cData->visited = str ? strtoul(str, NULL, 0) : now;
8147 str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
8148 cData->ownerTransfer = str ? strtoul(str, NULL, 0) : 0;
8149 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
8150 cData->max = str ? atoi(str) : 0;
8151 str = database_get_data(channel, KEY_MAX_TIME, RECDB_QSTRING);
8152 cData->max_time = str ? atoi(str) : 0;
8153 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
8154 cData->greeting = str ? strdup(str) : NULL;
8155 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
8156 cData->user_greeting = str ? strdup(str) : NULL;
8157 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
8158 cData->topic_mask = str ? strdup(str) : NULL;
8159 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
8160 cData->topic = str ? strdup(str) : NULL;
8162 str = database_get_data(channel, KEY_VOTE, RECDB_QSTRING);
8164 cData->vote = str ? strdup(str) : NULL;
8165 dict_delete(cData->vote_options);
8166 cData->vote_options = dict_new();
8167 dict_set_free_data(cData->vote_options, free_vote_options);
8168 str = database_get_data(channel, KEY_VOTE_START, RECDB_QSTRING);
8169 cData->vote_start = str ? atoi(str) : 0;
8170 obj = database_get_data(channel, KEY_VOTE_OPTIONS, RECDB_OBJECT);
8171 for(it = dict_first(obj); it; it = iter_next(it)) {
8172 vote_option_read_helper(iter_key(it), iter_data(it), cData);
8176 if(!IsSuspended(cData)
8177 && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
8178 && (argc = split_line(str, 0, ArrayLength(argv), argv))
8179 && (modes = mod_chanmode_parse(cNode, NULL, argv, argc, MCP_KEY_FREE|MCP_NO_APASS, 0))) {
8180 cData->modes = *modes;
8182 cData->modes.modes_set |= MODE_REGISTERED;
8183 if(cData->modes.argc > 1)
8184 cData->modes.argc = 1;
8185 mod_chanmode_announce(chanserv, cNode, &cData->modes);
8186 mod_chanmode_free(modes);
8189 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
8190 for(it = dict_first(obj); it; it = iter_next(it))
8191 user_read_helper(iter_key(it), iter_data(it), cData);
8193 if(!cData->users && !IsProtected(cData))
8195 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
8196 unregister_channel(cData, "has empty user list.");
8200 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
8201 for(it = dict_first(obj); it; it = iter_next(it))
8202 ban_read_helper(iter_key(it), iter_data(it), cData);
8204 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
8205 for(it = dict_first(obj); it; it = iter_next(it))
8207 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
8208 struct record_data *rd = iter_data(it);
8209 const char *note, *setter;
8211 if(rd->type != RECDB_OBJECT)
8213 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
8217 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
8219 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
8221 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
8225 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
8226 if(!setter) setter = "<unknown>";
8227 chanserv_add_channel_note(cData, ntype, setter, note);
8235 chanserv_dnr_read(const char *key, struct record_data *hir)
8237 const char *setter, *reason, *str;
8238 struct do_not_register *dnr;
8239 unsigned long expiry;
8241 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
8244 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
8247 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
8250 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
8253 str = database_get_data(hir->d.object, KEY_EXPIRES, RECDB_QSTRING);
8254 expiry = str ? strtoul(str, NULL, 0) : 0;
8255 if(expiry && expiry <= now)
8257 dnr = chanserv_add_dnr(key, setter, expiry, reason);
8260 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
8262 dnr->set = atoi(str);
8268 chanserv_saxdb_read(struct dict *database)
8270 struct dict *section;
8273 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
8274 for(it = dict_first(section); it; it = iter_next(it))
8275 chanserv_note_type_read(iter_key(it), iter_data(it));
8277 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
8278 for(it = dict_first(section); it; it = iter_next(it))
8279 chanserv_channel_read(iter_key(it), iter_data(it));
8281 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
8282 for(it = dict_first(section); it; it = iter_next(it))
8283 chanserv_dnr_read(iter_key(it), iter_data(it));
8289 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
8291 int high_present = 0;
8292 saxdb_start_record(ctx, KEY_USERS, 1);
8293 for(; uData; uData = uData->next)
8295 if((uData->access >= UL_PRESENT) && uData->present && !HANDLE_FLAGGED(uData->handle, BOT))
8297 saxdb_start_record(ctx, uData->handle->handle, 0);
8298 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
8299 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
8301 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
8302 if(uData->channel->vote && uData->voted)
8303 saxdb_write_int(ctx, KEY_VOTE_VOTED, uData->voted);
8304 if(uData->channel->vote && uData->votefor)
8305 saxdb_write_int(ctx, KEY_VOTE_VOTEDFOR, uData->votefor);
8307 saxdb_write_string(ctx, KEY_INFO, uData->info);
8308 saxdb_end_record(ctx);
8310 saxdb_end_record(ctx);
8311 return high_present;
8315 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
8319 saxdb_start_record(ctx, KEY_BANS, 1);
8320 for(; bData; bData = bData->next)
8322 saxdb_start_record(ctx, bData->mask, 0);
8323 saxdb_write_int(ctx, KEY_SET, bData->set);
8324 if(bData->triggered)
8325 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
8327 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
8329 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
8331 saxdb_write_string(ctx, KEY_REASON, bData->reason);
8332 saxdb_end_record(ctx);
8334 saxdb_end_record(ctx);
8338 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
8340 saxdb_start_record(ctx, name, 0);
8341 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
8342 saxdb_write_string(ctx, KEY_REASON, susp->reason);
8344 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
8346 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
8348 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
8350 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
8351 saxdb_end_record(ctx);
8355 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
8359 enum levelOption lvlOpt;
8360 enum charOption chOpt;
8363 saxdb_start_record(ctx, channel->channel->name, 1);
8365 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
8366 saxdb_write_int(ctx, KEY_MAX, channel->max);
8367 saxdb_write_int(ctx, KEY_MAX_TIME, channel->max_time);
8369 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
8370 if(channel->registrar)
8371 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
8372 if(channel->greeting)
8373 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
8374 if(channel->user_greeting)
8375 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
8376 if(channel->topic_mask)
8377 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
8378 if(channel->suspended)
8379 chanserv_write_suspended(ctx, "suspended", channel->suspended);
8381 saxdb_write_int(ctx, KEY_EXPIRE, channel->expiry);
8384 saxdb_write_string(ctx, KEY_VOTE, channel->vote);
8385 if(channel->vote_start)
8386 saxdb_write_int(ctx, KEY_VOTE_START, channel->vote_start);
8387 if (dict_size(channel->vote_options)) {
8388 saxdb_start_record(ctx, KEY_VOTE_OPTIONS, 1);
8389 for (it = dict_first(channel->vote_options); it; it = iter_next(it)) {
8390 struct vote_option *vOpt = iter_data(it);
8392 sprintf(str,"%i",vOpt->option_id);
8393 saxdb_start_record(ctx, str, 0);
8395 saxdb_write_int(ctx, KEY_VOTE_OPTION_VOTED, vOpt->voted);
8397 saxdb_write_string(ctx, KEY_VOTE_OPTION_NAME, vOpt->name);
8398 saxdb_end_record(ctx);
8400 saxdb_end_record(ctx);
8404 saxdb_start_record(ctx, KEY_OPTIONS, 0);
8405 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
8406 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8407 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
8408 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8410 buf[0] = channel->chOpts[chOpt];
8412 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
8414 saxdb_end_record(ctx);
8416 if(channel->modes.modes_set || channel->modes.modes_clear)
8418 mod_chanmode_format(&channel->modes, buf);
8419 saxdb_write_string(ctx, KEY_MODES, buf);
8422 high_present = chanserv_write_users(ctx, channel->users);
8423 chanserv_write_bans(ctx, channel->bans);
8425 if(dict_size(channel->notes))
8429 saxdb_start_record(ctx, KEY_NOTES, 1);
8430 for(it = dict_first(channel->notes); it; it = iter_next(it))
8432 struct note *note = iter_data(it);
8433 saxdb_start_record(ctx, iter_key(it), 0);
8434 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
8435 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
8436 saxdb_end_record(ctx);
8438 saxdb_end_record(ctx);
8441 if(channel->ownerTransfer)
8442 saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
8443 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
8444 saxdb_end_record(ctx);
8448 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
8452 saxdb_start_record(ctx, ntype->name, 0);
8453 switch(ntype->set_access_type)
8455 case NOTE_SET_CHANNEL_ACCESS:
8456 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
8458 case NOTE_SET_CHANNEL_SETTER:
8459 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
8461 case NOTE_SET_PRIVILEGED: default:
8462 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
8465 switch(ntype->visible_type)
8467 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
8468 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
8469 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
8471 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
8472 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
8473 saxdb_end_record(ctx);
8477 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
8479 struct do_not_register *dnr;
8480 dict_iterator_t it, next;
8482 for(it = dict_first(dnrs); it; it = next)
8484 next = iter_next(it);
8485 dnr = iter_data(it);
8486 if(dnr->expires && dnr->expires <= now)
8488 dict_remove(dnrs, iter_key(it));
8491 saxdb_start_record(ctx, dnr->chan_name, 0);
8493 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
8495 saxdb_write_int(ctx, KEY_EXPIRES, dnr->expires);
8496 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
8497 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
8498 saxdb_end_record(ctx);
8503 chanserv_saxdb_write(struct saxdb_context *ctx)
8506 struct chanData *channel;
8509 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
8510 for(it = dict_first(note_types); it; it = iter_next(it))
8511 chanserv_write_note_type(ctx, iter_data(it));
8512 saxdb_end_record(ctx);
8515 saxdb_start_record(ctx, KEY_DNR, 1);
8516 write_dnrs_helper(ctx, handle_dnrs);
8517 write_dnrs_helper(ctx, plain_dnrs);
8518 write_dnrs_helper(ctx, mask_dnrs);
8519 saxdb_end_record(ctx);
8522 saxdb_start_record(ctx, KEY_CHANNELS, 1);
8523 for(channel = channelList; channel; channel = channel->next)
8524 chanserv_write_channel(ctx, channel);
8525 saxdb_end_record(ctx);
8531 chanserv_db_cleanup(void) {
8533 unreg_part_func(handle_part);
8535 unregister_channel(channelList, "terminating.");
8536 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
8537 UnlockChannel(chanserv_conf.support_channels.list[ii]);
8538 free(chanserv_conf.support_channels.list);
8539 dict_delete(handle_dnrs);
8540 dict_delete(plain_dnrs);
8541 dict_delete(mask_dnrs);
8542 dict_delete(note_types);
8543 free_string_list(chanserv_conf.eightball);
8544 free_string_list(chanserv_conf.old_ban_names);
8545 free_string_list(chanserv_conf.set_shows);
8546 free(set_shows_list.list);
8547 free(uset_shows_list.list);
8550 struct userData *helper = helperList;
8551 helperList = helperList->next;
8556 #if defined(GCC_VARMACROS)
8557 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ARGS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ARGS)
8558 #elif defined(C99_VARMACROS)
8559 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, __VA_ARGS__)
8561 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
8562 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
8565 init_chanserv(const char *nick)
8567 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
8568 conf_register_reload(chanserv_conf_read);
8572 reg_server_link_func(handle_server_link);
8573 reg_new_channel_func(handle_new_channel);
8574 reg_join_func(handle_join);
8575 reg_part_func(handle_part);
8576 reg_kick_func(handle_kick);
8577 reg_topic_func(handle_topic);
8578 reg_mode_change_func(handle_mode);
8579 reg_nick_change_func(handle_nick_change);
8580 reg_auth_func(handle_auth);
8583 reg_handle_rename_func(handle_rename);
8584 reg_unreg_func(handle_unreg);
8586 handle_dnrs = dict_new();
8587 dict_set_free_data(handle_dnrs, free);
8588 plain_dnrs = dict_new();
8589 dict_set_free_data(plain_dnrs, free);
8590 mask_dnrs = dict_new();
8591 dict_set_free_data(mask_dnrs, free);
8593 reg_svccmd_unbind_func(handle_svccmd_unbind);
8594 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
8595 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
8596 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
8597 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
8598 DEFINE_COMMAND(dnrsearch, 3, 0, "template", "noregister", NULL);
8599 modcmd_register(chanserv_module, "dnrsearch print", NULL, 0, 0, NULL);
8600 modcmd_register(chanserv_module, "dnrsearch remove", NULL, 0, 0, NULL);
8601 modcmd_register(chanserv_module, "dnrsearch count", NULL, 0, 0, NULL);
8602 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
8603 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
8604 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
8605 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
8606 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
8608 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
8609 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
8611 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8612 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8613 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8614 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8615 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
8617 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
8618 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
8619 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
8620 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8621 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8623 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8624 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
8625 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8626 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
8628 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
8629 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
8630 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8631 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8632 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
8633 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8634 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8635 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8637 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8638 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8639 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8640 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
8641 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
8642 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
8643 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
8644 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
8645 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8646 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
8647 DEFINE_COMMAND(invitemeall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8648 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
8649 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
8650 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8651 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8653 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
8654 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8655 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8656 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8657 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
8659 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
8660 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
8662 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
8663 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8664 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8665 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8666 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8667 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8668 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8669 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8670 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8671 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8672 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8674 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
8675 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
8677 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
8678 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
8679 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
8680 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
8682 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
8683 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
8684 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
8685 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
8686 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
8688 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8689 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8690 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8691 DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8692 DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8693 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8695 DEFINE_COMMAND(addvote, 1, MODCMD_REQUIRE_AUTHED, NULL);
8696 DEFINE_COMMAND(delvote, 1, MODCMD_REQUIRE_AUTHED, NULL);
8697 DEFINE_COMMAND(addoption, 1, MODCMD_REQUIRE_AUTHED, NULL);
8698 DEFINE_COMMAND(deloption, 1, MODCMD_REQUIRE_AUTHED, NULL);
8699 DEFINE_COMMAND(vote, 1, MODCMD_REQUIRE_AUTHED, NULL);
8700 DEFINE_COMMAND(startvote, 1, MODCMD_REQUIRE_AUTHED, NULL);
8701 DEFINE_COMMAND(endvote, 1, MODCMD_REQUIRE_AUTHED, NULL);
8702 DEFINE_COMMAND(voteresults, 1, MODCMD_REQUIRE_AUTHED, NULL);
8704 DEFINE_COMMAND(opme, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
8706 /* Channel options */
8707 DEFINE_CHANNEL_OPTION(defaulttopic);
8708 DEFINE_CHANNEL_OPTION(topicmask);
8709 DEFINE_CHANNEL_OPTION(greeting);
8710 DEFINE_CHANNEL_OPTION(usergreeting);
8711 DEFINE_CHANNEL_OPTION(modes);
8712 DEFINE_CHANNEL_OPTION(enfops);
8713 DEFINE_CHANNEL_OPTION(giveops);
8714 DEFINE_CHANNEL_OPTION(protect);
8715 DEFINE_CHANNEL_OPTION(enfmodes);
8716 DEFINE_CHANNEL_OPTION(enftopic);
8717 DEFINE_CHANNEL_OPTION(pubcmd);
8718 DEFINE_CHANNEL_OPTION(givevoice);
8719 DEFINE_CHANNEL_OPTION(userinfo);
8720 DEFINE_CHANNEL_OPTION(dynlimit);
8721 DEFINE_CHANNEL_OPTION(topicsnarf);
8722 DEFINE_CHANNEL_OPTION(vote);
8723 DEFINE_CHANNEL_OPTION(nodelete);
8724 DEFINE_CHANNEL_OPTION(toys);
8725 DEFINE_CHANNEL_OPTION(setters);
8726 DEFINE_CHANNEL_OPTION(topicrefresh);
8727 DEFINE_CHANNEL_OPTION(ctcpusers);
8728 DEFINE_CHANNEL_OPTION(ctcpreaction);
8729 DEFINE_CHANNEL_OPTION(inviteme);
8730 DEFINE_CHANNEL_OPTION(unreviewed);
8731 modcmd_register(chanserv_module, "set expire", chan_opt_expire, 1, 0, "flags", "+helping", NULL);
8732 modcmd_register(chanserv_module, "set unreviewed on", NULL, 0, 0, "flags", "+helping", NULL);
8733 modcmd_register(chanserv_module, "set unreviewed off", NULL, 0, 0, "flags", "+oper", NULL);
8735 DEFINE_CHANNEL_OPTION(offchannel);
8736 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
8738 /* Alias set topic to set defaulttopic for compatibility. */
8739 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
8742 DEFINE_USER_OPTION(noautoop);
8743 DEFINE_USER_OPTION(autoinvite);
8744 DEFINE_USER_OPTION(info);
8746 /* Alias uset autovoice to uset autoop. */
8747 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
8749 note_types = dict_new();
8750 dict_set_free_data(note_types, chanserv_deref_note_type);
8753 const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
8754 chanserv = AddLocalUser(nick, nick, NULL, "Channel Services", modes);
8755 service_register(chanserv)->trigger = '!';
8756 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
8758 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
8760 if(chanserv_conf.channel_expire_frequency)
8761 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
8763 if(chanserv_conf.dnr_expire_frequency)
8764 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
8766 if(chanserv_conf.refresh_period)
8768 unsigned long next_refresh;
8769 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
8770 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
8773 reg_exit_func(chanserv_db_cleanup);
8774 message_register_table(msgtab);