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(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 && !data)
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 = GetChannelUser(channel->channel_info, user->handle_info);
2994 if(min_access > max_access)
2996 send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
3000 if(!actor || actor->access <= max_access)
3002 send_message(user, chanserv, "CSMSG_NO_ACCESS");
3007 limit = now - duration;
3008 for(uData = channel->channel_info->users; uData; uData = next)
3012 if((uData->seen > limit)
3014 || (HANDLE_FLAGGED(uData->handle, FROZEN) && !vacation))
3017 if(((uData->access >= min_access) && (uData->access <= max_access))
3018 || (!max_access && (uData->access < actor->access)))
3020 del_channel_user(uData, 1);
3028 max_access = (actor->access > UL_OWNER) ? UL_OWNER : (actor->access - 1);
3030 send_message(user, chanserv, "CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration, user->handle_info));
3034 static CHANSERV_FUNC(cmd_trim)
3036 unsigned long duration;
3037 unsigned short min_level, max_level;
3042 vacation = argc > 3 && !strcmp(argv[3], "vacation");
3043 duration = ParseInterval(argv[2]);
3046 reply("CSMSG_CANNOT_TRIM");
3050 if(!irccasecmp(argv[1], "bans"))
3052 cmd_trim_bans(user, channel, duration);
3055 else if(!irccasecmp(argv[1], "users"))
3057 cmd_trim_users(user, channel, 0, 0, duration, vacation);
3060 else if(parse_level_range(&min_level, &max_level, argv[1]))
3062 cmd_trim_users(user, channel, min_level, max_level, duration, vacation);
3065 else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
3067 cmd_trim_users(user, channel, min_level, min_level, duration, vacation);
3072 reply("CSMSG_INVALID_TRIM", argv[1]);
3077 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
3078 to the user. cmd_all takes advantage of this. */
3079 static CHANSERV_FUNC(cmd_up)
3081 struct mod_chanmode change;
3082 struct userData *uData;
3085 mod_chanmode_init(&change);
3087 change.args[0].u.member = GetUserMode(channel, user);
3088 if(!change.args[0].u.member)
3091 reply("MSG_CHANNEL_ABSENT", channel->name);
3095 uData = GetChannelAccess(channel->channel_info, user->handle_info);
3099 reply("CSMSG_GODMODE_UP", argv[0]);
3102 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveOps])
3104 change.args[0].mode = MODE_CHANOP;
3105 errmsg = "CSMSG_ALREADY_OPPED";
3107 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveVoice])
3109 change.args[0].mode = MODE_VOICE;
3110 errmsg = "CSMSG_ALREADY_VOICED";
3115 reply("CSMSG_NO_ACCESS");
3118 change.args[0].mode &= ~change.args[0].u.member->modes;
3119 if(!change.args[0].mode)
3122 reply(errmsg, channel->name);
3125 modcmd_chanmode_announce(&change);
3129 static CHANSERV_FUNC(cmd_down)
3131 struct mod_chanmode change;
3133 mod_chanmode_init(&change);
3135 change.args[0].u.member = GetUserMode(channel, user);
3136 if(!change.args[0].u.member)
3139 reply("MSG_CHANNEL_ABSENT", channel->name);
3143 if(!change.args[0].u.member->modes)
3146 reply("CSMSG_ALREADY_DOWN", channel->name);
3150 change.args[0].mode = MODE_REMOVE | change.args[0].u.member->modes;
3151 modcmd_chanmode_announce(&change);
3155 static int cmd_all(struct userNode *user, UNUSED_ARG(struct chanNode *channel), UNUSED_ARG(unsigned int argc), UNUSED_ARG(char *argv[]), struct svccmd *cmd, modcmd_func_t mcmd)
3157 struct userData *cList;
3159 for(cList = user->handle_info->channels; cList; cList = cList->u_next)
3161 if(IsSuspended(cList->channel)
3162 || IsUserSuspended(cList)
3163 || !GetUserMode(cList->channel->channel, user))
3166 mcmd(user, cList->channel->channel, 0, NULL, cmd);
3172 static CHANSERV_FUNC(cmd_upall)
3174 return cmd_all(CSFUNC_ARGS, cmd_up);
3177 static CHANSERV_FUNC(cmd_downall)
3179 return cmd_all(CSFUNC_ARGS, cmd_down);
3182 typedef int validate_func_t(struct userNode *user, struct chanNode *channel, struct userNode *victim);
3183 typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
3186 modify_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, validate_func_t validate, chan_mode_t mode, char *action)
3188 unsigned int ii, valid;
3189 struct userNode *victim;
3190 struct mod_chanmode *change;
3192 change = mod_chanmode_alloc(argc - 1);
3194 for(ii=valid=0; ++ii < argc; )
3196 if(!(victim = GetUserH(argv[ii])))
3198 change->args[valid].mode = mode;
3199 change->args[valid].u.member = GetUserMode(channel, victim);
3200 if(!change->args[valid].u.member)
3202 if(validate && !validate(user, channel, victim))
3207 change->argc = valid;
3208 if(valid < (argc-1))
3209 reply("CSMSG_PROCESS_FAILED");
3212 modcmd_chanmode_announce(change);
3213 reply(action, channel->name);
3215 mod_chanmode_free(change);
3219 static CHANSERV_FUNC(cmd_op)
3221 return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
3224 static CHANSERV_FUNC(cmd_deop)
3226 return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
3229 static CHANSERV_FUNC(cmd_voice)
3231 return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
3234 static CHANSERV_FUNC(cmd_devoice)
3236 return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
3239 static CHANSERV_FUNC(cmd_opme)
3241 struct mod_chanmode change;
3244 mod_chanmode_init(&change);
3246 change.args[0].u.member = GetUserMode(channel, user);
3247 if(!change.args[0].u.member)
3250 reply("MSG_CHANNEL_ABSENT", channel->name);
3254 struct devnull_class *devnull;
3255 if(user->handle_info->devnull && (devnull = devnull_get(user->handle_info->devnull)) && (devnull->modes & DEVNULL_MODE_OPME))
3257 change.args[0].mode = MODE_CHANOP;
3258 errmsg = "CSMSG_ALREADY_OPPED";
3263 reply("CSMSG_NO_ACCESS");
3266 change.args[0].mode &= ~change.args[0].u.member->modes;
3267 if(!change.args[0].mode)
3270 reply(errmsg, channel->name);
3273 modcmd_chanmode_announce(&change);
3278 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, unsigned int *victimCount, struct modeNode **victims)
3284 for(ii=0; ii<channel->members.used; ii++)
3286 struct modeNode *mn = channel->members.list[ii];
3288 if(IsService(mn->user))
3291 if(!user_matches_glob(mn->user, ban, MATCH_USENICK | MATCH_VISIBLE))
3294 if(protect_user(mn->user, user, channel->channel_info))
3298 victims[(*victimCount)++] = mn;
3304 eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3306 struct userNode *victim;
3307 struct modeNode **victims;
3308 unsigned int offset, n, victimCount, duration = 0;
3309 char *reason = "Bye.", *ban, *name;
3310 char interval[INTERVALLEN];
3312 offset = (action & ACTION_ADD_TIMED_BAN) ? 3 : 2;
3313 REQUIRE_PARAMS(offset);
3314 if(argc > offset && IsNetServ(user))
3316 if(*argv[offset] == '$') {
3317 struct userNode *hib;
3318 const char *accountnameb = argv[offset] + 1;
3319 if(!(hib = GetUserH(accountnameb)))
3321 reply("MSG_HANDLE_UNKNOWN", accountnameb);
3330 reason = unsplit_string(argv + offset, argc - offset, NULL);
3331 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
3333 /* Truncate the reason to a length of TOPICLEN, as
3334 the ircd does; however, leave room for an ellipsis
3335 and the kicker's nick. */
3336 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
3340 if((victim = GetUserH(argv[1])))
3342 victims = alloca(sizeof(victims[0]));
3343 victims[0] = GetUserMode(channel, victim);
3344 /* XXX: The comparison with ACTION_KICK is just because all
3345 * other actions can work on users outside the channel, and we
3346 * want to allow those (e.g. unbans) in that case. If we add
3347 * some other ejection action for in-channel users, change
3349 victimCount = victims[0] ? 1 : 0;
3351 if(IsService(victim))
3353 reply("MSG_SERVICE_IMMUNE", victim->nick);
3357 if((action == ACTION_KICK) && !victimCount)
3359 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
3363 if(protect_user(victim, user, channel->channel_info))
3365 reply("CSMSG_USER_PROTECTED", victim->nick);
3369 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
3370 name = victim->nick;
3372 else if(!is_ircmask(argv[1]) && (*argv[1] == '*'))
3374 struct handle_info *hi;
3375 extern const char *titlehost_suffix;
3376 char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
3377 const char *accountname = argv[1] + 1;
3379 if(!(hi = get_handle_info(accountname)))
3381 reply("MSG_HANDLE_UNKNOWN", accountname);
3385 snprintf(banmask, sizeof(banmask), "*!*@%s.*.%s", hi->handle, titlehost_suffix);
3386 victims = alloca(sizeof(victims[0]) * channel->members.used);
3388 if(bad_channel_ban(channel, user, banmask, &victimCount, victims))
3390 reply("CSMSG_MASK_PROTECTED", banmask);
3394 if((action == ACTION_KICK) && (victimCount == 0))
3396 reply("CSMSG_NO_MATCHING_USERS", channel->name, banmask);
3400 name = ban = strdup(banmask);
3404 if(!is_ircmask(argv[1]))
3406 reply("MSG_NICK_UNKNOWN", argv[1]);
3410 victims = alloca(sizeof(victims[0]) * channel->members.used);
3412 if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
3414 reply("CSMSG_MASK_PROTECTED", argv[1]);
3418 if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
3420 reply("CSMSG_LAME_MASK", argv[1]);
3424 if((action == ACTION_KICK) && (victimCount == 0))
3426 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
3430 name = ban = strdup(argv[1]);
3433 /* Truncate the ban in place if necessary; we must ensure
3434 that 'ban' is a valid ban mask before sanitizing it. */
3435 sanitize_ircmask(ban);
3437 if(action & ACTION_ADD_BAN)
3439 struct banData *bData, *next;
3441 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans)
3443 reply("CSMSG_MAXIMUM_BANS", chanserv_conf.max_chan_bans);
3448 if(action & ACTION_ADD_TIMED_BAN)
3450 duration = ParseInterval(argv[2]);
3454 reply("CSMSG_DURATION_TOO_LOW");
3458 else if(duration > (86400 * 365 * 2))
3460 reply("CSMSG_DURATION_TOO_HIGH");
3466 for(bData = channel->channel_info->bans; bData; bData = next)
3468 if(match_ircglobs(bData->mask, ban))
3470 int exact = !irccasecmp(bData->mask, ban);
3472 /* The ban is redundant; there is already a ban
3473 with the same effect in place. */
3477 free(bData->reason);
3478 bData->reason = strdup(reason);
3479 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
3481 reply("CSMSG_REASON_CHANGE", ban);
3485 if(exact && bData->expires)
3489 /* If the ban matches an existing one exactly,
3490 extend the expiration time if the provided
3491 duration is longer. */
3492 if(duration && (now + duration > bData->expires))
3494 bData->expires = now + duration;
3505 /* Delete the expiration timeq entry and
3506 requeue if necessary. */
3507 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
3510 timeq_add(bData->expires, expire_ban, bData);
3514 /* automated kickban */
3517 reply("CSMSG_BAN_EXTENDED", ban, intervalString(interval, duration, user->handle_info));
3519 reply("CSMSG_BAN_ADDED", name, channel->name);
3525 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3532 if(match_ircglobs(ban, bData->mask))
3534 /* The ban we are adding makes previously existing
3535 bans redundant; silently remove them. */
3536 del_channel_ban(bData);
3540 bData = add_channel_ban(channel->channel_info, ban, (user->handle_info ? user->handle_info->handle : user->nick), now, (victimCount ? now : 0), (duration ? now + duration : 0), reason);
3542 name = ban = strdup(bData->mask);
3546 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
3548 extern const char *hidden_host_suffix;
3549 const char *old_name = chanserv_conf.old_ban_names->list[n];
3551 unsigned int l1, l2;
3554 l2 = strlen(old_name);
3557 if(irccasecmp(ban + l1 - l2, old_name))
3559 new_mask = malloc(MAXLEN);
3560 sprintf(new_mask, "%.*s%s", (int)(l1-l2), ban, hidden_host_suffix);
3562 name = ban = new_mask;
3567 if(action & ACTION_BAN)
3569 unsigned int exists;
3570 struct mod_chanmode *change;
3572 if(channel->banlist.used >= MAXBANS)
3575 reply("CSMSG_BANLIST_FULL", channel->name);
3580 exists = ChannelBanExists(channel, ban);
3581 change = mod_chanmode_alloc(victimCount + 1);
3582 for(n = 0; n < victimCount; ++n)
3584 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_VOICE;
3585 change->args[n].u.member = victims[n];
3589 change->args[n].mode = MODE_BAN;
3590 change->args[n++].u.hostmask = ban;
3594 modcmd_chanmode_announce(change);
3596 mod_chanmode_announce(chanserv, channel, change);
3597 mod_chanmode_free(change);
3599 if(exists && (action == ACTION_BAN))
3602 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3608 if(action & ACTION_KICK)
3610 char kick_reason[MAXLEN];
3611 sprintf(kick_reason, "(%s) %s", user->nick, reason);
3613 for(n = 0; n < victimCount; n++)
3614 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
3619 /* No response, since it was automated. */
3621 else if(action & ACTION_ADD_BAN)
3624 reply("CSMSG_TIMED_BAN_ADDED", name, channel->name, intervalString(interval, duration, user->handle_info));
3626 reply("CSMSG_BAN_ADDED", name, channel->name);
3628 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
3629 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
3630 else if(action & ACTION_BAN)
3631 reply("CSMSG_BAN_DONE", name, channel->name);
3632 else if(action & ACTION_KICK && victimCount)
3633 reply("CSMSG_KICK_DONE", name, channel->name);
3639 static CHANSERV_FUNC(cmd_kickban)
3641 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
3644 static CHANSERV_FUNC(cmd_kick)
3646 return eject_user(CSFUNC_ARGS, ACTION_KICK);
3649 static CHANSERV_FUNC(cmd_ban)
3651 return eject_user(CSFUNC_ARGS, ACTION_BAN);
3654 static CHANSERV_FUNC(cmd_addban)
3656 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN);
3659 static CHANSERV_FUNC(cmd_addtimedban)
3661 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN);
3664 struct mod_chanmode *
3665 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
3667 struct mod_chanmode *change;
3668 unsigned char *match;
3669 unsigned int ii, count;
3671 match = alloca(bans->used);
3674 for(ii = count = 0; ii < bans->used; ++ii)
3676 match[ii] = user_matches_glob(actee, bans->list[ii]->ban,
3677 MATCH_USENICK | MATCH_VISIBLE);
3684 for(ii = count = 0; ii < bans->used; ++ii)
3686 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
3693 change = mod_chanmode_alloc(count);
3694 for(ii = count = 0; ii < bans->used; ++ii)
3698 change->args[count].mode = MODE_REMOVE | MODE_BAN;
3699 change->args[count++].u.hostmask = strdup(bans->list[ii]->ban);
3701 assert(count == change->argc);
3706 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3708 struct userNode *actee;
3714 /* may want to allow a comma delimited list of users... */
3715 if(!(actee = GetUserH(argv[1])))
3717 if(!is_ircmask(argv[1]) && *argv[1] == '*')
3719 char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
3720 const char *accountname = argv[1] + 1;
3722 snprintf(banmask, sizeof(banmask), "*!*@%s.*", accountname);
3723 mask = strdup(banmask);
3725 else if(!is_ircmask(argv[1]))
3727 reply("MSG_NICK_UNKNOWN", argv[1]);
3732 mask = strdup(argv[1]);
3736 /* We don't sanitize the mask here because ircu
3738 if(action & ACTION_UNBAN)
3740 struct mod_chanmode *change;
3741 change = find_matching_bans(&channel->banlist, actee, mask);
3746 modcmd_chanmode_announce(change);
3747 for(ii = 0; ii < change->argc; ++ii)
3748 free((char*)change->args[ii].u.hostmask);
3749 mod_chanmode_free(change);
3754 if(action & ACTION_DEL_BAN)
3756 struct banData *ban, *next;
3758 ban = channel->channel_info->bans;
3762 for( ; ban && !user_matches_glob(actee, ban->mask,
3763 MATCH_USENICK | MATCH_VISIBLE);
3766 for( ; ban && !match_ircglobs(mask, ban->mask);
3771 del_channel_ban(ban);
3778 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3780 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3786 static CHANSERV_FUNC(cmd_unban)
3788 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3791 static CHANSERV_FUNC(cmd_delban)
3793 /* it doesn't necessarily have to remove the channel ban - may want
3794 to make that an option. */
3795 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_BAN);
3798 static CHANSERV_FUNC(cmd_unbanme)
3800 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3801 long flags = ACTION_UNBAN;
3803 /* remove permanent bans if the user has the proper access. */
3804 if(uData->access >= UL_MASTER)
3805 flags |= ACTION_DEL_BAN;
3807 argv[1] = user->nick;
3808 return unban_user(user, channel, 2, argv, cmd, flags);
3811 static CHANSERV_FUNC(cmd_unbanall)
3813 struct mod_chanmode *change;
3816 if(!channel->banlist.used)
3818 reply("CSMSG_NO_BANS", channel->name);
3822 change = mod_chanmode_alloc(channel->banlist.used);
3823 for(ii=0; ii<channel->banlist.used; ii++)
3825 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3826 change->args[ii].u.hostmask = strdup(channel->banlist.list[ii]->ban);
3828 modcmd_chanmode_announce(change);
3829 for(ii = 0; ii < change->argc; ++ii)
3830 free((char*)change->args[ii].u.hostmask);
3831 mod_chanmode_free(change);
3832 reply("CSMSG_BANS_REMOVED", channel->name);
3836 static CHANSERV_FUNC(cmd_open)
3838 struct mod_chanmode *change;
3841 change = find_matching_bans(&channel->banlist, user, NULL);
3843 change = mod_chanmode_alloc(0);
3844 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3845 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3846 && channel->channel_info->modes.modes_set)
3847 change->modes_clear &= ~channel->channel_info->modes.modes_set;
3848 modcmd_chanmode_announce(change);
3849 reply("CSMSG_CHANNEL_OPENED", channel->name);
3850 for(ii = 0; ii < change->argc; ++ii)
3851 free((char*)change->args[ii].u.hostmask);
3852 mod_chanmode_free(change);
3856 static CHANSERV_FUNC(cmd_myaccess)
3858 static struct string_buffer sbuf;
3859 struct handle_info *target_handle;
3860 struct userData *uData;
3865 target_handle = user->handle_info;
3866 else if(!IsStaff(user))
3868 reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
3871 else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3874 if(!oper_outranks(user, target_handle))
3877 if(!target_handle->channels)
3879 reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
3883 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3884 for(uData = target_handle->channels; uData; uData = uData->u_next)
3886 struct chanData *cData = uData->channel;
3888 unsigned int base_len;
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 base_len = sbuf.used;
3903 if(IsUserSuspended(uData))
3904 string_buffer_append(&sbuf, 's');
3905 if(IsUserAutoOp(uData))
3907 if(uData->access >= cData->lvlOpts[lvlGiveOps])
3908 string_buffer_append(&sbuf, 'o');
3909 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
3910 string_buffer_append(&sbuf, 'v');
3912 if(IsUserAutoInvite(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe]))
3913 string_buffer_append(&sbuf, 'i');
3914 if(sbuf.used==base_len)
3917 string_buffer_append_printf(&sbuf, ")] %s", uData->info);
3919 string_buffer_append_string(&sbuf, ")]");
3920 string_buffer_append(&sbuf, '\0');
3921 send_message_type(4, user, cmd->parent->bot, "%s", sbuf.list);
3925 reply("CSMSG_MYACCESS_COUNT_1", target_handle->handle, ccount, ocount);
3927 reply("CSMSG_MYACCESS_COUNT", target_handle->handle, ccount, ocount);
3933 static CHANSERV_FUNC(cmd_access)
3935 struct userNode *target;
3936 struct handle_info *target_handle;
3937 struct userData *uData;
3939 char prefix[MAXLEN];
3944 target_handle = target->handle_info;
3946 else if((target = GetUserH(argv[1])))
3948 target_handle = target->handle_info;
3950 else if(argv[1][0] == '*')
3952 if(!(target_handle = get_handle_info(argv[1]+1)))
3954 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3960 reply("MSG_NICK_UNKNOWN", argv[1]);
3964 assert(target || target_handle);
3966 if(target == chanserv)
3968 reply("CSMSG_IS_CHANSERV");
3976 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
3981 reply("MSG_USER_AUTHENTICATE", target->nick);
3984 reply("MSG_AUTHENTICATE");
3990 const char *epithet = NULL, *type = NULL;
3993 epithet = chanserv_conf.irc_operator_epithet;
3994 type = user_find_message(user, "CSMSG_OPERATOR_TITLE");
3996 else if(IsNetworkHelper(target))
3998 epithet = chanserv_conf.network_helper_epithet;
3999 type = user_find_message(user, "CSMSG_UC_H_TITLE");
4001 else if(IsSupportHelper(target))
4003 epithet = chanserv_conf.support_helper_epithet;
4004 type = user_find_message(user, "CSMSG_LC_H_TITLE");
4008 if(target_handle->epithet)
4009 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
4011 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
4013 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
4017 sprintf(prefix, "%s", target_handle->handle);
4020 if(!channel->channel_info)
4022 reply("CSMSG_NOT_REGISTERED", channel->name);
4026 helping = HANDLE_FLAGGED(target_handle, HELPING)
4027 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
4028 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
4030 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
4031 /* To prevent possible information leaks, only show infolines
4032 * if the requestor is in the channel or it's their own
4034 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
4036 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
4038 /* Likewise, only say it's suspended if the user has active
4039 * access in that channel or it's their own entry. */
4040 if(IsUserSuspended(uData)
4041 && (GetChannelUser(channel->channel_info, user->handle_info)
4042 || (user->handle_info == uData->handle)))
4044 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
4049 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
4056 def_list(struct listData *list)
4060 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
4062 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
4063 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4064 if(list->table.length == 1)
4066 msg = user_find_message(list->user, "MSG_NONE");
4067 send_message_type(4, list->user, list->bot, " %s", msg);
4072 userData_access_comp(const void *arg_a, const void *arg_b)
4074 const struct userData *a = *(struct userData**)arg_a;
4075 const struct userData *b = *(struct userData**)arg_b;
4077 if(a->access != b->access)
4078 res = b->access - a->access;
4080 res = irccasecmp(a->handle->handle, b->handle->handle);
4085 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
4087 void (*send_list)(struct listData *);
4088 struct userData *uData;
4089 struct listData lData;
4090 unsigned int matches;
4094 lData.bot = cmd->parent->bot;
4095 lData.channel = channel;
4096 lData.lowest = lowest;
4097 lData.highest = highest;
4098 lData.search = (argc > 1) ? argv[1] : NULL;
4099 send_list = def_list;
4101 if(user->handle_info)
4103 switch(user->handle_info->userlist_style)
4105 case HI_STYLE_DEF: send_list = def_list; break;
4106 case HI_STYLE_ZOOT: send_list = def_list; break;
4110 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
4112 for(uData = channel->channel_info->users; uData; uData = uData->next)
4114 if((uData->access < lowest)
4115 || (uData->access > highest)
4116 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
4118 lData.users[matches++] = uData;
4120 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
4122 lData.table.length = matches+1;
4123 lData.table.width = 4;
4124 lData.table.flags = TABLE_NO_FREE;
4125 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
4126 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
4127 lData.table.contents[0] = ary;
4130 ary[2] = "Last Seen";
4132 for(matches = 1; matches < lData.table.length; ++matches)
4134 char seen[INTERVALLEN];
4136 uData = lData.users[matches-1];
4137 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
4138 lData.table.contents[matches] = ary;
4139 ary[0] = strtab(uData->access);
4140 ary[1] = uData->handle->handle;
4143 else if(!uData->seen)
4146 ary[2] = intervalString(seen, now - uData->seen, user->handle_info);
4147 ary[2] = strdup(ary[2]);
4148 if(IsUserSuspended(uData))
4149 ary[3] = "Suspended";
4150 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
4151 ary[3] = "Vacation";
4152 else if(HANDLE_FLAGGED(uData->handle, BOT))
4158 for(matches = 1; matches < lData.table.length; ++matches)
4160 free((char*)lData.table.contents[matches][2]);
4161 free(lData.table.contents[matches]);
4163 free(lData.table.contents[0]);
4164 free(lData.table.contents);
4165 reply("CSMSG_TOTAL_USERS",(lData.table.length - 1),channel->name);
4169 static CHANSERV_FUNC(cmd_users)
4171 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
4174 static CHANSERV_FUNC(cmd_wlist)
4176 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
4179 static CHANSERV_FUNC(cmd_clist)
4181 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
4184 static CHANSERV_FUNC(cmd_mlist)
4186 return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
4189 static CHANSERV_FUNC(cmd_olist)
4191 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
4194 static CHANSERV_FUNC(cmd_plist)
4196 return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
4199 static CHANSERV_FUNC(cmd_bans)
4201 struct userNode *search_u = NULL;
4202 struct helpfile_table tbl;
4203 unsigned int matches = 0, timed = 0, search_wilds = 0, ii;
4204 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
4205 const char *msg_never, *triggered, *expires;
4206 struct banData *ban, **bans;
4210 else if(strchr(search = argv[1], '!'))
4213 search_wilds = search[strcspn(search, "?*")];
4215 else if(!(search_u = GetUserH(search)))
4216 reply("MSG_NICK_UNKNOWN", search);
4218 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
4220 for(ban = channel->channel_info->bans; ban; ban = ban->next)
4224 if(!user_matches_glob(search_u, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
4229 if(search_wilds ? !match_ircglobs(search, ban->mask) : !match_ircglob(search, ban->mask))
4232 bans[matches++] = ban;
4237 tbl.length = matches + 1;
4238 tbl.width = 4 + timed;
4240 tbl.flags = TABLE_NO_FREE;
4241 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
4242 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4243 tbl.contents[0][0] = "Mask";
4244 tbl.contents[0][1] = "Set By";
4245 tbl.contents[0][2] = "Triggered";
4248 tbl.contents[0][3] = "Expires";
4249 tbl.contents[0][4] = "Reason";
4252 tbl.contents[0][3] = "Reason";
4255 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4257 free(tbl.contents[0]);
4262 msg_never = user_find_message(user, "MSG_NEVER");
4263 for(ii = 0; ii < matches; )
4269 else if(ban->expires)
4270 expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
4272 expires = msg_never;
4275 triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
4277 triggered = msg_never;
4279 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4280 tbl.contents[ii][0] = ban->mask;
4281 tbl.contents[ii][1] = ban->owner;
4282 tbl.contents[ii][2] = strdup(triggered);
4285 tbl.contents[ii][3] = strdup(expires);
4286 tbl.contents[ii][4] = ban->reason;
4289 tbl.contents[ii][3] = ban->reason;
4291 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4292 reply("MSG_MATCH_COUNT", matches);
4293 for(ii = 1; ii < tbl.length; ++ii)
4295 free((char*)tbl.contents[ii][2]);
4297 free((char*)tbl.contents[ii][3]);
4298 free(tbl.contents[ii]);
4300 free(tbl.contents[0]);
4306 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
4308 struct chanData *cData = channel->channel_info;
4309 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
4311 if(cData->topic_mask)
4312 return !match_ircglob(new_topic, cData->topic_mask);
4313 else if(cData->topic)
4314 return irccasecmp(new_topic, cData->topic);
4319 static CHANSERV_FUNC(cmd_topic)
4321 struct chanData *cData;
4324 cData = channel->channel_info;
4329 SetChannelTopic(channel, chanserv, cData->topic, 1);
4330 reply("CSMSG_TOPIC_SET", cData->topic);
4334 reply("CSMSG_NO_TOPIC", channel->name);
4338 topic = unsplit_string(argv + 1, argc - 1, NULL);
4339 /* If they say "!topic *", use an empty topic. */
4340 if((topic[0] == '*') && (topic[1] == 0))
4342 if(bad_topic(channel, user, topic))
4344 char *topic_mask = cData->topic_mask;
4347 char new_topic[TOPICLEN+1], tchar;
4348 int pos=0, starpos=-1, dpos=0, len;
4350 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
4357 len = strlen(topic);
4358 if((dpos + len) > TOPICLEN)
4359 len = TOPICLEN + 1 - dpos;
4360 memcpy(new_topic+dpos, topic, len);
4364 case '\\': tchar = topic_mask[pos++]; /* and fall through */
4365 default: new_topic[dpos++] = tchar; break;
4368 if((dpos > TOPICLEN) || tchar)
4371 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
4372 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
4375 new_topic[dpos] = 0;
4376 SetChannelTopic(channel, chanserv, new_topic, 1);
4378 reply("CSMSG_TOPIC_LOCKED", channel->name);
4383 SetChannelTopic(channel, chanserv, topic, 1);
4385 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
4387 /* Grab the topic and save it as the default topic. */
4389 cData->topic = strdup(channel->topic);
4395 static CHANSERV_FUNC(cmd_mode)
4397 struct userData *uData;
4398 struct mod_chanmode *change;
4404 change = &channel->channel_info->modes;
4405 if(change->modes_set || change->modes_clear) {
4406 modcmd_chanmode_announce(change);
4407 reply("CSMSG_DEFAULTED_MODES", channel->name);
4409 reply("CSMSG_NO_MODES", channel->name);
4413 uData = GetChannelUser(channel->channel_info, user->handle_info);
4415 base_oplevel = MAXOPLEVEL;
4416 else if (uData->access >= UL_OWNER)
4419 base_oplevel = 1 + UL_OWNER - uData->access;
4420 change = mod_chanmode_parse(channel, user, argv+1, argc-1, MCP_KEY_FREE|MCP_IGN_REGISTERED|MCP_NO_APASS, base_oplevel);
4423 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
4427 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
4428 && mode_lock_violated(&channel->channel_info->modes, change))
4431 mod_chanmode_format(&channel->channel_info->modes, modes);
4432 reply("CSMSG_MODE_LOCKED", modes, channel->name);
4436 modcmd_chanmode_announce(change);
4437 mod_chanmode_format(change, fmt);
4438 mod_chanmode_free(change);
4439 reply("CSMSG_MODES_SET", fmt);
4444 chanserv_del_invite_mark(void *data)
4446 struct ChanUser *chanuser = data;
4447 struct chanNode *channel = chanuser->chan;
4449 if(!channel) return;
4450 for(i = 0; i < channel->invited.used; i++)
4452 if(channel->invited.list[i] == chanuser->user) {
4453 userList_remove(&channel->invited, chanuser->user);
4459 static CHANSERV_FUNC(cmd_invite)
4461 struct userNode *invite;
4462 struct ChanUser *chanuser;
4467 if(!(invite = GetUserH(argv[1])))
4469 reply("MSG_NICK_UNKNOWN", argv[1]);
4476 if(GetUserMode(channel, invite))
4478 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
4482 for(i = 0; i < channel->invited.used; i++)
4484 if(channel->invited.list[i] == invite) {
4485 reply("CSMSG_ALREADY_INVITED", invite->nick, channel->name);
4494 char *reason = unsplit_string(argv + 2, argc - 2, NULL);
4495 send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
4498 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
4500 irc_invite(chanserv, invite, channel);
4502 reply("CSMSG_INVITED_USER", argv[1], channel->name);
4504 userList_append(&channel->invited, invite);
4505 chanuser = calloc(1, sizeof(*chanuser));
4506 chanuser->user=invite;
4507 chanuser->chan=channel;
4508 timeq_add(now + chanserv_conf.invited_timeout, chanserv_del_invite_mark, chanuser);
4513 static CHANSERV_FUNC(cmd_inviteme)
4515 if(GetUserMode(channel, user))
4517 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
4520 if(channel->channel_info
4521 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
4523 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
4526 irc_invite(cmd->parent->bot, user, channel);
4530 static CHANSERV_FUNC(cmd_invitemeall)
4532 struct handle_info *target = user->handle_info;
4533 struct userData *uData;
4535 if(!target->channels)
4537 reply("CSMSG_SQUAT_ACCESS", target->handle);
4541 for(uData = target->channels; uData; uData = uData->u_next)
4543 struct chanData *cData = uData->channel;
4544 if(uData->access >= cData->lvlOpts[lvlInviteMe])
4546 irc_invite(cmd->parent->bot, user, cData->channel);
4553 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
4556 char buf1[INTERVALLEN], buf2[INTERVALLEN];
4558 /* We display things based on two dimensions:
4559 * - Issue time: present or absent
4560 * - Expiration: revoked, expired, expires in future, or indefinite expiration
4561 * (in order of precedence, so something both expired and revoked
4562 * only counts as revoked)
4564 combo = (suspended->issued ? 4 : 0)
4565 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
4567 case 0: /* no issue time, indefinite expiration */
4568 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
4570 case 1: /* no issue time, expires in future */
4571 intervalString(buf1, suspended->expires-now, user->handle_info);
4572 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
4574 case 2: /* no issue time, expired */
4575 intervalString(buf1, now-suspended->expires, user->handle_info);
4576 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
4578 case 3: /* no issue time, revoked */
4579 intervalString(buf1, now-suspended->revoked, user->handle_info);
4580 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
4582 case 4: /* issue time set, indefinite expiration */
4583 intervalString(buf1, now-suspended->issued, user->handle_info);
4584 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
4586 case 5: /* issue time set, expires in future */
4587 intervalString(buf1, now-suspended->issued, user->handle_info);
4588 intervalString(buf2, suspended->expires-now, user->handle_info);
4589 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
4591 case 6: /* issue time set, expired */
4592 intervalString(buf1, now-suspended->issued, user->handle_info);
4593 intervalString(buf2, now-suspended->expires, user->handle_info);
4594 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
4596 case 7: /* issue time set, revoked */
4597 intervalString(buf1, now-suspended->issued, user->handle_info);
4598 intervalString(buf2, now-suspended->revoked, user->handle_info);
4599 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
4602 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
4607 static CHANSERV_FUNC(cmd_info)
4609 char modes[MAXLEN], buffer[INTERVALLEN];
4610 struct userData *uData, *owner;
4611 struct chanData *cData;
4612 struct do_not_register *dnr;
4617 cData = channel->channel_info;
4618 reply("CSMSG_CHANNEL_INFO", channel->name);
4620 uData = GetChannelUser(cData, user->handle_info);
4621 if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
4623 mod_chanmode_format(&cData->modes, modes);
4624 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
4625 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
4628 for(it = dict_first(cData->notes); it; it = iter_next(it))
4632 note = iter_data(it);
4633 if(!note_type_visible_to_user(cData, note->type, user))
4636 padding = PADLEN - 1 - strlen(iter_key(it));
4637 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
4640 if(cData->max_time) {
4641 reply("CSMSG_CHANNEL_MAX_TIME", cData->max, intervalString(buffer, now - cData->max_time, user->handle_info));
4643 reply("CSMSG_CHANNEL_MAX", cData->max);
4645 for(owner = cData->users; owner; owner = owner->next)
4646 if(owner->access == UL_OWNER)
4647 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
4648 reply("CSMSG_CHANNEL_USERS", cData->userCount);
4649 reply("CSMSG_CHANNEL_BANS", cData->banCount);
4650 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
4652 privileged = IsStaff(user);
4654 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
4655 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
4656 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
4658 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
4659 chanserv_show_dnrs(user, cmd, channel->name, NULL);
4661 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
4663 struct suspended *suspended;
4664 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
4665 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
4666 show_suspension_info(cmd, user, suspended);
4668 else if(IsSuspended(cData))
4670 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
4671 show_suspension_info(cmd, user, cData->suspended);
4676 static CHANSERV_FUNC(cmd_netinfo)
4678 extern unsigned long boot_time;
4679 extern unsigned long burst_length;
4680 char interval[INTERVALLEN];
4682 reply("CSMSG_NETWORK_INFO");
4683 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
4684 reply("CSMSG_NETWORK_USERS", dict_size(clients));
4685 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
4686 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
4687 reply("CSMSG_NETWORK_BANS", banCount);
4688 reply("CSMSG_NETWORK_CHANUSERS", userCount);
4689 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
4690 reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
4695 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
4697 struct helpfile_table table;
4699 struct userNode *user;
4704 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4705 table.contents = alloca(list->used*sizeof(*table.contents));
4706 for(nn=0; nn<list->used; nn++)
4708 user = list->list[nn];
4709 if(user->modes & skip_flags)
4715 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
4718 nick = alloca(strlen(user->nick)+3);
4719 sprintf(nick, "(%s)", user->nick);
4723 table.contents[table.length][0] = nick;
4726 table_send(chanserv, to->nick, 0, NULL, table);
4729 static CHANSERV_FUNC(cmd_ircops)
4731 reply("CSMSG_STAFF_OPERS");
4732 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
4736 static CHANSERV_FUNC(cmd_helpers)
4738 reply("CSMSG_STAFF_HELPERS");
4739 send_staff_list(user, &curr_helpers, FLAGS_OPER);
4743 static CHANSERV_FUNC(cmd_staff)
4745 reply("CSMSG_NETWORK_STAFF");
4746 cmd_ircops(CSFUNC_ARGS);
4747 cmd_helpers(CSFUNC_ARGS);
4751 static CHANSERV_FUNC(cmd_peek)
4753 struct modeNode *mn;
4754 char modes[MODELEN];
4756 struct helpfile_table table;
4757 int opcount = 0, voicecount = 0, srvcount = 0;
4759 irc_make_chanmode(channel, modes);
4761 reply("CSMSG_PEEK_INFO", channel->name);
4762 reply("CSMSG_PEEK_TOPIC", channel->topic);
4763 reply("CSMSG_PEEK_MODES", modes);
4767 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4768 table.contents = alloca(channel->members.used*sizeof(*table.contents));
4769 for(n = 0; n < channel->members.used; n++)
4771 mn = channel->members.list[n];
4772 if(IsLocal(mn->user))
4774 else if(mn->modes & MODE_CHANOP)
4776 else if(mn->modes & MODE_VOICE)
4779 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
4781 table.contents[table.length] = alloca(sizeof(**table.contents));
4782 table.contents[table.length][0] = mn->user->nick;
4786 reply("CSMSG_PEEK_USERS", channel->members.used, opcount, voicecount,
4787 (channel->members.used - opcount - voicecount - srvcount));
4791 reply("CSMSG_PEEK_OPS");
4792 table_send(chanserv, user->nick, 0, NULL, table);
4795 reply("CSMSG_PEEK_NO_OPS");
4799 static MODCMD_FUNC(cmd_wipeinfo)
4801 struct handle_info *victim;
4802 struct userData *ud, *actor, *real_actor;
4803 unsigned int override = 0;
4806 actor = GetChannelUser(channel->channel_info, user->handle_info);
4807 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
4808 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4810 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4812 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4815 if((ud->access >= actor->access) && (ud != actor))
4817 reply("MSG_USER_OUTRANKED", victim->handle);
4820 if((ud != real_actor) && (!real_actor || (ud->access >= real_actor->access)))
4821 override = CMD_LOG_OVERRIDE;
4825 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4826 return 1 | override;
4829 static CHANSERV_FUNC(cmd_resync)
4831 struct mod_chanmode *changes;
4832 struct chanData *cData = channel->channel_info;
4833 unsigned int ii, used;
4835 changes = mod_chanmode_alloc(channel->members.used * 2);
4836 for(ii = used = 0; ii < channel->members.used; ++ii)
4838 struct modeNode *mn = channel->members.list[ii];
4839 struct userData *uData;
4841 if(IsService(mn->user))
4844 uData = GetChannelAccess(cData, mn->user->handle_info);
4845 if(!cData->lvlOpts[lvlGiveOps]
4846 || (uData && uData->access >= cData->lvlOpts[lvlGiveOps]))
4848 if(!(mn->modes & MODE_CHANOP))
4850 if(!uData || IsUserAutoOp(uData))
4852 changes->args[used].mode = MODE_CHANOP;
4853 changes->args[used++].u.member = mn;
4854 if(!(mn->modes & MODE_VOICE))
4856 changes->args[used].mode = MODE_VOICE;
4857 changes->args[used++].u.member = mn;
4862 else if(!cData->lvlOpts[lvlGiveVoice]
4863 || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice]))
4865 if(mn->modes & MODE_CHANOP)
4867 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4868 changes->args[used++].u.member = mn;
4870 if(!(mn->modes & MODE_VOICE) && (!uData || IsUserAutoOp(uData)))
4872 changes->args[used].mode = MODE_VOICE;
4873 changes->args[used++].u.member = mn;
4880 changes->args[used].mode = MODE_REMOVE | mn->modes;
4881 changes->args[used++].u.member = mn;
4885 changes->argc = used;
4886 modcmd_chanmode_announce(changes);
4887 mod_chanmode_free(changes);
4888 reply("CSMSG_RESYNCED_USERS", channel->name);
4892 static CHANSERV_FUNC(cmd_seen)
4894 struct userData *uData;
4895 struct handle_info *handle;
4896 char seen[INTERVALLEN];
4900 if(!irccasecmp(argv[1], chanserv->nick))
4902 reply("CSMSG_IS_CHANSERV");
4906 if(!(handle = get_handle_info(argv[1])))
4908 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4912 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4914 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4919 reply("CSMSG_USER_PRESENT", handle->handle);
4920 else if(uData->seen)
4921 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
4923 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4925 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4926 reply("CSMSG_USER_VACATION", handle->handle);
4931 static MODCMD_FUNC(cmd_names)
4933 struct userNode *targ;
4934 struct userData *targData;
4935 unsigned int ii, pos;
4938 for(ii=pos=0; ii<channel->members.used; ++ii)
4940 targ = channel->members.list[ii]->user;
4941 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4944 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
4947 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4951 if(IsUserSuspended(targData))
4953 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4956 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4957 reply("CSMSG_END_NAMES", channel->name);
4962 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
4964 switch(ntype->visible_type)
4966 case NOTE_VIS_ALL: return 1;
4967 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
4968 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
4973 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
4975 struct userData *uData;
4977 switch(ntype->set_access_type)
4979 case NOTE_SET_CHANNEL_ACCESS:
4980 if(!user->handle_info)
4982 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
4984 return uData->access >= ntype->set_access.min_ulevel;
4985 case NOTE_SET_CHANNEL_SETTER:
4986 return check_user_level(channel, user, lvlSetters, 1, 0);
4987 case NOTE_SET_PRIVILEGED: default:
4988 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
4992 static CHANSERV_FUNC(cmd_note)
4994 struct chanData *cData;
4996 struct note_type *ntype;
4998 cData = channel->channel_info;
5001 reply("CSMSG_NOT_REGISTERED", channel->name);
5005 /* If no arguments, show all visible notes for the channel. */
5011 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
5013 note = iter_data(it);
5014 if(!note_type_visible_to_user(cData, note->type, user))
5017 reply("CSMSG_NOTELIST_HEADER", channel->name);
5018 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
5021 reply("CSMSG_NOTELIST_END", channel->name);
5023 reply("CSMSG_NOTELIST_EMPTY", channel->name);
5025 /* If one argument, show the named note. */
5028 if((note = dict_find(cData->notes, argv[1], NULL))
5029 && note_type_visible_to_user(cData, note->type, user))
5031 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
5033 else if((ntype = dict_find(note_types, argv[1], NULL))
5034 && note_type_visible_to_user(NULL, ntype, user))
5036 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
5041 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
5045 /* Assume they're trying to set a note. */
5049 ntype = dict_find(note_types, argv[1], NULL);
5052 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
5055 else if(note_type_settable_by_user(channel, ntype, user))
5057 note_text = unsplit_string(argv+2, argc-2, NULL);
5058 if((note = dict_find(cData->notes, argv[1], NULL)))
5059 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
5060 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
5061 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
5063 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
5065 /* The note is viewable to staff only, so return 0
5066 to keep the invocation from getting logged (or
5067 regular users can see it in !events). */
5073 reply("CSMSG_NO_ACCESS");
5080 static CHANSERV_FUNC(cmd_delnote)
5085 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
5086 || !note_type_settable_by_user(channel, note->type, user))
5088 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
5091 dict_remove(channel->channel_info->notes, note->type->name);
5092 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
5096 static CHANSERV_FUNC(cmd_events)
5098 struct logSearch discrim;
5099 struct logReport report;
5100 unsigned int matches, limit;
5102 limit = (argc > 1) ? atoi(argv[1]) : 10;
5103 if(limit < 1 || limit > 200)
5106 memset(&discrim, 0, sizeof(discrim));
5107 discrim.masks.bot = chanserv;
5108 discrim.masks.channel_name = channel->name;
5110 discrim.masks.command = argv[2];
5111 discrim.limit = limit;
5112 discrim.max_time = INT_MAX;
5113 discrim.severities = 1 << LOG_COMMAND;
5114 report.reporter = chanserv;
5116 reply("CSMSG_EVENT_SEARCH_RESULTS");
5117 matches = log_entry_search(&discrim, log_report_entry, &report);
5119 reply("MSG_MATCH_COUNT", matches);
5121 reply("MSG_NO_MATCHES");
5125 static CHANSERV_FUNC(cmd_say)
5131 msg = unsplit_string(argv + 1, argc - 1, NULL);
5132 send_channel_message(channel, cmd->parent->bot, "%s", msg);
5134 else if(*argv[1] == '*' && argv[1][1] != '\0')
5136 struct handle_info *hi;
5137 struct userNode *authed;
5140 msg = unsplit_string(argv + 2, argc - 2, NULL);
5142 if (!(hi = get_handle_info(argv[1] + 1)))
5144 reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
5148 for (authed = hi->users; authed; authed = authed->next_authed)
5149 send_target_message(5, authed->nick, cmd->parent->bot, "%s", msg);
5151 else if(GetUserH(argv[1]))
5154 msg = unsplit_string(argv + 2, argc - 2, NULL);
5155 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
5159 reply("MSG_NOT_TARGET_NAME");
5165 static CHANSERV_FUNC(cmd_emote)
5171 /* CTCP is so annoying. */
5172 msg = unsplit_string(argv + 1, argc - 1, NULL);
5173 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
5175 else if(*argv[1] == '*' && argv[1][1] != '\0')
5177 struct handle_info *hi;
5178 struct userNode *authed;
5181 msg = unsplit_string(argv + 2, argc - 2, NULL);
5183 if (!(hi = get_handle_info(argv[1] + 1)))
5185 reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
5189 for (authed = hi->users; authed; authed = authed->next_authed)
5190 send_target_message(5, authed->nick, cmd->parent->bot, "\001ACTION %s\001", msg);
5192 else if(GetUserH(argv[1]))
5194 msg = unsplit_string(argv + 2, argc - 2, NULL);
5195 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
5199 reply("MSG_NOT_TARGET_NAME");
5205 struct channelList *
5206 chanserv_support_channels(void)
5208 return &chanserv_conf.support_channels;
5211 static CHANSERV_FUNC(cmd_expire)
5213 int channel_count = registered_channels;
5214 expire_channels(chanserv);
5215 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
5220 chanserv_expire_suspension(void *data)
5222 struct suspended *suspended = data;
5223 struct chanNode *channel;
5226 /* Update the channel registration data structure. */
5227 if(!suspended->expires || (now < suspended->expires))
5228 suspended->revoked = now;
5229 channel = suspended->cData->channel;
5230 suspended->cData->channel = channel;
5231 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
5233 /* If appropriate, re-join ChanServ to the channel. */
5234 if(!IsOffChannel(suspended->cData))
5236 spamserv_cs_suspend(channel, 0, 0, NULL);
5237 ss_cs_join_channel(channel, 1);
5240 /* Mark everyone currently in the channel as present. */
5241 for(ii = 0; ii < channel->members.used; ++ii)
5243 struct userData *uData = GetChannelAccess(suspended->cData, channel->members.list[ii]->user->handle_info);
5252 static CHANSERV_FUNC(cmd_csuspend)
5254 struct suspended *suspended;
5255 char reason[MAXLEN];
5256 unsigned long expiry, duration;
5257 struct userData *uData;
5261 if(IsProtected(channel->channel_info))
5263 reply("CSMSG_SUSPEND_NODELETE", channel->name);
5267 if(argv[1][0] == '!')
5269 else if(IsSuspended(channel->channel_info))
5271 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
5272 show_suspension_info(cmd, user, channel->channel_info->suspended);
5276 if(!strcmp(argv[1], "0"))
5278 else if((duration = ParseInterval(argv[1])))
5279 expiry = now + duration;
5282 reply("MSG_INVALID_DURATION", argv[1]);
5286 unsplit_string(argv + 2, argc - 2, reason);
5288 suspended = calloc(1, sizeof(*suspended));
5289 suspended->revoked = 0;
5290 suspended->issued = now;
5291 suspended->suspender = strdup(user->handle_info->handle);
5292 suspended->expires = expiry;
5293 suspended->reason = strdup(reason);
5294 suspended->cData = channel->channel_info;
5295 suspended->previous = suspended->cData->suspended;
5296 suspended->cData->suspended = suspended;
5298 if(suspended->expires)
5299 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
5301 if(IsSuspended(channel->channel_info))
5303 suspended->previous->revoked = now;
5304 if(suspended->previous->expires)
5305 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
5306 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
5307 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5311 /* Mark all users in channel as absent. */
5312 for(uData = channel->channel_info->users; uData; uData = uData->next)
5321 /* Mark the channel as suspended, then part. */
5322 channel->channel_info->flags |= CHANNEL_SUSPENDED;
5323 spamserv_cs_suspend(channel, expiry, 1, suspended->reason);
5324 DelChannelUser(chanserv, channel, suspended->reason, 0);
5325 reply("CSMSG_SUSPENDED", channel->name);
5326 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
5327 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5332 static CHANSERV_FUNC(cmd_cunsuspend)
5334 struct suspended *suspended;
5335 char message[MAXLEN];
5337 if(!IsSuspended(channel->channel_info))
5339 reply("CSMSG_NOT_SUSPENDED", channel->name);
5343 suspended = channel->channel_info->suspended;
5345 /* Expire the suspension and join ChanServ to the channel. */
5346 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
5347 chanserv_expire_suspension(suspended);
5348 reply("CSMSG_UNSUSPENDED", channel->name);
5349 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
5350 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
5354 typedef struct chanservSearch
5359 unsigned long unvisited;
5360 unsigned long registered;
5362 unsigned long flags;
5366 typedef void (*channel_search_func)(struct chanData *channel, void *data);
5369 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
5374 search = malloc(sizeof(struct chanservSearch));
5375 memset(search, 0, sizeof(*search));
5378 for(i = 0; i < argc; i++)
5380 /* Assume all criteria require arguments. */
5383 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
5387 if(!irccasecmp(argv[i], "name"))
5388 search->name = argv[++i];
5389 else if(!irccasecmp(argv[i], "registrar"))
5390 search->registrar = argv[++i];
5391 else if(!irccasecmp(argv[i], "unvisited"))
5392 search->unvisited = ParseInterval(argv[++i]);
5393 else if(!irccasecmp(argv[i], "registered"))
5394 search->registered = ParseInterval(argv[++i]);
5395 else if(!irccasecmp(argv[i], "flags"))
5398 if(!irccasecmp(argv[i], "nodelete"))
5399 search->flags |= CHANNEL_NODELETE;
5400 else if(!irccasecmp(argv[i], "suspended"))
5401 search->flags |= CHANNEL_SUSPENDED;
5402 else if(!irccasecmp(argv[i], "unreviewed"))
5403 search->flags |= CHANNEL_UNREVIEWED;
5406 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
5410 else if(!irccasecmp(argv[i], "limit"))
5411 search->limit = strtoul(argv[++i], NULL, 10);
5414 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
5419 if(search->name && !strcmp(search->name, "*"))
5421 if(search->registrar && !strcmp(search->registrar, "*"))
5422 search->registrar = 0;
5431 chanserv_channel_match(struct chanData *channel, search_t search)
5433 const char *name = channel->channel->name;
5434 if((search->name && !match_ircglob(name, search->name)) ||
5435 (search->registrar && !channel->registrar) ||
5436 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
5437 (search->unvisited && (now - channel->visited) < search->unvisited) ||
5438 (search->registered && (now - channel->registered) > search->registered) ||
5439 (search->flags && ((search->flags & channel->flags) != search->flags)))
5446 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
5448 struct chanData *channel;
5449 unsigned int matches = 0;
5451 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
5453 if(!chanserv_channel_match(channel, search))
5463 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
5468 search_print(struct chanData *channel, void *data)
5470 send_message_type(4, data, chanserv, "%s", channel->channel->name);
5473 static CHANSERV_FUNC(cmd_search)
5476 unsigned int matches;
5477 channel_search_func action;
5481 if(!irccasecmp(argv[1], "count"))
5482 action = search_count;
5483 else if(!irccasecmp(argv[1], "print"))
5484 action = search_print;
5487 reply("CSMSG_ACTION_INVALID", argv[1]);
5491 search = chanserv_search_create(user, argc - 2, argv + 2);
5495 if(action == search_count)
5496 search->limit = INT_MAX;
5498 if(action == search_print)
5499 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
5501 matches = chanserv_channel_search(search, action, user);
5504 reply("MSG_MATCH_COUNT", matches);
5506 reply("MSG_NO_MATCHES");
5512 static CHANSERV_FUNC(cmd_unvisited)
5514 struct chanData *cData;
5515 unsigned long interval = chanserv_conf.channel_expire_delay;
5516 char buffer[INTERVALLEN];
5517 unsigned int limit = 25, matches = 0;
5521 interval = ParseInterval(argv[1]);
5523 limit = atoi(argv[2]);
5526 intervalString(buffer, interval, user->handle_info);
5527 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
5529 for(cData = channelList; cData && matches < limit; cData = cData->next)
5531 if((now - cData->visited) < interval)
5534 intervalString(buffer, now - cData->visited, user->handle_info);
5535 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
5542 static MODCMD_FUNC(chan_opt_defaulttopic)
5548 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5550 reply("CSMSG_TOPIC_LOCKED", channel->name);
5554 topic = unsplit_string(argv+1, argc-1, NULL);
5556 free(channel->channel_info->topic);
5557 if(topic[0] == '*' && topic[1] == 0)
5559 topic = channel->channel_info->topic = NULL;
5563 topic = channel->channel_info->topic = strdup(topic);
5564 if(channel->channel_info->topic_mask
5565 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
5566 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5568 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
5571 if(channel->channel_info->topic)
5572 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
5574 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
5578 static MODCMD_FUNC(chan_opt_topicmask)
5582 struct chanData *cData = channel->channel_info;
5585 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5587 reply("CSMSG_TOPIC_LOCKED", channel->name);
5591 mask = unsplit_string(argv+1, argc-1, NULL);
5593 if(cData->topic_mask)
5594 free(cData->topic_mask);
5595 if(mask[0] == '*' && mask[1] == 0)
5597 cData->topic_mask = 0;
5601 cData->topic_mask = strdup(mask);
5603 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
5604 else if(!match_ircglob(cData->topic, cData->topic_mask))
5605 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5609 if(channel->channel_info->topic_mask)
5610 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
5612 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
5616 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
5620 char *greeting = unsplit_string(argv+1, argc-1, NULL);
5624 if(greeting[0] == '*' && greeting[1] == 0)
5628 unsigned int length = strlen(greeting);
5629 if(length > chanserv_conf.greeting_length)
5631 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
5634 *data = strdup(greeting);
5643 reply(name, user_find_message(user, "MSG_NONE"));
5647 static MODCMD_FUNC(chan_opt_greeting)
5649 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
5652 static MODCMD_FUNC(chan_opt_usergreeting)
5654 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
5657 static MODCMD_FUNC(chan_opt_modes)
5659 struct mod_chanmode *new_modes;
5664 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
5666 reply("CSMSG_NO_ACCESS");
5669 if(argv[1][0] == '*' && argv[1][1] == 0)
5671 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
5673 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)))
5675 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5678 else if(new_modes->argc > 1)
5680 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5681 mod_chanmode_free(new_modes);
5686 channel->channel_info->modes = *new_modes;
5687 modcmd_chanmode_announce(new_modes);
5688 mod_chanmode_free(new_modes);
5692 mod_chanmode_format(&channel->channel_info->modes, modes);
5694 reply("CSMSG_SET_MODES", modes);
5696 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
5700 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5702 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5704 struct chanData *cData = channel->channel_info;
5709 /* Set flag according to value. */
5710 if(enabled_string(argv[1]))
5712 cData->flags |= mask;
5715 else if(disabled_string(argv[1]))
5717 cData->flags &= ~mask;
5722 reply("MSG_INVALID_BINARY", argv[1]);
5728 /* Find current option value. */
5729 value = (cData->flags & mask) ? 1 : 0;
5733 reply(name, user_find_message(user, "MSG_ON"));
5735 reply(name, user_find_message(user, "MSG_OFF"));
5739 static MODCMD_FUNC(chan_opt_nodelete)
5741 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
5743 reply("MSG_SETTING_PRIVILEGED", argv[0]);
5747 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
5750 static MODCMD_FUNC(chan_opt_dynlimit)
5752 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
5755 static MODCMD_FUNC(chan_opt_offchannel)
5757 struct chanData *cData = channel->channel_info;
5762 /* Set flag according to value. */
5763 if(enabled_string(argv[1]))
5765 if(!IsOffChannel(cData))
5766 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
5767 cData->flags |= CHANNEL_OFFCHANNEL;
5770 else if(disabled_string(argv[1]))
5772 if(IsOffChannel(cData))
5774 struct mod_chanmode change;
5775 mod_chanmode_init(&change);
5777 change.args[0].mode = MODE_CHANOP;
5778 change.args[0].u.member = AddChannelUser(chanserv, channel);
5779 mod_chanmode_announce(chanserv, channel, &change);
5781 cData->flags &= ~CHANNEL_OFFCHANNEL;
5786 reply("MSG_INVALID_BINARY", argv[1]);
5792 /* Find current option value. */
5793 value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
5797 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
5799 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
5803 static MODCMD_FUNC(chan_opt_unreviewed)
5805 struct chanData *cData = channel->channel_info;
5806 int value = (cData->flags & CHANNEL_UNREVIEWED) ? 1 : 0;
5812 /* The two directions can have different ACLs. */
5813 if(enabled_string(argv[1]))
5815 else if(disabled_string(argv[1]))
5819 reply("MSG_INVALID_BINARY", argv[1]);
5823 if (new_value != value)
5825 struct svccmd *subcmd;
5826 char subcmd_name[32];
5828 snprintf(subcmd_name, sizeof(subcmd_name), "%s %s", argv[0], (new_value ? "on" : "off"));
5829 subcmd = dict_find(cmd->parent->commands, subcmd_name, NULL);
5832 reply("MSG_COMMAND_DISABLED", subcmd_name);
5835 else if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
5839 cData->flags |= CHANNEL_UNREVIEWED;
5842 free(cData->registrar);
5843 cData->registrar = strdup(user->handle_info->handle);
5844 cData->flags &= ~CHANNEL_UNREVIEWED;
5851 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_ON"));
5853 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_OFF"));
5857 static MODCMD_FUNC(chan_opt_defaults)
5859 struct userData *uData;
5860 struct chanData *cData;
5861 const char *confirm;
5862 enum levelOption lvlOpt;
5863 enum charOption chOpt;
5865 cData = channel->channel_info;
5866 uData = GetChannelUser(cData, user->handle_info);
5867 if(!uData || (uData->access < UL_OWNER))
5869 reply("CSMSG_OWNER_DEFAULTS", channel->name);
5872 confirm = make_confirmation_string(uData);
5873 if((argc < 2) || strcmp(argv[1], confirm))
5875 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
5878 cData->flags = (CHANNEL_DEFAULT_FLAGS & ~CHANNEL_PRESERVED_FLAGS)
5879 | (cData->flags & CHANNEL_PRESERVED_FLAGS);
5880 cData->modes = chanserv_conf.default_modes;
5881 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
5882 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
5883 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
5884 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
5885 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
5890 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5892 struct chanData *cData = channel->channel_info;
5893 struct userData *uData;
5894 unsigned short value;
5898 if(!check_user_level(channel, user, option, 1, 1))
5900 reply("CSMSG_CANNOT_SET");
5903 value = user_level_from_name(argv[1], UL_OWNER+1);
5904 if(!value && strcmp(argv[1], "0"))
5906 reply("CSMSG_INVALID_ACCESS", argv[1]);
5909 uData = GetChannelUser(cData, user->handle_info);
5910 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
5912 reply("CSMSG_BAD_SETLEVEL");
5918 if(value > cData->lvlOpts[lvlGiveOps])
5920 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
5925 if(value < cData->lvlOpts[lvlGiveVoice])
5927 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
5932 /* This test only applies to owners, since non-owners
5933 * trying to set an option to above their level get caught
5934 * by the CSMSG_BAD_SETLEVEL test above.
5936 if(value > uData->access)
5938 reply("CSMSG_BAD_SETTERS");
5945 cData->lvlOpts[option] = value;
5947 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
5951 static MODCMD_FUNC(chan_opt_enfops)
5953 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
5956 static MODCMD_FUNC(chan_opt_giveops)
5958 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
5961 static MODCMD_FUNC(chan_opt_enfmodes)
5963 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
5966 static MODCMD_FUNC(chan_opt_enftopic)
5968 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
5971 static MODCMD_FUNC(chan_opt_pubcmd)
5973 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
5976 static MODCMD_FUNC(chan_opt_setters)
5978 return channel_level_option(lvlSetters, CSFUNC_ARGS);
5981 static MODCMD_FUNC(chan_opt_ctcpusers)
5983 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
5986 static MODCMD_FUNC(chan_opt_userinfo)
5988 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
5991 static MODCMD_FUNC(chan_opt_givevoice)
5993 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
5996 static MODCMD_FUNC(chan_opt_topicsnarf)
5998 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
6001 static MODCMD_FUNC(chan_opt_vote)
6003 return channel_level_option(lvlVote, CSFUNC_ARGS);
6006 static MODCMD_FUNC(chan_opt_inviteme)
6008 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
6012 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
6014 struct chanData *cData = channel->channel_info;
6015 int count = charOptions[option].count, idx;
6019 idx = atoi(argv[1]);
6021 if(!isdigit(argv[1][0]) || (idx < 0) || (idx >= count))
6023 reply("CSMSG_INVALID_NUMERIC", idx);
6024 /* Show possible values. */
6025 for(idx = 0; idx < count; idx++)
6026 reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
6030 cData->chOpts[option] = charOptions[option].values[idx].value;
6034 /* Find current option value. */
6037 (idx < count) && (cData->chOpts[option] != charOptions[option].values[idx].value);
6041 /* Somehow, the option value is corrupt; reset it to the default. */
6042 cData->chOpts[option] = charOptions[option].default_value;
6047 reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
6051 static MODCMD_FUNC(chan_opt_protect)
6053 return channel_multiple_option(chProtect, CSFUNC_ARGS);
6056 static MODCMD_FUNC(chan_opt_toys)
6058 return channel_multiple_option(chToys, CSFUNC_ARGS);
6061 static MODCMD_FUNC(chan_opt_ctcpreaction)
6063 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
6066 static MODCMD_FUNC(chan_opt_topicrefresh)
6068 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
6071 static struct svccmd_list set_shows_list;
6074 handle_svccmd_unbind(struct svccmd *target) {
6076 for(ii=0; ii<set_shows_list.used; ++ii)
6077 if(target == set_shows_list.list[ii])
6078 set_shows_list.used = 0;
6081 static CHANSERV_FUNC(cmd_set)
6083 struct svccmd *subcmd;
6087 /* Check if we need to (re-)initialize set_shows_list. */
6088 if(!set_shows_list.used)
6090 if(!set_shows_list.size)
6092 set_shows_list.size = chanserv_conf.set_shows->used;
6093 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
6095 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
6097 const char *name = chanserv_conf.set_shows->list[ii];
6098 sprintf(buf, "%s %s", argv[0], name);
6099 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6102 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
6105 svccmd_list_append(&set_shows_list, subcmd);
6111 reply("CSMSG_CHANNEL_OPTIONS");
6112 for(ii = 0; ii < set_shows_list.used; ii++)
6114 subcmd = set_shows_list.list[ii];
6115 subcmd->command->func(user, channel, 1, argv+1, subcmd);
6120 sprintf(buf, "%s %s", argv[0], argv[1]);
6121 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6124 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
6127 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
6129 reply("CSMSG_NO_ACCESS");
6135 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
6139 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
6141 struct userData *uData;
6143 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6146 reply("CSMSG_NOT_USER", channel->name);
6152 /* Just show current option value. */
6154 else if(enabled_string(argv[1]))
6156 uData->flags |= mask;
6158 else if(disabled_string(argv[1]))
6160 uData->flags &= ~mask;
6164 reply("MSG_INVALID_BINARY", argv[1]);
6168 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
6172 static MODCMD_FUNC(user_opt_noautoop)
6174 struct userData *uData;
6176 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6179 reply("CSMSG_NOT_USER", channel->name);
6182 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
6183 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
6185 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
6188 static MODCMD_FUNC(user_opt_autoinvite)
6190 if((argc > 1) && !check_user_level(channel, user, lvlInviteMe, 1, 0))
6192 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
6194 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
6197 static MODCMD_FUNC(user_opt_info)
6199 struct userData *uData;
6202 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6206 /* If they got past the command restrictions (which require access)
6207 * but fail this test, we have some fool with security override on.
6209 reply("CSMSG_NOT_USER", channel->name);
6216 infoline = unsplit_string(argv + 1, argc - 1, NULL);
6217 if(strlen(infoline) > chanserv_conf.max_userinfo_length)
6219 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
6222 bp = strcspn(infoline, "\001");
6225 reply("CSMSG_BAD_INFOLINE", infoline[bp]);
6230 if(infoline[0] == '*' && infoline[1] == 0)
6233 uData->info = strdup(infoline);
6236 reply("CSMSG_USET_INFO", uData->info);
6238 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
6242 struct svccmd_list uset_shows_list;
6244 static CHANSERV_FUNC(cmd_uset)
6246 struct svccmd *subcmd;
6250 /* Check if we need to (re-)initialize uset_shows_list. */
6251 if(!uset_shows_list.used)
6255 "NoAutoOp", "AutoInvite", "Info"
6258 if(!uset_shows_list.size)
6260 uset_shows_list.size = ArrayLength(options);
6261 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
6263 for(ii = 0; ii < ArrayLength(options); ii++)
6265 const char *name = options[ii];
6266 sprintf(buf, "%s %s", argv[0], name);
6267 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6270 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
6273 svccmd_list_append(&uset_shows_list, subcmd);
6279 /* Do this so options are presented in a consistent order. */
6280 reply("CSMSG_USER_OPTIONS");
6281 for(ii = 0; ii < uset_shows_list.used; ii++)
6282 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
6286 sprintf(buf, "%s %s", argv[0], argv[1]);
6287 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6290 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
6294 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
6297 static CHANSERV_FUNC(cmd_giveownership)
6299 struct handle_info *new_owner_hi;
6300 struct userData *new_owner;
6301 struct userData *curr_user;
6302 struct userData *invoker;
6303 struct chanData *cData = channel->channel_info;
6304 struct do_not_register *dnr;
6305 const char *confirm;
6307 unsigned short co_access;
6308 char reason[MAXLEN];
6311 curr_user = GetChannelAccess(cData, user->handle_info);
6312 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
6313 if(!curr_user || (curr_user->access != UL_OWNER))
6315 struct userData *owner = NULL;
6316 for(curr_user = channel->channel_info->users;
6318 curr_user = curr_user->next)
6320 if(curr_user->access != UL_OWNER)
6324 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
6331 else if(!force && (now < cData->ownerTransfer + chanserv_conf.giveownership_period))
6333 char delay[INTERVALLEN];
6334 intervalString(delay, cData->ownerTransfer + chanserv_conf.giveownership_period - now, user->handle_info);
6335 reply("CSMSG_TRANSFER_WAIT", delay, channel->name);
6338 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
6340 if(new_owner_hi == user->handle_info)
6342 reply("CSMSG_NO_TRANSFER_SELF");
6345 new_owner = GetChannelAccess(cData, new_owner_hi);
6350 new_owner = add_channel_user(cData, new_owner_hi, UL_OWNER - 1, 0, NULL);
6354 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
6358 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
6360 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
6363 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
6364 if(!IsHelping(user))
6365 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
6367 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi->handle);
6370 invoker = GetChannelUser(cData, user->handle_info);
6371 if(invoker->access <= UL_OWNER)
6373 confirm = make_confirmation_string(curr_user);
6374 if((argc < 3) || strcmp(argv[2], confirm))
6376 reply("CSMSG_CONFIRM_GIVEOWNERSHIP", new_owner_hi->handle, confirm);
6380 if(new_owner->access >= UL_COOWNER)
6381 co_access = new_owner->access;
6383 co_access = UL_COOWNER;
6384 new_owner->access = UL_OWNER;
6386 curr_user->access = co_access;
6387 cData->ownerTransfer = now;
6388 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
6389 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
6390 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
6394 static CHANSERV_FUNC(cmd_suspend)
6396 struct handle_info *hi;
6397 struct userData *actor, *real_actor, *target;
6398 unsigned int override = 0;
6401 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6402 actor = GetChannelUser(channel->channel_info, user->handle_info);
6403 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
6404 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6406 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6409 if(target->access >= actor->access)
6411 reply("MSG_USER_OUTRANKED", hi->handle);
6414 if(target->flags & USER_SUSPENDED)
6416 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
6421 target->present = 0;
6424 if(!real_actor || target->access >= real_actor->access)
6425 override = CMD_LOG_OVERRIDE;
6426 target->flags |= USER_SUSPENDED;
6427 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
6428 return 1 | override;
6431 static CHANSERV_FUNC(cmd_unsuspend)
6433 struct handle_info *hi;
6434 struct userData *actor, *real_actor, *target;
6435 unsigned int override = 0;
6438 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6439 actor = GetChannelUser(channel->channel_info, user->handle_info);
6440 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
6441 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6443 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6446 if(target->access >= actor->access)
6448 reply("MSG_USER_OUTRANKED", hi->handle);
6451 if(!(target->flags & USER_SUSPENDED))
6453 reply("CSMSG_NOT_SUSPENDED", hi->handle);
6456 if(!real_actor || target->access >= real_actor->access)
6457 override = CMD_LOG_OVERRIDE;
6458 target->flags &= ~USER_SUSPENDED;
6459 scan_user_presence(target, NULL);
6460 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
6461 return 1 | override;
6464 static MODCMD_FUNC(cmd_deleteme)
6466 struct handle_info *hi;
6467 struct userData *target;
6468 const char *confirm_string;
6469 unsigned short access_level;
6472 hi = user->handle_info;
6473 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6475 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6478 if(target->access == UL_OWNER)
6480 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
6483 confirm_string = make_confirmation_string(target);
6484 if((argc < 2) || strcmp(argv[1], confirm_string))
6486 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
6489 access_level = target->access;
6490 channel_name = strdup(channel->name);
6491 del_channel_user(target, 1);
6492 reply("CSMSG_DELETED_YOU", access_level, channel_name);
6497 static CHANSERV_FUNC(cmd_addvote)
6499 struct chanData *cData = channel->channel_info;
6500 struct userData *uData, *target;
6501 struct handle_info *hi;
6502 if (!cData) return 0;
6504 hi = user->handle_info;
6505 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6507 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6510 if(target->access < 300) {
6511 reply("CSMSG_NO_ACCESS");
6515 reply("CSMSG_ADDVOTE_FULL");
6519 msg = unsplit_string(argv + 1, argc - 1, NULL);
6520 cData->vote = strdup(msg);
6521 cData->vote_start=0;
6522 dict_delete(cData->vote_options);
6523 cData->vote_options = dict_new();
6524 dict_set_free_data(cData->vote_options, free_vote_options);
6525 for(uData = channel->channel_info->users; uData; uData = uData->next)
6530 reply("CSMSG_ADDVOTE_DONE");
6534 static CHANSERV_FUNC(cmd_delvote)
6536 struct chanData *cData = channel->channel_info;
6537 struct userData *target;
6538 struct handle_info *hi;
6539 if (!cData) return 0;
6540 hi = user->handle_info;
6541 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6543 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6546 if(target->access < 300) {
6547 reply("CSMSG_NO_ACCESS");
6551 reply("CSMSG_NO_VOTE");
6556 reply("CSMSG_DELVOTE_DONE");
6560 static CHANSERV_FUNC(cmd_addoption)
6562 struct chanData *cData = channel->channel_info;
6563 struct userData *target;
6564 struct handle_info *hi;
6565 if (!cData) return 0;
6567 hi = user->handle_info;
6568 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6570 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6573 if(target->access < 300) {
6574 reply("CSMSG_NO_ACCESS");
6578 reply("CSMSG_NO_VOTE");
6584 msg = unsplit_string(argv + 1, argc - 1, NULL);
6587 unsigned int lastid = 1;
6588 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6589 struct vote_option *cvOpt = iter_data(it);
6590 if(cvOpt->option_id > lastid)
6591 lastid = cvOpt->option_id;
6593 struct vote_option *vOpt;
6594 vOpt = calloc(1, sizeof(*vOpt));
6595 vOpt->name = strdup(msg);
6596 vOpt->option_id = (lastid + 1);
6598 sprintf(str,"%i",(lastid + 1));
6599 vOpt->option_str = strdup(str);
6601 dict_insert(cData->vote_options,vOpt->option_str,vOpt);
6603 reply("CSMSG_ADDOPTION_DONE",dict_size(cData->vote_options),lastid,(lastid + 1));
6607 static CHANSERV_FUNC(cmd_deloption)
6609 struct chanData *cData = channel->channel_info;
6610 struct userData *uData, *target;
6611 struct handle_info *hi;
6612 if (!cData) return 0;
6614 hi = user->handle_info;
6615 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6617 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6620 if(target->access < 300) {
6621 reply("CSMSG_NO_ACCESS");
6625 reply("CSMSG_NO_VOTE");
6628 if(cData->vote_start) {
6629 if(dict_size(cData->vote_options) < 3) {
6630 reply("CSMSG_VOTE_NEED_OPTIONS");
6635 int find_id = atoi(argv[1]);
6637 unsigned int found = 0;
6640 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6642 if (find_id == ii) {
6643 struct vote_option *vOpt = iter_data(it);
6644 found = vOpt->option_id;
6646 sprintf(str,"%i",vOpt->option_id);
6647 dict_remove(cData->vote_options, str);
6652 for(uData = channel->channel_info->users; uData; uData = uData->next) {
6653 if(uData->votefor == found) {
6658 reply("CSMSG_DELOPTION_DONE");
6661 reply("CSMSG_DELOPTION_NONE");
6666 static CHANSERV_FUNC(cmd_vote)
6668 struct chanData *cData = channel->channel_info;
6669 struct userData *target;
6670 struct handle_info *hi;
6671 unsigned int votedfor = 0;
6672 char *votedfor_str = NULL;
6674 if (!cData || !cData->vote) {
6675 reply("CSMSG_NO_VOTE");
6678 if(argc > 1 && cData->vote_start) {
6679 hi = user->handle_info;
6680 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6682 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6685 if(!check_user_level(channel, user, lvlVote, 1, 0)) {
6686 reply("CSMSG_NO_ACCESS");
6690 reply("CSMSG_VOTE_VOTED");
6693 int find_id = atoi(argv[1]);
6696 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6698 if (find_id == ii) {
6699 struct vote_option *vOpt = iter_data(it);
6702 target->votefor = vOpt->option_id;
6703 votedfor = vOpt->option_id;
6704 votedfor_str = vOpt->name;
6708 reply("CSMSG_VOTE_INVALID");
6712 if (!cData->vote_start) {
6713 reply("CSMSG_VOTE_NOT_STARTED");
6715 reply("CSMSG_VOTE_QUESTION",cData->vote);
6717 unsigned int voteid = 0;
6720 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6721 struct vote_option *vOpt = iter_data(it);
6723 reply("CSMSG_VOTE_OPTION",voteid,vOpt->name,vOpt->voted);
6725 if(argc > 1 && cData->vote_start && votedfor_str) {
6726 reply("CSMSG_VOTE_DONE",votedfor_str);
6731 static CHANSERV_FUNC(cmd_startvote)
6733 struct chanData *cData = channel->channel_info;
6734 struct userData *target;
6735 struct handle_info *hi;
6736 if (!cData) return 0;
6737 hi = user->handle_info;
6738 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6740 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6743 if(target->access < 300) {
6744 reply("CSMSG_NO_ACCESS");
6748 reply("CSMSG_NO_VOTE");
6751 if(cData->vote_start) {
6752 reply("CSMSG_STARTVOTE_RUNNING");
6755 if(dict_size(cData->vote_options) < 2) {
6756 reply("CSMSG_VOTE_NEED_OPTIONS");
6759 cData->vote_start = 1;
6760 char response[MAXLEN];
6761 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_TOP"), user->nick);
6762 irc_privmsg(cmd->parent->bot, channel->name, response);
6763 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_QUESTION"), cData->vote);
6764 irc_privmsg(cmd->parent->bot, channel->name, response);
6765 unsigned int voteid = 0;
6767 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6768 struct vote_option *vOpt = iter_data(it);
6770 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_OPTION"), voteid, vOpt->name);
6771 irc_privmsg(cmd->parent->bot, channel->name, response);
6773 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_ACCESS"), cData->lvlOpts[lvlVote]); //Todo
6774 irc_privmsg(cmd->parent->bot, channel->name, response);
6775 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_HOWTO")); //Todo
6776 irc_privmsg(cmd->parent->bot, channel->name, response);
6780 static CHANSERV_FUNC(cmd_endvote)
6782 struct chanData *cData = channel->channel_info;
6783 struct userData *target;
6784 struct handle_info *hi;
6785 if (!cData) return 0;
6786 hi = user->handle_info;
6787 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6789 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6792 if(target->access < 300) {
6793 reply("CSMSG_NO_ACCESS");
6797 reply("CSMSG_NO_VOTE");
6800 if(!cData->vote_start) {
6801 reply("CSMSG_ENDVOTE_STOPPED");
6804 cData->vote_start = 0;
6805 reply("CSMSG_ENDVOTE_DONE");
6809 static CHANSERV_FUNC(cmd_voteresults)
6811 struct chanData *cData = channel->channel_info;
6812 struct userData *target;
6813 struct handle_info *hi;
6814 if (!cData) return 0;
6816 reply("CSMSG_NO_VOTE");
6819 if (argc > 1 && !irccasecmp(argv[1], "*")) {
6820 hi = user->handle_info;
6821 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6823 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6826 if(target->access < 300) {
6827 reply("CSMSG_NO_ACCESS");
6830 char response[MAXLEN];
6831 sprintf(response, user_find_message(user, "CSMSG_VOTERES_QUESTION"), cData->vote);
6832 irc_privmsg(cmd->parent->bot, channel->name, response);
6833 unsigned int voteid = 0;
6835 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6836 struct vote_option *vOpt = iter_data(it);
6838 sprintf(response, user_find_message(user, "CSMSG_VOTERES_OPTION"), voteid, vOpt->name, vOpt->voted);
6839 irc_privmsg(cmd->parent->bot, channel->name, response);
6842 reply("CSMSG_VOTE_QUESTION",cData->vote);
6843 unsigned int voteid = 0;
6845 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6846 struct vote_option *vOpt = iter_data(it);
6848 reply("CSMSG_VOTE_OPTION",voteid,vOpt->name,vOpt->voted);
6855 chanserv_refresh_topics(UNUSED_ARG(void *data))
6857 unsigned int refresh_num = (now - self->link_time) / chanserv_conf.refresh_period;
6858 struct chanData *cData;
6861 for(cData = channelList; cData; cData = cData->next)
6863 if(IsSuspended(cData))
6865 opt = cData->chOpts[chTopicRefresh];
6868 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
6871 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
6872 cData->last_refresh = refresh_num;
6874 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
6877 static CHANSERV_FUNC(cmd_unf)
6881 char response[MAXLEN];
6882 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
6883 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6884 irc_privmsg(cmd->parent->bot, channel->name, response);
6887 reply("CSMSG_UNF_RESPONSE");
6891 static CHANSERV_FUNC(cmd_ping)
6895 char response[MAXLEN];
6896 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
6897 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6898 irc_privmsg(cmd->parent->bot, channel->name, response);
6901 reply("CSMSG_PING_RESPONSE");
6905 static CHANSERV_FUNC(cmd_wut)
6909 char response[MAXLEN];
6910 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
6911 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6912 irc_privmsg(cmd->parent->bot, channel->name, response);
6915 reply("CSMSG_WUT_RESPONSE");
6919 static CHANSERV_FUNC(cmd_8ball)
6921 unsigned int i, j, accum;
6926 for(i=1; i<argc; i++)
6927 for(j=0; argv[i][j]; j++)
6928 accum = (accum << 5) - accum + toupper(argv[i][j]);
6929 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
6932 char response[MAXLEN];
6933 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
6934 irc_privmsg(cmd->parent->bot, channel->name, response);
6937 send_message_type(4, user, cmd->parent->bot, "%s", resp);
6941 static CHANSERV_FUNC(cmd_d)
6943 unsigned long sides, count, modifier, ii, total;
6944 char response[MAXLEN], *sep;
6948 if((count = strtoul(argv[1], &sep, 10)) < 1)
6958 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
6959 && (sides = strtoul(sep+1, &sep, 10)) > 1)
6963 else if((sep[0] == '-') && isdigit(sep[1]))
6964 modifier = strtoul(sep, NULL, 10);
6965 else if((sep[0] == '+') && isdigit(sep[1]))
6966 modifier = strtoul(sep+1, NULL, 10);
6973 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
6978 reply("CSMSG_BAD_DICE_COUNT", count, 10);
6981 for(total = ii = 0; ii < count; ++ii)
6982 total += (rand() % sides) + 1;
6985 if((count > 1) || modifier)
6987 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
6988 sprintf(response, fmt, total, count, sides, modifier);
6992 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
6993 sprintf(response, fmt, total, sides);
6996 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
6998 send_message_type(4, user, cmd->parent->bot, "%s", response);
7002 static CHANSERV_FUNC(cmd_huggle)
7004 /* CTCP must be via PRIVMSG, never notice */
7006 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
7008 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
7013 chanserv_adjust_limit(void *data)
7015 struct mod_chanmode change;
7016 struct chanData *cData = data;
7017 struct chanNode *channel = cData->channel;
7020 if(IsSuspended(cData))
7023 cData->limitAdjusted = now;
7024 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
7025 if(cData->modes.modes_set & MODE_LIMIT)
7027 if(limit > cData->modes.new_limit)
7028 limit = cData->modes.new_limit;
7029 else if(limit == cData->modes.new_limit)
7033 mod_chanmode_init(&change);
7034 change.modes_set = MODE_LIMIT;
7035 change.new_limit = limit;
7036 mod_chanmode_announce(chanserv, channel, &change);
7040 handle_new_channel(struct chanNode *channel)
7042 struct chanData *cData;
7044 if(!(cData = channel->channel_info))
7047 if(cData->modes.modes_set || cData->modes.modes_clear)
7048 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
7050 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
7051 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
7054 void handle_new_channel_created(char *chan, struct userNode *user) {
7055 if(user->handle_info && chanserv_conf.new_channel_authed) {
7056 send_target_message(5, chan, chanserv, "%s", chanserv_conf.new_channel_authed);
7057 } else if(!user->handle_info && chanserv_conf.new_channel_unauthed) {
7058 send_target_message(5, chan, chanserv, "%s", chanserv_conf.new_channel_unauthed);
7060 if(chanserv_conf.new_channel_msg)
7061 send_target_message(5, chan, chanserv, "%s", chanserv_conf.new_channel_msg);
7064 /* Welcome to my worst nightmare. Warning: Read (or modify)
7065 the code below at your own risk. */
7067 handle_join(struct modeNode *mNode)
7069 struct mod_chanmode change;
7070 struct userNode *user = mNode->user;
7071 struct chanNode *channel = mNode->channel;
7072 struct chanData *cData;
7073 struct userData *uData = NULL;
7074 struct banData *bData;
7075 struct handle_info *handle;
7076 unsigned int modes = 0, info = 0;
7080 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
7083 cData = channel->channel_info;
7084 if(channel->members.used > cData->max) {
7085 cData->max = channel->members.used;
7086 cData->max_time = now;
7089 for(i = 0; i < channel->invited.used; i++)
7091 if(channel->invited.list[i] == user) {
7092 userList_remove(&channel->invited, user);
7096 /* Check for bans. If they're joining through a ban, one of two
7098 * 1: Join during a netburst, by riding the break. Kick them
7099 * unless they have ops or voice in the channel.
7100 * 2: They're allowed to join through the ban (an invite in
7101 * ircu2.10, or a +e on Hybrid, or something).
7102 * If they're not joining through a ban, and the banlist is not
7103 * full, see if they're on the banlist for the channel. If so,
7106 if(user->uplink->burst && !mNode->modes)
7109 for(ii = 0; ii < channel->banlist.used; ii++)
7111 if(user_matches_glob(user, channel->banlist.list[ii]->ban, MATCH_USENICK))
7113 /* Riding a netburst. Naughty. */
7114 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
7120 mod_chanmode_init(&change);
7122 if(channel->banlist.used < MAXBANS)
7124 /* Not joining through a ban. */
7125 for(bData = cData->bans;
7126 bData && !user_matches_glob(user, bData->mask, MATCH_USENICK);
7127 bData = bData->next);
7131 char kick_reason[MAXLEN];
7132 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
7134 bData->triggered = now;
7135 if(bData != cData->bans)
7137 /* Shuffle the ban to the head of the list. */
7139 bData->next->prev = bData->prev;
7141 bData->prev->next = bData->next;
7144 bData->next = cData->bans;
7147 cData->bans->prev = bData;
7148 cData->bans = bData;
7151 change.args[0].mode = MODE_BAN;
7152 change.args[0].u.hostmask = bData->mask;
7153 mod_chanmode_announce(chanserv, channel, &change);
7154 KickChannelUser(user, channel, chanserv, kick_reason);
7159 /* ChanServ will not modify the limits in join-flooded channels,
7160 or when there are enough slots left below the limit. */
7161 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
7162 && !channel->join_flooded
7163 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
7165 /* The user count has begun "bumping" into the channel limit,
7166 so set a timer to raise the limit a bit. Any previous
7167 timers are removed so three incoming users within the delay
7168 results in one limit change, not three. */
7170 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7171 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7174 if(channel->join_flooded)
7176 /* don't automatically give ops or voice during a join flood */
7178 else if(cData->lvlOpts[lvlGiveOps] == 0)
7179 modes |= MODE_CHANOP;
7180 else if(cData->lvlOpts[lvlGiveVoice] == 0)
7181 modes |= MODE_VOICE;
7183 greeting = cData->greeting;
7184 if(user->handle_info)
7186 handle = user->handle_info;
7188 if(IsHelper(user) && !IsHelping(user))
7191 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7193 if(channel == chanserv_conf.support_channels.list[ii])
7195 HANDLE_SET_FLAG(user->handle_info, HELPING);
7201 uData = GetTrueChannelAccess(cData, handle);
7202 if(uData && !IsUserSuspended(uData))
7204 /* Ops and above were handled by the above case. */
7205 if(IsUserAutoOp(uData))
7207 if(uData->access >= cData->lvlOpts[lvlGiveOps])
7208 modes |= MODE_CHANOP;
7209 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
7210 modes |= MODE_VOICE;
7212 if(uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
7213 cData->visited = now;
7214 if(cData->user_greeting)
7215 greeting = cData->user_greeting;
7217 && (uData->access >= cData->lvlOpts[lvlUserInfo])
7218 && ((now - uData->seen) >= chanserv_conf.info_delay)
7226 /* If user joining normally (not during burst), apply op or voice,
7227 * and send greeting/userinfo as appropriate.
7229 if(!user->uplink->burst)
7233 if(modes & MODE_CHANOP)
7234 modes &= ~MODE_VOICE;
7235 change.args[0].mode = modes;
7236 change.args[0].u.member = mNode;
7237 mod_chanmode_announce(chanserv, channel, &change);
7240 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
7241 if(uData && info && (modes || !(channel->modes & MODE_DELAYJOINS)))
7242 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
7248 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
7250 struct mod_chanmode change;
7251 struct userData *channel;
7252 unsigned int ii, jj;
7254 if(!user->handle_info)
7257 mod_chanmode_init(&change);
7259 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
7261 struct chanNode *cn;
7262 struct modeNode *mn;
7263 if(IsUserSuspended(channel)
7264 || IsSuspended(channel->channel)
7265 || !(cn = channel->channel->channel))
7268 mn = GetUserMode(cn, user);
7271 if(!IsUserSuspended(channel)
7272 && IsUserAutoInvite(channel)
7273 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
7275 && !user->uplink->burst)
7276 irc_invite(chanserv, user, cn);
7280 if(channel->access >= UL_PRESENT && !HANDLE_FLAGGED(channel->handle, BOT))
7281 channel->channel->visited = now;
7283 if(IsUserAutoOp(channel))
7285 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
7286 change.args[0].mode = MODE_CHANOP;
7287 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
7288 change.args[0].mode = MODE_VOICE;
7290 change.args[0].mode = 0;
7291 change.args[0].u.member = mn;
7292 if(change.args[0].mode)
7293 mod_chanmode_announce(chanserv, cn, &change);
7296 channel->seen = now;
7297 channel->present = 1;
7300 for(ii = 0; ii < user->channels.used; ++ii)
7302 struct chanNode *chan = user->channels.list[ii]->channel;
7303 struct banData *ban;
7305 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
7306 || !chan->channel_info
7307 || IsSuspended(chan->channel_info))
7309 for(jj = 0; jj < chan->banlist.used; ++jj)
7310 if(user_matches_glob(user, chan->banlist.list[jj]->ban, MATCH_USENICK))
7312 if(jj < chan->banlist.used)
7314 for(ban = chan->channel_info->bans; ban; ban = ban->next)
7316 char kick_reason[MAXLEN];
7317 if(!user_matches_glob(user, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
7319 change.args[0].mode = MODE_BAN;
7320 change.args[0].u.hostmask = ban->mask;
7321 mod_chanmode_announce(chanserv, chan, &change);
7322 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
7323 KickChannelUser(user, chan, chanserv, kick_reason);
7324 ban->triggered = now;
7329 if(IsSupportHelper(user))
7331 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7333 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
7335 HANDLE_SET_FLAG(user->handle_info, HELPING);
7343 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
7345 struct chanData *cData;
7346 struct userData *uData;
7348 cData = mn->channel->channel_info;
7349 if(!cData || IsSuspended(cData) || IsLocal(mn->user))
7352 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
7354 /* Allow for a bit of padding so that the limit doesn't
7355 track the user count exactly, which could get annoying. */
7356 if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
7358 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7359 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7363 if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
7365 scan_user_presence(uData, mn->user);
7367 if (uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
7368 cData->visited = now;
7371 if(IsHelping(mn->user) && IsSupportHelper(mn->user))
7374 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii) {
7375 struct chanNode *channel;
7376 struct userNode *exclude;
7377 /* When looking at the channel that is being /part'ed, we
7378 * have to skip over the client that is leaving. For
7379 * other channels, we must not do that.
7381 channel = chanserv_conf.support_channels.list[ii];
7382 exclude = (channel == mn->channel) ? mn->user : NULL;
7383 if(find_handle_in_channel(channel, mn->user->handle_info, exclude))
7386 if(ii == chanserv_conf.support_channels.used)
7387 HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
7392 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
7394 struct userData *uData;
7396 if(!channel->channel_info || !kicker || IsService(kicker)
7397 || (kicker == victim) || IsSuspended(channel->channel_info)
7398 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
7401 if(protect_user(victim, kicker, channel->channel_info))
7403 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED_2");
7404 KickChannelUser(kicker, channel, chanserv, reason);
7407 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
7412 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
7414 struct chanData *cData;
7416 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
7419 cData = channel->channel_info;
7420 if(bad_topic(channel, user, channel->topic))
7422 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
7423 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
7424 SetChannelTopic(channel, chanserv, old_topic, 1);
7425 else if(cData->topic)
7426 SetChannelTopic(channel, chanserv, cData->topic, 1);
7429 /* With topicsnarf, grab the topic and save it as the default topic. */
7430 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
7433 cData->topic = strdup(channel->topic);
7439 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
7441 struct mod_chanmode *bounce = NULL;
7442 unsigned int bnc, ii;
7445 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
7448 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
7449 && mode_lock_violated(&channel->channel_info->modes, change))
7451 char correct[MAXLEN];
7452 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
7453 mod_chanmode_format(&channel->channel_info->modes, correct);
7454 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
7456 for(ii = bnc = 0; ii < change->argc; ++ii)
7458 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
7460 const struct userNode *victim = change->args[ii].u.member->user;
7461 if(!protect_user(victim, user, channel->channel_info))
7464 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7467 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
7468 bounce->args[bnc].u.member = GetUserMode(channel, user);
7469 if(bounce->args[bnc].u.member)
7473 bounce->args[bnc].mode = MODE_CHANOP;
7474 bounce->args[bnc].u.member = change->args[ii].u.member;
7476 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
7478 else if(change->args[ii].mode & MODE_CHANOP)
7480 const struct userNode *victim = change->args[ii].u.member->user;
7481 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
7484 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7485 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
7486 bounce->args[bnc].u.member = change->args[ii].u.member;
7489 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
7491 const char *ban = change->args[ii].u.hostmask;
7492 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
7495 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7496 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
7497 bounce->args[bnc].u.hostmask = strdup(ban);
7499 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
7504 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
7505 mod_chanmode_announce(chanserv, channel, bounce);
7506 for(ii = 0; ii < change->argc; ++ii)
7507 if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
7508 free((char*)bounce->args[ii].u.hostmask);
7509 mod_chanmode_free(bounce);
7514 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
7516 struct chanNode *channel;
7517 struct banData *bData;
7518 struct mod_chanmode change;
7519 unsigned int ii, jj;
7520 char kick_reason[MAXLEN];
7522 mod_chanmode_init(&change);
7524 change.args[0].mode = MODE_BAN;
7525 for(ii = 0; ii < user->channels.used; ++ii)
7527 channel = user->channels.list[ii]->channel;
7528 /* Need not check for bans if they're opped or voiced. */
7529 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
7531 /* Need not check for bans unless channel registration is active. */
7532 if(!channel->channel_info || IsSuspended(channel->channel_info))
7534 /* Look for a matching ban already on the channel. */
7535 for(jj = 0; jj < channel->banlist.used; ++jj)
7536 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
7538 /* Need not act if we found one. */
7539 if(jj < channel->banlist.used)
7541 /* Look for a matching ban in this channel. */
7542 for(bData = channel->channel_info->bans; bData; bData = bData->next)
7544 if(!user_matches_glob(user, bData->mask, MATCH_USENICK | MATCH_VISIBLE))
7546 change.args[0].u.hostmask = bData->mask;
7547 mod_chanmode_announce(chanserv, channel, &change);
7548 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
7549 KickChannelUser(user, channel, chanserv, kick_reason);
7550 bData->triggered = now;
7551 break; /* we don't need to check any more bans in the channel */
7556 static void handle_rename(struct handle_info *handle, const char *old_handle)
7558 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
7562 dict_remove2(handle_dnrs, old_handle, 1);
7563 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
7564 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
7569 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
7571 struct userNode *h_user;
7573 if(handle->channels)
7575 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
7576 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
7578 while(handle->channels)
7579 del_channel_user(handle->channels, 1);
7584 handle_server_link(UNUSED_ARG(struct server *server))
7586 struct chanData *cData;
7588 for(cData = channelList; cData; cData = cData->next)
7590 if(!IsSuspended(cData))
7591 cData->may_opchan = 1;
7592 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
7593 && !cData->channel->join_flooded
7594 && ((cData->channel->limit - cData->channel->members.used)
7595 < chanserv_conf.adjust_threshold))
7597 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7598 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7604 chanserv_conf_read(void)
7608 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
7609 struct mod_chanmode *change;
7610 struct string_list *strlist;
7611 struct chanNode *chan;
7614 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
7616 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
7619 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7620 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7621 chanserv_conf.support_channels.used = 0;
7622 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
7624 for(ii = 0; ii < strlist->used; ++ii)
7626 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7629 chan = AddChannel(strlist->list[ii], now, str2, NULL);
7631 channelList_append(&chanserv_conf.support_channels, chan);
7634 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
7637 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7640 chan = AddChannel(str, now, str2, NULL);
7642 channelList_append(&chanserv_conf.support_channels, chan);
7644 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
7645 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
7646 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
7647 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
7648 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
7649 chanserv_conf.greeting_length = str ? atoi(str) : 200;
7650 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
7651 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
7652 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
7653 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
7654 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
7655 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
7656 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
7657 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
7658 str = database_get_data(conf_node, KEY_DNR_EXPIRE_FREQ, RECDB_QSTRING);
7659 chanserv_conf.dnr_expire_frequency = str ? ParseInterval(str) : 3600;
7660 str = database_get_data(conf_node, KEY_INVITED_INTERVAL, RECDB_QSTRING);
7661 chanserv_conf.invited_timeout = str ? ParseInterval(str) : 600*2;
7662 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
7663 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
7664 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
7665 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
7666 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
7667 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
7668 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
7669 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
7670 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
7672 NickChange(chanserv, str, 0);
7673 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
7674 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
7675 str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
7676 chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
7677 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
7678 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
7679 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
7680 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
7681 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
7682 chanserv_conf.max_owned = str ? atoi(str) : 5;
7683 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
7684 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
7685 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
7686 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
7687 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
7688 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
7689 str = database_get_data(conf_node, KEY_NEW_CHANNEL_AUTHED, RECDB_QSTRING);
7690 chanserv_conf.new_channel_authed = (str && *str) ? str : NULL;
7691 str = database_get_data(conf_node, KEY_NEW_CHANNEL_UNAUTHED, RECDB_QSTRING);
7692 chanserv_conf.new_channel_unauthed = (str && *str) ? str : NULL;
7693 str = database_get_data(conf_node, KEY_NEW_CHANNEL_MSG, RECDB_QSTRING);
7694 chanserv_conf.new_channel_msg = (str && *str) ? str : NULL;
7695 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
7698 safestrncpy(mode_line, str, sizeof(mode_line));
7699 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
7700 if((change = mod_chanmode_parse(NULL, NULL, modes, ii, MCP_KEY_FREE|MCP_NO_APASS, 0))
7701 && (change->argc < 2))
7703 chanserv_conf.default_modes = *change;
7704 mod_chanmode_free(change);
7706 free_string_list(chanserv_conf.set_shows);
7707 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
7709 strlist = string_list_copy(strlist);
7712 static const char *list[] = {
7713 /* free form text */
7714 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
7715 /* options based on user level */
7716 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
7717 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
7718 /* multiple choice options */
7719 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
7720 /* binary options */
7721 "DynLimit", "NoDelete", "expire", "Vote",
7725 strlist = alloc_string_list(ArrayLength(list)-1);
7726 for(ii=0; list[ii]; ii++)
7727 string_list_append(strlist, strdup(list[ii]));
7729 chanserv_conf.set_shows = strlist;
7730 /* We don't look things up now, in case the list refers to options
7731 * defined by modules initialized after this point. Just mark the
7732 * function list as invalid, so it will be initialized.
7734 set_shows_list.used = 0;
7735 free_string_list(chanserv_conf.eightball);
7736 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
7739 strlist = string_list_copy(strlist);
7743 strlist = alloc_string_list(4);
7744 string_list_append(strlist, strdup("Yes."));
7745 string_list_append(strlist, strdup("No."));
7746 string_list_append(strlist, strdup("Maybe so."));
7748 chanserv_conf.eightball = strlist;
7749 free_string_list(chanserv_conf.old_ban_names);
7750 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
7752 strlist = string_list_copy(strlist);
7754 strlist = alloc_string_list(2);
7755 chanserv_conf.old_ban_names = strlist;
7756 str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
7757 off_channel = str ? atoi(str) : 0;
7761 chanserv_note_type_read(const char *key, struct record_data *rd)
7764 struct note_type *ntype;
7767 if(!(obj = GET_RECORD_OBJECT(rd)))
7769 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
7772 if(!(ntype = chanserv_create_note_type(key)))
7774 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
7778 /* Figure out set access */
7779 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
7781 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7782 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
7784 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
7786 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
7787 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
7789 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
7791 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
7795 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
7796 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7797 ntype->set_access.min_opserv = 0;
7800 /* Figure out visibility */
7801 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
7802 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7803 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
7804 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7805 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
7806 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
7807 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
7808 ntype->visible_type = NOTE_VIS_ALL;
7810 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7812 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
7813 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
7817 vote_option_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7819 struct vote_option *vOpt;
7822 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7824 log_module(CS_LOG, LOG_ERROR, "Invalid vote option in %s.", chan->channel->name);
7828 vOpt = calloc(1, sizeof(*vOpt));
7829 vOpt->name = strdup(database_get_data(rd->d.object, KEY_VOTE_OPTION_NAME, RECDB_QSTRING));
7830 str = database_get_data(rd->d.object, KEY_VOTE_OPTION_VOTED, RECDB_QSTRING);
7831 vOpt->voted = str ? atoi(str) : 0;
7832 vOpt->option_id = str ? atoi(key) : 0;
7833 vOpt->option_str = strdup(key);
7834 dict_insert(chan->vote_options,vOpt->option_str,vOpt);
7838 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7840 struct handle_info *handle;
7841 struct userData *uData;
7842 char *seen, *inf, *flags, *voted, *votefor;
7843 unsigned long last_seen;
7844 unsigned short access_level;
7846 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7848 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
7852 access_level = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
7853 if(access_level > UL_OWNER)
7855 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
7859 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
7860 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
7861 last_seen = seen ? strtoul(seen, NULL, 0) : now;
7862 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
7863 voted = database_get_data(rd->d.object, KEY_VOTE_VOTED, RECDB_QSTRING);
7864 votefor = database_get_data(rd->d.object, KEY_VOTE_VOTEDFOR, RECDB_QSTRING);
7865 handle = get_handle_info(key);
7868 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
7872 uData = add_channel_user(chan, handle, access_level, last_seen, inf);
7873 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
7875 uData->voted = voted ? strtoul(voted, NULL, 0) : 0;
7876 uData->votefor = votefor ? strtoul(votefor, NULL, 0) : 0;
7884 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7886 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
7887 unsigned long set_time, triggered_time, expires_time;
7889 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7891 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
7895 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
7896 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
7897 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
7898 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
7899 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
7900 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
7901 if (!reason || !owner)
7904 set_time = set ? strtoul(set, NULL, 0) : now;
7905 triggered_time = triggered ? strtoul(triggered, NULL, 0) : 0;
7907 expires_time = strtoul(s_expires, NULL, 0);
7909 expires_time = set_time + atoi(s_duration);
7913 if(!reason || (expires_time && (expires_time < now)))
7916 add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
7919 static struct suspended *
7920 chanserv_read_suspended(dict_t obj)
7922 struct suspended *suspended = calloc(1, sizeof(*suspended));
7926 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
7927 suspended->expires = str ? strtoul(str, NULL, 0) : 0;
7928 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
7929 suspended->revoked = str ? strtoul(str, NULL, 0) : 0;
7930 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
7931 suspended->issued = str ? strtoul(str, NULL, 0) : 0;
7932 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
7933 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
7934 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
7935 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
7940 chanserv_channel_read(const char *key, struct record_data *hir)
7942 struct suspended *suspended;
7943 struct mod_chanmode *modes;
7944 struct chanNode *cNode;
7945 struct chanData *cData;
7946 struct dict *channel, *obj;
7947 char *str, *argv[10];
7951 channel = hir->d.object;
7953 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
7956 cNode = AddChannel(key, now, NULL, NULL);
7959 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
7962 cData = register_channel(cNode, str);
7965 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
7969 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
7971 enum levelOption lvlOpt;
7972 enum charOption chOpt;
7974 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
7975 cData->flags = atoi(str);
7977 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7979 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
7981 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
7982 else if(levelOptions[lvlOpt].old_flag)
7984 if(cData->flags & levelOptions[lvlOpt].old_flag)
7985 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
7987 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
7991 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7993 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
7995 cData->chOpts[chOpt] = str[0];
7998 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
8000 enum levelOption lvlOpt;
8001 enum charOption chOpt;
8004 cData->flags = base64toint(str, 5);
8005 count = strlen(str += 5);
8006 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8009 if(levelOptions[lvlOpt].old_flag)
8011 if(cData->flags & levelOptions[lvlOpt].old_flag)
8012 lvl = levelOptions[lvlOpt].flag_value;
8014 lvl = levelOptions[lvlOpt].default_value;
8016 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
8018 case 'c': lvl = UL_COOWNER; break;
8019 case 'm': lvl = UL_MASTER; break;
8020 case 'n': lvl = UL_OWNER+1; break;
8021 case 'o': lvl = UL_OP; break;
8022 case 'p': lvl = UL_PEON; break;
8023 case 'w': lvl = UL_OWNER; break;
8024 default: lvl = 0; break;
8026 cData->lvlOpts[lvlOpt] = lvl;
8028 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8029 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
8032 if((str = database_get_data(hir->d.object, KEY_EXPIRE, RECDB_QSTRING)))
8034 cData->expiry = atoi(str);
8035 if(cData->expiry > 0) {
8036 if(cData->expiry > now) {
8037 timeq_add(cData->expiry, chanserv_expire_channel, cData);
8039 timeq_add(1, chanserv_expire_channel, cData);
8046 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
8048 suspended = chanserv_read_suspended(obj);
8049 cData->suspended = suspended;
8050 suspended->cData = cData;
8051 /* We could use suspended->expires and suspended->revoked to
8052 * set the CHANNEL_SUSPENDED flag, but we don't. */
8054 else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
8056 suspended = calloc(1, sizeof(*suspended));
8057 suspended->issued = 0;
8058 suspended->revoked = 0;
8059 suspended->suspender = strdup(str);
8060 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
8061 suspended->expires = str ? atoi(str) : 0;
8062 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
8063 suspended->reason = strdup(str ? str : "No reason");
8064 suspended->previous = NULL;
8065 cData->suspended = suspended;
8066 suspended->cData = cData;
8070 cData->flags &= ~CHANNEL_SUSPENDED;
8071 suspended = NULL; /* to squelch a warning */
8074 if(IsSuspended(cData)) {
8075 if(suspended->expires > now)
8076 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
8077 else if(suspended->expires)
8078 cData->flags &= ~CHANNEL_SUSPENDED;
8081 if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
8082 struct mod_chanmode change;
8083 mod_chanmode_init(&change);
8085 change.args[0].mode = MODE_CHANOP;
8086 change.args[0].u.member = AddChannelUser(chanserv, cNode);
8087 mod_chanmode_announce(chanserv, cNode, &change);
8090 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
8091 cData->registered = str ? strtoul(str, NULL, 0) : now;
8092 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
8093 cData->visited = str ? strtoul(str, NULL, 0) : now;
8094 str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
8095 cData->ownerTransfer = str ? strtoul(str, NULL, 0) : 0;
8096 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
8097 cData->max = str ? atoi(str) : 0;
8098 str = database_get_data(channel, KEY_MAX_TIME, RECDB_QSTRING);
8099 cData->max_time = str ? atoi(str) : 0;
8100 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
8101 cData->greeting = str ? strdup(str) : NULL;
8102 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
8103 cData->user_greeting = str ? strdup(str) : NULL;
8104 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
8105 cData->topic_mask = str ? strdup(str) : NULL;
8106 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
8107 cData->topic = str ? strdup(str) : NULL;
8109 str = database_get_data(channel, KEY_VOTE, RECDB_QSTRING);
8111 cData->vote = str ? strdup(str) : NULL;
8112 dict_delete(cData->vote_options);
8113 cData->vote_options = dict_new();
8114 dict_set_free_data(cData->vote_options, free_vote_options);
8115 str = database_get_data(channel, KEY_VOTE_START, RECDB_QSTRING);
8116 cData->vote_start = str ? atoi(str) : 0;
8117 obj = database_get_data(channel, KEY_VOTE_OPTIONS, RECDB_OBJECT);
8118 for(it = dict_first(obj); it; it = iter_next(it)) {
8119 vote_option_read_helper(iter_key(it), iter_data(it), cData);
8123 if(!IsSuspended(cData)
8124 && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
8125 && (argc = split_line(str, 0, ArrayLength(argv), argv))
8126 && (modes = mod_chanmode_parse(cNode, NULL, argv, argc, MCP_KEY_FREE|MCP_NO_APASS, 0))) {
8127 cData->modes = *modes;
8129 cData->modes.modes_set |= MODE_REGISTERED;
8130 if(cData->modes.argc > 1)
8131 cData->modes.argc = 1;
8132 mod_chanmode_announce(chanserv, cNode, &cData->modes);
8133 mod_chanmode_free(modes);
8136 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
8137 for(it = dict_first(obj); it; it = iter_next(it))
8138 user_read_helper(iter_key(it), iter_data(it), cData);
8140 if(!cData->users && !IsProtected(cData))
8142 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
8143 unregister_channel(cData, "has empty user list.");
8147 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
8148 for(it = dict_first(obj); it; it = iter_next(it))
8149 ban_read_helper(iter_key(it), iter_data(it), cData);
8151 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
8152 for(it = dict_first(obj); it; it = iter_next(it))
8154 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
8155 struct record_data *rd = iter_data(it);
8156 const char *note, *setter;
8158 if(rd->type != RECDB_OBJECT)
8160 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
8164 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
8166 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
8168 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
8172 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
8173 if(!setter) setter = "<unknown>";
8174 chanserv_add_channel_note(cData, ntype, setter, note);
8182 chanserv_dnr_read(const char *key, struct record_data *hir)
8184 const char *setter, *reason, *str;
8185 struct do_not_register *dnr;
8186 unsigned long expiry;
8188 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
8191 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
8194 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
8197 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
8200 str = database_get_data(hir->d.object, KEY_EXPIRES, RECDB_QSTRING);
8201 expiry = str ? strtoul(str, NULL, 0) : 0;
8202 if(expiry && expiry <= now)
8204 dnr = chanserv_add_dnr(key, setter, expiry, reason);
8207 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
8209 dnr->set = atoi(str);
8215 chanserv_saxdb_read(struct dict *database)
8217 struct dict *section;
8220 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
8221 for(it = dict_first(section); it; it = iter_next(it))
8222 chanserv_note_type_read(iter_key(it), iter_data(it));
8224 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
8225 for(it = dict_first(section); it; it = iter_next(it))
8226 chanserv_channel_read(iter_key(it), iter_data(it));
8228 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
8229 for(it = dict_first(section); it; it = iter_next(it))
8230 chanserv_dnr_read(iter_key(it), iter_data(it));
8236 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
8238 int high_present = 0;
8239 saxdb_start_record(ctx, KEY_USERS, 1);
8240 for(; uData; uData = uData->next)
8242 if((uData->access >= UL_PRESENT) && uData->present && !HANDLE_FLAGGED(uData->handle, BOT))
8244 saxdb_start_record(ctx, uData->handle->handle, 0);
8245 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
8246 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
8248 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
8249 if(uData->channel->vote && uData->voted)
8250 saxdb_write_int(ctx, KEY_VOTE_VOTED, uData->voted);
8251 if(uData->channel->vote && uData->votefor)
8252 saxdb_write_int(ctx, KEY_VOTE_VOTEDFOR, uData->votefor);
8254 saxdb_write_string(ctx, KEY_INFO, uData->info);
8255 saxdb_end_record(ctx);
8257 saxdb_end_record(ctx);
8258 return high_present;
8262 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
8266 saxdb_start_record(ctx, KEY_BANS, 1);
8267 for(; bData; bData = bData->next)
8269 saxdb_start_record(ctx, bData->mask, 0);
8270 saxdb_write_int(ctx, KEY_SET, bData->set);
8271 if(bData->triggered)
8272 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
8274 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
8276 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
8278 saxdb_write_string(ctx, KEY_REASON, bData->reason);
8279 saxdb_end_record(ctx);
8281 saxdb_end_record(ctx);
8285 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
8287 saxdb_start_record(ctx, name, 0);
8288 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
8289 saxdb_write_string(ctx, KEY_REASON, susp->reason);
8291 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
8293 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
8295 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
8297 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
8298 saxdb_end_record(ctx);
8302 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
8306 enum levelOption lvlOpt;
8307 enum charOption chOpt;
8310 saxdb_start_record(ctx, channel->channel->name, 1);
8312 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
8313 saxdb_write_int(ctx, KEY_MAX, channel->max);
8314 saxdb_write_int(ctx, KEY_MAX_TIME, channel->max_time);
8316 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
8317 if(channel->registrar)
8318 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
8319 if(channel->greeting)
8320 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
8321 if(channel->user_greeting)
8322 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
8323 if(channel->topic_mask)
8324 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
8325 if(channel->suspended)
8326 chanserv_write_suspended(ctx, "suspended", channel->suspended);
8328 saxdb_write_int(ctx, KEY_EXPIRE, channel->expiry);
8331 saxdb_write_string(ctx, KEY_VOTE, channel->vote);
8332 if(channel->vote_start)
8333 saxdb_write_int(ctx, KEY_VOTE_START, channel->vote_start);
8334 if (dict_size(channel->vote_options)) {
8335 saxdb_start_record(ctx, KEY_VOTE_OPTIONS, 1);
8336 for (it = dict_first(channel->vote_options); it; it = iter_next(it)) {
8337 struct vote_option *vOpt = iter_data(it);
8339 sprintf(str,"%i",vOpt->option_id);
8340 saxdb_start_record(ctx, str, 0);
8342 saxdb_write_int(ctx, KEY_VOTE_OPTION_VOTED, vOpt->voted);
8344 saxdb_write_string(ctx, KEY_VOTE_OPTION_NAME, vOpt->name);
8345 saxdb_end_record(ctx);
8347 saxdb_end_record(ctx);
8351 saxdb_start_record(ctx, KEY_OPTIONS, 0);
8352 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
8353 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8354 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
8355 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8357 buf[0] = channel->chOpts[chOpt];
8359 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
8361 saxdb_end_record(ctx);
8363 if(channel->modes.modes_set || channel->modes.modes_clear)
8365 mod_chanmode_format(&channel->modes, buf);
8366 saxdb_write_string(ctx, KEY_MODES, buf);
8369 high_present = chanserv_write_users(ctx, channel->users);
8370 chanserv_write_bans(ctx, channel->bans);
8372 if(dict_size(channel->notes))
8376 saxdb_start_record(ctx, KEY_NOTES, 1);
8377 for(it = dict_first(channel->notes); it; it = iter_next(it))
8379 struct note *note = iter_data(it);
8380 saxdb_start_record(ctx, iter_key(it), 0);
8381 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
8382 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
8383 saxdb_end_record(ctx);
8385 saxdb_end_record(ctx);
8388 if(channel->ownerTransfer)
8389 saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
8390 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
8391 saxdb_end_record(ctx);
8395 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
8399 saxdb_start_record(ctx, ntype->name, 0);
8400 switch(ntype->set_access_type)
8402 case NOTE_SET_CHANNEL_ACCESS:
8403 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
8405 case NOTE_SET_CHANNEL_SETTER:
8406 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
8408 case NOTE_SET_PRIVILEGED: default:
8409 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
8412 switch(ntype->visible_type)
8414 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
8415 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
8416 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
8418 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
8419 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
8420 saxdb_end_record(ctx);
8424 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
8426 struct do_not_register *dnr;
8427 dict_iterator_t it, next;
8429 for(it = dict_first(dnrs); it; it = next)
8431 next = iter_next(it);
8432 dnr = iter_data(it);
8433 if(dnr->expires && dnr->expires <= now)
8435 dict_remove(dnrs, iter_key(it));
8438 saxdb_start_record(ctx, dnr->chan_name, 0);
8440 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
8442 saxdb_write_int(ctx, KEY_EXPIRES, dnr->expires);
8443 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
8444 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
8445 saxdb_end_record(ctx);
8450 chanserv_saxdb_write(struct saxdb_context *ctx)
8453 struct chanData *channel;
8456 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
8457 for(it = dict_first(note_types); it; it = iter_next(it))
8458 chanserv_write_note_type(ctx, iter_data(it));
8459 saxdb_end_record(ctx);
8462 saxdb_start_record(ctx, KEY_DNR, 1);
8463 write_dnrs_helper(ctx, handle_dnrs);
8464 write_dnrs_helper(ctx, plain_dnrs);
8465 write_dnrs_helper(ctx, mask_dnrs);
8466 saxdb_end_record(ctx);
8469 saxdb_start_record(ctx, KEY_CHANNELS, 1);
8470 for(channel = channelList; channel; channel = channel->next)
8471 chanserv_write_channel(ctx, channel);
8472 saxdb_end_record(ctx);
8478 chanserv_db_cleanup(void) {
8480 unreg_part_func(handle_part);
8482 unregister_channel(channelList, "terminating.");
8483 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
8484 UnlockChannel(chanserv_conf.support_channels.list[ii]);
8485 free(chanserv_conf.support_channels.list);
8486 dict_delete(handle_dnrs);
8487 dict_delete(plain_dnrs);
8488 dict_delete(mask_dnrs);
8489 dict_delete(note_types);
8490 free_string_list(chanserv_conf.eightball);
8491 free_string_list(chanserv_conf.old_ban_names);
8492 free_string_list(chanserv_conf.set_shows);
8493 free(set_shows_list.list);
8494 free(uset_shows_list.list);
8497 struct userData *helper = helperList;
8498 helperList = helperList->next;
8503 #if defined(GCC_VARMACROS)
8504 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ARGS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ARGS)
8505 #elif defined(C99_VARMACROS)
8506 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, __VA_ARGS__)
8508 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
8509 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
8512 init_chanserv(const char *nick)
8514 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
8515 conf_register_reload(chanserv_conf_read);
8519 reg_server_link_func(handle_server_link);
8520 reg_new_channel_func(handle_new_channel);
8521 reg_join_func(handle_join);
8522 reg_part_func(handle_part);
8523 reg_kick_func(handle_kick);
8524 reg_topic_func(handle_topic);
8525 reg_mode_change_func(handle_mode);
8526 reg_nick_change_func(handle_nick_change);
8527 reg_auth_func(handle_auth);
8530 reg_handle_rename_func(handle_rename);
8531 reg_unreg_func(handle_unreg);
8533 handle_dnrs = dict_new();
8534 dict_set_free_data(handle_dnrs, free);
8535 plain_dnrs = dict_new();
8536 dict_set_free_data(plain_dnrs, free);
8537 mask_dnrs = dict_new();
8538 dict_set_free_data(mask_dnrs, free);
8540 reg_svccmd_unbind_func(handle_svccmd_unbind);
8541 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
8542 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
8543 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
8544 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
8545 DEFINE_COMMAND(dnrsearch, 3, 0, "template", "noregister", NULL);
8546 modcmd_register(chanserv_module, "dnrsearch print", NULL, 0, 0, NULL);
8547 modcmd_register(chanserv_module, "dnrsearch remove", NULL, 0, 0, NULL);
8548 modcmd_register(chanserv_module, "dnrsearch count", NULL, 0, 0, NULL);
8549 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
8550 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
8551 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
8552 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
8553 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
8555 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
8556 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
8558 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8559 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8560 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8561 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8562 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
8564 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
8565 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
8566 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
8567 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8568 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8570 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8571 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
8572 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8573 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
8575 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
8576 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
8577 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8578 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8579 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
8580 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8581 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8582 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8584 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8585 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8586 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8587 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
8588 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
8589 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
8590 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
8591 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
8592 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8593 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
8594 DEFINE_COMMAND(invitemeall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8595 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
8596 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
8597 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8598 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8600 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
8601 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8602 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8603 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8604 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
8606 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
8607 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
8609 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
8610 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8611 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8612 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8613 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8614 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8615 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8616 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8617 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8618 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8619 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8621 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
8622 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
8624 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
8625 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
8626 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
8627 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
8629 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
8630 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
8631 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
8632 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
8633 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
8635 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8636 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8637 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8638 DEFINE_COMMAND(8ball, 2, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8639 DEFINE_COMMAND(d, 2, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8640 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8642 DEFINE_COMMAND(addvote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8643 DEFINE_COMMAND(delvote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8644 DEFINE_COMMAND(addoption, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8645 DEFINE_COMMAND(deloption, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8646 DEFINE_COMMAND(vote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8647 DEFINE_COMMAND(startvote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8648 DEFINE_COMMAND(endvote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8649 DEFINE_COMMAND(voteresults, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8651 DEFINE_COMMAND(opme, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
8653 /* Channel options */
8654 DEFINE_CHANNEL_OPTION(defaulttopic);
8655 DEFINE_CHANNEL_OPTION(topicmask);
8656 DEFINE_CHANNEL_OPTION(greeting);
8657 DEFINE_CHANNEL_OPTION(usergreeting);
8658 DEFINE_CHANNEL_OPTION(modes);
8659 DEFINE_CHANNEL_OPTION(enfops);
8660 DEFINE_CHANNEL_OPTION(giveops);
8661 DEFINE_CHANNEL_OPTION(protect);
8662 DEFINE_CHANNEL_OPTION(enfmodes);
8663 DEFINE_CHANNEL_OPTION(enftopic);
8664 DEFINE_CHANNEL_OPTION(pubcmd);
8665 DEFINE_CHANNEL_OPTION(givevoice);
8666 DEFINE_CHANNEL_OPTION(userinfo);
8667 DEFINE_CHANNEL_OPTION(dynlimit);
8668 DEFINE_CHANNEL_OPTION(topicsnarf);
8669 DEFINE_CHANNEL_OPTION(vote);
8670 DEFINE_CHANNEL_OPTION(nodelete);
8671 DEFINE_CHANNEL_OPTION(toys);
8672 DEFINE_CHANNEL_OPTION(setters);
8673 DEFINE_CHANNEL_OPTION(topicrefresh);
8674 DEFINE_CHANNEL_OPTION(ctcpusers);
8675 DEFINE_CHANNEL_OPTION(ctcpreaction);
8676 DEFINE_CHANNEL_OPTION(inviteme);
8677 DEFINE_CHANNEL_OPTION(unreviewed);
8678 modcmd_register(chanserv_module, "set expire", chan_opt_expire, 1, 0, "flags", "+helping", NULL);
8679 modcmd_register(chanserv_module, "set unreviewed on", NULL, 0, 0, "flags", "+helping", NULL);
8680 modcmd_register(chanserv_module, "set unreviewed off", NULL, 0, 0, "flags", "+oper", NULL);
8682 DEFINE_CHANNEL_OPTION(offchannel);
8683 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
8685 /* Alias set topic to set defaulttopic for compatibility. */
8686 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
8689 DEFINE_USER_OPTION(noautoop);
8690 DEFINE_USER_OPTION(autoinvite);
8691 DEFINE_USER_OPTION(info);
8693 /* Alias uset autovoice to uset autoop. */
8694 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
8696 note_types = dict_new();
8697 dict_set_free_data(note_types, chanserv_deref_note_type);
8700 const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
8701 chanserv = AddLocalUser(nick, nick, NULL, "Channel Services", modes);
8702 service_register(chanserv)->trigger = '!';
8703 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
8705 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
8707 if(chanserv_conf.channel_expire_frequency)
8708 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
8710 if(chanserv_conf.dnr_expire_frequency)
8711 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
8713 if(chanserv_conf.refresh_period)
8715 unsigned long next_refresh;
8716 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
8717 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
8720 reg_exit_func(chanserv_db_cleanup);
8721 message_register_table(msgtab);