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;
3242 struct userData *uData;
3245 mod_chanmode_init(&change);
3247 change.args[0].u.member = GetUserMode(channel, user);
3248 if(!change.args[0].u.member)
3251 reply("MSG_CHANNEL_ABSENT", channel->name);
3255 struct devnull_class *devnull;
3256 if(user->handle_info->devnull && (devnull = devnull_get(user->handle_info->devnull)) && (devnull->modes & DEVNULL_MODE_OPME))
3258 change.args[0].mode = MODE_CHANOP;
3259 errmsg = "CSMSG_ALREADY_OPPED";
3264 reply("CSMSG_NO_ACCESS");
3267 change.args[0].mode &= ~change.args[0].u.member->modes;
3268 if(!change.args[0].mode)
3271 reply(errmsg, channel->name);
3274 modcmd_chanmode_announce(&change);
3279 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, unsigned int *victimCount, struct modeNode **victims)
3285 for(ii=0; ii<channel->members.used; ii++)
3287 struct modeNode *mn = channel->members.list[ii];
3289 if(IsService(mn->user))
3292 if(!user_matches_glob(mn->user, ban, MATCH_USENICK | MATCH_VISIBLE))
3295 if(protect_user(mn->user, user, channel->channel_info))
3299 victims[(*victimCount)++] = mn;
3305 eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3307 struct userNode *victim;
3308 struct modeNode **victims;
3309 unsigned int offset, n, victimCount, duration = 0;
3310 char *reason = "Bye.", *ban, *name;
3311 char interval[INTERVALLEN];
3313 offset = (action & ACTION_ADD_TIMED_BAN) ? 3 : 2;
3314 REQUIRE_PARAMS(offset);
3315 if(argc > offset && IsNetServ(user))
3317 if(*argv[offset] == '$') {
3318 struct userNode *hib;
3319 const char *accountnameb = argv[offset] + 1;
3320 if(!(hib = GetUserH(accountnameb)))
3322 reply("MSG_HANDLE_UNKNOWN", accountnameb);
3331 reason = unsplit_string(argv + offset, argc - offset, NULL);
3332 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
3334 /* Truncate the reason to a length of TOPICLEN, as
3335 the ircd does; however, leave room for an ellipsis
3336 and the kicker's nick. */
3337 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
3341 if((victim = GetUserH(argv[1])))
3343 victims = alloca(sizeof(victims[0]));
3344 victims[0] = GetUserMode(channel, victim);
3345 /* XXX: The comparison with ACTION_KICK is just because all
3346 * other actions can work on users outside the channel, and we
3347 * want to allow those (e.g. unbans) in that case. If we add
3348 * some other ejection action for in-channel users, change
3350 victimCount = victims[0] ? 1 : 0;
3352 if(IsService(victim))
3354 reply("MSG_SERVICE_IMMUNE", victim->nick);
3358 if((action == ACTION_KICK) && !victimCount)
3360 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
3364 if(protect_user(victim, user, channel->channel_info))
3366 reply("CSMSG_USER_PROTECTED", victim->nick);
3370 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
3371 name = victim->nick;
3373 else if(!is_ircmask(argv[1]) && (*argv[1] == '*'))
3375 struct handle_info *hi;
3376 extern const char *titlehost_suffix;
3377 char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
3378 const char *accountname = argv[1] + 1;
3380 if(!(hi = get_handle_info(accountname)))
3382 reply("MSG_HANDLE_UNKNOWN", accountname);
3386 snprintf(banmask, sizeof(banmask), "*!*@%s.*.%s", hi->handle, titlehost_suffix);
3387 victims = alloca(sizeof(victims[0]) * channel->members.used);
3389 if(bad_channel_ban(channel, user, banmask, &victimCount, victims))
3391 reply("CSMSG_MASK_PROTECTED", banmask);
3395 if((action == ACTION_KICK) && (victimCount == 0))
3397 reply("CSMSG_NO_MATCHING_USERS", channel->name, banmask);
3401 name = ban = strdup(banmask);
3405 if(!is_ircmask(argv[1]))
3407 reply("MSG_NICK_UNKNOWN", argv[1]);
3411 victims = alloca(sizeof(victims[0]) * channel->members.used);
3413 if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
3415 reply("CSMSG_MASK_PROTECTED", argv[1]);
3419 if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
3421 reply("CSMSG_LAME_MASK", argv[1]);
3425 if((action == ACTION_KICK) && (victimCount == 0))
3427 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
3431 name = ban = strdup(argv[1]);
3434 /* Truncate the ban in place if necessary; we must ensure
3435 that 'ban' is a valid ban mask before sanitizing it. */
3436 sanitize_ircmask(ban);
3438 if(action & ACTION_ADD_BAN)
3440 struct banData *bData, *next;
3442 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans)
3444 reply("CSMSG_MAXIMUM_BANS", chanserv_conf.max_chan_bans);
3449 if(action & ACTION_ADD_TIMED_BAN)
3451 duration = ParseInterval(argv[2]);
3455 reply("CSMSG_DURATION_TOO_LOW");
3459 else if(duration > (86400 * 365 * 2))
3461 reply("CSMSG_DURATION_TOO_HIGH");
3467 for(bData = channel->channel_info->bans; bData; bData = next)
3469 if(match_ircglobs(bData->mask, ban))
3471 int exact = !irccasecmp(bData->mask, ban);
3473 /* The ban is redundant; there is already a ban
3474 with the same effect in place. */
3478 free(bData->reason);
3479 bData->reason = strdup(reason);
3480 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
3482 reply("CSMSG_REASON_CHANGE", ban);
3486 if(exact && bData->expires)
3490 /* If the ban matches an existing one exactly,
3491 extend the expiration time if the provided
3492 duration is longer. */
3493 if(duration && (now + duration > bData->expires))
3495 bData->expires = now + duration;
3506 /* Delete the expiration timeq entry and
3507 requeue if necessary. */
3508 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
3511 timeq_add(bData->expires, expire_ban, bData);
3515 /* automated kickban */
3518 reply("CSMSG_BAN_EXTENDED", ban, intervalString(interval, duration, user->handle_info));
3520 reply("CSMSG_BAN_ADDED", name, channel->name);
3526 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3533 if(match_ircglobs(ban, bData->mask))
3535 /* The ban we are adding makes previously existing
3536 bans redundant; silently remove them. */
3537 del_channel_ban(bData);
3541 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);
3543 name = ban = strdup(bData->mask);
3547 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
3549 extern const char *hidden_host_suffix;
3550 const char *old_name = chanserv_conf.old_ban_names->list[n];
3552 unsigned int l1, l2;
3555 l2 = strlen(old_name);
3558 if(irccasecmp(ban + l1 - l2, old_name))
3560 new_mask = malloc(MAXLEN);
3561 sprintf(new_mask, "%.*s%s", (int)(l1-l2), ban, hidden_host_suffix);
3563 name = ban = new_mask;
3568 if(action & ACTION_BAN)
3570 unsigned int exists;
3571 struct mod_chanmode *change;
3573 if(channel->banlist.used >= MAXBANS)
3576 reply("CSMSG_BANLIST_FULL", channel->name);
3581 exists = ChannelBanExists(channel, ban);
3582 change = mod_chanmode_alloc(victimCount + 1);
3583 for(n = 0; n < victimCount; ++n)
3585 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_VOICE;
3586 change->args[n].u.member = victims[n];
3590 change->args[n].mode = MODE_BAN;
3591 change->args[n++].u.hostmask = ban;
3595 modcmd_chanmode_announce(change);
3597 mod_chanmode_announce(chanserv, channel, change);
3598 mod_chanmode_free(change);
3600 if(exists && (action == ACTION_BAN))
3603 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3609 if(action & ACTION_KICK)
3611 char kick_reason[MAXLEN];
3612 sprintf(kick_reason, "(%s) %s", user->nick, reason);
3614 for(n = 0; n < victimCount; n++)
3615 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
3620 /* No response, since it was automated. */
3622 else if(action & ACTION_ADD_BAN)
3625 reply("CSMSG_TIMED_BAN_ADDED", name, channel->name, intervalString(interval, duration, user->handle_info));
3627 reply("CSMSG_BAN_ADDED", name, channel->name);
3629 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
3630 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
3631 else if(action & ACTION_BAN)
3632 reply("CSMSG_BAN_DONE", name, channel->name);
3633 else if(action & ACTION_KICK && victimCount)
3634 reply("CSMSG_KICK_DONE", name, channel->name);
3640 static CHANSERV_FUNC(cmd_kickban)
3642 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
3645 static CHANSERV_FUNC(cmd_kick)
3647 return eject_user(CSFUNC_ARGS, ACTION_KICK);
3650 static CHANSERV_FUNC(cmd_ban)
3652 return eject_user(CSFUNC_ARGS, ACTION_BAN);
3655 static CHANSERV_FUNC(cmd_addban)
3657 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN);
3660 static CHANSERV_FUNC(cmd_addtimedban)
3662 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN);
3665 struct mod_chanmode *
3666 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
3668 struct mod_chanmode *change;
3669 unsigned char *match;
3670 unsigned int ii, count;
3672 match = alloca(bans->used);
3675 for(ii = count = 0; ii < bans->used; ++ii)
3677 match[ii] = user_matches_glob(actee, bans->list[ii]->ban,
3678 MATCH_USENICK | MATCH_VISIBLE);
3685 for(ii = count = 0; ii < bans->used; ++ii)
3687 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
3694 change = mod_chanmode_alloc(count);
3695 for(ii = count = 0; ii < bans->used; ++ii)
3699 change->args[count].mode = MODE_REMOVE | MODE_BAN;
3700 change->args[count++].u.hostmask = strdup(bans->list[ii]->ban);
3702 assert(count == change->argc);
3707 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3709 struct userNode *actee;
3715 /* may want to allow a comma delimited list of users... */
3716 if(!(actee = GetUserH(argv[1])))
3718 if(!is_ircmask(argv[1]) && *argv[1] == '*')
3720 char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
3721 const char *accountname = argv[1] + 1;
3723 snprintf(banmask, sizeof(banmask), "*!*@%s.*", accountname);
3724 mask = strdup(banmask);
3726 else if(!is_ircmask(argv[1]))
3728 reply("MSG_NICK_UNKNOWN", argv[1]);
3733 mask = strdup(argv[1]);
3737 /* We don't sanitize the mask here because ircu
3739 if(action & ACTION_UNBAN)
3741 struct mod_chanmode *change;
3742 change = find_matching_bans(&channel->banlist, actee, mask);
3747 modcmd_chanmode_announce(change);
3748 for(ii = 0; ii < change->argc; ++ii)
3749 free((char*)change->args[ii].u.hostmask);
3750 mod_chanmode_free(change);
3755 if(action & ACTION_DEL_BAN)
3757 struct banData *ban, *next;
3759 ban = channel->channel_info->bans;
3763 for( ; ban && !user_matches_glob(actee, ban->mask,
3764 MATCH_USENICK | MATCH_VISIBLE);
3767 for( ; ban && !match_ircglobs(mask, ban->mask);
3772 del_channel_ban(ban);
3779 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3781 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3787 static CHANSERV_FUNC(cmd_unban)
3789 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3792 static CHANSERV_FUNC(cmd_delban)
3794 /* it doesn't necessarily have to remove the channel ban - may want
3795 to make that an option. */
3796 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_BAN);
3799 static CHANSERV_FUNC(cmd_unbanme)
3801 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3802 long flags = ACTION_UNBAN;
3804 /* remove permanent bans if the user has the proper access. */
3805 if(uData->access >= UL_MASTER)
3806 flags |= ACTION_DEL_BAN;
3808 argv[1] = user->nick;
3809 return unban_user(user, channel, 2, argv, cmd, flags);
3812 static CHANSERV_FUNC(cmd_unbanall)
3814 struct mod_chanmode *change;
3817 if(!channel->banlist.used)
3819 reply("CSMSG_NO_BANS", channel->name);
3823 change = mod_chanmode_alloc(channel->banlist.used);
3824 for(ii=0; ii<channel->banlist.used; ii++)
3826 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3827 change->args[ii].u.hostmask = strdup(channel->banlist.list[ii]->ban);
3829 modcmd_chanmode_announce(change);
3830 for(ii = 0; ii < change->argc; ++ii)
3831 free((char*)change->args[ii].u.hostmask);
3832 mod_chanmode_free(change);
3833 reply("CSMSG_BANS_REMOVED", channel->name);
3837 static CHANSERV_FUNC(cmd_open)
3839 struct mod_chanmode *change;
3842 change = find_matching_bans(&channel->banlist, user, NULL);
3844 change = mod_chanmode_alloc(0);
3845 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3846 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3847 && channel->channel_info->modes.modes_set)
3848 change->modes_clear &= ~channel->channel_info->modes.modes_set;
3849 modcmd_chanmode_announce(change);
3850 reply("CSMSG_CHANNEL_OPENED", channel->name);
3851 for(ii = 0; ii < change->argc; ++ii)
3852 free((char*)change->args[ii].u.hostmask);
3853 mod_chanmode_free(change);
3857 static CHANSERV_FUNC(cmd_myaccess)
3859 static struct string_buffer sbuf;
3860 struct handle_info *target_handle;
3861 struct userData *uData;
3866 target_handle = user->handle_info;
3867 else if(!IsStaff(user))
3869 reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
3872 else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3875 if(!oper_outranks(user, target_handle))
3878 if(!target_handle->channels)
3880 reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
3884 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3885 for(uData = target_handle->channels; uData; uData = uData->u_next)
3887 struct chanData *cData = uData->channel;
3890 if(uData->access > UL_OWNER)
3892 if(uData->access == UL_OWNER)
3895 if(IsProtected(cData)
3896 && (target_handle != user->handle_info)
3897 && !GetTrueChannelAccess(cData, user->handle_info)
3898 && !IsNetworkHelper(user))
3901 string_buffer_append_printf(&sbuf, "[%s (%d", cData->channel->name, uData->access);
3902 if(uData->flags != USER_AUTO_OP)
3903 string_buffer_append(&sbuf, ',');
3904 if(IsUserSuspended(uData))
3905 string_buffer_append(&sbuf, 's');
3906 if(IsUserAutoOp(uData))
3908 if(uData->access >= cData->lvlOpts[lvlGiveOps])
3909 string_buffer_append(&sbuf, 'o');
3910 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
3911 string_buffer_append(&sbuf, 'v');
3913 if(IsUserAutoInvite(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe]))
3914 string_buffer_append(&sbuf, 'i');
3916 string_buffer_append_printf(&sbuf, ")] %s", uData->info);
3918 string_buffer_append_string(&sbuf, ")]");
3919 string_buffer_append(&sbuf, '\0');
3920 send_message_type(4, user, cmd->parent->bot, "%s", sbuf.list);
3924 reply("CSMSG_MYACCESS_COUNT_1", target_handle->handle, ccount, ocount);
3926 reply("CSMSG_MYACCESS_COUNT", target_handle->handle, ccount, ocount);
3932 static CHANSERV_FUNC(cmd_access)
3934 struct userNode *target;
3935 struct handle_info *target_handle;
3936 struct userData *uData;
3938 char prefix[MAXLEN];
3943 target_handle = target->handle_info;
3945 else if((target = GetUserH(argv[1])))
3947 target_handle = target->handle_info;
3949 else if(argv[1][0] == '*')
3951 if(!(target_handle = get_handle_info(argv[1]+1)))
3953 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3959 reply("MSG_NICK_UNKNOWN", argv[1]);
3963 assert(target || target_handle);
3965 if(target == chanserv)
3967 reply("CSMSG_IS_CHANSERV");
3975 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
3980 reply("MSG_USER_AUTHENTICATE", target->nick);
3983 reply("MSG_AUTHENTICATE");
3989 const char *epithet = NULL, *type = NULL;
3992 epithet = chanserv_conf.irc_operator_epithet;
3993 type = user_find_message(user, "CSMSG_OPERATOR_TITLE");
3995 else if(IsNetworkHelper(target))
3997 epithet = chanserv_conf.network_helper_epithet;
3998 type = user_find_message(user, "CSMSG_UC_H_TITLE");
4000 else if(IsSupportHelper(target))
4002 epithet = chanserv_conf.support_helper_epithet;
4003 type = user_find_message(user, "CSMSG_LC_H_TITLE");
4007 if(target_handle->epithet)
4008 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
4010 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
4012 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
4016 sprintf(prefix, "%s", target_handle->handle);
4019 if(!channel->channel_info)
4021 reply("CSMSG_NOT_REGISTERED", channel->name);
4025 helping = HANDLE_FLAGGED(target_handle, HELPING)
4026 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
4027 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
4029 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
4030 /* To prevent possible information leaks, only show infolines
4031 * if the requestor is in the channel or it's their own
4033 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
4035 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
4037 /* Likewise, only say it's suspended if the user has active
4038 * access in that channel or it's their own entry. */
4039 if(IsUserSuspended(uData)
4040 && (GetChannelUser(channel->channel_info, user->handle_info)
4041 || (user->handle_info == uData->handle)))
4043 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
4048 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
4055 zoot_list(struct listData *list)
4057 struct userData *uData;
4058 unsigned int start, curr, highest, lowest;
4059 struct helpfile_table tmp_table;
4060 const char **temp, *msg;
4062 if(list->table.length == 1)
4065 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
4067 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
4068 msg = user_find_message(list->user, "MSG_NONE");
4069 send_message_type(4, list->user, list->bot, " %s", msg);
4071 tmp_table.width = list->table.width;
4072 tmp_table.flags = list->table.flags;
4073 list->table.contents[0][0] = " ";
4074 highest = list->highest;
4075 if(list->lowest != 0)
4076 lowest = list->lowest;
4077 else if(highest < 100)
4080 lowest = highest - 100;
4081 for(start = curr = 1; curr < list->table.length; )
4083 uData = list->users[curr-1];
4084 list->table.contents[curr++][0] = " ";
4085 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
4088 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, lowest, highest, list->search);
4090 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, lowest, highest);
4091 temp = list->table.contents[--start];
4092 list->table.contents[start] = list->table.contents[0];
4093 tmp_table.contents = list->table.contents + start;
4094 tmp_table.length = curr - start;
4095 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
4096 list->table.contents[start] = temp;
4098 highest = lowest - 1;
4099 lowest = (highest < 100) ? 0 : (highest - 99);
4105 def_list(struct listData *list)
4109 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
4111 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
4112 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4113 if(list->table.length == 1)
4115 msg = user_find_message(list->user, "MSG_NONE");
4116 send_message_type(4, list->user, list->bot, " %s", msg);
4121 userData_access_comp(const void *arg_a, const void *arg_b)
4123 const struct userData *a = *(struct userData**)arg_a;
4124 const struct userData *b = *(struct userData**)arg_b;
4126 if(a->access != b->access)
4127 res = b->access - a->access;
4129 res = irccasecmp(a->handle->handle, b->handle->handle);
4134 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
4136 void (*send_list)(struct listData *);
4137 struct userData *uData;
4138 struct listData lData;
4139 unsigned int matches;
4143 lData.bot = cmd->parent->bot;
4144 lData.channel = channel;
4145 lData.lowest = lowest;
4146 lData.highest = highest;
4147 lData.search = (argc > 1) ? argv[1] : NULL;
4148 send_list = def_list;
4149 (void)zoot_list; /* since it doesn't show user levels */
4151 if(user->handle_info)
4153 switch(user->handle_info->userlist_style)
4155 case HI_STYLE_DEF: send_list = def_list; break;
4156 case HI_STYLE_ZOOT: send_list = def_list; break;
4160 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
4162 for(uData = channel->channel_info->users; uData; uData = uData->next)
4164 if((uData->access < lowest)
4165 || (uData->access > highest)
4166 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
4168 lData.users[matches++] = uData;
4170 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
4172 lData.table.length = matches+1;
4173 lData.table.width = 4;
4174 lData.table.flags = TABLE_NO_FREE;
4175 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
4176 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
4177 lData.table.contents[0] = ary;
4180 ary[2] = "Last Seen";
4182 for(matches = 1; matches < lData.table.length; ++matches)
4184 char seen[INTERVALLEN];
4186 uData = lData.users[matches-1];
4187 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
4188 lData.table.contents[matches] = ary;
4189 ary[0] = strtab(uData->access);
4190 ary[1] = uData->handle->handle;
4193 else if(!uData->seen)
4196 ary[2] = intervalString(seen, now - uData->seen, user->handle_info);
4197 ary[2] = strdup(ary[2]);
4198 if(IsUserSuspended(uData))
4199 ary[3] = "Suspended";
4200 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
4201 ary[3] = "Vacation";
4202 else if(HANDLE_FLAGGED(uData->handle, BOT))
4208 for(matches = 1; matches < lData.table.length; ++matches)
4210 free((char*)lData.table.contents[matches][2]);
4211 free(lData.table.contents[matches]);
4213 free(lData.table.contents[0]);
4214 free(lData.table.contents);
4215 reply("CSMSG_TOTAL_USERS",(lData.table.length - 1),channel->name);
4219 static CHANSERV_FUNC(cmd_users)
4221 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
4224 static CHANSERV_FUNC(cmd_wlist)
4226 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
4229 static CHANSERV_FUNC(cmd_clist)
4231 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
4234 static CHANSERV_FUNC(cmd_mlist)
4236 return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
4239 static CHANSERV_FUNC(cmd_olist)
4241 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
4244 static CHANSERV_FUNC(cmd_plist)
4246 return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
4249 static CHANSERV_FUNC(cmd_bans)
4251 struct userNode *search_u = NULL;
4252 struct helpfile_table tbl;
4253 unsigned int matches = 0, timed = 0, search_wilds = 0, ii;
4254 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
4255 const char *msg_never, *triggered, *expires;
4256 struct banData *ban, **bans;
4260 else if(strchr(search = argv[1], '!'))
4263 search_wilds = search[strcspn(search, "?*")];
4265 else if(!(search_u = GetUserH(search)))
4266 reply("MSG_NICK_UNKNOWN", search);
4268 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
4270 for(ban = channel->channel_info->bans; ban; ban = ban->next)
4274 if(!user_matches_glob(search_u, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
4279 if(search_wilds ? !match_ircglobs(search, ban->mask) : !match_ircglob(search, ban->mask))
4282 bans[matches++] = ban;
4287 tbl.length = matches + 1;
4288 tbl.width = 4 + timed;
4290 tbl.flags = TABLE_NO_FREE;
4291 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
4292 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4293 tbl.contents[0][0] = "Mask";
4294 tbl.contents[0][1] = "Set By";
4295 tbl.contents[0][2] = "Triggered";
4298 tbl.contents[0][3] = "Expires";
4299 tbl.contents[0][4] = "Reason";
4302 tbl.contents[0][3] = "Reason";
4305 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4307 free(tbl.contents[0]);
4312 msg_never = user_find_message(user, "MSG_NEVER");
4313 for(ii = 0; ii < matches; )
4319 else if(ban->expires)
4320 expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
4322 expires = msg_never;
4325 triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
4327 triggered = msg_never;
4329 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4330 tbl.contents[ii][0] = ban->mask;
4331 tbl.contents[ii][1] = ban->owner;
4332 tbl.contents[ii][2] = strdup(triggered);
4335 tbl.contents[ii][3] = strdup(expires);
4336 tbl.contents[ii][4] = ban->reason;
4339 tbl.contents[ii][3] = ban->reason;
4341 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4342 reply("MSG_MATCH_COUNT", matches);
4343 for(ii = 1; ii < tbl.length; ++ii)
4345 free((char*)tbl.contents[ii][2]);
4347 free((char*)tbl.contents[ii][3]);
4348 free(tbl.contents[ii]);
4350 free(tbl.contents[0]);
4356 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
4358 struct chanData *cData = channel->channel_info;
4359 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
4361 if(cData->topic_mask)
4362 return !match_ircglob(new_topic, cData->topic_mask);
4363 else if(cData->topic)
4364 return irccasecmp(new_topic, cData->topic);
4369 static CHANSERV_FUNC(cmd_topic)
4371 struct chanData *cData;
4374 cData = channel->channel_info;
4379 SetChannelTopic(channel, chanserv, cData->topic, 1);
4380 reply("CSMSG_TOPIC_SET", cData->topic);
4384 reply("CSMSG_NO_TOPIC", channel->name);
4388 topic = unsplit_string(argv + 1, argc - 1, NULL);
4389 /* If they say "!topic *", use an empty topic. */
4390 if((topic[0] == '*') && (topic[1] == 0))
4392 if(bad_topic(channel, user, topic))
4394 char *topic_mask = cData->topic_mask;
4397 char new_topic[TOPICLEN+1], tchar;
4398 int pos=0, starpos=-1, dpos=0, len;
4400 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
4407 len = strlen(topic);
4408 if((dpos + len) > TOPICLEN)
4409 len = TOPICLEN + 1 - dpos;
4410 memcpy(new_topic+dpos, topic, len);
4414 case '\\': tchar = topic_mask[pos++]; /* and fall through */
4415 default: new_topic[dpos++] = tchar; break;
4418 if((dpos > TOPICLEN) || tchar)
4421 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
4422 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
4425 new_topic[dpos] = 0;
4426 SetChannelTopic(channel, chanserv, new_topic, 1);
4428 reply("CSMSG_TOPIC_LOCKED", channel->name);
4433 SetChannelTopic(channel, chanserv, topic, 1);
4435 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
4437 /* Grab the topic and save it as the default topic. */
4439 cData->topic = strdup(channel->topic);
4445 static CHANSERV_FUNC(cmd_mode)
4447 struct userData *uData;
4448 struct mod_chanmode *change;
4454 change = &channel->channel_info->modes;
4455 if(change->modes_set || change->modes_clear) {
4456 modcmd_chanmode_announce(change);
4457 reply("CSMSG_DEFAULTED_MODES", channel->name);
4459 reply("CSMSG_NO_MODES", channel->name);
4463 uData = GetChannelUser(channel->channel_info, user->handle_info);
4465 base_oplevel = MAXOPLEVEL;
4466 else if (uData->access >= UL_OWNER)
4469 base_oplevel = 1 + UL_OWNER - uData->access;
4470 change = mod_chanmode_parse(channel, user, argv+1, argc-1, MCP_KEY_FREE|MCP_IGN_REGISTERED|MCP_NO_APASS, base_oplevel);
4473 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
4477 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
4478 && mode_lock_violated(&channel->channel_info->modes, change))
4481 mod_chanmode_format(&channel->channel_info->modes, modes);
4482 reply("CSMSG_MODE_LOCKED", modes, channel->name);
4486 modcmd_chanmode_announce(change);
4487 mod_chanmode_format(change, fmt);
4488 mod_chanmode_free(change);
4489 reply("CSMSG_MODES_SET", fmt);
4494 chanserv_del_invite_mark(void *data)
4496 struct ChanUser *chanuser = data;
4497 struct chanNode *channel = chanuser->chan;
4499 if(!channel) return;
4500 for(i = 0; i < channel->invited.used; i++)
4502 if(channel->invited.list[i] == chanuser->user) {
4503 userList_remove(&channel->invited, chanuser->user);
4509 static CHANSERV_FUNC(cmd_invite)
4511 struct userData *uData;
4512 struct userNode *invite;
4513 struct ChanUser *chanuser;
4516 uData = GetChannelUser(channel->channel_info, user->handle_info);
4520 if(!(invite = GetUserH(argv[1])))
4522 reply("MSG_NICK_UNKNOWN", argv[1]);
4529 if(GetUserMode(channel, invite))
4531 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
4535 for(i = 0; i < channel->invited.used; i++)
4537 if(channel->invited.list[i] == invite) {
4538 reply("CSMSG_ALREADY_INVITED", invite->nick, channel->name);
4547 char *reason = unsplit_string(argv + 2, argc - 2, NULL);
4548 send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
4551 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
4553 irc_invite(chanserv, invite, channel);
4555 reply("CSMSG_INVITED_USER", argv[1], channel->name);
4557 userList_append(&channel->invited, invite);
4558 chanuser = calloc(1, sizeof(*chanuser));
4559 chanuser->user=invite;
4560 chanuser->chan=channel;
4561 timeq_add(now + chanserv_conf.invited_timeout, chanserv_del_invite_mark, chanuser);
4566 static CHANSERV_FUNC(cmd_inviteme)
4568 if(GetUserMode(channel, user))
4570 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
4573 if(channel->channel_info
4574 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
4576 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
4579 irc_invite(cmd->parent->bot, user, channel);
4583 static CHANSERV_FUNC(cmd_invitemeall)
4585 struct handle_info *target = user->handle_info;
4586 struct userData *uData;
4588 if(!target->channels)
4590 reply("CSMSG_SQUAT_ACCESS", target->handle);
4594 for(uData = target->channels; uData; uData = uData->u_next)
4596 struct chanData *cData = uData->channel;
4597 if(uData->access >= cData->lvlOpts[lvlInviteMe])
4599 irc_invite(cmd->parent->bot, user, cData->channel);
4606 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
4609 char buf1[INTERVALLEN], buf2[INTERVALLEN];
4611 /* We display things based on two dimensions:
4612 * - Issue time: present or absent
4613 * - Expiration: revoked, expired, expires in future, or indefinite expiration
4614 * (in order of precedence, so something both expired and revoked
4615 * only counts as revoked)
4617 combo = (suspended->issued ? 4 : 0)
4618 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
4620 case 0: /* no issue time, indefinite expiration */
4621 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
4623 case 1: /* no issue time, expires in future */
4624 intervalString(buf1, suspended->expires-now, user->handle_info);
4625 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
4627 case 2: /* no issue time, expired */
4628 intervalString(buf1, now-suspended->expires, user->handle_info);
4629 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
4631 case 3: /* no issue time, revoked */
4632 intervalString(buf1, now-suspended->revoked, user->handle_info);
4633 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
4635 case 4: /* issue time set, indefinite expiration */
4636 intervalString(buf1, now-suspended->issued, user->handle_info);
4637 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
4639 case 5: /* issue time set, expires in future */
4640 intervalString(buf1, now-suspended->issued, user->handle_info);
4641 intervalString(buf2, suspended->expires-now, user->handle_info);
4642 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
4644 case 6: /* issue time set, expired */
4645 intervalString(buf1, now-suspended->issued, user->handle_info);
4646 intervalString(buf2, now-suspended->expires, user->handle_info);
4647 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
4649 case 7: /* issue time set, revoked */
4650 intervalString(buf1, now-suspended->issued, user->handle_info);
4651 intervalString(buf2, now-suspended->revoked, user->handle_info);
4652 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
4655 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
4660 static CHANSERV_FUNC(cmd_info)
4662 char modes[MAXLEN], buffer[INTERVALLEN];
4663 struct userData *uData, *owner;
4664 struct chanData *cData;
4665 struct do_not_register *dnr;
4670 cData = channel->channel_info;
4671 reply("CSMSG_CHANNEL_INFO", channel->name);
4673 uData = GetChannelUser(cData, user->handle_info);
4674 if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
4676 mod_chanmode_format(&cData->modes, modes);
4677 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
4678 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
4681 for(it = dict_first(cData->notes); it; it = iter_next(it))
4685 note = iter_data(it);
4686 if(!note_type_visible_to_user(cData, note->type, user))
4689 padding = PADLEN - 1 - strlen(iter_key(it));
4690 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
4693 if(cData->max_time) {
4694 reply("CSMSG_CHANNEL_MAX_TIME", cData->max, intervalString(buffer, now - cData->max_time, user->handle_info));
4696 reply("CSMSG_CHANNEL_MAX", cData->max);
4698 for(owner = cData->users; owner; owner = owner->next)
4699 if(owner->access == UL_OWNER)
4700 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
4701 reply("CSMSG_CHANNEL_USERS", cData->userCount);
4702 reply("CSMSG_CHANNEL_BANS", cData->banCount);
4703 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
4705 privileged = IsStaff(user);
4707 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
4708 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
4709 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
4711 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
4712 chanserv_show_dnrs(user, cmd, channel->name, NULL);
4714 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
4716 struct suspended *suspended;
4717 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
4718 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
4719 show_suspension_info(cmd, user, suspended);
4721 else if(IsSuspended(cData))
4723 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
4724 show_suspension_info(cmd, user, cData->suspended);
4729 static CHANSERV_FUNC(cmd_netinfo)
4731 extern unsigned long boot_time;
4732 extern unsigned long burst_length;
4733 char interval[INTERVALLEN];
4735 reply("CSMSG_NETWORK_INFO");
4736 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
4737 reply("CSMSG_NETWORK_USERS", dict_size(clients));
4738 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
4739 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
4740 reply("CSMSG_NETWORK_BANS", banCount);
4741 reply("CSMSG_NETWORK_CHANUSERS", userCount);
4742 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
4743 reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
4748 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
4750 struct helpfile_table table;
4752 struct userNode *user;
4757 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4758 table.contents = alloca(list->used*sizeof(*table.contents));
4759 for(nn=0; nn<list->used; nn++)
4761 user = list->list[nn];
4762 if(user->modes & skip_flags)
4768 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
4771 nick = alloca(strlen(user->nick)+3);
4772 sprintf(nick, "(%s)", user->nick);
4776 table.contents[table.length][0] = nick;
4779 table_send(chanserv, to->nick, 0, NULL, table);
4782 static CHANSERV_FUNC(cmd_ircops)
4784 reply("CSMSG_STAFF_OPERS");
4785 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
4789 static CHANSERV_FUNC(cmd_helpers)
4791 reply("CSMSG_STAFF_HELPERS");
4792 send_staff_list(user, &curr_helpers, FLAGS_OPER);
4796 static CHANSERV_FUNC(cmd_staff)
4798 reply("CSMSG_NETWORK_STAFF");
4799 cmd_ircops(CSFUNC_ARGS);
4800 cmd_helpers(CSFUNC_ARGS);
4804 static CHANSERV_FUNC(cmd_peek)
4806 struct modeNode *mn;
4807 char modes[MODELEN];
4809 struct helpfile_table table;
4810 int opcount = 0, voicecount = 0, srvcount = 0;
4812 irc_make_chanmode(channel, modes);
4814 reply("CSMSG_PEEK_INFO", channel->name);
4815 reply("CSMSG_PEEK_TOPIC", channel->topic);
4816 reply("CSMSG_PEEK_MODES", modes);
4820 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4821 table.contents = alloca(channel->members.used*sizeof(*table.contents));
4822 for(n = 0; n < channel->members.used; n++)
4824 mn = channel->members.list[n];
4825 if(IsLocal(mn->user))
4827 else if(mn->modes & MODE_CHANOP)
4829 else if(mn->modes & MODE_VOICE)
4832 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
4834 table.contents[table.length] = alloca(sizeof(**table.contents));
4835 table.contents[table.length][0] = mn->user->nick;
4839 reply("CSMSG_PEEK_USERS", channel->members.used, opcount, voicecount,
4840 (channel->members.used - opcount - voicecount - srvcount));
4844 reply("CSMSG_PEEK_OPS");
4845 table_send(chanserv, user->nick, 0, NULL, table);
4848 reply("CSMSG_PEEK_NO_OPS");
4852 static MODCMD_FUNC(cmd_wipeinfo)
4854 struct handle_info *victim;
4855 struct userData *ud, *actor, *real_actor;
4856 unsigned int override = 0;
4859 actor = GetChannelUser(channel->channel_info, user->handle_info);
4860 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
4861 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4863 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4865 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4868 if((ud->access >= actor->access) && (ud != actor))
4870 reply("MSG_USER_OUTRANKED", victim->handle);
4873 if((ud != real_actor) && (!real_actor || (ud->access >= real_actor->access)))
4874 override = CMD_LOG_OVERRIDE;
4878 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4879 return 1 | override;
4882 static CHANSERV_FUNC(cmd_resync)
4884 struct mod_chanmode *changes;
4885 struct chanData *cData = channel->channel_info;
4886 unsigned int ii, used;
4888 changes = mod_chanmode_alloc(channel->members.used * 2);
4889 for(ii = used = 0; ii < channel->members.used; ++ii)
4891 struct modeNode *mn = channel->members.list[ii];
4892 struct userData *uData;
4894 if(IsService(mn->user))
4897 uData = GetChannelAccess(cData, mn->user->handle_info);
4898 if(!cData->lvlOpts[lvlGiveOps]
4899 || (uData && uData->access >= cData->lvlOpts[lvlGiveOps]))
4901 if(!(mn->modes & MODE_CHANOP))
4903 if(!uData || IsUserAutoOp(uData))
4905 changes->args[used].mode = MODE_CHANOP;
4906 changes->args[used++].u.member = mn;
4907 if(!(mn->modes & MODE_VOICE))
4909 changes->args[used].mode = MODE_VOICE;
4910 changes->args[used++].u.member = mn;
4915 else if(!cData->lvlOpts[lvlGiveVoice]
4916 || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice]))
4918 if(mn->modes & MODE_CHANOP)
4920 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4921 changes->args[used++].u.member = mn;
4923 if(!(mn->modes & MODE_VOICE) && (!uData || IsUserAutoOp(uData)))
4925 changes->args[used].mode = MODE_VOICE;
4926 changes->args[used++].u.member = mn;
4933 changes->args[used].mode = MODE_REMOVE | mn->modes;
4934 changes->args[used++].u.member = mn;
4938 changes->argc = used;
4939 modcmd_chanmode_announce(changes);
4940 mod_chanmode_free(changes);
4941 reply("CSMSG_RESYNCED_USERS", channel->name);
4945 static CHANSERV_FUNC(cmd_seen)
4947 struct userData *uData;
4948 struct handle_info *handle;
4949 char seen[INTERVALLEN];
4953 if(!irccasecmp(argv[1], chanserv->nick))
4955 reply("CSMSG_IS_CHANSERV");
4959 if(!(handle = get_handle_info(argv[1])))
4961 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4965 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4967 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4972 reply("CSMSG_USER_PRESENT", handle->handle);
4973 else if(uData->seen)
4974 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
4976 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4978 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4979 reply("CSMSG_USER_VACATION", handle->handle);
4984 static MODCMD_FUNC(cmd_names)
4986 struct userNode *targ;
4987 struct userData *targData;
4988 unsigned int ii, pos;
4991 for(ii=pos=0; ii<channel->members.used; ++ii)
4993 targ = channel->members.list[ii]->user;
4994 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4997 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
5000 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
5004 if(IsUserSuspended(targData))
5006 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
5009 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
5010 reply("CSMSG_END_NAMES", channel->name);
5015 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
5017 switch(ntype->visible_type)
5019 case NOTE_VIS_ALL: return 1;
5020 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
5021 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
5026 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
5028 struct userData *uData;
5030 switch(ntype->set_access_type)
5032 case NOTE_SET_CHANNEL_ACCESS:
5033 if(!user->handle_info)
5035 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
5037 return uData->access >= ntype->set_access.min_ulevel;
5038 case NOTE_SET_CHANNEL_SETTER:
5039 return check_user_level(channel, user, lvlSetters, 1, 0);
5040 case NOTE_SET_PRIVILEGED: default:
5041 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
5045 static CHANSERV_FUNC(cmd_note)
5047 struct chanData *cData;
5049 struct note_type *ntype;
5051 cData = channel->channel_info;
5054 reply("CSMSG_NOT_REGISTERED", channel->name);
5058 /* If no arguments, show all visible notes for the channel. */
5064 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
5066 note = iter_data(it);
5067 if(!note_type_visible_to_user(cData, note->type, user))
5070 reply("CSMSG_NOTELIST_HEADER", channel->name);
5071 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
5074 reply("CSMSG_NOTELIST_END", channel->name);
5076 reply("CSMSG_NOTELIST_EMPTY", channel->name);
5078 /* If one argument, show the named note. */
5081 if((note = dict_find(cData->notes, argv[1], NULL))
5082 && note_type_visible_to_user(cData, note->type, user))
5084 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
5086 else if((ntype = dict_find(note_types, argv[1], NULL))
5087 && note_type_visible_to_user(NULL, ntype, user))
5089 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
5094 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
5098 /* Assume they're trying to set a note. */
5102 ntype = dict_find(note_types, argv[1], NULL);
5105 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
5108 else if(note_type_settable_by_user(channel, ntype, user))
5110 note_text = unsplit_string(argv+2, argc-2, NULL);
5111 if((note = dict_find(cData->notes, argv[1], NULL)))
5112 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
5113 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
5114 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
5116 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
5118 /* The note is viewable to staff only, so return 0
5119 to keep the invocation from getting logged (or
5120 regular users can see it in !events). */
5126 reply("CSMSG_NO_ACCESS");
5133 static CHANSERV_FUNC(cmd_delnote)
5138 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
5139 || !note_type_settable_by_user(channel, note->type, user))
5141 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
5144 dict_remove(channel->channel_info->notes, note->type->name);
5145 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
5149 static CHANSERV_FUNC(cmd_events)
5151 struct logSearch discrim;
5152 struct logReport report;
5153 unsigned int matches, limit;
5155 limit = (argc > 1) ? atoi(argv[1]) : 10;
5156 if(limit < 1 || limit > 200)
5159 memset(&discrim, 0, sizeof(discrim));
5160 discrim.masks.bot = chanserv;
5161 discrim.masks.channel_name = channel->name;
5163 discrim.masks.command = argv[2];
5164 discrim.limit = limit;
5165 discrim.max_time = INT_MAX;
5166 discrim.severities = 1 << LOG_COMMAND;
5167 report.reporter = chanserv;
5169 reply("CSMSG_EVENT_SEARCH_RESULTS");
5170 matches = log_entry_search(&discrim, log_report_entry, &report);
5172 reply("MSG_MATCH_COUNT", matches);
5174 reply("MSG_NO_MATCHES");
5178 static CHANSERV_FUNC(cmd_say)
5184 msg = unsplit_string(argv + 1, argc - 1, NULL);
5185 send_channel_message(channel, cmd->parent->bot, "%s", msg);
5187 else if(*argv[1] == '*' && argv[1][1] != '\0')
5189 struct handle_info *hi;
5190 struct userNode *authed;
5193 msg = unsplit_string(argv + 2, argc - 2, NULL);
5195 if (!(hi = get_handle_info(argv[1] + 1)))
5197 reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
5201 for (authed = hi->users; authed; authed = authed->next_authed)
5202 send_target_message(5, authed->nick, cmd->parent->bot, "%s", msg);
5204 else if(GetUserH(argv[1]))
5207 msg = unsplit_string(argv + 2, argc - 2, NULL);
5208 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
5212 reply("MSG_NOT_TARGET_NAME");
5218 static CHANSERV_FUNC(cmd_emote)
5224 /* CTCP is so annoying. */
5225 msg = unsplit_string(argv + 1, argc - 1, NULL);
5226 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
5228 else if(*argv[1] == '*' && argv[1][1] != '\0')
5230 struct handle_info *hi;
5231 struct userNode *authed;
5234 msg = unsplit_string(argv + 2, argc - 2, NULL);
5236 if (!(hi = get_handle_info(argv[1] + 1)))
5238 reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
5242 for (authed = hi->users; authed; authed = authed->next_authed)
5243 send_target_message(5, authed->nick, cmd->parent->bot, "\001ACTION %s\001", msg);
5245 else if(GetUserH(argv[1]))
5247 msg = unsplit_string(argv + 2, argc - 2, NULL);
5248 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
5252 reply("MSG_NOT_TARGET_NAME");
5258 struct channelList *
5259 chanserv_support_channels(void)
5261 return &chanserv_conf.support_channels;
5264 static CHANSERV_FUNC(cmd_expire)
5266 int channel_count = registered_channels;
5267 expire_channels(NULL);
5268 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
5273 chanserv_expire_suspension(void *data)
5275 struct suspended *suspended = data;
5276 struct chanNode *channel;
5279 /* Update the channel registration data structure. */
5280 if(!suspended->expires || (now < suspended->expires))
5281 suspended->revoked = now;
5282 channel = suspended->cData->channel;
5283 suspended->cData->channel = channel;
5284 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
5286 /* If appropriate, re-join ChanServ to the channel. */
5287 if(!IsOffChannel(suspended->cData))
5289 spamserv_cs_suspend(channel, 0, 0, NULL);
5290 ss_cs_join_channel(channel, 1);
5293 /* Mark everyone currently in the channel as present. */
5294 for(ii = 0; ii < channel->members.used; ++ii)
5296 struct userData *uData = GetChannelAccess(suspended->cData, channel->members.list[ii]->user->handle_info);
5305 static CHANSERV_FUNC(cmd_csuspend)
5307 struct suspended *suspended;
5308 char reason[MAXLEN];
5309 unsigned long expiry, duration;
5310 struct userData *uData;
5314 if(IsProtected(channel->channel_info))
5316 reply("CSMSG_SUSPEND_NODELETE", channel->name);
5320 if(argv[1][0] == '!')
5322 else if(IsSuspended(channel->channel_info))
5324 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
5325 show_suspension_info(cmd, user, channel->channel_info->suspended);
5329 if(!strcmp(argv[1], "0"))
5331 else if((duration = ParseInterval(argv[1])))
5332 expiry = now + duration;
5335 reply("MSG_INVALID_DURATION", argv[1]);
5339 unsplit_string(argv + 2, argc - 2, reason);
5341 suspended = calloc(1, sizeof(*suspended));
5342 suspended->revoked = 0;
5343 suspended->issued = now;
5344 suspended->suspender = strdup(user->handle_info->handle);
5345 suspended->expires = expiry;
5346 suspended->reason = strdup(reason);
5347 suspended->cData = channel->channel_info;
5348 suspended->previous = suspended->cData->suspended;
5349 suspended->cData->suspended = suspended;
5351 if(suspended->expires)
5352 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
5354 if(IsSuspended(channel->channel_info))
5356 suspended->previous->revoked = now;
5357 if(suspended->previous->expires)
5358 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
5359 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
5360 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5364 /* Mark all users in channel as absent. */
5365 for(uData = channel->channel_info->users; uData; uData = uData->next)
5374 /* Mark the channel as suspended, then part. */
5375 channel->channel_info->flags |= CHANNEL_SUSPENDED;
5376 spamserv_cs_suspend(channel, expiry, 1, suspended->reason);
5377 DelChannelUser(chanserv, channel, suspended->reason, 0);
5378 reply("CSMSG_SUSPENDED", channel->name);
5379 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
5380 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5385 static CHANSERV_FUNC(cmd_cunsuspend)
5387 struct suspended *suspended;
5388 char message[MAXLEN];
5390 if(!IsSuspended(channel->channel_info))
5392 reply("CSMSG_NOT_SUSPENDED", channel->name);
5396 suspended = channel->channel_info->suspended;
5398 /* Expire the suspension and join ChanServ to the channel. */
5399 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
5400 chanserv_expire_suspension(suspended);
5401 reply("CSMSG_UNSUSPENDED", channel->name);
5402 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
5403 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
5407 typedef struct chanservSearch
5412 unsigned long unvisited;
5413 unsigned long registered;
5415 unsigned long flags;
5419 typedef void (*channel_search_func)(struct chanData *channel, void *data);
5422 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
5427 search = malloc(sizeof(struct chanservSearch));
5428 memset(search, 0, sizeof(*search));
5431 for(i = 0; i < argc; i++)
5433 /* Assume all criteria require arguments. */
5436 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
5440 if(!irccasecmp(argv[i], "name"))
5441 search->name = argv[++i];
5442 else if(!irccasecmp(argv[i], "registrar"))
5443 search->registrar = argv[++i];
5444 else if(!irccasecmp(argv[i], "unvisited"))
5445 search->unvisited = ParseInterval(argv[++i]);
5446 else if(!irccasecmp(argv[i], "registered"))
5447 search->registered = ParseInterval(argv[++i]);
5448 else if(!irccasecmp(argv[i], "flags"))
5451 if(!irccasecmp(argv[i], "nodelete"))
5452 search->flags |= CHANNEL_NODELETE;
5453 else if(!irccasecmp(argv[i], "suspended"))
5454 search->flags |= CHANNEL_SUSPENDED;
5455 else if(!irccasecmp(argv[i], "unreviewed"))
5456 search->flags |= CHANNEL_UNREVIEWED;
5459 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
5463 else if(!irccasecmp(argv[i], "limit"))
5464 search->limit = strtoul(argv[++i], NULL, 10);
5467 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
5472 if(search->name && !strcmp(search->name, "*"))
5474 if(search->registrar && !strcmp(search->registrar, "*"))
5475 search->registrar = 0;
5484 chanserv_channel_match(struct chanData *channel, search_t search)
5486 const char *name = channel->channel->name;
5487 if((search->name && !match_ircglob(name, search->name)) ||
5488 (search->registrar && !channel->registrar) ||
5489 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
5490 (search->unvisited && (now - channel->visited) < search->unvisited) ||
5491 (search->registered && (now - channel->registered) > search->registered) ||
5492 (search->flags && ((search->flags & channel->flags) != search->flags)))
5499 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
5501 struct chanData *channel;
5502 unsigned int matches = 0;
5504 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
5506 if(!chanserv_channel_match(channel, search))
5516 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
5521 search_print(struct chanData *channel, void *data)
5523 send_message_type(4, data, chanserv, "%s", channel->channel->name);
5526 static CHANSERV_FUNC(cmd_search)
5529 unsigned int matches;
5530 channel_search_func action;
5534 if(!irccasecmp(argv[1], "count"))
5535 action = search_count;
5536 else if(!irccasecmp(argv[1], "print"))
5537 action = search_print;
5540 reply("CSMSG_ACTION_INVALID", argv[1]);
5544 search = chanserv_search_create(user, argc - 2, argv + 2);
5548 if(action == search_count)
5549 search->limit = INT_MAX;
5551 if(action == search_print)
5552 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
5554 matches = chanserv_channel_search(search, action, user);
5557 reply("MSG_MATCH_COUNT", matches);
5559 reply("MSG_NO_MATCHES");
5565 static CHANSERV_FUNC(cmd_unvisited)
5567 struct chanData *cData;
5568 unsigned long interval = chanserv_conf.channel_expire_delay;
5569 char buffer[INTERVALLEN];
5570 unsigned int limit = 25, matches = 0;
5574 interval = ParseInterval(argv[1]);
5576 limit = atoi(argv[2]);
5579 intervalString(buffer, interval, user->handle_info);
5580 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
5582 for(cData = channelList; cData && matches < limit; cData = cData->next)
5584 if((now - cData->visited) < interval)
5587 intervalString(buffer, now - cData->visited, user->handle_info);
5588 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
5595 static MODCMD_FUNC(chan_opt_defaulttopic)
5601 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5603 reply("CSMSG_TOPIC_LOCKED", channel->name);
5607 topic = unsplit_string(argv+1, argc-1, NULL);
5609 free(channel->channel_info->topic);
5610 if(topic[0] == '*' && topic[1] == 0)
5612 topic = channel->channel_info->topic = NULL;
5616 topic = channel->channel_info->topic = strdup(topic);
5617 if(channel->channel_info->topic_mask
5618 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
5619 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5621 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
5624 if(channel->channel_info->topic)
5625 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
5627 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
5631 static MODCMD_FUNC(chan_opt_topicmask)
5635 struct chanData *cData = channel->channel_info;
5638 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5640 reply("CSMSG_TOPIC_LOCKED", channel->name);
5644 mask = unsplit_string(argv+1, argc-1, NULL);
5646 if(cData->topic_mask)
5647 free(cData->topic_mask);
5648 if(mask[0] == '*' && mask[1] == 0)
5650 cData->topic_mask = 0;
5654 cData->topic_mask = strdup(mask);
5656 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
5657 else if(!match_ircglob(cData->topic, cData->topic_mask))
5658 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5662 if(channel->channel_info->topic_mask)
5663 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
5665 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
5669 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
5673 char *greeting = unsplit_string(argv+1, argc-1, NULL);
5677 if(greeting[0] == '*' && greeting[1] == 0)
5681 unsigned int length = strlen(greeting);
5682 if(length > chanserv_conf.greeting_length)
5684 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
5687 *data = strdup(greeting);
5696 reply(name, user_find_message(user, "MSG_NONE"));
5700 static MODCMD_FUNC(chan_opt_greeting)
5702 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
5705 static MODCMD_FUNC(chan_opt_usergreeting)
5707 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
5710 static MODCMD_FUNC(chan_opt_modes)
5712 struct mod_chanmode *new_modes;
5717 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
5719 reply("CSMSG_NO_ACCESS");
5722 if(argv[1][0] == '*' && argv[1][1] == 0)
5724 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
5726 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)))
5728 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5731 else if(new_modes->argc > 1)
5733 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5734 mod_chanmode_free(new_modes);
5739 channel->channel_info->modes = *new_modes;
5740 modcmd_chanmode_announce(new_modes);
5741 mod_chanmode_free(new_modes);
5745 mod_chanmode_format(&channel->channel_info->modes, modes);
5747 reply("CSMSG_SET_MODES", modes);
5749 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
5753 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5755 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5757 struct chanData *cData = channel->channel_info;
5762 /* Set flag according to value. */
5763 if(enabled_string(argv[1]))
5765 cData->flags |= mask;
5768 else if(disabled_string(argv[1]))
5770 cData->flags &= ~mask;
5775 reply("MSG_INVALID_BINARY", argv[1]);
5781 /* Find current option value. */
5782 value = (cData->flags & mask) ? 1 : 0;
5786 reply(name, user_find_message(user, "MSG_ON"));
5788 reply(name, user_find_message(user, "MSG_OFF"));
5792 static MODCMD_FUNC(chan_opt_nodelete)
5794 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
5796 reply("MSG_SETTING_PRIVILEGED", argv[0]);
5800 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
5803 static MODCMD_FUNC(chan_opt_dynlimit)
5805 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
5808 static MODCMD_FUNC(chan_opt_offchannel)
5810 struct chanData *cData = channel->channel_info;
5815 /* Set flag according to value. */
5816 if(enabled_string(argv[1]))
5818 if(!IsOffChannel(cData))
5819 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
5820 cData->flags |= CHANNEL_OFFCHANNEL;
5823 else if(disabled_string(argv[1]))
5825 if(IsOffChannel(cData))
5827 struct mod_chanmode change;
5828 mod_chanmode_init(&change);
5830 change.args[0].mode = MODE_CHANOP;
5831 change.args[0].u.member = AddChannelUser(chanserv, channel);
5832 mod_chanmode_announce(chanserv, channel, &change);
5834 cData->flags &= ~CHANNEL_OFFCHANNEL;
5839 reply("MSG_INVALID_BINARY", argv[1]);
5845 /* Find current option value. */
5846 value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
5850 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
5852 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
5856 static MODCMD_FUNC(chan_opt_unreviewed)
5858 struct chanData *cData = channel->channel_info;
5859 int value = (cData->flags & CHANNEL_UNREVIEWED) ? 1 : 0;
5865 /* The two directions can have different ACLs. */
5866 if(enabled_string(argv[1]))
5868 else if(disabled_string(argv[1]))
5872 reply("MSG_INVALID_BINARY", argv[1]);
5876 if (new_value != value)
5878 struct svccmd *subcmd;
5879 char subcmd_name[32];
5881 snprintf(subcmd_name, sizeof(subcmd_name), "%s %s", argv[0], (new_value ? "on" : "off"));
5882 subcmd = dict_find(cmd->parent->commands, subcmd_name, NULL);
5885 reply("MSG_COMMAND_DISABLED", subcmd_name);
5888 else if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
5892 cData->flags |= CHANNEL_UNREVIEWED;
5895 free(cData->registrar);
5896 cData->registrar = strdup(user->handle_info->handle);
5897 cData->flags &= ~CHANNEL_UNREVIEWED;
5904 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_ON"));
5906 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_OFF"));
5910 static MODCMD_FUNC(chan_opt_defaults)
5912 struct userData *uData;
5913 struct chanData *cData;
5914 const char *confirm;
5915 enum levelOption lvlOpt;
5916 enum charOption chOpt;
5918 cData = channel->channel_info;
5919 uData = GetChannelUser(cData, user->handle_info);
5920 if(!uData || (uData->access < UL_OWNER))
5922 reply("CSMSG_OWNER_DEFAULTS", channel->name);
5925 confirm = make_confirmation_string(uData);
5926 if((argc < 2) || strcmp(argv[1], confirm))
5928 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
5931 cData->flags = (CHANNEL_DEFAULT_FLAGS & ~CHANNEL_PRESERVED_FLAGS)
5932 | (cData->flags & CHANNEL_PRESERVED_FLAGS);
5933 cData->modes = chanserv_conf.default_modes;
5934 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
5935 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
5936 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
5937 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
5938 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
5943 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5945 struct chanData *cData = channel->channel_info;
5946 struct userData *uData;
5947 unsigned short value;
5951 if(!check_user_level(channel, user, option, 1, 1))
5953 reply("CSMSG_CANNOT_SET");
5956 value = user_level_from_name(argv[1], UL_OWNER+1);
5957 if(!value && strcmp(argv[1], "0"))
5959 reply("CSMSG_INVALID_ACCESS", argv[1]);
5962 uData = GetChannelUser(cData, user->handle_info);
5963 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
5965 reply("CSMSG_BAD_SETLEVEL");
5971 if(value > cData->lvlOpts[lvlGiveOps])
5973 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
5978 if(value < cData->lvlOpts[lvlGiveVoice])
5980 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
5985 /* This test only applies to owners, since non-owners
5986 * trying to set an option to above their level get caught
5987 * by the CSMSG_BAD_SETLEVEL test above.
5989 if(value > uData->access)
5991 reply("CSMSG_BAD_SETTERS");
5998 cData->lvlOpts[option] = value;
6000 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
6004 static MODCMD_FUNC(chan_opt_enfops)
6006 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
6009 static MODCMD_FUNC(chan_opt_giveops)
6011 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
6014 static MODCMD_FUNC(chan_opt_enfmodes)
6016 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
6019 static MODCMD_FUNC(chan_opt_enftopic)
6021 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
6024 static MODCMD_FUNC(chan_opt_pubcmd)
6026 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
6029 static MODCMD_FUNC(chan_opt_setters)
6031 return channel_level_option(lvlSetters, CSFUNC_ARGS);
6034 static MODCMD_FUNC(chan_opt_ctcpusers)
6036 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
6039 static MODCMD_FUNC(chan_opt_userinfo)
6041 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
6044 static MODCMD_FUNC(chan_opt_givevoice)
6046 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
6049 static MODCMD_FUNC(chan_opt_topicsnarf)
6051 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
6054 static MODCMD_FUNC(chan_opt_vote)
6056 return channel_level_option(lvlVote, CSFUNC_ARGS);
6059 static MODCMD_FUNC(chan_opt_inviteme)
6061 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
6065 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
6067 struct chanData *cData = channel->channel_info;
6068 int count = charOptions[option].count, idx;
6072 idx = atoi(argv[1]);
6074 if(!isdigit(argv[1][0]) || (idx < 0) || (idx >= count))
6076 reply("CSMSG_INVALID_NUMERIC", idx);
6077 /* Show possible values. */
6078 for(idx = 0; idx < count; idx++)
6079 reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
6083 cData->chOpts[option] = charOptions[option].values[idx].value;
6087 /* Find current option value. */
6090 (idx < count) && (cData->chOpts[option] != charOptions[option].values[idx].value);
6094 /* Somehow, the option value is corrupt; reset it to the default. */
6095 cData->chOpts[option] = charOptions[option].default_value;
6100 reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
6104 static MODCMD_FUNC(chan_opt_protect)
6106 return channel_multiple_option(chProtect, CSFUNC_ARGS);
6109 static MODCMD_FUNC(chan_opt_toys)
6111 return channel_multiple_option(chToys, CSFUNC_ARGS);
6114 static MODCMD_FUNC(chan_opt_ctcpreaction)
6116 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
6119 static MODCMD_FUNC(chan_opt_topicrefresh)
6121 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
6124 static struct svccmd_list set_shows_list;
6127 handle_svccmd_unbind(struct svccmd *target) {
6129 for(ii=0; ii<set_shows_list.used; ++ii)
6130 if(target == set_shows_list.list[ii])
6131 set_shows_list.used = 0;
6134 static CHANSERV_FUNC(cmd_set)
6136 struct svccmd *subcmd;
6140 /* Check if we need to (re-)initialize set_shows_list. */
6141 if(!set_shows_list.used)
6143 if(!set_shows_list.size)
6145 set_shows_list.size = chanserv_conf.set_shows->used;
6146 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
6148 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
6150 const char *name = chanserv_conf.set_shows->list[ii];
6151 sprintf(buf, "%s %s", argv[0], name);
6152 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6155 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
6158 svccmd_list_append(&set_shows_list, subcmd);
6164 reply("CSMSG_CHANNEL_OPTIONS");
6165 for(ii = 0; ii < set_shows_list.used; ii++)
6167 subcmd = set_shows_list.list[ii];
6168 subcmd->command->func(user, channel, 1, argv+1, subcmd);
6173 sprintf(buf, "%s %s", argv[0], argv[1]);
6174 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6177 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
6180 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
6182 reply("CSMSG_NO_ACCESS");
6188 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
6192 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
6194 struct userData *uData;
6196 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6199 reply("CSMSG_NOT_USER", channel->name);
6205 /* Just show current option value. */
6207 else if(enabled_string(argv[1]))
6209 uData->flags |= mask;
6211 else if(disabled_string(argv[1]))
6213 uData->flags &= ~mask;
6217 reply("MSG_INVALID_BINARY", argv[1]);
6221 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
6225 static MODCMD_FUNC(user_opt_noautoop)
6227 struct userData *uData;
6229 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6232 reply("CSMSG_NOT_USER", channel->name);
6235 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
6236 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
6238 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
6241 static MODCMD_FUNC(user_opt_autoinvite)
6243 if((argc > 1) && !check_user_level(channel, user, lvlInviteMe, 1, 0))
6245 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
6247 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
6250 static MODCMD_FUNC(user_opt_info)
6252 struct userData *uData;
6255 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6259 /* If they got past the command restrictions (which require access)
6260 * but fail this test, we have some fool with security override on.
6262 reply("CSMSG_NOT_USER", channel->name);
6269 infoline = unsplit_string(argv + 1, argc - 1, NULL);
6270 if(strlen(infoline) > chanserv_conf.max_userinfo_length)
6272 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
6275 bp = strcspn(infoline, "\001");
6278 reply("CSMSG_BAD_INFOLINE", infoline[bp]);
6283 if(infoline[0] == '*' && infoline[1] == 0)
6286 uData->info = strdup(infoline);
6289 reply("CSMSG_USET_INFO", uData->info);
6291 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
6295 struct svccmd_list uset_shows_list;
6297 static CHANSERV_FUNC(cmd_uset)
6299 struct svccmd *subcmd;
6303 /* Check if we need to (re-)initialize uset_shows_list. */
6304 if(!uset_shows_list.used)
6308 "NoAutoOp", "AutoInvite", "Info"
6311 if(!uset_shows_list.size)
6313 uset_shows_list.size = ArrayLength(options);
6314 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
6316 for(ii = 0; ii < ArrayLength(options); ii++)
6318 const char *name = options[ii];
6319 sprintf(buf, "%s %s", argv[0], name);
6320 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6323 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
6326 svccmd_list_append(&uset_shows_list, subcmd);
6332 /* Do this so options are presented in a consistent order. */
6333 reply("CSMSG_USER_OPTIONS");
6334 for(ii = 0; ii < uset_shows_list.used; ii++)
6335 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
6339 sprintf(buf, "%s %s", argv[0], argv[1]);
6340 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6343 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
6347 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
6350 static CHANSERV_FUNC(cmd_giveownership)
6352 struct handle_info *new_owner_hi;
6353 struct userData *new_owner;
6354 struct userData *curr_user;
6355 struct userData *invoker;
6356 struct chanData *cData = channel->channel_info;
6357 struct do_not_register *dnr;
6358 const char *confirm;
6360 unsigned short co_access;
6361 char reason[MAXLEN];
6364 curr_user = GetChannelAccess(cData, user->handle_info);
6365 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
6366 if(!curr_user || (curr_user->access != UL_OWNER))
6368 struct userData *owner = NULL;
6369 for(curr_user = channel->channel_info->users;
6371 curr_user = curr_user->next)
6373 if(curr_user->access != UL_OWNER)
6377 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
6384 else if(!force && (now < cData->ownerTransfer + chanserv_conf.giveownership_period))
6386 char delay[INTERVALLEN];
6387 intervalString(delay, cData->ownerTransfer + chanserv_conf.giveownership_period - now, user->handle_info);
6388 reply("CSMSG_TRANSFER_WAIT", delay, channel->name);
6391 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
6393 if(new_owner_hi == user->handle_info)
6395 reply("CSMSG_NO_TRANSFER_SELF");
6398 new_owner = GetChannelAccess(cData, new_owner_hi);
6403 new_owner = add_channel_user(cData, new_owner_hi, UL_OWNER - 1, 0, NULL);
6407 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
6411 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
6413 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
6416 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
6417 if(!IsHelping(user))
6418 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
6420 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi->handle);
6423 invoker = GetChannelUser(cData, user->handle_info);
6424 if(invoker->access <= UL_OWNER)
6426 confirm = make_confirmation_string(curr_user);
6427 if((argc < 3) || strcmp(argv[2], confirm))
6429 reply("CSMSG_CONFIRM_GIVEOWNERSHIP", new_owner_hi->handle, confirm);
6433 if(new_owner->access >= UL_COOWNER)
6434 co_access = new_owner->access;
6436 co_access = UL_COOWNER;
6437 new_owner->access = UL_OWNER;
6439 curr_user->access = co_access;
6440 cData->ownerTransfer = now;
6441 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
6442 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
6443 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
6447 static CHANSERV_FUNC(cmd_suspend)
6449 struct handle_info *hi;
6450 struct userData *actor, *real_actor, *target;
6451 unsigned int override = 0;
6454 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6455 actor = GetChannelUser(channel->channel_info, user->handle_info);
6456 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
6457 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6459 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6462 if(target->access >= actor->access)
6464 reply("MSG_USER_OUTRANKED", hi->handle);
6467 if(target->flags & USER_SUSPENDED)
6469 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
6474 target->present = 0;
6477 if(!real_actor || target->access >= real_actor->access)
6478 override = CMD_LOG_OVERRIDE;
6479 target->flags |= USER_SUSPENDED;
6480 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
6481 return 1 | override;
6484 static CHANSERV_FUNC(cmd_unsuspend)
6486 struct handle_info *hi;
6487 struct userData *actor, *real_actor, *target;
6488 unsigned int override = 0;
6491 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6492 actor = GetChannelUser(channel->channel_info, user->handle_info);
6493 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
6494 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6496 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6499 if(target->access >= actor->access)
6501 reply("MSG_USER_OUTRANKED", hi->handle);
6504 if(!(target->flags & USER_SUSPENDED))
6506 reply("CSMSG_NOT_SUSPENDED", hi->handle);
6509 if(!real_actor || target->access >= real_actor->access)
6510 override = CMD_LOG_OVERRIDE;
6511 target->flags &= ~USER_SUSPENDED;
6512 scan_user_presence(target, NULL);
6513 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
6514 return 1 | override;
6517 static MODCMD_FUNC(cmd_deleteme)
6519 struct handle_info *hi;
6520 struct userData *target;
6521 const char *confirm_string;
6522 unsigned short access_level;
6525 hi = user->handle_info;
6526 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6528 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6531 if(target->access == UL_OWNER)
6533 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
6536 confirm_string = make_confirmation_string(target);
6537 if((argc < 2) || strcmp(argv[1], confirm_string))
6539 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
6542 access_level = target->access;
6543 channel_name = strdup(channel->name);
6544 del_channel_user(target, 1);
6545 reply("CSMSG_DELETED_YOU", access_level, channel_name);
6550 static CHANSERV_FUNC(cmd_addvote)
6552 struct chanData *cData = channel->channel_info;
6553 struct userData *uData, *target;
6554 struct handle_info *hi;
6555 if (!cData) return 0;
6557 hi = user->handle_info;
6558 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6560 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6563 if(target->access < 300) {
6564 reply("CSMSG_NO_ACCESS");
6568 reply("CSMSG_ADDVOTE_FULL");
6572 msg = unsplit_string(argv + 1, argc - 1, NULL);
6573 cData->vote = strdup(msg);
6574 cData->vote_start=0;
6575 dict_delete(cData->vote_options);
6576 cData->vote_options = dict_new();
6577 dict_set_free_data(cData->vote_options, free_vote_options);
6578 for(uData = channel->channel_info->users; uData; uData = uData->next)
6583 reply("CSMSG_ADDVOTE_DONE");
6587 static CHANSERV_FUNC(cmd_delvote)
6589 struct chanData *cData = channel->channel_info;
6590 struct userData *target;
6591 struct handle_info *hi;
6592 if (!cData) return 0;
6593 hi = user->handle_info;
6594 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6596 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6599 if(target->access < 300) {
6600 reply("CSMSG_NO_ACCESS");
6604 reply("CSMSG_NO_VOTE");
6609 reply("CSMSG_DELVOTE_DONE");
6613 static CHANSERV_FUNC(cmd_addoption)
6615 struct chanData *cData = channel->channel_info;
6616 struct userData *target;
6617 struct handle_info *hi;
6618 if (!cData) return 0;
6620 hi = user->handle_info;
6621 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6623 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6626 if(target->access < 300) {
6627 reply("CSMSG_NO_ACCESS");
6631 reply("CSMSG_NO_VOTE");
6637 msg = unsplit_string(argv + 1, argc - 1, NULL);
6640 unsigned int lastid = 1;
6641 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6642 struct vote_option *cvOpt = iter_data(it);
6643 if(cvOpt->option_id > lastid)
6644 lastid = cvOpt->option_id;
6646 struct vote_option *vOpt;
6647 vOpt = calloc(1, sizeof(*vOpt));
6648 vOpt->name = strdup(msg);
6649 vOpt->option_id = (lastid + 1);
6651 sprintf(str,"%i",(lastid + 1));
6652 vOpt->option_str = strdup(str);
6654 dict_insert(cData->vote_options,vOpt->option_str,vOpt);
6656 reply("CSMSG_ADDOPTION_DONE",dict_size(cData->vote_options),lastid,(lastid + 1));
6660 static CHANSERV_FUNC(cmd_deloption)
6662 struct chanData *cData = channel->channel_info;
6663 struct userData *uData, *target;
6664 struct handle_info *hi;
6665 if (!cData) return 0;
6667 hi = user->handle_info;
6668 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6670 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6673 if(target->access < 300) {
6674 reply("CSMSG_NO_ACCESS");
6678 reply("CSMSG_NO_VOTE");
6681 if(cData->vote_start) {
6682 if(dict_size(cData->vote_options) < 3) {
6683 reply("CSMSG_VOTE_NEED_OPTIONS");
6688 int find_id = atoi(argv[1]);
6690 unsigned int found = 0;
6693 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6695 if (find_id == ii) {
6696 struct vote_option *vOpt = iter_data(it);
6697 found = vOpt->option_id;
6699 sprintf(str,"%i",vOpt->option_id);
6700 dict_remove(cData->vote_options, str);
6705 for(uData = channel->channel_info->users; uData; uData = uData->next) {
6706 if(uData->votefor == found) {
6711 reply("CSMSG_DELOPTION_DONE");
6714 reply("CSMSG_DELOPTION_NONE");
6719 static CHANSERV_FUNC(cmd_vote)
6721 struct chanData *cData = channel->channel_info;
6722 struct userData *target;
6723 struct handle_info *hi;
6724 unsigned int votedfor = 0;
6725 char *votedfor_str = NULL;
6727 if (!cData || !cData->vote) {
6728 reply("CSMSG_NO_VOTE");
6731 if(argc > 1 && cData->vote_start) {
6732 hi = user->handle_info;
6733 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6735 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6738 if(!check_user_level(channel, user, lvlVote, 1, 0)) {
6739 reply("CSMSG_NO_ACCESS");
6743 reply("CSMSG_VOTE_VOTED");
6746 int find_id = atoi(argv[1]);
6749 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6751 if (find_id == ii) {
6752 struct vote_option *vOpt = iter_data(it);
6755 target->votefor = vOpt->option_id;
6756 votedfor = vOpt->option_id;
6757 votedfor_str = vOpt->name;
6761 reply("CSMSG_VOTE_INVALID");
6765 if (!cData->vote_start) {
6766 reply("CSMSG_VOTE_NOT_STARTED");
6768 reply("CSMSG_VOTE_QUESTION",cData->vote);
6770 unsigned int voteid = 0;
6773 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6774 struct vote_option *vOpt = iter_data(it);
6776 reply("CSMSG_VOTE_OPTION",voteid,vOpt->name,vOpt->voted);
6778 if(argc > 1 && cData->vote_start && votedfor_str) {
6779 reply("CSMSG_VOTE_DONE",votedfor_str);
6784 static CHANSERV_FUNC(cmd_startvote)
6786 struct chanData *cData = channel->channel_info;
6787 struct userData *target;
6788 struct handle_info *hi;
6789 if (!cData) return 0;
6790 hi = user->handle_info;
6791 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6793 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6796 if(target->access < 300) {
6797 reply("CSMSG_NO_ACCESS");
6801 reply("CSMSG_NO_VOTE");
6804 if(cData->vote_start) {
6805 reply("CSMSG_STARTVOTE_RUNNING");
6808 if(dict_size(cData->vote_options) < 2) {
6809 reply("CSMSG_VOTE_NEED_OPTIONS");
6812 cData->vote_start = 1;
6813 char response[MAXLEN];
6814 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_TOP"), user->nick);
6815 irc_privmsg(cmd->parent->bot, channel->name, response);
6816 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_QUESTION"), cData->vote);
6817 irc_privmsg(cmd->parent->bot, channel->name, response);
6818 unsigned int voteid = 0;
6820 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6821 struct vote_option *vOpt = iter_data(it);
6823 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_OPTION"), voteid, vOpt->name);
6824 irc_privmsg(cmd->parent->bot, channel->name, response);
6826 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_ACCESS"), cData->lvlOpts[lvlVote]); //Todo
6827 irc_privmsg(cmd->parent->bot, channel->name, response);
6828 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_HOWTO")); //Todo
6829 irc_privmsg(cmd->parent->bot, channel->name, response);
6833 static CHANSERV_FUNC(cmd_endvote)
6835 struct chanData *cData = channel->channel_info;
6836 struct userData *target;
6837 struct handle_info *hi;
6838 if (!cData) return 0;
6839 hi = user->handle_info;
6840 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6842 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6845 if(target->access < 300) {
6846 reply("CSMSG_NO_ACCESS");
6850 reply("CSMSG_NO_VOTE");
6853 if(!cData->vote_start) {
6854 reply("CSMSG_ENDVOTE_STOPPED");
6857 cData->vote_start = 0;
6858 reply("CSMSG_ENDVOTE_DONE");
6862 static CHANSERV_FUNC(cmd_voteresults)
6864 struct chanData *cData = channel->channel_info;
6865 struct userData *target;
6866 struct handle_info *hi;
6867 if (!cData) return 0;
6869 reply("CSMSG_NO_VOTE");
6872 if (argc > 1 && !irccasecmp(argv[1], "*")) {
6873 hi = user->handle_info;
6874 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6876 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6879 if(target->access < 300) {
6880 reply("CSMSG_NO_ACCESS");
6883 char response[MAXLEN];
6884 sprintf(response, user_find_message(user, "CSMSG_VOTERES_QUESTION"), cData->vote);
6885 irc_privmsg(cmd->parent->bot, channel->name, response);
6886 unsigned int voteid = 0;
6888 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6889 struct vote_option *vOpt = iter_data(it);
6891 sprintf(response, user_find_message(user, "CSMSG_VOTERES_OPTION"), voteid, vOpt->name, vOpt->voted);
6892 irc_privmsg(cmd->parent->bot, channel->name, response);
6895 reply("CSMSG_VOTE_QUESTION",cData->vote);
6896 unsigned int voteid = 0;
6898 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6899 struct vote_option *vOpt = iter_data(it);
6901 reply("CSMSG_VOTE_OPTION",voteid,vOpt->name,vOpt->voted);
6908 chanserv_refresh_topics(UNUSED_ARG(void *data))
6910 unsigned int refresh_num = (now - self->link_time) / chanserv_conf.refresh_period;
6911 struct chanData *cData;
6914 for(cData = channelList; cData; cData = cData->next)
6916 if(IsSuspended(cData))
6918 opt = cData->chOpts[chTopicRefresh];
6921 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
6924 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
6925 cData->last_refresh = refresh_num;
6927 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
6930 static CHANSERV_FUNC(cmd_unf)
6934 char response[MAXLEN];
6935 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
6936 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6937 irc_privmsg(cmd->parent->bot, channel->name, response);
6940 reply("CSMSG_UNF_RESPONSE");
6944 static CHANSERV_FUNC(cmd_ping)
6948 char response[MAXLEN];
6949 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
6950 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6951 irc_privmsg(cmd->parent->bot, channel->name, response);
6954 reply("CSMSG_PING_RESPONSE");
6958 static CHANSERV_FUNC(cmd_wut)
6962 char response[MAXLEN];
6963 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
6964 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6965 irc_privmsg(cmd->parent->bot, channel->name, response);
6968 reply("CSMSG_WUT_RESPONSE");
6972 static CHANSERV_FUNC(cmd_8ball)
6974 unsigned int i, j, accum;
6979 for(i=1; i<argc; i++)
6980 for(j=0; argv[i][j]; j++)
6981 accum = (accum << 5) - accum + toupper(argv[i][j]);
6982 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
6985 char response[MAXLEN];
6986 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
6987 irc_privmsg(cmd->parent->bot, channel->name, response);
6990 send_message_type(4, user, cmd->parent->bot, "%s", resp);
6994 static CHANSERV_FUNC(cmd_d)
6996 unsigned long sides, count, modifier, ii, total;
6997 char response[MAXLEN], *sep;
7001 if((count = strtoul(argv[1], &sep, 10)) < 1)
7011 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
7012 && (sides = strtoul(sep+1, &sep, 10)) > 1)
7016 else if((sep[0] == '-') && isdigit(sep[1]))
7017 modifier = strtoul(sep, NULL, 10);
7018 else if((sep[0] == '+') && isdigit(sep[1]))
7019 modifier = strtoul(sep+1, NULL, 10);
7026 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
7031 reply("CSMSG_BAD_DICE_COUNT", count, 10);
7034 for(total = ii = 0; ii < count; ++ii)
7035 total += (rand() % sides) + 1;
7038 if((count > 1) || modifier)
7040 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
7041 sprintf(response, fmt, total, count, sides, modifier);
7045 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
7046 sprintf(response, fmt, total, sides);
7049 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
7051 send_message_type(4, user, cmd->parent->bot, "%s", response);
7055 static CHANSERV_FUNC(cmd_huggle)
7057 /* CTCP must be via PRIVMSG, never notice */
7059 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
7061 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
7066 chanserv_adjust_limit(void *data)
7068 struct mod_chanmode change;
7069 struct chanData *cData = data;
7070 struct chanNode *channel = cData->channel;
7073 if(IsSuspended(cData))
7076 cData->limitAdjusted = now;
7077 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
7078 if(cData->modes.modes_set & MODE_LIMIT)
7080 if(limit > cData->modes.new_limit)
7081 limit = cData->modes.new_limit;
7082 else if(limit == cData->modes.new_limit)
7086 mod_chanmode_init(&change);
7087 change.modes_set = MODE_LIMIT;
7088 change.new_limit = limit;
7089 mod_chanmode_announce(chanserv, channel, &change);
7093 handle_new_channel(struct chanNode *channel)
7095 struct chanData *cData;
7097 if(!(cData = channel->channel_info))
7100 if(cData->modes.modes_set || cData->modes.modes_clear)
7101 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
7103 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
7104 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
7107 void handle_new_channel_created(char *chan, struct userNode *user) {
7108 if(user->handle_info && chanserv_conf.new_channel_authed) {
7109 send_target_message(5, chan, chanserv, "%s", chanserv_conf.new_channel_authed);
7110 } else if(!user->handle_info && chanserv_conf.new_channel_unauthed) {
7111 send_target_message(5, chan, chanserv, "%s", chanserv_conf.new_channel_unauthed);
7113 if(chanserv_conf.new_channel_msg)
7114 send_target_message(5, chan, chanserv, "%s", chanserv_conf.new_channel_msg);
7117 /* Welcome to my worst nightmare. Warning: Read (or modify)
7118 the code below at your own risk. */
7120 handle_join(struct modeNode *mNode)
7122 struct mod_chanmode change;
7123 struct userNode *user = mNode->user;
7124 struct chanNode *channel = mNode->channel;
7125 struct chanData *cData;
7126 struct userData *uData = NULL;
7127 struct banData *bData;
7128 struct handle_info *handle;
7129 unsigned int modes = 0, info = 0;
7133 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
7136 cData = channel->channel_info;
7137 if(channel->members.used > cData->max) {
7138 cData->max = channel->members.used;
7139 cData->max_time = now;
7142 for(i = 0; i < channel->invited.used; i++)
7144 if(channel->invited.list[i] == user) {
7145 userList_remove(&channel->invited, user);
7149 /* Check for bans. If they're joining through a ban, one of two
7151 * 1: Join during a netburst, by riding the break. Kick them
7152 * unless they have ops or voice in the channel.
7153 * 2: They're allowed to join through the ban (an invite in
7154 * ircu2.10, or a +e on Hybrid, or something).
7155 * If they're not joining through a ban, and the banlist is not
7156 * full, see if they're on the banlist for the channel. If so,
7159 if(user->uplink->burst && !mNode->modes)
7162 for(ii = 0; ii < channel->banlist.used; ii++)
7164 if(user_matches_glob(user, channel->banlist.list[ii]->ban, MATCH_USENICK))
7166 /* Riding a netburst. Naughty. */
7167 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
7173 mod_chanmode_init(&change);
7175 if(channel->banlist.used < MAXBANS)
7177 /* Not joining through a ban. */
7178 for(bData = cData->bans;
7179 bData && !user_matches_glob(user, bData->mask, MATCH_USENICK);
7180 bData = bData->next);
7184 char kick_reason[MAXLEN];
7185 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
7187 bData->triggered = now;
7188 if(bData != cData->bans)
7190 /* Shuffle the ban to the head of the list. */
7192 bData->next->prev = bData->prev;
7194 bData->prev->next = bData->next;
7197 bData->next = cData->bans;
7200 cData->bans->prev = bData;
7201 cData->bans = bData;
7204 change.args[0].mode = MODE_BAN;
7205 change.args[0].u.hostmask = bData->mask;
7206 mod_chanmode_announce(chanserv, channel, &change);
7207 KickChannelUser(user, channel, chanserv, kick_reason);
7212 /* ChanServ will not modify the limits in join-flooded channels,
7213 or when there are enough slots left below the limit. */
7214 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
7215 && !channel->join_flooded
7216 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
7218 /* The user count has begun "bumping" into the channel limit,
7219 so set a timer to raise the limit a bit. Any previous
7220 timers are removed so three incoming users within the delay
7221 results in one limit change, not three. */
7223 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7224 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7227 if(channel->join_flooded)
7229 /* don't automatically give ops or voice during a join flood */
7231 else if(cData->lvlOpts[lvlGiveOps] == 0)
7232 modes |= MODE_CHANOP;
7233 else if(cData->lvlOpts[lvlGiveVoice] == 0)
7234 modes |= MODE_VOICE;
7236 greeting = cData->greeting;
7237 if(user->handle_info)
7239 handle = user->handle_info;
7241 if(IsHelper(user) && !IsHelping(user))
7244 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7246 if(channel == chanserv_conf.support_channels.list[ii])
7248 HANDLE_SET_FLAG(user->handle_info, HELPING);
7254 uData = GetTrueChannelAccess(cData, handle);
7255 if(uData && !IsUserSuspended(uData))
7257 /* Ops and above were handled by the above case. */
7258 if(IsUserAutoOp(uData))
7260 if(uData->access >= cData->lvlOpts[lvlGiveOps])
7261 modes |= MODE_CHANOP;
7262 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
7263 modes |= MODE_VOICE;
7265 if(uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
7266 cData->visited = now;
7267 if(cData->user_greeting)
7268 greeting = cData->user_greeting;
7270 && (uData->access >= cData->lvlOpts[lvlUserInfo])
7271 && ((now - uData->seen) >= chanserv_conf.info_delay)
7279 /* If user joining normally (not during burst), apply op or voice,
7280 * and send greeting/userinfo as appropriate.
7282 if(!user->uplink->burst)
7286 if(modes & MODE_CHANOP)
7287 modes &= ~MODE_VOICE;
7288 change.args[0].mode = modes;
7289 change.args[0].u.member = mNode;
7290 mod_chanmode_announce(chanserv, channel, &change);
7293 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
7294 if(uData && info && (modes || !(channel->modes & MODE_DELAYJOINS)))
7295 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
7301 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
7303 struct mod_chanmode change;
7304 struct userData *channel;
7305 unsigned int ii, jj;
7307 if(!user->handle_info)
7310 mod_chanmode_init(&change);
7312 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
7314 struct chanNode *cn;
7315 struct modeNode *mn;
7316 if(IsUserSuspended(channel)
7317 || IsSuspended(channel->channel)
7318 || !(cn = channel->channel->channel))
7321 mn = GetUserMode(cn, user);
7324 if(!IsUserSuspended(channel)
7325 && IsUserAutoInvite(channel)
7326 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
7328 && !user->uplink->burst)
7329 irc_invite(chanserv, user, cn);
7333 if(channel->access >= UL_PRESENT && !HANDLE_FLAGGED(channel->handle, BOT))
7334 channel->channel->visited = now;
7336 if(IsUserAutoOp(channel))
7338 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
7339 change.args[0].mode = MODE_CHANOP;
7340 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
7341 change.args[0].mode = MODE_VOICE;
7343 change.args[0].mode = 0;
7344 change.args[0].u.member = mn;
7345 if(change.args[0].mode)
7346 mod_chanmode_announce(chanserv, cn, &change);
7349 channel->seen = now;
7350 channel->present = 1;
7353 for(ii = 0; ii < user->channels.used; ++ii)
7355 struct chanNode *chan = user->channels.list[ii]->channel;
7356 struct banData *ban;
7358 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
7359 || !chan->channel_info
7360 || IsSuspended(chan->channel_info))
7362 for(jj = 0; jj < chan->banlist.used; ++jj)
7363 if(user_matches_glob(user, chan->banlist.list[jj]->ban, MATCH_USENICK))
7365 if(jj < chan->banlist.used)
7367 for(ban = chan->channel_info->bans; ban; ban = ban->next)
7369 char kick_reason[MAXLEN];
7370 if(!user_matches_glob(user, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
7372 change.args[0].mode = MODE_BAN;
7373 change.args[0].u.hostmask = ban->mask;
7374 mod_chanmode_announce(chanserv, chan, &change);
7375 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
7376 KickChannelUser(user, chan, chanserv, kick_reason);
7377 ban->triggered = now;
7382 if(IsSupportHelper(user))
7384 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7386 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
7388 HANDLE_SET_FLAG(user->handle_info, HELPING);
7396 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
7398 struct chanData *cData;
7399 struct userData *uData;
7401 cData = mn->channel->channel_info;
7402 if(!cData || IsSuspended(cData) || IsLocal(mn->user))
7405 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
7407 /* Allow for a bit of padding so that the limit doesn't
7408 track the user count exactly, which could get annoying. */
7409 if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
7411 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7412 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7416 if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
7418 scan_user_presence(uData, mn->user);
7420 if (uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
7421 cData->visited = now;
7424 if(IsHelping(mn->user) && IsSupportHelper(mn->user))
7427 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii) {
7428 struct chanNode *channel;
7429 struct userNode *exclude;
7430 /* When looking at the channel that is being /part'ed, we
7431 * have to skip over the client that is leaving. For
7432 * other channels, we must not do that.
7434 channel = chanserv_conf.support_channels.list[ii];
7435 exclude = (channel == mn->channel) ? mn->user : NULL;
7436 if(find_handle_in_channel(channel, mn->user->handle_info, exclude))
7439 if(ii == chanserv_conf.support_channels.used)
7440 HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
7445 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
7447 struct userData *uData;
7449 if(!channel->channel_info || !kicker || IsService(kicker)
7450 || (kicker == victim) || IsSuspended(channel->channel_info)
7451 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
7454 if(protect_user(victim, kicker, channel->channel_info))
7456 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED_2");
7457 KickChannelUser(kicker, channel, chanserv, reason);
7460 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
7465 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
7467 struct chanData *cData;
7469 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
7472 cData = channel->channel_info;
7473 if(bad_topic(channel, user, channel->topic))
7475 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
7476 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
7477 SetChannelTopic(channel, chanserv, old_topic, 1);
7478 else if(cData->topic)
7479 SetChannelTopic(channel, chanserv, cData->topic, 1);
7482 /* With topicsnarf, grab the topic and save it as the default topic. */
7483 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
7486 cData->topic = strdup(channel->topic);
7492 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
7494 struct mod_chanmode *bounce = NULL;
7495 unsigned int bnc, ii;
7498 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
7501 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
7502 && mode_lock_violated(&channel->channel_info->modes, change))
7504 char correct[MAXLEN];
7505 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
7506 mod_chanmode_format(&channel->channel_info->modes, correct);
7507 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
7509 for(ii = bnc = 0; ii < change->argc; ++ii)
7511 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
7513 const struct userNode *victim = change->args[ii].u.member->user;
7514 if(!protect_user(victim, user, channel->channel_info))
7517 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7520 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
7521 bounce->args[bnc].u.member = GetUserMode(channel, user);
7522 if(bounce->args[bnc].u.member)
7526 bounce->args[bnc].mode = MODE_CHANOP;
7527 bounce->args[bnc].u.member = change->args[ii].u.member;
7529 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
7531 else if(change->args[ii].mode & MODE_CHANOP)
7533 const struct userNode *victim = change->args[ii].u.member->user;
7534 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
7537 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7538 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
7539 bounce->args[bnc].u.member = change->args[ii].u.member;
7542 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
7544 const char *ban = change->args[ii].u.hostmask;
7545 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
7548 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7549 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
7550 bounce->args[bnc].u.hostmask = strdup(ban);
7552 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
7557 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
7558 mod_chanmode_announce(chanserv, channel, bounce);
7559 for(ii = 0; ii < change->argc; ++ii)
7560 if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
7561 free((char*)bounce->args[ii].u.hostmask);
7562 mod_chanmode_free(bounce);
7567 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
7569 struct chanNode *channel;
7570 struct banData *bData;
7571 struct mod_chanmode change;
7572 unsigned int ii, jj;
7573 char kick_reason[MAXLEN];
7575 mod_chanmode_init(&change);
7577 change.args[0].mode = MODE_BAN;
7578 for(ii = 0; ii < user->channels.used; ++ii)
7580 channel = user->channels.list[ii]->channel;
7581 /* Need not check for bans if they're opped or voiced. */
7582 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
7584 /* Need not check for bans unless channel registration is active. */
7585 if(!channel->channel_info || IsSuspended(channel->channel_info))
7587 /* Look for a matching ban already on the channel. */
7588 for(jj = 0; jj < channel->banlist.used; ++jj)
7589 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
7591 /* Need not act if we found one. */
7592 if(jj < channel->banlist.used)
7594 /* Look for a matching ban in this channel. */
7595 for(bData = channel->channel_info->bans; bData; bData = bData->next)
7597 if(!user_matches_glob(user, bData->mask, MATCH_USENICK | MATCH_VISIBLE))
7599 change.args[0].u.hostmask = bData->mask;
7600 mod_chanmode_announce(chanserv, channel, &change);
7601 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
7602 KickChannelUser(user, channel, chanserv, kick_reason);
7603 bData->triggered = now;
7604 break; /* we don't need to check any more bans in the channel */
7609 static void handle_rename(struct handle_info *handle, const char *old_handle)
7611 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
7615 dict_remove2(handle_dnrs, old_handle, 1);
7616 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
7617 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
7622 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
7624 struct userNode *h_user;
7626 if(handle->channels)
7628 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
7629 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
7631 while(handle->channels)
7632 del_channel_user(handle->channels, 1);
7637 handle_server_link(UNUSED_ARG(struct server *server))
7639 struct chanData *cData;
7641 for(cData = channelList; cData; cData = cData->next)
7643 if(!IsSuspended(cData))
7644 cData->may_opchan = 1;
7645 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
7646 && !cData->channel->join_flooded
7647 && ((cData->channel->limit - cData->channel->members.used)
7648 < chanserv_conf.adjust_threshold))
7650 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7651 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7657 chanserv_conf_read(void)
7661 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
7662 struct mod_chanmode *change;
7663 struct string_list *strlist;
7664 struct chanNode *chan;
7667 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
7669 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
7672 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7673 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7674 chanserv_conf.support_channels.used = 0;
7675 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
7677 for(ii = 0; ii < strlist->used; ++ii)
7679 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7682 chan = AddChannel(strlist->list[ii], now, str2, NULL);
7684 channelList_append(&chanserv_conf.support_channels, chan);
7687 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
7690 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7693 chan = AddChannel(str, now, str2, NULL);
7695 channelList_append(&chanserv_conf.support_channels, chan);
7697 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
7698 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
7699 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
7700 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
7701 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
7702 chanserv_conf.greeting_length = str ? atoi(str) : 200;
7703 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
7704 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
7705 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
7706 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
7707 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
7708 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
7709 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
7710 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
7711 str = database_get_data(conf_node, KEY_DNR_EXPIRE_FREQ, RECDB_QSTRING);
7712 chanserv_conf.dnr_expire_frequency = str ? ParseInterval(str) : 3600;
7713 str = database_get_data(conf_node, KEY_INVITED_INTERVAL, RECDB_QSTRING);
7714 chanserv_conf.invited_timeout = str ? ParseInterval(str) : 600*2;
7715 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
7716 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
7717 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
7718 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
7719 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
7720 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
7721 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
7722 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
7723 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
7725 NickChange(chanserv, str, 0);
7726 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
7727 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
7728 str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
7729 chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
7730 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
7731 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
7732 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
7733 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
7734 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
7735 chanserv_conf.max_owned = str ? atoi(str) : 5;
7736 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
7737 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
7738 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
7739 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
7740 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
7741 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
7742 str = database_get_data(conf_node, KEY_NEW_CHANNEL_AUTHED, RECDB_QSTRING);
7743 chanserv_conf.new_channel_authed = str ? str : NULL;
7744 str = database_get_data(conf_node, KEY_NEW_CHANNEL_UNAUTHED, RECDB_QSTRING);
7745 chanserv_conf.new_channel_unauthed = str ? str : NULL;
7746 str = database_get_data(conf_node, KEY_NEW_CHANNEL_MSG, RECDB_QSTRING);
7747 chanserv_conf.new_channel_msg = str ? str : NULL;
7748 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
7751 safestrncpy(mode_line, str, sizeof(mode_line));
7752 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
7753 if((change = mod_chanmode_parse(NULL, NULL, modes, ii, MCP_KEY_FREE|MCP_NO_APASS, 0))
7754 && (change->argc < 2))
7756 chanserv_conf.default_modes = *change;
7757 mod_chanmode_free(change);
7759 free_string_list(chanserv_conf.set_shows);
7760 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
7762 strlist = string_list_copy(strlist);
7765 static const char *list[] = {
7766 /* free form text */
7767 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
7768 /* options based on user level */
7769 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
7770 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
7771 /* multiple choice options */
7772 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
7773 /* binary options */
7774 "DynLimit", "NoDelete", "expire", "Vote",
7778 strlist = alloc_string_list(ArrayLength(list)-1);
7779 for(ii=0; list[ii]; ii++)
7780 string_list_append(strlist, strdup(list[ii]));
7782 chanserv_conf.set_shows = strlist;
7783 /* We don't look things up now, in case the list refers to options
7784 * defined by modules initialized after this point. Just mark the
7785 * function list as invalid, so it will be initialized.
7787 set_shows_list.used = 0;
7788 free_string_list(chanserv_conf.eightball);
7789 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
7792 strlist = string_list_copy(strlist);
7796 strlist = alloc_string_list(4);
7797 string_list_append(strlist, strdup("Yes."));
7798 string_list_append(strlist, strdup("No."));
7799 string_list_append(strlist, strdup("Maybe so."));
7801 chanserv_conf.eightball = strlist;
7802 free_string_list(chanserv_conf.old_ban_names);
7803 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
7805 strlist = string_list_copy(strlist);
7807 strlist = alloc_string_list(2);
7808 chanserv_conf.old_ban_names = strlist;
7809 str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
7810 off_channel = str ? atoi(str) : 0;
7814 chanserv_note_type_read(const char *key, struct record_data *rd)
7817 struct note_type *ntype;
7820 if(!(obj = GET_RECORD_OBJECT(rd)))
7822 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
7825 if(!(ntype = chanserv_create_note_type(key)))
7827 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
7831 /* Figure out set access */
7832 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
7834 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7835 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
7837 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
7839 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
7840 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
7842 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
7844 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
7848 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
7849 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7850 ntype->set_access.min_opserv = 0;
7853 /* Figure out visibility */
7854 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
7855 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7856 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
7857 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7858 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
7859 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
7860 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
7861 ntype->visible_type = NOTE_VIS_ALL;
7863 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7865 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
7866 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
7870 vote_option_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7872 struct vote_option *vOpt;
7875 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7877 log_module(CS_LOG, LOG_ERROR, "Invalid vote option in %s.", chan->channel->name);
7881 vOpt = calloc(1, sizeof(*vOpt));
7882 vOpt->name = strdup(database_get_data(rd->d.object, KEY_VOTE_OPTION_NAME, RECDB_QSTRING));
7883 str = database_get_data(rd->d.object, KEY_VOTE_OPTION_VOTED, RECDB_QSTRING);
7884 vOpt->voted = str ? atoi(str) : 0;
7885 vOpt->option_id = str ? atoi(key) : 0;
7886 vOpt->option_str = strdup(key);
7887 dict_insert(chan->vote_options,vOpt->option_str,vOpt);
7891 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7893 struct handle_info *handle;
7894 struct userData *uData;
7895 char *seen, *inf, *flags, *voted, *votefor;
7896 unsigned long last_seen;
7897 unsigned short access_level;
7899 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7901 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
7905 access_level = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
7906 if(access_level > UL_OWNER)
7908 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
7912 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
7913 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
7914 last_seen = seen ? strtoul(seen, NULL, 0) : now;
7915 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
7916 voted = database_get_data(rd->d.object, KEY_VOTE_VOTED, RECDB_QSTRING);
7917 votefor = database_get_data(rd->d.object, KEY_VOTE_VOTEDFOR, RECDB_QSTRING);
7918 handle = get_handle_info(key);
7921 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
7925 uData = add_channel_user(chan, handle, access_level, last_seen, inf);
7926 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
7928 uData->voted = voted ? strtoul(voted, NULL, 0) : 0;
7929 uData->votefor = votefor ? strtoul(votefor, NULL, 0) : 0;
7937 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7939 struct banData *bData;
7940 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
7941 unsigned long set_time, triggered_time, expires_time;
7943 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7945 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
7949 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
7950 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
7951 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
7952 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
7953 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
7954 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
7955 if (!reason || !owner)
7958 set_time = set ? strtoul(set, NULL, 0) : now;
7959 triggered_time = triggered ? strtoul(triggered, NULL, 0) : 0;
7961 expires_time = strtoul(s_expires, NULL, 0);
7963 expires_time = set_time + atoi(s_duration);
7967 if(!reason || (expires_time && (expires_time < now)))
7970 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
7973 static struct suspended *
7974 chanserv_read_suspended(dict_t obj)
7976 struct suspended *suspended = calloc(1, sizeof(*suspended));
7980 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
7981 suspended->expires = str ? strtoul(str, NULL, 0) : 0;
7982 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
7983 suspended->revoked = str ? strtoul(str, NULL, 0) : 0;
7984 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
7985 suspended->issued = str ? strtoul(str, NULL, 0) : 0;
7986 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
7987 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
7988 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
7989 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
7994 chanserv_channel_read(const char *key, struct record_data *hir)
7996 struct suspended *suspended;
7997 struct mod_chanmode *modes;
7998 struct chanNode *cNode;
7999 struct chanData *cData;
8000 struct dict *channel, *obj;
8001 char *str, *argv[10];
8005 channel = hir->d.object;
8007 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
8010 cNode = AddChannel(key, now, NULL, NULL);
8013 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
8016 cData = register_channel(cNode, str);
8019 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
8023 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
8025 enum levelOption lvlOpt;
8026 enum charOption chOpt;
8028 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
8029 cData->flags = atoi(str);
8031 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8033 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
8035 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
8036 else if(levelOptions[lvlOpt].old_flag)
8038 if(cData->flags & levelOptions[lvlOpt].old_flag)
8039 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
8041 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
8045 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8047 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
8049 cData->chOpts[chOpt] = str[0];
8052 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
8054 enum levelOption lvlOpt;
8055 enum charOption chOpt;
8058 cData->flags = base64toint(str, 5);
8059 count = strlen(str += 5);
8060 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8063 if(levelOptions[lvlOpt].old_flag)
8065 if(cData->flags & levelOptions[lvlOpt].old_flag)
8066 lvl = levelOptions[lvlOpt].flag_value;
8068 lvl = levelOptions[lvlOpt].default_value;
8070 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
8072 case 'c': lvl = UL_COOWNER; break;
8073 case 'm': lvl = UL_MASTER; break;
8074 case 'n': lvl = UL_OWNER+1; break;
8075 case 'o': lvl = UL_OP; break;
8076 case 'p': lvl = UL_PEON; break;
8077 case 'w': lvl = UL_OWNER; break;
8078 default: lvl = 0; break;
8080 cData->lvlOpts[lvlOpt] = lvl;
8082 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8083 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
8086 if((str = database_get_data(hir->d.object, KEY_EXPIRE, RECDB_QSTRING)))
8088 cData->expiry = atoi(str);
8089 if(cData->expiry > 0) {
8090 if(cData->expiry > now) {
8091 timeq_add(cData->expiry, chanserv_expire_channel, cData);
8093 timeq_add(1, chanserv_expire_channel, cData);
8100 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
8102 suspended = chanserv_read_suspended(obj);
8103 cData->suspended = suspended;
8104 suspended->cData = cData;
8105 /* We could use suspended->expires and suspended->revoked to
8106 * set the CHANNEL_SUSPENDED flag, but we don't. */
8108 else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
8110 suspended = calloc(1, sizeof(*suspended));
8111 suspended->issued = 0;
8112 suspended->revoked = 0;
8113 suspended->suspender = strdup(str);
8114 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
8115 suspended->expires = str ? atoi(str) : 0;
8116 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
8117 suspended->reason = strdup(str ? str : "No reason");
8118 suspended->previous = NULL;
8119 cData->suspended = suspended;
8120 suspended->cData = cData;
8124 cData->flags &= ~CHANNEL_SUSPENDED;
8125 suspended = NULL; /* to squelch a warning */
8128 if(IsSuspended(cData)) {
8129 if(suspended->expires > now)
8130 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
8131 else if(suspended->expires)
8132 cData->flags &= ~CHANNEL_SUSPENDED;
8135 if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
8136 struct mod_chanmode change;
8137 mod_chanmode_init(&change);
8139 change.args[0].mode = MODE_CHANOP;
8140 change.args[0].u.member = AddChannelUser(chanserv, cNode);
8141 mod_chanmode_announce(chanserv, cNode, &change);
8144 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
8145 cData->registered = str ? strtoul(str, NULL, 0) : now;
8146 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
8147 cData->visited = str ? strtoul(str, NULL, 0) : now;
8148 str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
8149 cData->ownerTransfer = str ? strtoul(str, NULL, 0) : 0;
8150 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
8151 cData->max = str ? atoi(str) : 0;
8152 str = database_get_data(channel, KEY_MAX_TIME, RECDB_QSTRING);
8153 cData->max_time = str ? atoi(str) : 0;
8154 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
8155 cData->greeting = str ? strdup(str) : NULL;
8156 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
8157 cData->user_greeting = str ? strdup(str) : NULL;
8158 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
8159 cData->topic_mask = str ? strdup(str) : NULL;
8160 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
8161 cData->topic = str ? strdup(str) : NULL;
8163 str = database_get_data(channel, KEY_VOTE, RECDB_QSTRING);
8165 cData->vote = str ? strdup(str) : NULL;
8166 dict_delete(cData->vote_options);
8167 cData->vote_options = dict_new();
8168 dict_set_free_data(cData->vote_options, free_vote_options);
8169 str = database_get_data(channel, KEY_VOTE_START, RECDB_QSTRING);
8170 cData->vote_start = str ? atoi(str) : 0;
8171 obj = database_get_data(channel, KEY_VOTE_OPTIONS, RECDB_OBJECT);
8172 for(it = dict_first(obj); it; it = iter_next(it)) {
8173 vote_option_read_helper(iter_key(it), iter_data(it), cData);
8177 if(!IsSuspended(cData)
8178 && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
8179 && (argc = split_line(str, 0, ArrayLength(argv), argv))
8180 && (modes = mod_chanmode_parse(cNode, NULL, argv, argc, MCP_KEY_FREE|MCP_NO_APASS, 0))) {
8181 cData->modes = *modes;
8183 cData->modes.modes_set |= MODE_REGISTERED;
8184 if(cData->modes.argc > 1)
8185 cData->modes.argc = 1;
8186 mod_chanmode_announce(chanserv, cNode, &cData->modes);
8187 mod_chanmode_free(modes);
8190 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
8191 for(it = dict_first(obj); it; it = iter_next(it))
8192 user_read_helper(iter_key(it), iter_data(it), cData);
8194 if(!cData->users && !IsProtected(cData))
8196 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
8197 unregister_channel(cData, "has empty user list.");
8201 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
8202 for(it = dict_first(obj); it; it = iter_next(it))
8203 ban_read_helper(iter_key(it), iter_data(it), cData);
8205 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
8206 for(it = dict_first(obj); it; it = iter_next(it))
8208 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
8209 struct record_data *rd = iter_data(it);
8210 const char *note, *setter;
8212 if(rd->type != RECDB_OBJECT)
8214 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
8218 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
8220 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
8222 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
8226 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
8227 if(!setter) setter = "<unknown>";
8228 chanserv_add_channel_note(cData, ntype, setter, note);
8236 chanserv_dnr_read(const char *key, struct record_data *hir)
8238 const char *setter, *reason, *str;
8239 struct do_not_register *dnr;
8240 unsigned long expiry;
8242 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
8245 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
8248 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
8251 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
8254 str = database_get_data(hir->d.object, KEY_EXPIRES, RECDB_QSTRING);
8255 expiry = str ? strtoul(str, NULL, 0) : 0;
8256 if(expiry && expiry <= now)
8258 dnr = chanserv_add_dnr(key, setter, expiry, reason);
8261 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
8263 dnr->set = atoi(str);
8269 chanserv_saxdb_read(struct dict *database)
8271 struct dict *section;
8274 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
8275 for(it = dict_first(section); it; it = iter_next(it))
8276 chanserv_note_type_read(iter_key(it), iter_data(it));
8278 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
8279 for(it = dict_first(section); it; it = iter_next(it))
8280 chanserv_channel_read(iter_key(it), iter_data(it));
8282 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
8283 for(it = dict_first(section); it; it = iter_next(it))
8284 chanserv_dnr_read(iter_key(it), iter_data(it));
8290 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
8292 int high_present = 0;
8293 saxdb_start_record(ctx, KEY_USERS, 1);
8294 for(; uData; uData = uData->next)
8296 if((uData->access >= UL_PRESENT) && uData->present && !HANDLE_FLAGGED(uData->handle, BOT))
8298 saxdb_start_record(ctx, uData->handle->handle, 0);
8299 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
8300 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
8302 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
8303 if(uData->channel->vote && uData->voted)
8304 saxdb_write_int(ctx, KEY_VOTE_VOTED, uData->voted);
8305 if(uData->channel->vote && uData->votefor)
8306 saxdb_write_int(ctx, KEY_VOTE_VOTEDFOR, uData->votefor);
8308 saxdb_write_string(ctx, KEY_INFO, uData->info);
8309 saxdb_end_record(ctx);
8311 saxdb_end_record(ctx);
8312 return high_present;
8316 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
8320 saxdb_start_record(ctx, KEY_BANS, 1);
8321 for(; bData; bData = bData->next)
8323 saxdb_start_record(ctx, bData->mask, 0);
8324 saxdb_write_int(ctx, KEY_SET, bData->set);
8325 if(bData->triggered)
8326 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
8328 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
8330 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
8332 saxdb_write_string(ctx, KEY_REASON, bData->reason);
8333 saxdb_end_record(ctx);
8335 saxdb_end_record(ctx);
8339 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
8341 saxdb_start_record(ctx, name, 0);
8342 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
8343 saxdb_write_string(ctx, KEY_REASON, susp->reason);
8345 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
8347 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
8349 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
8351 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
8352 saxdb_end_record(ctx);
8356 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
8360 enum levelOption lvlOpt;
8361 enum charOption chOpt;
8364 saxdb_start_record(ctx, channel->channel->name, 1);
8366 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
8367 saxdb_write_int(ctx, KEY_MAX, channel->max);
8368 saxdb_write_int(ctx, KEY_MAX_TIME, channel->max_time);
8370 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
8371 if(channel->registrar)
8372 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
8373 if(channel->greeting)
8374 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
8375 if(channel->user_greeting)
8376 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
8377 if(channel->topic_mask)
8378 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
8379 if(channel->suspended)
8380 chanserv_write_suspended(ctx, "suspended", channel->suspended);
8382 saxdb_write_int(ctx, KEY_EXPIRE, channel->expiry);
8385 saxdb_write_string(ctx, KEY_VOTE, channel->vote);
8386 if(channel->vote_start)
8387 saxdb_write_int(ctx, KEY_VOTE_START, channel->vote_start);
8388 if (dict_size(channel->vote_options)) {
8389 saxdb_start_record(ctx, KEY_VOTE_OPTIONS, 1);
8390 for (it = dict_first(channel->vote_options); it; it = iter_next(it)) {
8391 struct vote_option *vOpt = iter_data(it);
8393 sprintf(str,"%i",vOpt->option_id);
8394 saxdb_start_record(ctx, str, 0);
8396 saxdb_write_int(ctx, KEY_VOTE_OPTION_VOTED, vOpt->voted);
8398 saxdb_write_string(ctx, KEY_VOTE_OPTION_NAME, vOpt->name);
8399 saxdb_end_record(ctx);
8401 saxdb_end_record(ctx);
8405 saxdb_start_record(ctx, KEY_OPTIONS, 0);
8406 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
8407 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8408 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
8409 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8411 buf[0] = channel->chOpts[chOpt];
8413 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
8415 saxdb_end_record(ctx);
8417 if(channel->modes.modes_set || channel->modes.modes_clear)
8419 mod_chanmode_format(&channel->modes, buf);
8420 saxdb_write_string(ctx, KEY_MODES, buf);
8423 high_present = chanserv_write_users(ctx, channel->users);
8424 chanserv_write_bans(ctx, channel->bans);
8426 if(dict_size(channel->notes))
8430 saxdb_start_record(ctx, KEY_NOTES, 1);
8431 for(it = dict_first(channel->notes); it; it = iter_next(it))
8433 struct note *note = iter_data(it);
8434 saxdb_start_record(ctx, iter_key(it), 0);
8435 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
8436 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
8437 saxdb_end_record(ctx);
8439 saxdb_end_record(ctx);
8442 if(channel->ownerTransfer)
8443 saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
8444 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
8445 saxdb_end_record(ctx);
8449 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
8453 saxdb_start_record(ctx, ntype->name, 0);
8454 switch(ntype->set_access_type)
8456 case NOTE_SET_CHANNEL_ACCESS:
8457 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
8459 case NOTE_SET_CHANNEL_SETTER:
8460 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
8462 case NOTE_SET_PRIVILEGED: default:
8463 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
8466 switch(ntype->visible_type)
8468 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
8469 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
8470 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
8472 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
8473 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
8474 saxdb_end_record(ctx);
8478 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
8480 struct do_not_register *dnr;
8481 dict_iterator_t it, next;
8483 for(it = dict_first(dnrs); it; it = next)
8485 next = iter_next(it);
8486 dnr = iter_data(it);
8487 if(dnr->expires && dnr->expires <= now)
8489 dict_remove(dnrs, iter_key(it));
8492 saxdb_start_record(ctx, dnr->chan_name, 0);
8494 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
8496 saxdb_write_int(ctx, KEY_EXPIRES, dnr->expires);
8497 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
8498 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
8499 saxdb_end_record(ctx);
8504 chanserv_saxdb_write(struct saxdb_context *ctx)
8507 struct chanData *channel;
8510 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
8511 for(it = dict_first(note_types); it; it = iter_next(it))
8512 chanserv_write_note_type(ctx, iter_data(it));
8513 saxdb_end_record(ctx);
8516 saxdb_start_record(ctx, KEY_DNR, 1);
8517 write_dnrs_helper(ctx, handle_dnrs);
8518 write_dnrs_helper(ctx, plain_dnrs);
8519 write_dnrs_helper(ctx, mask_dnrs);
8520 saxdb_end_record(ctx);
8523 saxdb_start_record(ctx, KEY_CHANNELS, 1);
8524 for(channel = channelList; channel; channel = channel->next)
8525 chanserv_write_channel(ctx, channel);
8526 saxdb_end_record(ctx);
8532 chanserv_db_cleanup(void) {
8534 unreg_part_func(handle_part);
8536 unregister_channel(channelList, "terminating.");
8537 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
8538 UnlockChannel(chanserv_conf.support_channels.list[ii]);
8539 free(chanserv_conf.support_channels.list);
8540 dict_delete(handle_dnrs);
8541 dict_delete(plain_dnrs);
8542 dict_delete(mask_dnrs);
8543 dict_delete(note_types);
8544 free_string_list(chanserv_conf.eightball);
8545 free_string_list(chanserv_conf.old_ban_names);
8546 free_string_list(chanserv_conf.set_shows);
8547 free(set_shows_list.list);
8548 free(uset_shows_list.list);
8551 struct userData *helper = helperList;
8552 helperList = helperList->next;
8557 #if defined(GCC_VARMACROS)
8558 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ARGS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ARGS)
8559 #elif defined(C99_VARMACROS)
8560 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, __VA_ARGS__)
8562 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
8563 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
8566 init_chanserv(const char *nick)
8568 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
8569 conf_register_reload(chanserv_conf_read);
8573 reg_server_link_func(handle_server_link);
8574 reg_new_channel_func(handle_new_channel);
8575 reg_join_func(handle_join);
8576 reg_part_func(handle_part);
8577 reg_kick_func(handle_kick);
8578 reg_topic_func(handle_topic);
8579 reg_mode_change_func(handle_mode);
8580 reg_nick_change_func(handle_nick_change);
8581 reg_auth_func(handle_auth);
8584 reg_handle_rename_func(handle_rename);
8585 reg_unreg_func(handle_unreg);
8587 handle_dnrs = dict_new();
8588 dict_set_free_data(handle_dnrs, free);
8589 plain_dnrs = dict_new();
8590 dict_set_free_data(plain_dnrs, free);
8591 mask_dnrs = dict_new();
8592 dict_set_free_data(mask_dnrs, free);
8594 reg_svccmd_unbind_func(handle_svccmd_unbind);
8595 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
8596 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
8597 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
8598 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
8599 DEFINE_COMMAND(dnrsearch, 3, 0, "template", "noregister", NULL);
8600 modcmd_register(chanserv_module, "dnrsearch print", NULL, 0, 0, NULL);
8601 modcmd_register(chanserv_module, "dnrsearch remove", NULL, 0, 0, NULL);
8602 modcmd_register(chanserv_module, "dnrsearch count", NULL, 0, 0, NULL);
8603 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
8604 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
8605 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
8606 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
8607 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
8609 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
8610 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
8612 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8613 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8614 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8615 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8616 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
8618 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
8619 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
8620 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
8621 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8622 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8624 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8625 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
8626 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8627 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
8629 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
8630 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
8631 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8632 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8633 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
8634 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8635 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8636 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8638 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8639 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8640 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8641 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
8642 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
8643 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
8644 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
8645 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
8646 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8647 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
8648 DEFINE_COMMAND(invitemeall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8649 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
8650 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
8651 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8652 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8654 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
8655 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8656 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8657 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8658 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
8660 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
8661 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
8663 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
8664 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8665 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8666 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8667 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8668 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8669 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8670 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8671 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8672 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8673 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8675 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
8676 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
8678 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
8679 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
8680 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
8681 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
8683 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
8684 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
8685 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
8686 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
8687 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
8689 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8690 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8691 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8692 DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8693 DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8694 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8696 DEFINE_COMMAND(addvote, 1, MODCMD_REQUIRE_AUTHED, NULL);
8697 DEFINE_COMMAND(delvote, 1, MODCMD_REQUIRE_AUTHED, NULL);
8698 DEFINE_COMMAND(addoption, 1, MODCMD_REQUIRE_AUTHED, NULL);
8699 DEFINE_COMMAND(deloption, 1, MODCMD_REQUIRE_AUTHED, NULL);
8700 DEFINE_COMMAND(vote, 1, MODCMD_REQUIRE_AUTHED, NULL);
8701 DEFINE_COMMAND(startvote, 1, MODCMD_REQUIRE_AUTHED, NULL);
8702 DEFINE_COMMAND(endvote, 1, MODCMD_REQUIRE_AUTHED, NULL);
8703 DEFINE_COMMAND(voteresults, 1, MODCMD_REQUIRE_AUTHED, NULL);
8705 DEFINE_COMMAND(opme, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
8707 /* Channel options */
8708 DEFINE_CHANNEL_OPTION(defaulttopic);
8709 DEFINE_CHANNEL_OPTION(topicmask);
8710 DEFINE_CHANNEL_OPTION(greeting);
8711 DEFINE_CHANNEL_OPTION(usergreeting);
8712 DEFINE_CHANNEL_OPTION(modes);
8713 DEFINE_CHANNEL_OPTION(enfops);
8714 DEFINE_CHANNEL_OPTION(giveops);
8715 DEFINE_CHANNEL_OPTION(protect);
8716 DEFINE_CHANNEL_OPTION(enfmodes);
8717 DEFINE_CHANNEL_OPTION(enftopic);
8718 DEFINE_CHANNEL_OPTION(pubcmd);
8719 DEFINE_CHANNEL_OPTION(givevoice);
8720 DEFINE_CHANNEL_OPTION(userinfo);
8721 DEFINE_CHANNEL_OPTION(dynlimit);
8722 DEFINE_CHANNEL_OPTION(topicsnarf);
8723 DEFINE_CHANNEL_OPTION(vote);
8724 DEFINE_CHANNEL_OPTION(nodelete);
8725 DEFINE_CHANNEL_OPTION(toys);
8726 DEFINE_CHANNEL_OPTION(setters);
8727 DEFINE_CHANNEL_OPTION(topicrefresh);
8728 DEFINE_CHANNEL_OPTION(ctcpusers);
8729 DEFINE_CHANNEL_OPTION(ctcpreaction);
8730 DEFINE_CHANNEL_OPTION(inviteme);
8731 DEFINE_CHANNEL_OPTION(unreviewed);
8732 modcmd_register(chanserv_module, "set expire", chan_opt_expire, 1, 0, "flags", "+helping", NULL);
8733 modcmd_register(chanserv_module, "set unreviewed on", NULL, 0, 0, "flags", "+helping", NULL);
8734 modcmd_register(chanserv_module, "set unreviewed off", NULL, 0, 0, "flags", "+oper", NULL);
8736 DEFINE_CHANNEL_OPTION(offchannel);
8737 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
8739 /* Alias set topic to set defaulttopic for compatibility. */
8740 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
8743 DEFINE_USER_OPTION(noautoop);
8744 DEFINE_USER_OPTION(autoinvite);
8745 DEFINE_USER_OPTION(info);
8747 /* Alias uset autovoice to uset autoop. */
8748 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
8750 note_types = dict_new();
8751 dict_set_free_data(note_types, chanserv_deref_note_type);
8754 const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
8755 chanserv = AddLocalUser(nick, nick, NULL, "Channel Services", modes);
8756 service_register(chanserv)->trigger = '!';
8757 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
8759 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
8761 if(chanserv_conf.channel_expire_frequency)
8762 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
8764 if(chanserv_conf.dnr_expire_frequency)
8765 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
8767 if(chanserv_conf.refresh_period)
8769 unsigned long next_refresh;
8770 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
8771 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
8774 reg_exit_func(chanserv_db_cleanup);
8775 message_register_table(msgtab);