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() */
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"
62 /* ChanServ database */
63 #define KEY_CHANNELS "channels"
64 #define KEY_NOTE_TYPES "note_types"
66 /* Note type parameters */
67 #define KEY_NOTE_OPSERV_ACCESS "opserv_access"
68 #define KEY_NOTE_CHANNEL_ACCESS "channel_access"
69 #define KEY_NOTE_SETTER_ACCESS "setter_access"
70 #define KEY_NOTE_VISIBILITY "visibility"
71 #define KEY_NOTE_VIS_PRIVILEGED "privileged"
72 #define KEY_NOTE_VIS_CHANNEL_USERS "channel_users"
73 #define KEY_NOTE_VIS_ALL "all"
74 #define KEY_NOTE_MAX_LENGTH "max_length"
75 #define KEY_NOTE_SETTER "setter"
76 #define KEY_NOTE_NOTE "note"
78 /* Do-not-register channels */
80 #define KEY_DNR_SET "set"
81 #define KEY_DNR_SETTER "setter"
82 #define KEY_DNR_REASON "reason"
85 #define KEY_REGISTERED "registered"
86 #define KEY_REGISTRAR "registrar"
87 #define KEY_SUSPENDED "suspended"
88 #define KEY_PREVIOUS "previous"
89 #define KEY_SUSPENDER "suspender"
90 #define KEY_ISSUED "issued"
91 #define KEY_REVOKED "revoked"
92 #define KEY_SUSPEND_EXPIRES "suspend_expires"
93 #define KEY_SUSPEND_REASON "suspend_reason"
94 #define KEY_VISITED "visited"
95 #define KEY_TOPIC "topic"
96 #define KEY_GREETING "greeting"
97 #define KEY_USER_GREETING "user_greeting"
98 #define KEY_MODES "modes"
99 #define KEY_FLAGS "flags"
100 #define KEY_OPTIONS "options"
101 #define KEY_USERS "users"
102 #define KEY_BANS "bans"
103 #define KEY_MAX "max"
104 #define KEY_MAX_TIME "max_time"
105 #define KEY_NOTES "notes"
106 #define KEY_TOPIC_MASK "topic_mask"
107 #define KEY_OWNER_TRANSFER "owner_transfer"
108 #define KEY_EXPIRE "expire"
111 #define KEY_LEVEL "level"
112 #define KEY_INFO "info"
113 #define KEY_SEEN "seen"
116 #define KEY_VOTE "vote"
117 #define KEY_VOTE_START "votestart"
118 #define KEY_VOTE_OPTIONS "voptions"
119 #define KEY_VOTE_OPTION_NAME "voptionname"
120 #define KEY_VOTE_VOTED "vvoted"
121 #define KEY_VOTE_VOTEDFOR "vvotefor"
122 #define KEY_VOTE_OPTION_ID "voptionid"
123 #define KEY_VOTE_OPTION_VOTED "voptionvoted"
126 #define KEY_OWNER "owner"
127 #define KEY_REASON "reason"
128 #define KEY_SET "set"
129 #define KEY_DURATION "duration"
130 #define KEY_EXPIRES "expires"
131 #define KEY_TRIGGERED "triggered"
133 #define CHANNEL_DEFAULT_FLAGS (CHANNEL_OFFCHANNEL | CHANNEL_UNREVIEWED)
134 #define CHANNEL_PRESERVED_FLAGS (CHANNEL_UNREVIEWED)
135 #define CHANNEL_DEFAULT_OPTIONS "lmoooanpcnat"
137 /* Administrative messages */
138 static const struct message_entry msgtab[] = {
139 { "CSMSG_CHANNELS_EXPIRED", "%i channels expired." },
141 /* Channel registration */
142 { "CSMSG_REG_SUCCESS", "You now have ownership of $b%s$b." },
143 { "CSMSG_PROXY_SUCCESS", "%s now has ownership of $b%s$b." },
144 { "CSMSG_ALREADY_REGGED", "$b%s$b is registered to someone else." },
145 { "CSMSG_MUST_BE_OPPED", "You must be a channel operator in $b%s$b to register it." },
146 { "CSMSG_PROXY_FORBIDDEN", "You may not register a channel for someone else." },
147 { "CSMSG_OWN_TOO_MANY", "%s already owns enough channels (at least %d); use FORCE to override." },
149 /* Do-not-register channels */
150 { "CSMSG_NOT_DNR", "$b%s$b is not a valid channel name or *account." },
151 { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
152 { "CSMSG_DNR_INFO", "$b%s$b is do-not-register (by $b%s$b): %s" },
153 { "CSMSG_DNR_INFO_SET", "$b%s$b is do-not-register (set %s by $b%s$b): %s" },
154 { "CSMSG_DNR_INFO_SET_EXPIRES", "$b%s$b is do-not-register (set %s by $b%s$b; expires %s): %s" },
155 { "CSMSG_MORE_DNRS", "%d more do-not-register entries skipped." },
156 { "CSMSG_DNR_CHANNEL", "Only network staff may register $b%s$b." },
157 { "CSMSG_DNR_CHANNEL_MOVE", "Only network staff may move $b%s$b." },
158 { "CSMSG_DNR_ACCOUNT", "Only network staff may register channels to $b%s$b." },
159 { "CSMSG_NOREGISTER_CHANNEL", "$b%s$b has been added to the do-not-register list." },
160 { "CSMSG_NO_SUCH_DNR", "$b%s$b is not in the do-not-register list." },
161 { "CSMSG_DNR_REMOVED", "$b%s$b has been removed from the do-not-register list." },
162 { "CSMSG_DNR_BAD_ACTION", "$b%s$b is not a recognized do-not-register action." },
163 { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
165 /* Channel unregistration */
166 { "CSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
167 { "CSMSG_UNREG_NODELETE", "$b%s$b is protected from unregistration." },
168 { "CSMSG_CHAN_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended (%s)." },
169 { "CSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must use 'unregister %s'." },
172 { "CSMSG_MOVE_SUCCESS", "Channel registration has been moved to $b%s$b." },
173 { "CSMSG_MOVE_NODELETE", "$b%s$b is protected from unregistration, and cannot be moved." },
175 /* Channel merging */
176 { "CSMSG_MERGE_SUCCESS", "Channel successfully merged into $b%s$b." },
177 { "CSMSG_MERGE_SELF", "Merging cannot be performed if the source and target channels are the same." },
178 { "CSMSG_MERGE_NODELETE", "You may not merge a channel that is marked NoDelete." },
179 { "CSMSG_MERGE_SUSPENDED", "Merging cannot be performed if the source or target channel is suspended." },
180 { "CSMSG_MERGE_NOT_OWNER", "You must be the owner of the target channel (or a helper) to merge into the channel." },
182 /* Handle unregistration */
183 { "CSMSG_HANDLE_UNREGISTERED", "As a result of your account unregistration, you have been deleted from all of your channels' userlists." },
186 { "CSMSG_NOT_USER", "You lack access to $b%s$b." },
187 { "CSMSG_NO_CHAN_USER", "%s lacks access to $b%s$b." },
188 { "CSMSG_NO_ACCESS", "You lack sufficient access to use this command." },
189 { "CSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$C$b." },
190 { "CSMSG_MAXIMUM_BANS", "This channel has reached the ban count limit of $b%d$b." },
191 { "CSMSG_MAXIMUM_USERS", "This channel has reached the user count limit of $b%d$b." },
192 { "CSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel, and cannot be registered." },
193 { "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." },
194 { "CSMSG_ALREADY_OPPED", "You are already opped in $b%s$b." },
195 { "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." },
196 { "CSMSG_ALREADY_DOWN", "You are not opped or voiced in $b%s$b." },
197 { "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." },
198 { "CSMSG_OUT_OF_CHANNEL", "For some reason I don't seem to be in $b%s$b." },
199 { "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." },
201 /* Removing yourself from a channel. */
202 { "CSMSG_NO_OWNER_DELETEME", "You cannot delete your owner access in $b%s$b." },
203 { "CSMSG_CONFIRM_DELETEME", "To really remove yourself, you must use 'deleteme %s'." },
204 { "CSMSG_DELETED_YOU", "Your $b%d$b access has been deleted from $b%s$b." },
206 /* User management */
207 { "CSMSG_ADDED_USER", "Added %s to the %s user list with access %d." },
208 { "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." },
209 { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be greater than maximum (%d)." },
210 { "CSMSG_DELETED_USERS", "Deleted accounts matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list." },
211 { "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." },
212 { "CSMSG_INCORRECT_ACCESS", "%s has access $b%d$b, not %s." },
213 { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with access %d)." },
214 { "CSMSG_CANNOT_TRIM", "You must include a minimum inactivity duration of at least 60 seconds to trim." },
216 { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." },
217 { "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." },
218 { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
219 { "CSMSG_TRANSFER_WAIT", "You must wait %s before you can give ownership of $b%s$b to someone else." },
220 { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
221 { "CSMSG_CONFIRM_GIVEOWNERSHIP", "To really give ownership to $b%1$s$b, you must use 'giveownership *%1$s %2$s'." },
222 { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
225 { "CSMSG_BAN_ADDED", "Permanently banned $b%s$b from %s." },
226 { "CSMSG_TIMED_BAN_ADDED", "Banned $b%s$b from %s for %s." },
227 { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
228 { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
229 { "CSMSG_REASON_CHANGE", "Reason for ban $b%s$b changed." },
230 { "CSMSG_BAN_EXTENDED", "Extended ban for $b%s$b expires in %s." },
231 { "CSMSG_BAN_REMOVED", "Matching ban(s) for $b%s$b removed." },
232 { "CSMSG_TRIMMED_BANS", "Trimmed $b%d bans$b from the %s ban list that were inactive for at least %s." },
233 { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
234 { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
235 { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
236 { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
237 { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
238 { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
239 { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban found for $b%s$b." },
240 { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
242 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
244 /* Channel management */
245 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
246 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
247 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
249 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
250 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
251 { "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" },
252 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic is at most %d characters and matches the topic mask pattern." },
253 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
254 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
255 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
257 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
258 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
259 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
260 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
261 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
262 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
263 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you must use 'set defaults %s'." },
264 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
265 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
266 { "CSMSG_BAD_GIVEVOICE", "You cannot change GiveVoice to above GiveOps (%d)." },
267 { "CSMSG_BAD_GIVEOPS", "You cannot change GiveOps to below GiveVoice (%d)." },
268 { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
269 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
270 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
271 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
272 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
273 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
274 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
275 { "CSMSG_SET_MODES", "$bModes $b %s" },
276 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
277 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s" },
278 { "CSMSG_SET_OFFCHANNEL", "$bOffChannel $b %s" },
279 { "CSMSG_SET_USERINFO", "$bUserInfo $b %d" },
280 { "CSMSG_SET_GIVE_VOICE", "$bGiveVoice $b %d" },
281 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" },
282 { "CSMSG_SET_VOTE", "$bVote $b %d" },
283 { "CSMSG_SET_INVITEME", "$bInviteMe $b %d" },
284 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d" },
285 { "CSMSG_SET_GIVE_OPS", "$bGiveOps $b %d" },
286 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d" },
287 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d" },
288 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d" },
289 { "CSMSG_SET_SETTERS", "$bSetters $b %d" },
290 { "CSMSG_SET_CTCPUSERS", "$bCTCPUsers $b %d" },
291 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
292 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
293 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
294 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
295 { "CSMSG_SET_UNREVIEWED", "$bUnreviewed $b %s" },
296 { "CSMSG_SET_EXPIRE", "$bExpire $b %s" },
297 { "CSMSG_SET_EXPIRE_OFF", "$bExpire $b off" },
298 { "CSMSG_USET_NOAUTOOP", "$bNoAutoOp $b %s" },
299 { "CSMSG_USET_NOAUTOVOICE", "$bNoAutoVoice $b %s" },
300 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
301 { "CSMSG_USET_INFO", "$bInfo $b %s" },
303 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
304 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
305 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
306 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
307 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
308 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
309 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
310 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
311 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
312 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
313 { "CSMSG_PROTECT_NONE", "No users will be protected." },
314 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
315 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
316 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
317 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
318 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
319 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
320 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
321 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
322 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
323 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
324 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
325 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
327 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
328 { "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" },
329 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s." },
330 { "CSMSG_ALREADY_PRESENT", "%s is already in $b%s$b." },
331 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
332 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s for $S to invite you." },
333 { "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
334 { "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
336 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
337 { "CSMSG_NO_BANS", "No channel bans found on $b%s$b." },
338 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
340 /* Channel userlist */
341 { "CSMSG_ACCESS_ALL_HEADER", "%s users from level %d to %d:" },
342 { "CSMSG_ACCESS_SEARCH_HEADER", "%s users from level %d to %d matching %s:" },
343 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
344 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%d$b in %s." },
346 /* Channel note list */
347 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
348 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
349 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
350 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
351 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
352 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
353 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
354 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
355 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
356 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
357 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
358 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
359 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
360 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
361 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
363 /* Channel [un]suspension */
364 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
365 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
366 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
367 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
368 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
369 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
370 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
372 /* Access information */
373 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
374 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
375 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
376 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
377 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
378 { "CSMSG_USER_HAS_ACCESS", "%s has access $b%d$b in %s." },
379 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
380 { "CSMSG_HELPER_HAS_ACCESS", "%s has access $b%d$b in %s and has $bsecurity override$b enabled." },
381 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
382 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
383 { "CSMSG_OPERATOR_TITLE", "IRC operator" },
384 { "CSMSG_UC_H_TITLE", "network helper" },
385 { "CSMSG_LC_H_TITLE", "support helper" },
386 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
387 { "CSMSG_MYACCESS_COUNT", "%s has access in $b%d$b channels." },
388 { "CSMSG_MYACCESS_COUNT_1", "%s has access in $b%d$b channel." },
390 /* Seen information */
391 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
392 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
393 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
394 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
396 /* Names information */
397 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
398 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
400 /* Channel information */
401 { "CSMSG_CHANNEL_INFO", "$b%s$b Information:" },
402 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
403 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
404 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
405 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
406 { "CSMSG_CHANNEL_MAX_TIME", "$bRecord Visitors: $b%i (%s ago.)" },
407 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
408 { "CSMSG_CHANNEL_BANS", "$bBan Count: $b%i" },
409 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
410 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
411 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
412 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
413 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
414 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
415 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
416 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
417 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
418 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
419 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
420 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
421 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
422 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
424 { "CSMSG_PEEK_INFO", "$b%s$b Status:" },
425 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
426 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
427 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d (%d ops, %d voices, %d regulars)" },
428 { "CSMSG_PEEK_OPS", "$bOps:$b" },
429 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
431 /* Network information */
432 { "CSMSG_NETWORK_INFO", "Network Information:" },
433 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
434 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
435 { "CSMSG_NETWORK_BANS", "$bTotal Ban Count: $b%i" },
436 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
437 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
438 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
439 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
440 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
443 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
444 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
445 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
447 /* Channel searches */
448 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
449 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
450 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
451 { "CSMSG_CHANNEL_SEARCH_RESULTS", "The following channels were found:" },
453 /* Channel configuration */
454 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
455 { "CSMSG_INVALID_CFLAG", "$b%s$b is not a recognized channel flag." },
456 { "CSMSG_CHANNEL_OPTIONS", "Channel Options:" },
457 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
460 { "CSMSG_USER_OPTIONS", "User Options:" },
461 { "CSMSG_USER_PROTECTED_2", "That user is protected." },
464 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
465 { "CSMSG_PING_RESPONSE", "Pong!" },
466 { "CSMSG_WUT_RESPONSE", "wut" },
467 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
468 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
469 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
470 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
471 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
472 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
473 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
476 { "CSMSG_ADDVOTE_DONE", "Vote added. Use $baddoption$b to add at least 2 vote options and then $bstartvote$b to start the voting." },
477 { "CSMSG_ADDVOTE_FULL", "There is already a vote in this channel. Use $bdelvote$b to delete it." },
478 { "CSMSG_DELVOTE_DONE", "Vote deleted." },
479 { "CSMSG_NO_VOTE", "There is no vote in this channel." },
480 { "CSMSG_ADDOPTION_DONE", "Vote option added with id %i (%i - %i)." },
481 { "CSMSG_DELOPTION_DONE", "Vote option deleted." },
482 { "CSMSG_DELOPTION_NONE", "Vote option does not exist." },
483 { "CSMSG_VOTE_NEED_OPTIONS", "There must be at least 2 options in a vote." },
484 { "CSMSG_VOTE_NOT_STARTED", "The vote is not started. Use $bstartvote$b to allow voting." },
485 { "CSMSG_VOTE_QUESTION", "Question: %s" },
486 { "CSMSG_VOTE_OPTION", "$b%i$b: %s ($b%i$b votes)" },
487 { "CSMSG_VOTERES_QUESTION", "Question: %s" },
488 { "CSMSG_VOTERES_OPTION", "
\ 2%i
\ 2: %s (
\ 2%i
\ 2 votes)" },
489 { "CSMSG_STARTVOTE_TOP", "
\ 2%s
\ 2 has started a voting:" },
490 { "CSMSG_STARTVOTE_QUESTION", "
\ 2Question:
\ 2 %s" },
491 { "CSMSG_STARTVOTE_OPTION", "
\ 2%i:
\ 2 %s" },
492 { "CSMSG_STARTVOTE_ACCESS", "All channel users with at least
\ 2%i
\ 2 access can vote." },
493 { "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." },
494 { "CSMSG_STARTVOTE_RUNNING", "The vote is already running." },
495 { "CSMSG_VOTE_VOTED", "You have already voted." },
496 { "CSMSG_VOTE_INVALID", "Vote option does not exist." },
497 { "CSMSG_VOTE_DONE", "You voted for $b%s$b." },
498 { "CSMSG_ENDVOTE_DONE", "The vote has been finished." },
499 { "CSMSG_ENDVOTE_STOPPED", "The vote is not running." },
502 { "CSMSG_EVENT_SEARCH_RESULTS", "The following channel events were found:" },
506 /* eject_user and unban_user flags */
507 #define ACTION_KICK 0x0001
508 #define ACTION_BAN 0x0002
509 #define ACTION_ADD_BAN 0x0004
510 #define ACTION_ADD_TIMED_BAN 0x0008
511 #define ACTION_UNBAN 0x0010
512 #define ACTION_DEL_BAN 0x0020
514 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
515 #define MODELEN 40 + KEYLEN
519 #define CSFUNC_ARGS user, channel, argc, argv, cmd
521 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
522 #define CHANSERV_SYNTAX() svccmd_send_help(user, chanserv, cmd)
523 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
524 reply("MSG_MISSING_PARAMS", argv[0]); \
528 DECLARE_LIST(dnrList, struct do_not_register *);
529 DEFINE_LIST(dnrList, struct do_not_register *)
531 static int eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action);
533 struct userNode *chanserv;
536 static dict_t plain_dnrs, mask_dnrs, handle_dnrs;
537 static struct log_type *CS_LOG;
541 struct channelList support_channels;
542 struct mod_chanmode default_modes;
544 unsigned long db_backup_frequency;
545 unsigned long channel_expire_frequency;
546 unsigned long dnr_expire_frequency;
548 unsigned long invited_timeout;
550 unsigned long info_delay;
551 unsigned long adjust_delay;
552 unsigned long channel_expire_delay;
553 unsigned int nodelete_level;
555 unsigned int adjust_threshold;
556 int join_flood_threshold;
558 unsigned int greeting_length;
559 unsigned int refresh_period;
560 unsigned int giveownership_period;
562 unsigned int max_owned;
563 unsigned int max_chan_users;
564 unsigned int max_chan_bans;
565 unsigned int max_userinfo_length;
567 struct string_list *set_shows;
568 struct string_list *eightball;
569 struct string_list *old_ban_names;
571 const char *ctcp_short_ban_duration;
572 const char *ctcp_long_ban_duration;
574 const char *irc_operator_epithet;
575 const char *network_helper_epithet;
576 const char *support_helper_epithet;
581 struct userNode *user;
582 struct userNode *bot;
583 struct chanNode *channel;
585 unsigned short lowest;
586 unsigned short highest;
587 struct userData **users;
588 struct helpfile_table table;
593 struct userNode *user;
594 struct chanNode *chan;
597 enum note_access_type
599 NOTE_SET_CHANNEL_ACCESS,
600 NOTE_SET_CHANNEL_SETTER,
604 enum note_visible_type
607 NOTE_VIS_CHANNEL_USERS,
613 enum note_access_type set_access_type;
615 unsigned int min_opserv;
616 unsigned short min_ulevel;
618 enum note_visible_type visible_type;
619 unsigned int max_length;
626 struct note_type *type;
627 char setter[NICKSERV_HANDLE_LEN+1];
631 static unsigned int registered_channels;
632 static unsigned int banCount;
634 static const struct {
637 unsigned short level;
640 { "peon", "Peon", UL_PEON, '+' },
641 { "op", "Op", UL_OP, '@' },
642 { "master", "Master", UL_MASTER, '%' },
643 { "coowner", "Coowner", UL_COOWNER, '*' },
644 { "owner", "Owner", UL_OWNER, '!' },
645 { "helper", "BUG:", UL_HELPER, 'X' }
648 static const struct {
651 unsigned short default_value;
652 unsigned int old_idx;
653 unsigned int old_flag;
654 unsigned short flag_value;
656 { "CSMSG_SET_GIVE_VOICE", "givevoice", 100, ~0, CHANNEL_VOICE_ALL, 0 },
657 { "CSMSG_SET_GIVE_OPS", "giveops", 200, 2, 0, 0 },
658 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
659 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
660 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
661 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
662 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
663 { "CSMSG_SET_CTCPUSERS", "ctcpusers", 0, 9, 0, 0 },
664 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES, 1 },
665 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE, 200 },
666 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF, 1 },
667 { "CSMSG_SET_VOTE", "vote", 100, ~0, 0, 0 }
670 struct charOptionValues {
673 } protectValues[] = {
674 { 'a', "CSMSG_PROTECT_ALL" },
675 { 'e', "CSMSG_PROTECT_EQUAL" },
676 { 'l', "CSMSG_PROTECT_LOWER" },
677 { 'n', "CSMSG_PROTECT_NONE" }
679 { 'd', "CSMSG_TOYS_DISABLED" },
680 { 'n', "CSMSG_TOYS_PRIVATE" },
681 { 'p', "CSMSG_TOYS_PUBLIC" }
682 }, topicRefreshValues[] = {
683 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
684 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
685 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
686 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
687 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
688 }, ctcpReactionValues[] = {
689 { 'k', "CSMSG_CTCPREACTION_KICK" },
690 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
691 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
692 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
695 static const struct {
699 unsigned int old_idx;
701 struct charOptionValues *values;
703 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues), protectValues },
704 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues), toysValues },
705 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues), topicRefreshValues },
706 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 't', 10, ArrayLength(ctcpReactionValues), ctcpReactionValues }
709 struct userData *helperList;
710 struct chanData *channelList;
711 static struct module *chanserv_module;
712 static unsigned int userCount;
714 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
715 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
716 static void unregister_channel(struct chanData *channel, const char *reason);
719 user_level_from_name(const char *name, unsigned short clamp_level)
721 unsigned int level = 0, ii;
723 level = strtoul(name, NULL, 10);
724 else for(ii = 0; (ii < ArrayLength(accessLevels)) && !level; ++ii)
725 if(!irccasecmp(name, accessLevels[ii].name))
726 level = accessLevels[ii].level;
727 if(level > clamp_level)
733 parse_level_range(unsigned short *minl, unsigned short *maxl, const char *arg)
736 *minl = strtoul(arg, &sep, 10);
744 *maxl = strtoul(sep+1, &sep, 10);
752 _GetChannelUser(struct chanData *channel, struct handle_info *handle, int override, int allow_suspended)
754 struct userData *uData, **head;
756 if(!channel || !handle)
759 if(override && HANDLE_FLAGGED(handle, HELPING)
760 && ((handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel)))
762 for(uData = helperList;
763 uData && uData->handle != handle;
764 uData = uData->next);
768 uData = calloc(1, sizeof(struct userData));
769 uData->handle = handle;
771 uData->access = UL_HELPER;
777 uData->next = helperList;
779 helperList->prev = uData;
787 for(uData = channel->users; uData; uData = uData->next)
788 if((uData->handle == handle) && (allow_suspended || !IsUserSuspended(uData)))
791 head = &(channel->users);
794 if(uData && (uData != *head))
796 /* Shuffle the user to the head of whatever list he was in. */
798 uData->next->prev = uData->prev;
800 uData->prev->next = uData->next;
806 (**head).prev = uData;
813 /* Returns non-zero if user has at least the minimum access.
814 * exempt_owner is set when handling !set, so the owner can set things
817 int check_user_level(struct chanNode *channel, struct userNode *user, enum levelOption opt, int allow_override, int exempt_owner)
819 struct userData *uData;
820 struct chanData *cData = channel->channel_info;
821 unsigned short minimum = cData->lvlOpts[opt];
824 uData = _GetChannelUser(cData, user->handle_info, allow_override, 0);
827 if(minimum <= uData->access)
829 if((minimum > UL_OWNER) && (uData->access == UL_OWNER) && exempt_owner)
834 /* Scan for other users authenticated to the same handle
835 still in the channel. If so, keep them listed as present.
837 user is optional, if not null, it skips checking that userNode
838 (for the handle_part function) */
840 scan_user_presence(struct userData *uData, struct userNode *user)
844 if(IsSuspended(uData->channel)
845 || IsUserSuspended(uData)
846 || !(mn = find_handle_in_channel(uData->channel->channel, uData->handle, user)))
858 chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, const char *text, UNUSED_ARG(struct userNode *bot), UNUSED_ARG(unsigned int is_notice))
860 unsigned int eflags, argc;
862 static char *bad_ctcp_reason = "CTCPs to this channel are forbidden.";
864 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
865 if(!channel->channel_info
866 || IsSuspended(channel->channel_info)
868 || !ircncasecmp(text, "ACTION ", 7))
870 /* Figure out the minimum level needed to CTCP the channel */
871 if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
873 /* We need to enforce against them; do so. */
875 argv[0] = (char*)text;
876 argv[1] = user->nick;
878 if(GetUserMode(channel, user))
879 eflags |= ACTION_KICK;
880 switch(channel->channel_info->chOpts[chCTCPReaction]) {
881 default: case 'k': /* just do the kick */ break;
883 eflags |= ACTION_BAN;
886 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
887 argv[argc++] = (char*)chanserv_conf.ctcp_short_ban_duration;
890 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
891 argv[argc++] = (char*)chanserv_conf.ctcp_long_ban_duration;
894 argv[argc++] = bad_ctcp_reason;
895 eject_user(chanserv, channel, argc, argv, NULL, eflags);
899 chanserv_create_note_type(const char *name)
901 struct note_type *ntype = calloc(1, sizeof(*ntype) + strlen(name));
902 strcpy(ntype->name, name);
904 dict_insert(note_types, ntype->name, ntype);
909 free_vote_options(void *data)
911 struct vote_option *vOpt = data;
913 free(vOpt->option_str);
918 chanserv_deref_note_type(void *data)
920 struct note_type *ntype = data;
922 if(--ntype->refs > 0)
928 chanserv_flush_note_type(struct note_type *ntype)
930 struct chanData *cData;
931 for(cData = channelList; cData; cData = cData->next)
932 dict_remove(cData->notes, ntype->name);
936 chanserv_truncate_notes(struct note_type *ntype)
938 struct chanData *cData;
940 unsigned int size = sizeof(*note) + ntype->max_length;
942 for(cData = channelList; cData; cData = cData->next) {
943 note = dict_find(cData->notes, ntype->name, NULL);
946 if(strlen(note->note) <= ntype->max_length)
948 dict_remove2(cData->notes, ntype->name, 1);
949 note = realloc(note, size);
950 note->note[ntype->max_length] = 0;
951 dict_insert(cData->notes, ntype->name, note);
955 static int note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user);
958 chanserv_add_channel_note(struct chanData *channel, struct note_type *type, const char *setter, const char *text)
961 unsigned int len = strlen(text);
963 if(len > type->max_length) len = type->max_length;
964 note = calloc(1, sizeof(*note) + len);
966 strncpy(note->setter, setter, sizeof(note->setter)-1);
967 memcpy(note->note, text, len);
969 dict_insert(channel->notes, type->name, note);
975 chanserv_free_note(void *data)
977 struct note *note = data;
979 chanserv_deref_note_type(note->type);
980 assert(note->type->refs > 0); /* must use delnote to remove the type */
984 static MODCMD_FUNC(cmd_createnote) {
985 struct note_type *ntype;
986 unsigned int arg = 1, existed = 0, max_length;
988 if((ntype = dict_find(note_types, argv[1], NULL)))
991 ntype = chanserv_create_note_type(argv[arg]);
992 if(!irccasecmp(argv[++arg], "privileged"))
995 ntype->set_access_type = NOTE_SET_PRIVILEGED;
996 ntype->set_access.min_opserv = strtoul(argv[arg], NULL, 0);
998 else if(!irccasecmp(argv[arg], "channel"))
1000 unsigned short ulvl = user_level_from_name(argv[++arg], UL_OWNER);
1003 reply("CSMSG_INVALID_ACCESS", argv[arg]);
1006 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
1007 ntype->set_access.min_ulevel = ulvl;
1009 else if(!irccasecmp(argv[arg], "setter"))
1011 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
1015 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
1019 if(!irccasecmp(argv[++arg], "privileged"))
1020 ntype->visible_type = NOTE_VIS_PRIVILEGED;
1021 else if(!irccasecmp(argv[arg], "channel_users"))
1022 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
1023 else if(!irccasecmp(argv[arg], "all"))
1024 ntype->visible_type = NOTE_VIS_ALL;
1026 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
1030 if((arg+1) >= argc) {
1031 reply("MSG_MISSING_PARAMS", argv[0]);
1034 max_length = strtoul(argv[++arg], NULL, 0);
1035 if(max_length < 20 || max_length > 450)
1037 reply("CSMSG_BAD_MAX_LENGTH", argv[arg]);
1040 if(existed && (max_length < ntype->max_length))
1042 ntype->max_length = max_length;
1043 chanserv_truncate_notes(ntype);
1045 ntype->max_length = max_length;
1048 reply("CSMSG_NOTE_MODIFIED", ntype->name);
1050 reply("CSMSG_NOTE_CREATED", ntype->name);
1055 dict_remove(note_types, ntype->name);
1059 static MODCMD_FUNC(cmd_removenote) {
1060 struct note_type *ntype;
1063 ntype = dict_find(note_types, argv[1], NULL);
1064 force = (argc > 2) && !irccasecmp(argv[2], "force");
1067 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
1074 reply("CSMSG_NOTE_TYPE_USED", ntype->name);
1077 chanserv_flush_note_type(ntype);
1079 dict_remove(note_types, argv[1]);
1080 reply("CSMSG_NOTE_DELETED", argv[1]);
1085 chanserv_expire_channel(void *data)
1087 struct chanData *channel = data;
1088 char reason[MAXLEN];
1089 sprintf(reason, "channel expired.");
1090 channel->expiry = 0;
1091 spamserv_cs_unregister(NULL, channel->channel, expire, NULL);
1092 unregister_channel(channel, reason);
1095 static MODCMD_FUNC(chan_opt_expire)
1097 struct chanData *cData = channel->channel_info;
1098 unsigned long value = cData->expiry;
1102 if((!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
1104 reply("MSG_SETTING_PRIVILEGED", argv[0]);
1107 unsigned long expiry,duration;
1109 /* The two directions can have different ACLs. */
1110 if(!strcmp(argv[1], "0"))
1112 else if((duration = ParseInterval(argv[1])))
1113 expiry = now + duration;
1116 reply("MSG_INVALID_DURATION", argv[1]);
1120 if (expiry != value)
1124 timeq_del(value, chanserv_expire_channel, cData, 0);
1127 cData->expiry = value;
1130 timeq_add(expiry, chanserv_expire_channel, cData);
1135 if(cData->expiry > now) {
1136 char expirestr[INTERVALLEN];
1137 reply("CSMSG_SET_EXPIRE", intervalString(expirestr, cData->expiry - now, user->handle_info));
1139 reply("CSMSG_SET_EXPIRE_OFF");
1144 mode_lock_violated(const struct mod_chanmode *orig, const struct mod_chanmode *change)
1148 if(orig->modes_set & change->modes_clear)
1150 if(orig->modes_clear & change->modes_set)
1152 if((orig->modes_set & MODE_KEY) && (change->modes_set & MODE_KEY)
1153 && strcmp(orig->new_key, change->new_key))
1155 if((orig->modes_set & MODE_LIMIT) && (change->modes_set & MODE_LIMIT)
1156 && (orig->new_limit != change->new_limit))
1161 static char max_length_text[MAXLEN+1][16];
1163 static struct helpfile_expansion
1164 chanserv_expand_variable(const char *variable)
1166 struct helpfile_expansion exp;
1168 if(!irccasecmp(variable, "notes"))
1171 exp.type = HF_TABLE;
1172 exp.value.table.length = 1;
1173 exp.value.table.width = 3;
1174 exp.value.table.flags = 0;
1175 exp.value.table.contents = calloc(dict_size(note_types)+1, sizeof(char**));
1176 exp.value.table.contents[0] = calloc(exp.value.table.width, sizeof(char*));
1177 exp.value.table.contents[0][0] = "Note Type";
1178 exp.value.table.contents[0][1] = "Visibility";
1179 exp.value.table.contents[0][2] = "Max Length";
1180 for(it=dict_first(note_types); it; it=iter_next(it))
1182 struct note_type *ntype = iter_data(it);
1185 if(!note_type_visible_to_user(NULL, ntype, message_dest)) continue;
1186 row = exp.value.table.length++;
1187 exp.value.table.contents[row] = calloc(exp.value.table.width, sizeof(char*));
1188 exp.value.table.contents[row][0] = ntype->name;
1189 exp.value.table.contents[row][1] = (ntype->visible_type == NOTE_VIS_ALL) ? "all" :
1190 (ntype->visible_type == NOTE_VIS_CHANNEL_USERS) ? "chan users" :
1192 if(!max_length_text[ntype->max_length][0])
1193 snprintf(max_length_text[ntype->max_length], sizeof(max_length_text[ntype->max_length]), "%u", ntype->max_length);
1194 exp.value.table.contents[row][2] = max_length_text[ntype->max_length];
1199 exp.type = HF_STRING;
1200 exp.value.str = NULL;
1204 static struct chanData*
1205 register_channel(struct chanNode *cNode, char *registrar)
1207 struct chanData *channel;
1208 enum levelOption lvlOpt;
1209 enum charOption chOpt;
1211 channel = calloc(1, sizeof(struct chanData));
1213 channel->notes = dict_new();
1214 dict_set_free_data(channel->notes, chanserv_free_note);
1216 channel->registrar = strdup(registrar);
1217 channel->registered = now;
1218 channel->visited = now;
1219 channel->limitAdjusted = now;
1220 channel->ownerTransfer = now;
1221 channel->flags = CHANNEL_DEFAULT_FLAGS;
1222 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
1223 channel->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
1224 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
1225 channel->chOpts[chOpt] = charOptions[chOpt].default_value;
1227 channel->prev = NULL;
1228 channel->next = channelList;
1231 channelList->prev = channel;
1232 channelList = channel;
1233 registered_channels++;
1235 channel->channel = cNode;
1237 cNode->channel_info = channel;
1239 channel->vote = NULL;
1244 static struct userData*
1245 add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned short access_level, unsigned long seen, const char *info)
1247 struct userData *ud;
1249 if(access_level > UL_OWNER)
1252 ud = calloc(1, sizeof(*ud));
1253 ud->channel = channel;
1254 ud->handle = handle;
1256 ud->access = access_level;
1257 ud->info = info ? strdup(info) : NULL;
1260 ud->next = channel->users;
1262 channel->users->prev = ud;
1263 channel->users = ud;
1265 channel->userCount++;
1269 ud->u_next = ud->handle->channels;
1271 ud->u_next->u_prev = ud;
1272 ud->handle->channels = ud;
1278 del_channel_user(struct userData *user, int do_gc)
1280 struct chanData *channel = user->channel;
1282 channel->userCount--;
1286 user->prev->next = user->next;
1288 channel->users = user->next;
1290 user->next->prev = user->prev;
1293 user->u_prev->u_next = user->u_next;
1295 user->handle->channels = user->u_next;
1297 user->u_next->u_prev = user->u_prev;
1301 if(do_gc && !channel->users && !IsProtected(channel)) {
1302 spamserv_cs_unregister(NULL, channel->channel, lost_all_users, NULL);
1303 unregister_channel(channel, "lost all users.");
1307 static void expire_ban(void *data);
1310 add_channel_ban(struct chanData *channel, const char *mask, char *owner, unsigned long set, unsigned long triggered, unsigned long expires, char *reason)
1313 unsigned int ii, l1, l2;
1318 bd = malloc(sizeof(struct banData));
1320 bd->channel = channel;
1322 bd->triggered = triggered;
1323 bd->expires = expires;
1325 for(ii = 0; ii < chanserv_conf.old_ban_names->used; ++ii)
1327 extern const char *hidden_host_suffix;
1328 const char *old_name = chanserv_conf.old_ban_names->list[ii];
1332 l2 = strlen(old_name);
1335 if(irccasecmp(mask + l1 - l2, old_name))
1337 new_mask = alloca(MAXLEN);
1338 sprintf(new_mask, "%.*s%s", (int)(l1-l2), mask, hidden_host_suffix);
1341 safestrncpy(bd->mask, mask, sizeof(bd->mask));
1343 safestrncpy(bd->owner, owner, sizeof(bd->owner));
1344 bd->reason = strdup(reason);
1347 timeq_add(expires, expire_ban, bd);
1350 bd->next = channel->bans;
1352 channel->bans->prev = bd;
1354 channel->banCount++;
1361 del_channel_ban(struct banData *ban)
1363 ban->channel->banCount--;
1367 ban->prev->next = ban->next;
1369 ban->channel->bans = ban->next;
1372 ban->next->prev = ban->prev;
1375 timeq_del(0, expire_ban, ban, TIMEQ_IGNORE_WHEN);
1384 expire_ban(void *data)
1386 struct banData *bd = data;
1387 if(!IsSuspended(bd->channel))
1389 struct banList bans;
1390 struct mod_chanmode change;
1392 bans = bd->channel->channel->banlist;
1393 mod_chanmode_init(&change);
1394 for(ii=0; ii<bans.used; ii++)
1396 if(!strcmp(bans.list[ii]->ban, bd->mask))
1399 change.args[0].mode = MODE_REMOVE|MODE_BAN;
1400 change.args[0].u.hostmask = bd->mask;
1401 mod_chanmode_announce(chanserv, bd->channel->channel, &change);
1407 del_channel_ban(bd);
1410 static void chanserv_expire_suspension(void *data);
1413 unregister_channel(struct chanData *channel, const char *reason)
1415 struct mod_chanmode change;
1416 char msgbuf[MAXLEN];
1418 /* After channel unregistration, the following must be cleaned
1420 - Channel information.
1423 - Channel suspension data.
1424 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1430 timeq_del(0, NULL, channel, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
1434 mod_chanmode_init(&change);
1435 change.modes_clear |= MODE_REGISTERED;
1436 mod_chanmode_announce(chanserv, channel->channel, &change);
1439 while(channel->users)
1440 del_channel_user(channel->users, 0);
1442 while(channel->bans)
1443 del_channel_ban(channel->bans);
1445 free(channel->topic);
1446 free(channel->registrar);
1447 free(channel->greeting);
1448 free(channel->user_greeting);
1449 free(channel->topic_mask);
1452 channel->prev->next = channel->next;
1454 channelList = channel->next;
1457 channel->next->prev = channel->prev;
1459 if(channel->suspended)
1461 struct chanNode *cNode = channel->channel;
1462 struct suspended *suspended, *next_suspended;
1464 for(suspended = channel->suspended; suspended; suspended = next_suspended)
1466 next_suspended = suspended->previous;
1467 free(suspended->suspender);
1468 free(suspended->reason);
1469 if(suspended->expires)
1470 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
1475 cNode->channel_info = NULL;
1478 timeq_del(channel->expiry, chanserv_expire_channel, channel, 0);
1479 channel->channel->channel_info = NULL;
1481 dict_delete(channel->notes);
1482 sprintf(msgbuf, "%s %s", channel->channel->name, reason);
1483 if(!IsSuspended(channel))
1484 DelChannelUser(chanserv, channel->channel, msgbuf, 0);
1485 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, msgbuf);
1486 UnlockChannel(channel->channel);
1488 registered_channels--;
1492 expire_channels(UNUSED_ARG(void *data))
1494 struct chanData *channel, *next;
1495 struct userData *user;
1496 char delay[INTERVALLEN], reason[INTERVALLEN + 64];
1498 intervalString(delay, chanserv_conf.channel_expire_delay, NULL);
1499 sprintf(reason, "Channel registration automatically expired after %s of disuse.", delay);
1501 for(channel = channelList; channel; channel = next)
1503 next = channel->next;
1505 /* See if the channel can be expired. */
1506 if(((now - channel->visited) <= chanserv_conf.channel_expire_delay)
1507 || IsProtected(channel))
1510 /* Make sure there are no high-ranking users still in the channel. */
1511 for(user=channel->users; user; user=user->next)
1512 if(user->present && (user->access >= UL_PRESENT) && !HANDLE_FLAGGED(user->handle, BOT))
1517 /* Unregister the channel */
1518 log_module(CS_LOG, LOG_INFO, "(%s) Channel registration expired.", channel->channel->name);
1519 spamserv_cs_unregister(NULL, channel->channel, expire, NULL);
1520 unregister_channel(channel, "registration expired.");
1523 if(chanserv_conf.channel_expire_frequency)
1524 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
1528 expire_dnrs(UNUSED_ARG(void *data))
1530 dict_iterator_t it, next;
1531 struct do_not_register *dnr;
1533 for(it = dict_first(handle_dnrs); it; it = next)
1535 dnr = iter_data(it);
1536 next = iter_next(it);
1537 if(dnr->expires && dnr->expires <= now)
1538 dict_remove(handle_dnrs, dnr->chan_name + 1);
1540 for(it = dict_first(plain_dnrs); it; it = next)
1542 dnr = iter_data(it);
1543 next = iter_next(it);
1544 if(dnr->expires && dnr->expires <= now)
1545 dict_remove(plain_dnrs, dnr->chan_name + 1);
1547 for(it = dict_first(mask_dnrs); it; it = next)
1549 dnr = iter_data(it);
1550 next = iter_next(it);
1551 if(dnr->expires && dnr->expires <= now)
1552 dict_remove(mask_dnrs, dnr->chan_name + 1);
1555 if(chanserv_conf.dnr_expire_frequency)
1556 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
1560 protect_user(const struct userNode *victim, const struct userNode *aggressor, struct chanData *channel)
1562 char protect = channel->chOpts[chProtect];
1563 struct userData *cs_victim, *cs_aggressor;
1565 /* Don't protect if no one is to be protected, someone is attacking
1566 himself, or if the aggressor is an IRC Operator. */
1567 if(protect == 'n' || victim == aggressor || IsOper(aggressor))
1570 /* Don't protect if the victim isn't authenticated (because they
1571 can't be a channel user), unless we are to protect non-users
1573 cs_victim = GetChannelAccess(channel, victim->handle_info);
1574 if(protect != 'a' && !cs_victim)
1577 /* Protect if the aggressor isn't a user because at this point,
1578 the aggressor can only be less than or equal to the victim. */
1579 cs_aggressor = GetChannelAccess(channel, aggressor->handle_info);
1583 /* If the aggressor was a user, then the victim can't be helped. */
1590 if(cs_victim->access > cs_aggressor->access)
1595 if(cs_victim->access >= cs_aggressor->access)
1604 validate_op(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1606 struct chanData *cData = channel->channel_info;
1607 struct userData *cs_victim;
1609 if((!(cs_victim = GetChannelUser(cData, victim->handle_info))
1610 || (cs_victim->access < cData->lvlOpts[lvlGiveOps]))
1611 && !check_user_level(channel, user, lvlEnfOps, 0, 0))
1613 send_message(user, chanserv, "CSMSG_OPBY_LOCKED");
1621 validate_deop(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1623 if(IsService(victim))
1625 send_message(user, chanserv, "MSG_SERVICE_IMMUNE", victim->nick);
1629 if(protect_user(victim, user, channel->channel_info))
1631 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
1638 static struct do_not_register *
1639 chanserv_add_dnr(const char *chan_name, const char *setter, unsigned long expires, const char *reason)
1641 struct do_not_register *dnr = calloc(1, sizeof(*dnr)+strlen(reason));
1642 safestrncpy(dnr->chan_name, chan_name, sizeof(dnr->chan_name));
1643 safestrncpy(dnr->setter, setter, sizeof(dnr->setter));
1644 strcpy(dnr->reason, reason);
1646 dnr->expires = expires;
1647 if(dnr->chan_name[0] == '*')
1648 dict_insert(handle_dnrs, dnr->chan_name+1, dnr);
1649 else if(strpbrk(dnr->chan_name, "*?"))
1650 dict_insert(mask_dnrs, dnr->chan_name, dnr);
1652 dict_insert(plain_dnrs, dnr->chan_name, dnr);
1656 static struct dnrList
1657 chanserv_find_dnrs(const char *chan_name, const char *handle, unsigned int max)
1659 struct dnrList list;
1660 dict_iterator_t it, next;
1661 struct do_not_register *dnr;
1663 dnrList_init(&list);
1665 if(handle && (dnr = dict_find(handle_dnrs, handle, NULL)))
1667 if(dnr->expires && dnr->expires <= now)
1668 dict_remove(handle_dnrs, handle);
1669 else if(list.used < max)
1670 dnrList_append(&list, dnr);
1673 if(chan_name && (dnr = dict_find(plain_dnrs, chan_name, NULL)))
1675 if(dnr->expires && dnr->expires <= now)
1676 dict_remove(plain_dnrs, chan_name);
1677 else if(list.used < max)
1678 dnrList_append(&list, dnr);
1683 for(it = dict_first(mask_dnrs); it && list.used < max; it = next)
1685 next = iter_next(it);
1686 if(!match_ircglob(chan_name, iter_key(it)))
1688 dnr = iter_data(it);
1689 if(dnr->expires && dnr->expires <= now)
1690 dict_remove(mask_dnrs, iter_key(it));
1692 dnrList_append(&list, dnr);
1699 static int dnr_print_func(struct do_not_register *dnr, void *extra)
1701 struct userNode *user;
1702 char buf1[INTERVALLEN];
1703 char buf2[INTERVALLEN];
1710 strftime(buf1, sizeof(buf1), "%d %b %Y", localtime(&feh));
1715 strftime(buf2, sizeof(buf2), "%d %b %Y", localtime(&feh));
1716 send_message(user, chanserv, "CSMSG_DNR_INFO_SET_EXPIRES", dnr->chan_name, buf1, dnr->setter, buf2, dnr->reason);
1720 send_message(user, chanserv, "CSMSG_DNR_INFO_SET", dnr->chan_name, buf1, dnr->setter, dnr->reason);
1723 send_message(user, chanserv, "CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1728 chanserv_show_dnrs(struct userNode *user, struct svccmd *cmd, const char *chan_name, const char *handle)
1730 struct dnrList list;
1733 list = chanserv_find_dnrs(chan_name, handle, UINT_MAX);
1734 for(ii = 0; (ii < list.used) && (ii < 10); ++ii)
1735 dnr_print_func(list.list[ii], user);
1737 reply("CSMSG_MORE_DNRS", list.used - ii);
1742 struct do_not_register *
1743 chanserv_is_dnr(const char *chan_name, struct handle_info *handle)
1745 struct dnrList list;
1746 struct do_not_register *dnr;
1748 list = chanserv_find_dnrs(chan_name, handle ? handle->handle : NULL, 1);
1749 dnr = list.used ? list.list[0] : NULL;
1754 static unsigned int send_dnrs(struct userNode *user, dict_t dict)
1756 struct do_not_register *dnr;
1757 dict_iterator_t it, next;
1758 unsigned int matches = 0;
1760 for(it = dict_first(dict); it; it = next)
1762 dnr = iter_data(it);
1763 next = iter_next(it);
1764 if(dnr->expires && dnr->expires <= now)
1766 dict_remove(dict, iter_key(it));
1769 dnr_print_func(dnr, user);
1776 static CHANSERV_FUNC(cmd_noregister)
1780 unsigned long expiry, duration;
1781 unsigned int matches;
1785 reply("CSMSG_DNR_SEARCH_RESULTS");
1786 matches = send_dnrs(user, handle_dnrs);
1787 matches += send_dnrs(user, plain_dnrs);
1788 matches += send_dnrs(user, mask_dnrs);
1790 reply("MSG_MATCH_COUNT", matches);
1792 reply("MSG_NO_MATCHES");
1798 if(!IsChannelName(target) && (*target != '*'))
1800 reply("CSMSG_NOT_DNR", target);
1808 reply("MSG_INVALID_DURATION", argv[2]);
1812 if(!strcmp(argv[2], "0"))
1814 else if((duration = ParseInterval(argv[2])))
1815 expiry = now + duration;
1818 reply("MSG_INVALID_DURATION", argv[2]);
1822 reason = unsplit_string(argv + 3, argc - 3, NULL);
1823 if((*target == '*') && !get_handle_info(target + 1))
1825 reply("MSG_HANDLE_UNKNOWN", target + 1);
1828 chanserv_add_dnr(target, user->handle_info->handle, expiry, reason);
1829 reply("CSMSG_NOREGISTER_CHANNEL", target);
1833 reply("CSMSG_DNR_SEARCH_RESULTS");
1835 matches = chanserv_show_dnrs(user, cmd, NULL, target + 1);
1837 matches = chanserv_show_dnrs(user, cmd, target, NULL);
1839 reply("MSG_NO_MATCHES");
1843 static CHANSERV_FUNC(cmd_allowregister)
1845 const char *chan_name = argv[1];
1847 if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
1848 || dict_remove(plain_dnrs, chan_name)
1849 || dict_remove(mask_dnrs, chan_name))
1851 reply("CSMSG_DNR_REMOVED", chan_name);
1854 reply("CSMSG_NO_SUCH_DNR", chan_name);
1859 struct userNode *source;
1863 unsigned long min_set, max_set;
1864 unsigned long min_expires, max_expires;
1869 dnr_search_matches(const struct do_not_register *dnr, const struct dnr_search *search)
1871 return !((dnr->set < search->min_set)
1872 || (dnr->set > search->max_set)
1873 || (dnr->expires < search->min_expires)
1874 || (search->max_expires
1875 && ((dnr->expires == 0)
1876 || (dnr->expires > search->max_expires)))
1877 || (search->chan_mask
1878 && !match_ircglob(dnr->chan_name, search->chan_mask))
1879 || (search->setter_mask
1880 && !match_ircglob(dnr->setter, search->setter_mask))
1881 || (search->reason_mask
1882 && !match_ircglob(dnr->reason, search->reason_mask)));
1885 static struct dnr_search *
1886 dnr_search_create(struct userNode *user, struct svccmd *cmd, unsigned int argc, char *argv[])
1888 struct dnr_search *discrim;
1891 discrim = calloc(1, sizeof(*discrim));
1892 discrim->source = user;
1893 discrim->chan_mask = NULL;
1894 discrim->setter_mask = NULL;
1895 discrim->reason_mask = NULL;
1896 discrim->max_set = INT_MAX;
1897 discrim->limit = 50;
1899 for(ii=0; ii<argc; ++ii)
1903 reply("MSG_MISSING_PARAMS", argv[ii]);
1906 else if(0 == irccasecmp(argv[ii], "channel"))
1908 discrim->chan_mask = argv[++ii];
1910 else if(0 == irccasecmp(argv[ii], "setter"))
1912 discrim->setter_mask = argv[++ii];
1914 else if(0 == irccasecmp(argv[ii], "reason"))
1916 discrim->reason_mask = argv[++ii];
1918 else if(0 == irccasecmp(argv[ii], "limit"))
1920 discrim->limit = strtoul(argv[++ii], NULL, 0);
1922 else if(0 == irccasecmp(argv[ii], "set"))
1924 const char *cmp = argv[++ii];
1927 discrim->min_set = now - ParseInterval(cmp + 2);
1929 discrim->min_set = now - (ParseInterval(cmp + 1) - 1);
1930 } else if(cmp[0] == '=') {
1931 discrim->min_set = discrim->max_set = now - ParseInterval(cmp + 1);
1932 } else if(cmp[0] == '>') {
1934 discrim->max_set = now - ParseInterval(cmp + 2);
1936 discrim->max_set = now - (ParseInterval(cmp + 1) - 1);
1938 discrim->max_set = now - (ParseInterval(cmp) - 1);
1941 else if(0 == irccasecmp(argv[ii], "expires"))
1943 const char *cmp = argv[++ii];
1946 discrim->max_expires = now + ParseInterval(cmp + 2);
1948 discrim->max_expires = now + (ParseInterval(cmp + 1) - 1);
1949 } else if(cmp[0] == '=') {
1950 discrim->min_expires = discrim->max_expires = now + ParseInterval(cmp + 1);
1951 } else if(cmp[0] == '>') {
1953 discrim->min_expires = now + ParseInterval(cmp + 2);
1955 discrim->min_expires = now + (ParseInterval(cmp + 1) - 1);
1957 discrim->min_expires = now + (ParseInterval(cmp) - 1);
1962 reply("MSG_INVALID_CRITERIA", argv[ii]);
1973 typedef int (*dnr_search_func)(struct do_not_register *match, void *extra);
1976 dnr_search(struct dnr_search *discrim, dnr_search_func dsf, void *data)
1978 struct do_not_register *dnr;
1979 dict_iterator_t next;
1984 /* Initialize local variables. */
1987 if(discrim->chan_mask)
1989 int shift = (discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*') ? 2 : 0;
1990 if('\0' == discrim->chan_mask[shift + strcspn(discrim->chan_mask+shift, "*?")])
1994 if(target_fixed && discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*')
1996 /* Check against account-based DNRs. */
1997 dnr = dict_find(handle_dnrs, discrim->chan_mask + 2, NULL);
1998 if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
2001 else if(target_fixed)
2003 /* Check against channel-based DNRs. */
2004 dnr = dict_find(plain_dnrs, discrim->chan_mask, NULL);
2005 if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
2010 /* Exhaustively search account DNRs. */
2011 for(it = dict_first(handle_dnrs); it; it = next)
2013 next = iter_next(it);
2014 dnr = iter_data(it);
2015 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
2019 /* Do the same for channel DNRs. */
2020 for(it = dict_first(plain_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 wildcarded channel DNRs. */
2029 for(it = dict_first(mask_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))
2041 dnr_remove_func(struct do_not_register *match, void *extra)
2043 struct userNode *user;
2046 chan_name = alloca(strlen(match->chan_name) + 1);
2047 strcpy(chan_name, match->chan_name);
2049 if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
2050 || dict_remove(plain_dnrs, chan_name)
2051 || dict_remove(mask_dnrs, chan_name))
2053 send_message(user, chanserv, "CSMSG_DNR_REMOVED", chan_name);
2059 dnr_count_func(struct do_not_register *match, void *extra)
2061 return 0; (void)match; (void)extra;
2064 static MODCMD_FUNC(cmd_dnrsearch)
2066 struct dnr_search *discrim;
2067 dnr_search_func action;
2068 struct svccmd *subcmd;
2069 unsigned int matches;
2072 sprintf(buf, "dnrsearch %s", argv[1]);
2073 subcmd = dict_find(cmd->parent->commands, buf, NULL);
2076 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
2079 if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
2081 if(!irccasecmp(argv[1], "print"))
2082 action = dnr_print_func;
2083 else if(!irccasecmp(argv[1], "remove"))
2084 action = dnr_remove_func;
2085 else if(!irccasecmp(argv[1], "count"))
2086 action = dnr_count_func;
2089 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
2093 discrim = dnr_search_create(user, cmd, argc-2, argv+2);
2097 if(action == dnr_print_func)
2098 reply("CSMSG_DNR_SEARCH_RESULTS");
2099 matches = dnr_search(discrim, action, user);
2101 reply("MSG_MATCH_COUNT", matches);
2103 reply("MSG_NO_MATCHES");
2109 chanserv_get_owned_count(struct handle_info *hi)
2111 struct userData *cList;
2114 for(owned=0, cList=hi->channels; cList; cList=cList->u_next)
2115 if(cList->access == UL_OWNER)
2120 static CHANSERV_FUNC(cmd_register)
2122 struct handle_info *handle;
2123 struct chanData *cData;
2124 struct modeNode *mn;
2125 char reason[MAXLEN];
2127 unsigned int new_channel, force=0;
2128 struct do_not_register *dnr;
2132 if(channel->channel_info)
2134 reply("CSMSG_ALREADY_REGGED", channel->name);
2138 if(channel->bad_channel)
2140 reply("CSMSG_ILLEGAL_CHANNEL", channel->name);
2145 && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
2147 reply("CSMSG_MUST_BE_OPPED", channel->name);
2152 chan_name = channel->name;
2156 if((argc < 2) || !IsChannelName(argv[1]))
2158 reply("MSG_NOT_CHANNEL_NAME");
2162 if(opserv_bad_channel(argv[1]))
2164 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2169 chan_name = argv[1];
2172 if(argc >= (new_channel+2))
2174 if(!IsHelping(user))
2176 reply("CSMSG_PROXY_FORBIDDEN");
2180 if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
2182 force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
2183 dnr = chanserv_is_dnr(chan_name, handle);
2187 handle = user->handle_info;
2188 dnr = chanserv_is_dnr(chan_name, handle);
2192 if(!IsHelping(user))
2193 reply("CSMSG_DNR_CHANNEL", chan_name);
2195 chanserv_show_dnrs(user, cmd, chan_name, handle->handle);
2199 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2201 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
2206 channel = AddChannel(argv[1], now, NULL, NULL);
2208 cData = register_channel(channel, user->handle_info->handle);
2209 scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL), NULL);
2210 cData->modes = chanserv_conf.default_modes;
2212 cData->modes.modes_set |= MODE_REGISTERED;
2213 if (IsOffChannel(cData))
2215 mod_chanmode_announce(chanserv, channel, &cData->modes);
2219 struct mod_chanmode *change = mod_chanmode_dup(&cData->modes, 1);
2220 change->args[change->argc].mode = MODE_CHANOP;
2221 change->args[change->argc].u.member = AddChannelUser(chanserv, channel);
2223 mod_chanmode_announce(chanserv, channel, change);
2224 mod_chanmode_free(change);
2227 /* Initialize the channel's max user record. */
2228 cData->max = channel->members.used;
2229 cData->max_time = 0;
2231 if(handle != user->handle_info)
2232 reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
2234 reply("CSMSG_REG_SUCCESS", channel->name);
2236 sprintf(reason, "%s registered to %s by %s.", channel->name, handle->handle, user->handle_info->handle);
2237 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2242 make_confirmation_string(struct userData *uData)
2244 static char strbuf[16];
2249 for(src = uData->handle->handle; *src; )
2250 accum = accum * 31 + toupper(*src++);
2252 for(src = uData->channel->channel->name; *src; )
2253 accum = accum * 31 + toupper(*src++);
2254 sprintf(strbuf, "%08x", accum);
2258 static CHANSERV_FUNC(cmd_unregister)
2261 char reason[MAXLEN];
2262 struct chanData *cData;
2263 struct userData *uData;
2265 cData = channel->channel_info;
2268 reply("CSMSG_NOT_REGISTERED", channel->name);
2272 uData = GetChannelUser(cData, user->handle_info);
2273 if(!uData || (uData->access < UL_OWNER))
2275 reply("CSMSG_NO_ACCESS");
2279 if(IsProtected(cData))
2281 reply("CSMSG_UNREG_NODELETE", channel->name);
2285 if(!IsHelping(user))
2287 const char *confirm_string;
2288 if(IsSuspended(cData))
2290 reply("CSMSG_CHAN_SUSPENDED", channel->name, cData->suspended->reason);
2293 confirm_string = make_confirmation_string(uData);
2294 if((argc < 2) || strcmp(argv[1], confirm_string))
2296 reply("CSMSG_CONFIRM_UNREG", confirm_string);
2301 sprintf(reason, "unregistered by %s.", user->handle_info->handle);
2302 name = strdup(channel->name);
2303 unregister_channel(cData, reason);
2304 spamserv_cs_unregister(user, channel, manually, "unregistered");
2305 reply("CSMSG_UNREG_SUCCESS", name);
2311 ss_cs_join_channel(struct chanNode *channel, int spamserv_join)
2313 extern struct userNode *spamserv;
2314 struct mod_chanmode *change;
2316 if(spamserv && spamserv_join && get_chanInfo(channel->name))
2318 change = mod_chanmode_alloc(2);
2320 change->args[0].mode = MODE_CHANOP;
2321 change->args[0].u.member = AddChannelUser(chanserv, channel);
2322 change->args[1].mode = MODE_CHANOP;
2323 change->args[1].u.member = AddChannelUser(spamserv, channel);
2327 change = mod_chanmode_alloc(1);
2329 change->args[0].mode = MODE_CHANOP;
2330 change->args[0].u.member = AddChannelUser(chanserv, channel);
2333 mod_chanmode_announce(chanserv, channel, change);
2334 mod_chanmode_free(change);
2337 static CHANSERV_FUNC(cmd_move)
2339 struct mod_chanmode change;
2340 struct chanNode *target;
2341 struct modeNode *mn;
2342 struct userData *uData;
2343 char reason[MAXLEN];
2344 struct do_not_register *dnr;
2345 int chanserv_join = 0, spamserv_join;
2349 if(IsProtected(channel->channel_info))
2351 reply("CSMSG_MOVE_NODELETE", channel->name);
2355 if(!IsChannelName(argv[1]))
2357 reply("MSG_NOT_CHANNEL_NAME");
2361 if(opserv_bad_channel(argv[1]))
2363 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2367 if(!IsHelping(user) || (argc < 3) || irccasecmp(argv[2], "force"))
2369 for(uData = channel->channel_info->users; uData; uData = uData->next)
2371 if((uData->access == UL_OWNER) && (dnr = chanserv_is_dnr(argv[1], uData->handle)))
2373 if(!IsHelping(user))
2374 reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]);
2376 chanserv_show_dnrs(user, cmd, argv[1], uData->handle->handle);
2382 mod_chanmode_init(&change);
2383 if(!(target = GetChannel(argv[1])))
2385 target = AddChannel(argv[1], now, NULL, NULL);
2386 if(!IsSuspended(channel->channel_info))
2389 else if(target->channel_info)
2391 reply("CSMSG_ALREADY_REGGED", target->name);
2394 else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
2395 && !IsHelping(user))
2397 reply("CSMSG_MUST_BE_OPPED", target->name);
2400 else if(!IsSuspended(channel->channel_info))
2405 /* Clear MODE_REGISTERED from old channel, add it to new. */
2407 change.modes_clear = MODE_REGISTERED;
2408 mod_chanmode_announce(chanserv, channel, &change);
2409 change.modes_clear = 0;
2410 change.modes_set = MODE_REGISTERED;
2411 mod_chanmode_announce(chanserv, target, &change);
2414 /* Move the channel_info to the target channel; it
2415 shouldn't be necessary to clear timeq callbacks
2416 for the old channel. */
2417 target->channel_info = channel->channel_info;
2418 target->channel_info->channel = target;
2419 channel->channel_info = NULL;
2421 /* Check whether users are present in the new channel. */
2422 for(uData = target->channel_info->users; uData; uData = uData->next)
2423 scan_user_presence(uData, NULL);
2425 spamserv_join = spamserv_cs_move_merge(user, channel, target, 1);
2428 ss_cs_join_channel(target, spamserv_join);
2430 sprintf(reason, "%s moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
2431 if(!IsSuspended(target->channel_info))
2433 char reason2[MAXLEN];
2434 sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
2435 DelChannelUser(chanserv, channel, reason2, 0);
2437 UnlockChannel(channel);
2438 LockChannel(target);
2439 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2440 reply("CSMSG_MOVE_SUCCESS", target->name);
2445 merge_users(struct chanData *source, struct chanData *target)
2447 struct userData *suData, *tuData, *next;
2453 /* Insert the source's users into the scratch area. */
2454 for(suData = source->users; suData; suData = suData->next)
2455 dict_insert(merge, suData->handle->handle, suData);
2457 /* Iterate through the target's users, looking for
2458 users common to both channels. The lower access is
2459 removed from either the scratch area or target user
2461 for(tuData = target->users; tuData; tuData = next)
2463 struct userData *choice;
2465 next = tuData->next;
2467 /* If a source user exists with the same handle as a target
2468 channel's user, resolve the conflict by removing one. */
2469 suData = dict_find(merge, tuData->handle->handle, NULL);
2473 /* Pick the data we want to keep. */
2474 /* If the access is the same, use the later seen time. */
2475 if(suData->access == tuData->access)
2476 choice = (suData->seen > tuData->seen) ? suData : tuData;
2477 else /* Otherwise, keep the higher access level. */
2478 choice = (suData->access > tuData->access) ? suData : tuData;
2479 /* Use the later seen time. */
2480 if(suData->seen < tuData->seen)
2481 suData->seen = tuData->seen;
2483 tuData->seen = suData->seen;
2485 /* Remove the user that wasn't picked. */
2486 if(choice == tuData)
2488 dict_remove(merge, suData->handle->handle);
2489 del_channel_user(suData, 0);
2492 del_channel_user(tuData, 0);
2495 /* Move the remaining users to the target channel. */
2496 for(it = dict_first(merge); it; it = iter_next(it))
2498 suData = iter_data(it);
2500 /* Insert the user into the target channel's linked list. */
2501 suData->prev = NULL;
2502 suData->next = target->users;
2503 suData->channel = target;
2506 target->users->prev = suData;
2507 target->users = suData;
2509 /* Update the user counts for the target channel; the
2510 source counts are left alone. */
2511 target->userCount++;
2513 /* Check whether the user is in the target channel. */
2514 scan_user_presence(suData, NULL);
2517 /* Possible to assert (source->users == NULL) here. */
2518 source->users = NULL;
2523 merge_bans(struct chanData *source, struct chanData *target)
2525 struct banData *sbData, *tbData, *sNext, *tNext, *tFront;
2527 /* Hold on to the original head of the target ban list
2528 to avoid comparing source bans with source bans. */
2529 tFront = target->bans;
2531 /* Perform a totally expensive O(n*m) merge, ick. */
2532 for(sbData = source->bans; sbData; sbData = sNext)
2534 /* Flag to track whether the ban's been moved
2535 to the destination yet. */
2538 /* Possible to assert (sbData->prev == NULL) here. */
2539 sNext = sbData->next;
2541 for(tbData = tFront; tbData; tbData = tNext)
2543 tNext = tbData->next;
2545 /* Perform two comparisons between each source
2546 and target ban, conflicts are resolved by
2547 keeping the broader ban and copying the later
2548 expiration and triggered time. */
2549 if(match_ircglobs(tbData->mask, sbData->mask))
2551 /* There is a broader ban in the target channel that
2552 overrides one in the source channel; remove the
2553 source ban and break. */
2554 if(sbData->expires > tbData->expires)
2555 tbData->expires = sbData->expires;
2556 if(sbData->triggered > tbData->triggered)
2557 tbData->triggered = sbData->triggered;
2558 del_channel_ban(sbData);
2561 else if(match_ircglobs(sbData->mask, tbData->mask))
2563 /* There is a broader ban in the source channel that
2564 overrides one in the target channel; remove the
2565 target ban, fall through and move the source over. */
2566 if(tbData->expires > sbData->expires)
2567 sbData->expires = tbData->expires;
2568 if(tbData->triggered > sbData->triggered)
2569 sbData->triggered = tbData->triggered;
2570 if(tbData == tFront)
2572 del_channel_ban(tbData);
2575 /* Source bans can override multiple target bans, so
2576 we allow a source to run through this loop multiple
2577 times, but we can only move it once. */
2582 /* Remove the source ban from the source ban list. */
2584 sbData->next->prev = sbData->prev;
2586 /* Modify the source ban's associated channel. */
2587 sbData->channel = target;
2589 /* Insert the ban into the target channel's linked list. */
2590 sbData->prev = NULL;
2591 sbData->next = target->bans;
2594 target->bans->prev = sbData;
2595 target->bans = sbData;
2597 /* Update the user counts for the target channel. */
2602 /* Possible to assert (source->bans == NULL) here. */
2603 source->bans = NULL;
2607 merge_data(struct chanData *source, struct chanData *target)
2609 /* Use more recent visited and owner-transfer time; use older
2610 * registered time. Bitwise or may_opchan. Use higher max.
2611 * Do not touch last_refresh, ban count or user counts.
2613 if(source->visited > target->visited)
2614 target->visited = source->visited;
2615 if(source->registered < target->registered)
2616 target->registered = source->registered;
2617 if(source->ownerTransfer > target->ownerTransfer)
2618 target->ownerTransfer = source->ownerTransfer;
2619 if(source->may_opchan)
2620 target->may_opchan = 1;
2621 if(source->max > target->max) {
2622 target->max = source->max;
2623 target->max_time = source->max_time;
2628 merge_channel(struct chanData *source, struct chanData *target)
2630 merge_users(source, target);
2631 merge_bans(source, target);
2632 merge_data(source, target);
2635 static CHANSERV_FUNC(cmd_merge)
2637 struct userData *target_user;
2638 struct chanNode *target;
2639 char reason[MAXLEN];
2643 /* Make sure the target channel exists and is registered to the user
2644 performing the command. */
2645 if(!(target = GetChannel(argv[1])))
2647 reply("MSG_INVALID_CHANNEL");
2651 if(!target->channel_info)
2653 reply("CSMSG_NOT_REGISTERED", target->name);
2657 if(IsProtected(channel->channel_info))
2659 reply("CSMSG_MERGE_NODELETE");
2663 if(IsSuspended(target->channel_info))
2665 reply("CSMSG_MERGE_SUSPENDED");
2669 if(channel == target)
2671 reply("CSMSG_MERGE_SELF");
2675 target_user = GetChannelUser(target->channel_info, user->handle_info);
2676 if(!target_user || (target_user->access < UL_OWNER))
2678 reply("CSMSG_MERGE_NOT_OWNER");
2682 /* Merge the channel structures and associated data. */
2683 merge_channel(channel->channel_info, target->channel_info);
2684 spamserv_cs_move_merge(user, channel, target, 0);
2685 sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
2686 unregister_channel(channel->channel_info, reason);
2687 reply("CSMSG_MERGE_SUCCESS", target->name);
2691 static CHANSERV_FUNC(cmd_opchan)
2693 struct mod_chanmode change;
2694 if(!IsHelping(user) && !channel->channel_info->may_opchan)
2696 reply("CSMSG_ALREADY_OPCHANNED", channel->name);
2699 channel->channel_info->may_opchan = 0;
2700 mod_chanmode_init(&change);
2702 change.args[0].mode = MODE_CHANOP;
2703 change.args[0].u.member = GetUserMode(channel, chanserv);
2704 if(!change.args[0].u.member)
2706 reply("CSMSG_OUT_OF_CHANNEL", channel->name);
2709 mod_chanmode_announce(chanserv, channel, &change);
2710 reply("CSMSG_OPCHAN_DONE", channel->name);
2714 static CHANSERV_FUNC(cmd_adduser)
2716 struct userData *actee;
2717 struct userData *actor, *real_actor;
2718 struct handle_info *handle;
2719 unsigned short access_level, override = 0;
2723 if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
2725 reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
2729 access_level = user_level_from_name(argv[2], UL_OWNER);
2732 reply("CSMSG_INVALID_ACCESS", argv[2]);
2736 actor = GetChannelUser(channel->channel_info, user->handle_info);
2737 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2739 if(actor->access <= access_level)
2741 reply("CSMSG_NO_BUMP_ACCESS");
2745 /* Trying to add someone with equal/more access? */
2746 if (!real_actor || real_actor->access <= access_level)
2747 override = CMD_LOG_OVERRIDE;
2749 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2752 if((actee = GetTrueChannelAccess(channel->channel_info, handle)))
2754 reply("CSMSG_USER_EXISTS", handle->handle, channel->name, actee->access);
2758 actee = add_channel_user(channel->channel_info, handle, access_level, 0, NULL);
2759 scan_user_presence(actee, NULL);
2760 reply("CSMSG_ADDED_USER", handle->handle, channel->name, access_level);
2761 return 1 | override;
2764 static CHANSERV_FUNC(cmd_clvl)
2766 struct handle_info *handle;
2767 struct userData *victim;
2768 struct userData *actor, *real_actor;
2769 unsigned short new_access, override = 0;
2770 int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
2774 actor = GetChannelUser(channel->channel_info, user->handle_info);
2775 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2777 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2780 if(handle == user->handle_info && !privileged)
2782 reply("CSMSG_NO_SELF_CLVL");
2786 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2788 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2792 if(actor->access <= victim->access && !privileged)
2794 reply("MSG_USER_OUTRANKED", handle->handle);
2798 new_access = user_level_from_name(argv[2], UL_OWNER);
2802 reply("CSMSG_INVALID_ACCESS", argv[2]);
2806 if(new_access >= actor->access && !privileged)
2808 reply("CSMSG_NO_BUMP_ACCESS");
2812 /* Trying to clvl a equal/higher user? */
2813 if(!real_actor || (real_actor->access <= victim->access && handle != user->handle_info))
2814 override = CMD_LOG_OVERRIDE;
2815 /* Trying to clvl someone to equal/higher access? */
2816 if(!real_actor || new_access >= real_actor->access)
2817 override = CMD_LOG_OVERRIDE;
2818 /* Helpers clvling themselves get caught by the "clvl someone to equal/higher access" check.
2819 * If they lower their own access it's not a big problem.
2822 victim->access = new_access;
2823 reply("CSMSG_CHANGED_ACCESS", handle->handle, new_access, channel->name);
2824 return 1 | override;
2827 static CHANSERV_FUNC(cmd_deluser)
2829 struct handle_info *handle;
2830 struct userData *victim;
2831 struct userData *actor, *real_actor;
2832 unsigned short access_level, override = 0;
2837 actor = GetChannelUser(channel->channel_info, user->handle_info);
2838 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2840 if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
2843 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2845 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2851 access_level = user_level_from_name(argv[1], UL_OWNER);
2854 reply("CSMSG_INVALID_ACCESS", argv[1]);
2857 if(access_level != victim->access)
2859 reply("CSMSG_INCORRECT_ACCESS", handle->handle, victim->access, argv[1]);
2865 access_level = victim->access;
2868 if((actor->access <= victim->access) && !IsHelping(user))
2870 reply("MSG_USER_OUTRANKED", victim->handle->handle);
2874 /* If people delete themselves it is an override, but they
2875 * could've used deleteme so we don't log it as an override
2877 if(!real_actor || (real_actor->access <= victim->access && real_actor != victim))
2878 override = CMD_LOG_OVERRIDE;
2880 chan_name = strdup(channel->name);
2881 del_channel_user(victim, 1);
2882 reply("CSMSG_DELETED_USER", handle->handle, access_level, chan_name);
2884 return 1 | override;
2888 cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
2890 struct userData *actor, *real_actor, *uData, *next;
2891 unsigned int override = 0;
2893 actor = GetChannelUser(channel->channel_info, user->handle_info);
2894 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2896 if(min_access > max_access)
2898 reply("CSMSG_BAD_RANGE", min_access, max_access);
2902 if(actor->access <= max_access)
2904 reply("CSMSG_NO_ACCESS");
2908 if(!real_actor || real_actor->access <= max_access)
2909 override = CMD_LOG_OVERRIDE;
2911 for(uData = channel->channel_info->users; uData; uData = next)
2915 if((uData->access >= min_access)
2916 && (uData->access <= max_access)
2917 && match_ircglob(uData->handle->handle, mask))
2918 del_channel_user(uData, 1);
2921 reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
2922 return 1 | override;
2925 static CHANSERV_FUNC(cmd_mdelowner)
2927 return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
2930 static CHANSERV_FUNC(cmd_mdelcoowner)
2932 return cmd_mdel_user(user, channel, UL_COOWNER, UL_COOWNER, argv[1], cmd);
2935 static CHANSERV_FUNC(cmd_mdelmaster)
2937 return cmd_mdel_user(user, channel, UL_MASTER, UL_MASTER, argv[1], cmd);
2940 static CHANSERV_FUNC(cmd_mdelop)
2942 return cmd_mdel_user(user, channel, UL_OP, UL_OP, argv[1], cmd);
2945 static CHANSERV_FUNC(cmd_mdelpeon)
2947 return cmd_mdel_user(user, channel, UL_PEON, UL_PEON, argv[1], cmd);
2951 cmd_trim_bans(struct userNode *user, struct chanNode *channel, unsigned long duration)
2953 struct banData *bData, *next;
2954 char interval[INTERVALLEN];
2956 unsigned long limit;
2959 limit = now - duration;
2960 for(bData = channel->channel_info->bans; bData; bData = next)
2964 if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
2967 del_channel_ban(bData);
2971 intervalString(interval, duration, user->handle_info);
2972 send_message(user, chanserv, "CSMSG_TRIMMED_BANS", count, channel->name, interval);
2977 cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration, int vacation)
2979 struct userData *actor, *uData, *next;
2980 char interval[INTERVALLEN];
2982 unsigned long limit;
2984 actor = GetChannelAccess(channel->channel_info, user->handle_info);
2985 if(min_access > max_access)
2987 send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
2991 if(!actor || actor->access <= max_access)
2993 send_message(user, chanserv, "CSMSG_NO_ACCESS");
2998 limit = now - duration;
2999 for(uData = channel->channel_info->users; uData; uData = next)
3003 if((uData->seen > limit)
3005 || (HANDLE_FLAGGED(uData->handle, FROZEN) && !vacation))
3008 if(((uData->access >= min_access) && (uData->access <= max_access))
3009 || (!max_access && (uData->access < actor->access)))
3011 del_channel_user(uData, 1);
3019 max_access = (actor->access > UL_OWNER) ? UL_OWNER : (actor->access - 1);
3021 send_message(user, chanserv, "CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration, user->handle_info));
3025 static CHANSERV_FUNC(cmd_trim)
3027 unsigned long duration;
3028 unsigned short min_level, max_level;
3033 vacation = argc > 3 && !strcmp(argv[3], "vacation");
3034 duration = ParseInterval(argv[2]);
3037 reply("CSMSG_CANNOT_TRIM");
3041 if(!irccasecmp(argv[1], "bans"))
3043 cmd_trim_bans(user, channel, duration);
3046 else if(!irccasecmp(argv[1], "users"))
3048 cmd_trim_users(user, channel, 0, 0, duration, vacation);
3051 else if(parse_level_range(&min_level, &max_level, argv[1]))
3053 cmd_trim_users(user, channel, min_level, max_level, duration, vacation);
3056 else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
3058 cmd_trim_users(user, channel, min_level, min_level, duration, vacation);
3063 reply("CSMSG_INVALID_TRIM", argv[1]);
3068 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
3069 to the user. cmd_all takes advantage of this. */
3070 static CHANSERV_FUNC(cmd_up)
3072 struct mod_chanmode change;
3073 struct userData *uData;
3076 mod_chanmode_init(&change);
3078 change.args[0].u.member = GetUserMode(channel, user);
3079 if(!change.args[0].u.member)
3082 reply("MSG_CHANNEL_ABSENT", channel->name);
3086 uData = GetChannelAccess(channel->channel_info, user->handle_info);
3090 reply("CSMSG_GODMODE_UP", argv[0]);
3093 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveOps])
3095 change.args[0].mode = MODE_CHANOP;
3096 errmsg = "CSMSG_ALREADY_OPPED";
3098 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveVoice])
3100 change.args[0].mode = MODE_VOICE;
3101 errmsg = "CSMSG_ALREADY_VOICED";
3106 reply("CSMSG_NO_ACCESS");
3109 change.args[0].mode &= ~change.args[0].u.member->modes;
3110 if(!change.args[0].mode)
3113 reply(errmsg, channel->name);
3116 modcmd_chanmode_announce(&change);
3120 static CHANSERV_FUNC(cmd_down)
3122 struct mod_chanmode change;
3124 mod_chanmode_init(&change);
3126 change.args[0].u.member = GetUserMode(channel, user);
3127 if(!change.args[0].u.member)
3130 reply("MSG_CHANNEL_ABSENT", channel->name);
3134 if(!change.args[0].u.member->modes)
3137 reply("CSMSG_ALREADY_DOWN", channel->name);
3141 change.args[0].mode = MODE_REMOVE | change.args[0].u.member->modes;
3142 modcmd_chanmode_announce(&change);
3146 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)
3148 struct userData *cList;
3150 for(cList = user->handle_info->channels; cList; cList = cList->u_next)
3152 if(IsSuspended(cList->channel)
3153 || IsUserSuspended(cList)
3154 || !GetUserMode(cList->channel->channel, user))
3157 mcmd(user, cList->channel->channel, 0, NULL, cmd);
3163 static CHANSERV_FUNC(cmd_upall)
3165 return cmd_all(CSFUNC_ARGS, cmd_up);
3168 static CHANSERV_FUNC(cmd_downall)
3170 return cmd_all(CSFUNC_ARGS, cmd_down);
3173 typedef int validate_func_t(struct userNode *user, struct chanNode *channel, struct userNode *victim);
3174 typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
3177 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)
3179 unsigned int ii, valid;
3180 struct userNode *victim;
3181 struct mod_chanmode *change;
3183 change = mod_chanmode_alloc(argc - 1);
3185 for(ii=valid=0; ++ii < argc; )
3187 if(!(victim = GetUserH(argv[ii])))
3189 change->args[valid].mode = mode;
3190 change->args[valid].u.member = GetUserMode(channel, victim);
3191 if(!change->args[valid].u.member)
3193 if(validate && !validate(user, channel, victim))
3198 change->argc = valid;
3199 if(valid < (argc-1))
3200 reply("CSMSG_PROCESS_FAILED");
3203 modcmd_chanmode_announce(change);
3204 reply(action, channel->name);
3206 mod_chanmode_free(change);
3210 static CHANSERV_FUNC(cmd_op)
3212 return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
3215 static CHANSERV_FUNC(cmd_deop)
3217 return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
3220 static CHANSERV_FUNC(cmd_voice)
3222 return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
3225 static CHANSERV_FUNC(cmd_devoice)
3227 return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
3231 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, unsigned int *victimCount, struct modeNode **victims)
3237 for(ii=0; ii<channel->members.used; ii++)
3239 struct modeNode *mn = channel->members.list[ii];
3241 if(IsService(mn->user))
3244 if(!user_matches_glob(mn->user, ban, MATCH_USENICK | MATCH_VISIBLE))
3247 if(protect_user(mn->user, user, channel->channel_info))
3251 victims[(*victimCount)++] = mn;
3257 eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3259 struct userNode *victim;
3260 struct modeNode **victims;
3261 unsigned int offset, n, victimCount, duration = 0;
3262 char *reason = "Bye.", *ban, *name;
3263 char interval[INTERVALLEN];
3265 offset = (action & ACTION_ADD_TIMED_BAN) ? 3 : 2;
3266 REQUIRE_PARAMS(offset);
3269 reason = unsplit_string(argv + offset, argc - offset, NULL);
3270 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
3272 /* Truncate the reason to a length of TOPICLEN, as
3273 the ircd does; however, leave room for an ellipsis
3274 and the kicker's nick. */
3275 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
3279 if((victim = GetUserH(argv[1])))
3281 victims = alloca(sizeof(victims[0]));
3282 victims[0] = GetUserMode(channel, victim);
3283 /* XXX: The comparison with ACTION_KICK is just because all
3284 * other actions can work on users outside the channel, and we
3285 * want to allow those (e.g. unbans) in that case. If we add
3286 * some other ejection action for in-channel users, change
3288 victimCount = victims[0] ? 1 : 0;
3290 if(IsService(victim))
3292 reply("MSG_SERVICE_IMMUNE", victim->nick);
3296 if((action == ACTION_KICK) && !victimCount)
3298 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
3302 if(protect_user(victim, user, channel->channel_info))
3304 reply("CSMSG_USER_PROTECTED", victim->nick);
3308 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
3309 name = victim->nick;
3311 else if(!is_ircmask(argv[1]) && (*argv[1] == '*'))
3313 struct handle_info *hi;
3314 extern const char *titlehost_suffix;
3315 char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
3316 const char *accountname = argv[1] + 1;
3318 if(!(hi = get_handle_info(accountname)))
3320 reply("MSG_HANDLE_UNKNOWN", accountname);
3324 snprintf(banmask, sizeof(banmask), "*!*@%s.*.%s", hi->handle, titlehost_suffix);
3325 victims = alloca(sizeof(victims[0]) * channel->members.used);
3327 if(bad_channel_ban(channel, user, banmask, &victimCount, victims))
3329 reply("CSMSG_MASK_PROTECTED", banmask);
3333 if((action == ACTION_KICK) && (victimCount == 0))
3335 reply("CSMSG_NO_MATCHING_USERS", channel->name, banmask);
3339 name = ban = strdup(banmask);
3343 if(!is_ircmask(argv[1]))
3345 reply("MSG_NICK_UNKNOWN", argv[1]);
3349 victims = alloca(sizeof(victims[0]) * channel->members.used);
3351 if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
3353 reply("CSMSG_MASK_PROTECTED", argv[1]);
3357 if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
3359 reply("CSMSG_LAME_MASK", argv[1]);
3363 if((action == ACTION_KICK) && (victimCount == 0))
3365 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
3369 name = ban = strdup(argv[1]);
3372 /* Truncate the ban in place if necessary; we must ensure
3373 that 'ban' is a valid ban mask before sanitizing it. */
3374 sanitize_ircmask(ban);
3376 if(action & ACTION_ADD_BAN)
3378 struct banData *bData, *next;
3380 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans)
3382 reply("CSMSG_MAXIMUM_BANS", chanserv_conf.max_chan_bans);
3387 if(action & ACTION_ADD_TIMED_BAN)
3389 duration = ParseInterval(argv[2]);
3393 reply("CSMSG_DURATION_TOO_LOW");
3397 else if(duration > (86400 * 365 * 2))
3399 reply("CSMSG_DURATION_TOO_HIGH");
3405 for(bData = channel->channel_info->bans; bData; bData = next)
3407 if(match_ircglobs(bData->mask, ban))
3409 int exact = !irccasecmp(bData->mask, ban);
3411 /* The ban is redundant; there is already a ban
3412 with the same effect in place. */
3416 free(bData->reason);
3417 bData->reason = strdup(reason);
3418 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
3420 reply("CSMSG_REASON_CHANGE", ban);
3424 if(exact && bData->expires)
3428 /* If the ban matches an existing one exactly,
3429 extend the expiration time if the provided
3430 duration is longer. */
3431 if(duration && (now + duration > bData->expires))
3433 bData->expires = now + duration;
3444 /* Delete the expiration timeq entry and
3445 requeue if necessary. */
3446 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
3449 timeq_add(bData->expires, expire_ban, bData);
3453 /* automated kickban */
3456 reply("CSMSG_BAN_EXTENDED", ban, intervalString(interval, duration, user->handle_info));
3458 reply("CSMSG_BAN_ADDED", name, channel->name);
3464 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3471 if(match_ircglobs(ban, bData->mask))
3473 /* The ban we are adding makes previously existing
3474 bans redundant; silently remove them. */
3475 del_channel_ban(bData);
3479 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);
3481 name = ban = strdup(bData->mask);
3485 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
3487 extern const char *hidden_host_suffix;
3488 const char *old_name = chanserv_conf.old_ban_names->list[n];
3490 unsigned int l1, l2;
3493 l2 = strlen(old_name);
3496 if(irccasecmp(ban + l1 - l2, old_name))
3498 new_mask = malloc(MAXLEN);
3499 sprintf(new_mask, "%.*s%s", (int)(l1-l2), ban, hidden_host_suffix);
3501 name = ban = new_mask;
3506 if(action & ACTION_BAN)
3508 unsigned int exists;
3509 struct mod_chanmode *change;
3511 if(channel->banlist.used >= MAXBANS)
3514 reply("CSMSG_BANLIST_FULL", channel->name);
3519 exists = ChannelBanExists(channel, ban);
3520 change = mod_chanmode_alloc(victimCount + 1);
3521 for(n = 0; n < victimCount; ++n)
3523 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_VOICE;
3524 change->args[n].u.member = victims[n];
3528 change->args[n].mode = MODE_BAN;
3529 change->args[n++].u.hostmask = ban;
3533 modcmd_chanmode_announce(change);
3535 mod_chanmode_announce(chanserv, channel, change);
3536 mod_chanmode_free(change);
3538 if(exists && (action == ACTION_BAN))
3541 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3547 if(action & ACTION_KICK)
3549 char kick_reason[MAXLEN];
3550 sprintf(kick_reason, "(%s) %s", user->nick, reason);
3552 for(n = 0; n < victimCount; n++)
3553 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
3558 /* No response, since it was automated. */
3560 else if(action & ACTION_ADD_BAN)
3563 reply("CSMSG_TIMED_BAN_ADDED", name, channel->name, intervalString(interval, duration, user->handle_info));
3565 reply("CSMSG_BAN_ADDED", name, channel->name);
3567 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
3568 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
3569 else if(action & ACTION_BAN)
3570 reply("CSMSG_BAN_DONE", name, channel->name);
3571 else if(action & ACTION_KICK && victimCount)
3572 reply("CSMSG_KICK_DONE", name, channel->name);
3578 static CHANSERV_FUNC(cmd_kickban)
3580 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
3583 static CHANSERV_FUNC(cmd_kick)
3585 return eject_user(CSFUNC_ARGS, ACTION_KICK);
3588 static CHANSERV_FUNC(cmd_ban)
3590 return eject_user(CSFUNC_ARGS, ACTION_BAN);
3593 static CHANSERV_FUNC(cmd_addban)
3595 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN);
3598 static CHANSERV_FUNC(cmd_addtimedban)
3600 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN);
3603 static struct mod_chanmode *
3604 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
3606 struct mod_chanmode *change;
3607 unsigned char *match;
3608 unsigned int ii, count;
3610 match = alloca(bans->used);
3613 for(ii = count = 0; ii < bans->used; ++ii)
3615 match[ii] = user_matches_glob(actee, bans->list[ii]->ban,
3616 MATCH_USENICK | MATCH_VISIBLE);
3623 for(ii = count = 0; ii < bans->used; ++ii)
3625 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
3632 change = mod_chanmode_alloc(count);
3633 for(ii = count = 0; ii < bans->used; ++ii)
3637 change->args[count].mode = MODE_REMOVE | MODE_BAN;
3638 change->args[count++].u.hostmask = strdup(bans->list[ii]->ban);
3640 assert(count == change->argc);
3645 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3647 struct userNode *actee;
3653 /* may want to allow a comma delimited list of users... */
3654 if(!(actee = GetUserH(argv[1])))
3656 if(!is_ircmask(argv[1]) && *argv[1] == '*')
3658 char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
3659 const char *accountname = argv[1] + 1;
3661 snprintf(banmask, sizeof(banmask), "*!*@%s.*", accountname);
3662 mask = strdup(banmask);
3664 else if(!is_ircmask(argv[1]))
3666 reply("MSG_NICK_UNKNOWN", argv[1]);
3671 mask = strdup(argv[1]);
3675 /* We don't sanitize the mask here because ircu
3677 if(action & ACTION_UNBAN)
3679 struct mod_chanmode *change;
3680 change = find_matching_bans(&channel->banlist, actee, mask);
3685 modcmd_chanmode_announce(change);
3686 for(ii = 0; ii < change->argc; ++ii)
3687 free((char*)change->args[ii].u.hostmask);
3688 mod_chanmode_free(change);
3693 if(action & ACTION_DEL_BAN)
3695 struct banData *ban, *next;
3697 ban = channel->channel_info->bans;
3701 for( ; ban && !user_matches_glob(actee, ban->mask,
3702 MATCH_USENICK | MATCH_VISIBLE);
3705 for( ; ban && !match_ircglobs(mask, ban->mask);
3710 del_channel_ban(ban);
3717 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3719 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3725 static CHANSERV_FUNC(cmd_unban)
3727 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3730 static CHANSERV_FUNC(cmd_delban)
3732 /* it doesn't necessarily have to remove the channel ban - may want
3733 to make that an option. */
3734 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_BAN);
3737 static CHANSERV_FUNC(cmd_unbanme)
3739 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3740 long flags = ACTION_UNBAN;
3742 /* remove permanent bans if the user has the proper access. */
3743 if(uData->access >= UL_MASTER)
3744 flags |= ACTION_DEL_BAN;
3746 argv[1] = user->nick;
3747 return unban_user(user, channel, 2, argv, cmd, flags);
3750 static CHANSERV_FUNC(cmd_unbanall)
3752 struct mod_chanmode *change;
3755 if(!channel->banlist.used)
3757 reply("CSMSG_NO_BANS", channel->name);
3761 change = mod_chanmode_alloc(channel->banlist.used);
3762 for(ii=0; ii<channel->banlist.used; ii++)
3764 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3765 change->args[ii].u.hostmask = strdup(channel->banlist.list[ii]->ban);
3767 modcmd_chanmode_announce(change);
3768 for(ii = 0; ii < change->argc; ++ii)
3769 free((char*)change->args[ii].u.hostmask);
3770 mod_chanmode_free(change);
3771 reply("CSMSG_BANS_REMOVED", channel->name);
3775 static CHANSERV_FUNC(cmd_open)
3777 struct mod_chanmode *change;
3780 change = find_matching_bans(&channel->banlist, user, NULL);
3782 change = mod_chanmode_alloc(0);
3783 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3784 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3785 && channel->channel_info->modes.modes_set)
3786 change->modes_clear &= ~channel->channel_info->modes.modes_set;
3787 modcmd_chanmode_announce(change);
3788 reply("CSMSG_CHANNEL_OPENED", channel->name);
3789 for(ii = 0; ii < change->argc; ++ii)
3790 free((char*)change->args[ii].u.hostmask);
3791 mod_chanmode_free(change);
3795 static CHANSERV_FUNC(cmd_myaccess)
3797 static struct string_buffer sbuf;
3798 struct handle_info *target_handle;
3799 struct userData *uData;
3803 target_handle = user->handle_info;
3804 else if(!IsStaff(user))
3806 reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
3809 else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3812 if(!oper_outranks(user, target_handle))
3815 if(!target_handle->channels)
3817 reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
3821 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3822 for(uData = target_handle->channels; uData; uData = uData->u_next)
3824 struct chanData *cData = uData->channel;
3827 if(uData->access > UL_OWNER)
3829 if(IsProtected(cData)
3830 && (target_handle != user->handle_info)
3831 && !GetTrueChannelAccess(cData, user->handle_info))
3834 string_buffer_append_printf(&sbuf, "[%s (%d", cData->channel->name, uData->access);
3835 if(uData->flags != USER_AUTO_OP)
3836 string_buffer_append(&sbuf, ',');
3837 if(IsUserSuspended(uData))
3838 string_buffer_append(&sbuf, 's');
3839 if(IsUserAutoOp(uData))
3841 if(uData->access >= cData->lvlOpts[lvlGiveOps])
3842 string_buffer_append(&sbuf, 'o');
3843 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
3844 string_buffer_append(&sbuf, 'v');
3846 if(IsUserAutoInvite(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe]))
3847 string_buffer_append(&sbuf, 'i');
3849 string_buffer_append_printf(&sbuf, ")] %s", uData->info);
3851 string_buffer_append_string(&sbuf, ")]");
3852 string_buffer_append(&sbuf, '\0');
3853 send_message_type(4, user, cmd->parent->bot, "%s", sbuf.list);
3857 reply("CSMSG_MYACCESS_COUNT_1", target_handle->handle, ccount);
3859 reply("CSMSG_MYACCESS_COUNT", target_handle->handle, ccount);
3865 static CHANSERV_FUNC(cmd_access)
3867 struct userNode *target;
3868 struct handle_info *target_handle;
3869 struct userData *uData;
3871 char prefix[MAXLEN];
3876 target_handle = target->handle_info;
3878 else if((target = GetUserH(argv[1])))
3880 target_handle = target->handle_info;
3882 else if(argv[1][0] == '*')
3884 if(!(target_handle = get_handle_info(argv[1]+1)))
3886 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3892 reply("MSG_NICK_UNKNOWN", argv[1]);
3896 assert(target || target_handle);
3898 if(target == chanserv)
3900 reply("CSMSG_IS_CHANSERV");
3908 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
3913 reply("MSG_USER_AUTHENTICATE", target->nick);
3916 reply("MSG_AUTHENTICATE");
3922 const char *epithet = NULL, *type = NULL;
3925 epithet = chanserv_conf.irc_operator_epithet;
3926 type = user_find_message(user, "CSMSG_OPERATOR_TITLE");
3928 else if(IsNetworkHelper(target))
3930 epithet = chanserv_conf.network_helper_epithet;
3931 type = user_find_message(user, "CSMSG_UC_H_TITLE");
3933 else if(IsSupportHelper(target))
3935 epithet = chanserv_conf.support_helper_epithet;
3936 type = user_find_message(user, "CSMSG_LC_H_TITLE");
3940 if(target_handle->epithet)
3941 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
3943 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
3945 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
3949 sprintf(prefix, "%s", target_handle->handle);
3952 if(!channel->channel_info)
3954 reply("CSMSG_NOT_REGISTERED", channel->name);
3958 helping = HANDLE_FLAGGED(target_handle, HELPING)
3959 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
3960 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
3962 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
3963 /* To prevent possible information leaks, only show infolines
3964 * if the requestor is in the channel or it's their own
3966 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
3968 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
3970 /* Likewise, only say it's suspended if the user has active
3971 * access in that channel or it's their own entry. */
3972 if(IsUserSuspended(uData)
3973 && (GetChannelUser(channel->channel_info, user->handle_info)
3974 || (user->handle_info == uData->handle)))
3976 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
3981 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
3988 zoot_list(struct listData *list)
3990 struct userData *uData;
3991 unsigned int start, curr, highest, lowest;
3992 struct helpfile_table tmp_table;
3993 const char **temp, *msg;
3995 if(list->table.length == 1)
3998 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
4000 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
4001 msg = user_find_message(list->user, "MSG_NONE");
4002 send_message_type(4, list->user, list->bot, " %s", msg);
4004 tmp_table.width = list->table.width;
4005 tmp_table.flags = list->table.flags;
4006 list->table.contents[0][0] = " ";
4007 highest = list->highest;
4008 if(list->lowest != 0)
4009 lowest = list->lowest;
4010 else if(highest < 100)
4013 lowest = highest - 100;
4014 for(start = curr = 1; curr < list->table.length; )
4016 uData = list->users[curr-1];
4017 list->table.contents[curr++][0] = " ";
4018 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
4021 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, lowest, highest, list->search);
4023 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, lowest, highest);
4024 temp = list->table.contents[--start];
4025 list->table.contents[start] = list->table.contents[0];
4026 tmp_table.contents = list->table.contents + start;
4027 tmp_table.length = curr - start;
4028 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
4029 list->table.contents[start] = temp;
4031 highest = lowest - 1;
4032 lowest = (highest < 100) ? 0 : (highest - 99);
4038 def_list(struct listData *list)
4042 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
4044 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
4045 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4046 if(list->table.length == 1)
4048 msg = user_find_message(list->user, "MSG_NONE");
4049 send_message_type(4, list->user, list->bot, " %s", msg);
4054 userData_access_comp(const void *arg_a, const void *arg_b)
4056 const struct userData *a = *(struct userData**)arg_a;
4057 const struct userData *b = *(struct userData**)arg_b;
4059 if(a->access != b->access)
4060 res = b->access - a->access;
4062 res = irccasecmp(a->handle->handle, b->handle->handle);
4067 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
4069 void (*send_list)(struct listData *);
4070 struct userData *uData;
4071 struct listData lData;
4072 unsigned int matches;
4076 lData.bot = cmd->parent->bot;
4077 lData.channel = channel;
4078 lData.lowest = lowest;
4079 lData.highest = highest;
4080 lData.search = (argc > 1) ? argv[1] : NULL;
4081 send_list = def_list;
4082 (void)zoot_list; /* since it doesn't show user levels */
4084 if(user->handle_info)
4086 switch(user->handle_info->userlist_style)
4088 case HI_STYLE_DEF: send_list = def_list; break;
4089 case HI_STYLE_ZOOT: send_list = def_list; break;
4093 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
4095 for(uData = channel->channel_info->users; uData; uData = uData->next)
4097 if((uData->access < lowest)
4098 || (uData->access > highest)
4099 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
4101 lData.users[matches++] = uData;
4103 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
4105 lData.table.length = matches+1;
4106 lData.table.width = 4;
4107 lData.table.flags = TABLE_NO_FREE;
4108 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
4109 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
4110 lData.table.contents[0] = ary;
4113 ary[2] = "Last Seen";
4115 for(matches = 1; matches < lData.table.length; ++matches)
4117 char seen[INTERVALLEN];
4119 uData = lData.users[matches-1];
4120 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
4121 lData.table.contents[matches] = ary;
4122 ary[0] = strtab(uData->access);
4123 ary[1] = uData->handle->handle;
4126 else if(!uData->seen)
4129 ary[2] = intervalString(seen, now - uData->seen, user->handle_info);
4130 ary[2] = strdup(ary[2]);
4131 if(IsUserSuspended(uData))
4132 ary[3] = "Suspended";
4133 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
4134 ary[3] = "Vacation";
4135 else if(HANDLE_FLAGGED(uData->handle, BOT))
4141 for(matches = 1; matches < lData.table.length; ++matches)
4143 free((char*)lData.table.contents[matches][2]);
4144 free(lData.table.contents[matches]);
4146 free(lData.table.contents[0]);
4147 free(lData.table.contents);
4151 static CHANSERV_FUNC(cmd_users)
4153 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
4156 static CHANSERV_FUNC(cmd_wlist)
4158 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
4161 static CHANSERV_FUNC(cmd_clist)
4163 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
4166 static CHANSERV_FUNC(cmd_mlist)
4168 return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
4171 static CHANSERV_FUNC(cmd_olist)
4173 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
4176 static CHANSERV_FUNC(cmd_plist)
4178 return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
4181 static CHANSERV_FUNC(cmd_bans)
4183 struct userNode *search_u = NULL;
4184 struct helpfile_table tbl;
4185 unsigned int matches = 0, timed = 0, search_wilds = 0, ii;
4186 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
4187 const char *msg_never, *triggered, *expires;
4188 struct banData *ban, **bans;
4192 else if(strchr(search = argv[1], '!'))
4195 search_wilds = search[strcspn(search, "?*")];
4197 else if(!(search_u = GetUserH(search)))
4198 reply("MSG_NICK_UNKNOWN", search);
4200 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
4202 for(ban = channel->channel_info->bans; ban; ban = ban->next)
4206 if(!user_matches_glob(search_u, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
4211 if(search_wilds ? !match_ircglobs(search, ban->mask) : !match_ircglob(search, ban->mask))
4214 bans[matches++] = ban;
4219 tbl.length = matches + 1;
4220 tbl.width = 4 + timed;
4222 tbl.flags = TABLE_NO_FREE;
4223 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
4224 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4225 tbl.contents[0][0] = "Mask";
4226 tbl.contents[0][1] = "Set By";
4227 tbl.contents[0][2] = "Triggered";
4230 tbl.contents[0][3] = "Expires";
4231 tbl.contents[0][4] = "Reason";
4234 tbl.contents[0][3] = "Reason";
4237 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4239 free(tbl.contents[0]);
4244 msg_never = user_find_message(user, "MSG_NEVER");
4245 for(ii = 0; ii < matches; )
4251 else if(ban->expires)
4252 expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
4254 expires = msg_never;
4257 triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
4259 triggered = msg_never;
4261 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4262 tbl.contents[ii][0] = ban->mask;
4263 tbl.contents[ii][1] = ban->owner;
4264 tbl.contents[ii][2] = strdup(triggered);
4267 tbl.contents[ii][3] = strdup(expires);
4268 tbl.contents[ii][4] = ban->reason;
4271 tbl.contents[ii][3] = ban->reason;
4273 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4274 reply("MSG_MATCH_COUNT", matches);
4275 for(ii = 1; ii < tbl.length; ++ii)
4277 free((char*)tbl.contents[ii][2]);
4279 free((char*)tbl.contents[ii][3]);
4280 free(tbl.contents[ii]);
4282 free(tbl.contents[0]);
4288 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
4290 struct chanData *cData = channel->channel_info;
4291 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
4293 if(cData->topic_mask)
4294 return !match_ircglob(new_topic, cData->topic_mask);
4295 else if(cData->topic)
4296 return irccasecmp(new_topic, cData->topic);
4301 static CHANSERV_FUNC(cmd_topic)
4303 struct chanData *cData;
4306 cData = channel->channel_info;
4311 SetChannelTopic(channel, chanserv, cData->topic, 1);
4312 reply("CSMSG_TOPIC_SET", cData->topic);
4316 reply("CSMSG_NO_TOPIC", channel->name);
4320 topic = unsplit_string(argv + 1, argc - 1, NULL);
4321 /* If they say "!topic *", use an empty topic. */
4322 if((topic[0] == '*') && (topic[1] == 0))
4324 if(bad_topic(channel, user, topic))
4326 char *topic_mask = cData->topic_mask;
4329 char new_topic[TOPICLEN+1], tchar;
4330 int pos=0, starpos=-1, dpos=0, len;
4332 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
4339 len = strlen(topic);
4340 if((dpos + len) > TOPICLEN)
4341 len = TOPICLEN + 1 - dpos;
4342 memcpy(new_topic+dpos, topic, len);
4346 case '\\': tchar = topic_mask[pos++]; /* and fall through */
4347 default: new_topic[dpos++] = tchar; break;
4350 if((dpos > TOPICLEN) || tchar)
4353 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
4354 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
4357 new_topic[dpos] = 0;
4358 SetChannelTopic(channel, chanserv, new_topic, 1);
4360 reply("CSMSG_TOPIC_LOCKED", channel->name);
4365 SetChannelTopic(channel, chanserv, topic, 1);
4367 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
4369 /* Grab the topic and save it as the default topic. */
4371 cData->topic = strdup(channel->topic);
4377 static CHANSERV_FUNC(cmd_mode)
4379 struct userData *uData;
4380 struct mod_chanmode *change;
4386 change = &channel->channel_info->modes;
4387 if(change->modes_set || change->modes_clear) {
4388 modcmd_chanmode_announce(change);
4389 reply("CSMSG_DEFAULTED_MODES", channel->name);
4391 reply("CSMSG_NO_MODES", channel->name);
4395 uData = GetChannelUser(channel->channel_info, user->handle_info);
4397 base_oplevel = MAXOPLEVEL;
4398 else if (uData->access >= UL_OWNER)
4401 base_oplevel = 1 + UL_OWNER - uData->access;
4402 change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED|MCP_NO_APASS, base_oplevel);
4405 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
4409 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
4410 && mode_lock_violated(&channel->channel_info->modes, change))
4413 mod_chanmode_format(&channel->channel_info->modes, modes);
4414 reply("CSMSG_MODE_LOCKED", modes, channel->name);
4418 modcmd_chanmode_announce(change);
4419 mod_chanmode_format(change, fmt);
4420 mod_chanmode_free(change);
4421 reply("CSMSG_MODES_SET", fmt);
4426 chanserv_del_invite_mark(void *data)
4428 struct ChanUser *chanuser = data;
4429 struct chanNode *channel = chanuser->chan;
4431 if(!channel) return;
4432 for(i = 0; i < channel->invited.used; i++)
4434 if(channel->invited.list[i] == chanuser->user) {
4435 userList_remove(&channel->invited, chanuser->user);
4441 static CHANSERV_FUNC(cmd_invite)
4443 struct userData *uData;
4444 struct userNode *invite;
4445 struct ChanUser *chanuser;
4448 uData = GetChannelUser(channel->channel_info, user->handle_info);
4452 if(!(invite = GetUserH(argv[1])))
4454 reply("MSG_NICK_UNKNOWN", argv[1]);
4461 if(GetUserMode(channel, invite))
4463 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
4467 for(i = 0; i < channel->invited.used; i++)
4469 if(channel->invited.list[i] == invite) {
4470 reply("CSMSG_ALREADY_INVITED", invite->nick, channel->name);
4479 char *reason = unsplit_string(argv + 2, argc - 2, NULL);
4480 send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
4483 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
4485 irc_invite(chanserv, invite, channel);
4487 reply("CSMSG_INVITED_USER", argv[1], channel->name);
4489 userList_append(&channel->invited, invite);
4490 chanuser = calloc(1, sizeof(*chanuser));
4491 chanuser->user=invite;
4492 chanuser->chan=channel;
4493 timeq_add(now + chanserv_conf.invited_timeout, chanserv_del_invite_mark, chanuser);
4498 static CHANSERV_FUNC(cmd_inviteme)
4500 if(GetUserMode(channel, user))
4502 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
4505 if(channel->channel_info
4506 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
4508 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
4511 irc_invite(cmd->parent->bot, user, channel);
4516 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
4519 char buf1[INTERVALLEN], buf2[INTERVALLEN];
4521 /* We display things based on two dimensions:
4522 * - Issue time: present or absent
4523 * - Expiration: revoked, expired, expires in future, or indefinite expiration
4524 * (in order of precedence, so something both expired and revoked
4525 * only counts as revoked)
4527 combo = (suspended->issued ? 4 : 0)
4528 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
4530 case 0: /* no issue time, indefinite expiration */
4531 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
4533 case 1: /* no issue time, expires in future */
4534 intervalString(buf1, suspended->expires-now, user->handle_info);
4535 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
4537 case 2: /* no issue time, expired */
4538 intervalString(buf1, now-suspended->expires, user->handle_info);
4539 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
4541 case 3: /* no issue time, revoked */
4542 intervalString(buf1, now-suspended->revoked, user->handle_info);
4543 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
4545 case 4: /* issue time set, indefinite expiration */
4546 intervalString(buf1, now-suspended->issued, user->handle_info);
4547 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
4549 case 5: /* issue time set, expires in future */
4550 intervalString(buf1, now-suspended->issued, user->handle_info);
4551 intervalString(buf2, suspended->expires-now, user->handle_info);
4552 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
4554 case 6: /* issue time set, expired */
4555 intervalString(buf1, now-suspended->issued, user->handle_info);
4556 intervalString(buf2, now-suspended->expires, user->handle_info);
4557 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
4559 case 7: /* issue time set, revoked */
4560 intervalString(buf1, now-suspended->issued, user->handle_info);
4561 intervalString(buf2, now-suspended->revoked, user->handle_info);
4562 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
4565 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
4570 static CHANSERV_FUNC(cmd_info)
4572 char modes[MAXLEN], buffer[INTERVALLEN];
4573 struct userData *uData, *owner;
4574 struct chanData *cData;
4575 struct do_not_register *dnr;
4580 cData = channel->channel_info;
4581 reply("CSMSG_CHANNEL_INFO", channel->name);
4583 uData = GetChannelUser(cData, user->handle_info);
4584 if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
4586 mod_chanmode_format(&cData->modes, modes);
4587 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
4588 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
4591 for(it = dict_first(cData->notes); it; it = iter_next(it))
4595 note = iter_data(it);
4596 if(!note_type_visible_to_user(cData, note->type, user))
4599 padding = PADLEN - 1 - strlen(iter_key(it));
4600 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
4603 if(cData->max_time) {
4604 reply("CSMSG_CHANNEL_MAX_TIME", cData->max, intervalString(buffer, now - cData->max_time, user->handle_info));
4606 reply("CSMSG_CHANNEL_MAX", cData->max);
4608 for(owner = cData->users; owner; owner = owner->next)
4609 if(owner->access == UL_OWNER)
4610 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
4611 reply("CSMSG_CHANNEL_USERS", cData->userCount);
4612 reply("CSMSG_CHANNEL_BANS", cData->banCount);
4613 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
4615 privileged = IsStaff(user);
4617 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
4618 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
4619 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
4621 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
4622 chanserv_show_dnrs(user, cmd, channel->name, NULL);
4624 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
4626 struct suspended *suspended;
4627 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
4628 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
4629 show_suspension_info(cmd, user, suspended);
4631 else if(IsSuspended(cData))
4633 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
4634 show_suspension_info(cmd, user, cData->suspended);
4639 static CHANSERV_FUNC(cmd_netinfo)
4641 extern unsigned long boot_time;
4642 extern unsigned long burst_length;
4643 char interval[INTERVALLEN];
4645 reply("CSMSG_NETWORK_INFO");
4646 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
4647 reply("CSMSG_NETWORK_USERS", dict_size(clients));
4648 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
4649 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
4650 reply("CSMSG_NETWORK_BANS", banCount);
4651 reply("CSMSG_NETWORK_CHANUSERS", userCount);
4652 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
4653 reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
4658 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
4660 struct helpfile_table table;
4662 struct userNode *user;
4667 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4668 table.contents = alloca(list->used*sizeof(*table.contents));
4669 for(nn=0; nn<list->used; nn++)
4671 user = list->list[nn];
4672 if(user->modes & skip_flags)
4676 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
4679 nick = alloca(strlen(user->nick)+3);
4680 sprintf(nick, "(%s)", user->nick);
4684 table.contents[table.length][0] = nick;
4687 table_send(chanserv, to->nick, 0, NULL, table);
4690 static CHANSERV_FUNC(cmd_ircops)
4692 reply("CSMSG_STAFF_OPERS");
4693 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
4697 static CHANSERV_FUNC(cmd_helpers)
4699 reply("CSMSG_STAFF_HELPERS");
4700 send_staff_list(user, &curr_helpers, FLAGS_OPER);
4704 static CHANSERV_FUNC(cmd_staff)
4706 reply("CSMSG_NETWORK_STAFF");
4707 cmd_ircops(CSFUNC_ARGS);
4708 cmd_helpers(CSFUNC_ARGS);
4712 static CHANSERV_FUNC(cmd_peek)
4714 struct modeNode *mn;
4715 char modes[MODELEN];
4717 struct helpfile_table table;
4718 int opcount = 0, voicecount = 0, srvcount = 0;
4720 irc_make_chanmode(channel, modes);
4722 reply("CSMSG_PEEK_INFO", channel->name);
4723 reply("CSMSG_PEEK_TOPIC", channel->topic);
4724 reply("CSMSG_PEEK_MODES", modes);
4728 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4729 table.contents = alloca(channel->members.used*sizeof(*table.contents));
4730 for(n = 0; n < channel->members.used; n++)
4732 mn = channel->members.list[n];
4733 if(IsLocal(mn->user))
4735 else if(mn->modes & MODE_CHANOP)
4737 else if(mn->modes & MODE_VOICE)
4740 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
4742 table.contents[table.length] = alloca(sizeof(**table.contents));
4743 table.contents[table.length][0] = mn->user->nick;
4747 reply("CSMSG_PEEK_USERS", channel->members.used, opcount, voicecount,
4748 (channel->members.used - opcount - voicecount - srvcount));
4752 reply("CSMSG_PEEK_OPS");
4753 table_send(chanserv, user->nick, 0, NULL, table);
4756 reply("CSMSG_PEEK_NO_OPS");
4760 static MODCMD_FUNC(cmd_wipeinfo)
4762 struct handle_info *victim;
4763 struct userData *ud, *actor, *real_actor;
4764 unsigned int override = 0;
4767 actor = GetChannelUser(channel->channel_info, user->handle_info);
4768 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
4769 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4771 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4773 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4776 if((ud->access >= actor->access) && (ud != actor))
4778 reply("MSG_USER_OUTRANKED", victim->handle);
4781 if((ud != real_actor) && (!real_actor || (ud->access >= real_actor->access)))
4782 override = CMD_LOG_OVERRIDE;
4786 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4787 return 1 | override;
4790 static CHANSERV_FUNC(cmd_resync)
4792 struct mod_chanmode *changes;
4793 struct chanData *cData = channel->channel_info;
4794 unsigned int ii, used;
4796 changes = mod_chanmode_alloc(channel->members.used * 2);
4797 for(ii = used = 0; ii < channel->members.used; ++ii)
4799 struct modeNode *mn = channel->members.list[ii];
4800 struct userData *uData;
4802 if(IsService(mn->user))
4805 uData = GetChannelAccess(cData, mn->user->handle_info);
4806 if(!cData->lvlOpts[lvlGiveOps]
4807 || (uData && uData->access >= cData->lvlOpts[lvlGiveOps]))
4809 if(!(mn->modes & MODE_CHANOP))
4811 changes->args[used].mode = MODE_CHANOP;
4812 changes->args[used++].u.member = mn;
4815 else if(!cData->lvlOpts[lvlGiveVoice]
4816 || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice]))
4818 if(mn->modes & MODE_CHANOP)
4820 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4821 changes->args[used++].u.member = mn;
4823 if(!(mn->modes & MODE_VOICE))
4825 changes->args[used].mode = MODE_VOICE;
4826 changes->args[used++].u.member = mn;
4833 changes->args[used].mode = MODE_REMOVE | mn->modes;
4834 changes->args[used++].u.member = mn;
4838 changes->argc = used;
4839 modcmd_chanmode_announce(changes);
4840 mod_chanmode_free(changes);
4841 reply("CSMSG_RESYNCED_USERS", channel->name);
4845 static CHANSERV_FUNC(cmd_seen)
4847 struct userData *uData;
4848 struct handle_info *handle;
4849 char seen[INTERVALLEN];
4853 if(!irccasecmp(argv[1], chanserv->nick))
4855 reply("CSMSG_IS_CHANSERV");
4859 if(!(handle = get_handle_info(argv[1])))
4861 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4865 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4867 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4872 reply("CSMSG_USER_PRESENT", handle->handle);
4873 else if(uData->seen)
4874 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
4876 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4878 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4879 reply("CSMSG_USER_VACATION", handle->handle);
4884 static MODCMD_FUNC(cmd_names)
4886 struct userNode *targ;
4887 struct userData *targData;
4888 unsigned int ii, pos;
4891 for(ii=pos=0; ii<channel->members.used; ++ii)
4893 targ = channel->members.list[ii]->user;
4894 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4897 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
4900 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4904 if(IsUserSuspended(targData))
4906 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4909 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4910 reply("CSMSG_END_NAMES", channel->name);
4915 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
4917 switch(ntype->visible_type)
4919 case NOTE_VIS_ALL: return 1;
4920 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
4921 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
4926 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
4928 struct userData *uData;
4930 switch(ntype->set_access_type)
4932 case NOTE_SET_CHANNEL_ACCESS:
4933 if(!user->handle_info)
4935 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
4937 return uData->access >= ntype->set_access.min_ulevel;
4938 case NOTE_SET_CHANNEL_SETTER:
4939 return check_user_level(channel, user, lvlSetters, 1, 0);
4940 case NOTE_SET_PRIVILEGED: default:
4941 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
4945 static CHANSERV_FUNC(cmd_note)
4947 struct chanData *cData;
4949 struct note_type *ntype;
4951 cData = channel->channel_info;
4954 reply("CSMSG_NOT_REGISTERED", channel->name);
4958 /* If no arguments, show all visible notes for the channel. */
4964 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
4966 note = iter_data(it);
4967 if(!note_type_visible_to_user(cData, note->type, user))
4970 reply("CSMSG_NOTELIST_HEADER", channel->name);
4971 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
4974 reply("CSMSG_NOTELIST_END", channel->name);
4976 reply("CSMSG_NOTELIST_EMPTY", channel->name);
4978 /* If one argument, show the named note. */
4981 if((note = dict_find(cData->notes, argv[1], NULL))
4982 && note_type_visible_to_user(cData, note->type, user))
4984 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
4986 else if((ntype = dict_find(note_types, argv[1], NULL))
4987 && note_type_visible_to_user(NULL, ntype, user))
4989 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
4994 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4998 /* Assume they're trying to set a note. */
5002 ntype = dict_find(note_types, argv[1], NULL);
5005 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
5008 else if(note_type_settable_by_user(channel, ntype, user))
5010 note_text = unsplit_string(argv+2, argc-2, NULL);
5011 if((note = dict_find(cData->notes, argv[1], NULL)))
5012 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
5013 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
5014 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
5016 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
5018 /* The note is viewable to staff only, so return 0
5019 to keep the invocation from getting logged (or
5020 regular users can see it in !events). */
5026 reply("CSMSG_NO_ACCESS");
5033 static CHANSERV_FUNC(cmd_delnote)
5038 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
5039 || !note_type_settable_by_user(channel, note->type, user))
5041 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
5044 dict_remove(channel->channel_info->notes, note->type->name);
5045 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
5049 static CHANSERV_FUNC(cmd_events)
5051 struct logSearch discrim;
5052 struct logReport report;
5053 unsigned int matches, limit;
5055 limit = (argc > 1) ? atoi(argv[1]) : 10;
5056 if(limit < 1 || limit > 200)
5059 memset(&discrim, 0, sizeof(discrim));
5060 discrim.masks.bot = chanserv;
5061 discrim.masks.channel_name = channel->name;
5063 discrim.masks.command = argv[2];
5064 discrim.limit = limit;
5065 discrim.max_time = INT_MAX;
5066 discrim.severities = 1 << LOG_COMMAND;
5067 report.reporter = chanserv;
5069 reply("CSMSG_EVENT_SEARCH_RESULTS");
5070 matches = log_entry_search(&discrim, log_report_entry, &report);
5072 reply("MSG_MATCH_COUNT", matches);
5074 reply("MSG_NO_MATCHES");
5078 static CHANSERV_FUNC(cmd_say)
5084 msg = unsplit_string(argv + 1, argc - 1, NULL);
5085 send_channel_message(channel, cmd->parent->bot, "%s", msg);
5087 else if(*argv[1] == '*' && argv[1][1] != '\0')
5089 struct handle_info *hi;
5090 struct userNode *authed;
5093 msg = unsplit_string(argv + 2, argc - 2, NULL);
5095 if (!(hi = get_handle_info(argv[1] + 1)))
5097 reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
5101 for (authed = hi->users; authed; authed = authed->next_authed)
5102 send_target_message(5, authed->nick, cmd->parent->bot, "%s", msg);
5104 else if(GetUserH(argv[1]))
5107 msg = unsplit_string(argv + 2, argc - 2, NULL);
5108 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
5112 reply("MSG_NOT_TARGET_NAME");
5118 static CHANSERV_FUNC(cmd_emote)
5124 /* CTCP is so annoying. */
5125 msg = unsplit_string(argv + 1, argc - 1, NULL);
5126 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
5128 else if(*argv[1] == '*' && argv[1][1] != '\0')
5130 struct handle_info *hi;
5131 struct userNode *authed;
5134 msg = unsplit_string(argv + 2, argc - 2, NULL);
5136 if (!(hi = get_handle_info(argv[1] + 1)))
5138 reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
5142 for (authed = hi->users; authed; authed = authed->next_authed)
5143 send_target_message(5, authed->nick, cmd->parent->bot, "\001ACTION %s\001", msg);
5145 else if(GetUserH(argv[1]))
5147 msg = unsplit_string(argv + 2, argc - 2, NULL);
5148 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
5152 reply("MSG_NOT_TARGET_NAME");
5158 struct channelList *
5159 chanserv_support_channels(void)
5161 return &chanserv_conf.support_channels;
5164 static CHANSERV_FUNC(cmd_expire)
5166 int channel_count = registered_channels;
5167 expire_channels(NULL);
5168 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
5173 chanserv_expire_suspension(void *data)
5175 struct suspended *suspended = data;
5176 struct chanNode *channel;
5179 /* Update the channel registration data structure. */
5180 if(!suspended->expires || (now < suspended->expires))
5181 suspended->revoked = now;
5182 channel = suspended->cData->channel;
5183 suspended->cData->channel = channel;
5184 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
5186 /* If appropriate, re-join ChanServ to the channel. */
5187 if(!IsOffChannel(suspended->cData))
5189 spamserv_cs_suspend(channel, 0, 0, NULL);
5190 ss_cs_join_channel(channel, 1);
5193 /* Mark everyone currently in the channel as present. */
5194 for(ii = 0; ii < channel->members.used; ++ii)
5196 struct userData *uData = GetChannelAccess(suspended->cData, channel->members.list[ii]->user->handle_info);
5205 static CHANSERV_FUNC(cmd_csuspend)
5207 struct suspended *suspended;
5208 char reason[MAXLEN];
5209 unsigned long expiry, duration;
5210 struct userData *uData;
5214 if(IsProtected(channel->channel_info))
5216 reply("CSMSG_SUSPEND_NODELETE", channel->name);
5220 if(argv[1][0] == '!')
5222 else if(IsSuspended(channel->channel_info))
5224 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
5225 show_suspension_info(cmd, user, channel->channel_info->suspended);
5229 if(!strcmp(argv[1], "0"))
5231 else if((duration = ParseInterval(argv[1])))
5232 expiry = now + duration;
5235 reply("MSG_INVALID_DURATION", argv[1]);
5239 unsplit_string(argv + 2, argc - 2, reason);
5241 suspended = calloc(1, sizeof(*suspended));
5242 suspended->revoked = 0;
5243 suspended->issued = now;
5244 suspended->suspender = strdup(user->handle_info->handle);
5245 suspended->expires = expiry;
5246 suspended->reason = strdup(reason);
5247 suspended->cData = channel->channel_info;
5248 suspended->previous = suspended->cData->suspended;
5249 suspended->cData->suspended = suspended;
5251 if(suspended->expires)
5252 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
5254 if(IsSuspended(channel->channel_info))
5256 suspended->previous->revoked = now;
5257 if(suspended->previous->expires)
5258 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
5259 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
5260 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5264 /* Mark all users in channel as absent. */
5265 for(uData = channel->channel_info->users; uData; uData = uData->next)
5274 /* Mark the channel as suspended, then part. */
5275 channel->channel_info->flags |= CHANNEL_SUSPENDED;
5276 spamserv_cs_suspend(channel, expiry, 1, suspended->reason);
5277 DelChannelUser(chanserv, channel, suspended->reason, 0);
5278 reply("CSMSG_SUSPENDED", channel->name);
5279 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
5280 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5285 static CHANSERV_FUNC(cmd_cunsuspend)
5287 struct suspended *suspended;
5288 char message[MAXLEN];
5290 if(!IsSuspended(channel->channel_info))
5292 reply("CSMSG_NOT_SUSPENDED", channel->name);
5296 suspended = channel->channel_info->suspended;
5298 /* Expire the suspension and join ChanServ to the channel. */
5299 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
5300 chanserv_expire_suspension(suspended);
5301 reply("CSMSG_UNSUSPENDED", channel->name);
5302 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
5303 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
5307 typedef struct chanservSearch
5312 unsigned long unvisited;
5313 unsigned long registered;
5315 unsigned long flags;
5319 typedef void (*channel_search_func)(struct chanData *channel, void *data);
5322 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
5327 search = malloc(sizeof(struct chanservSearch));
5328 memset(search, 0, sizeof(*search));
5331 for(i = 0; i < argc; i++)
5333 /* Assume all criteria require arguments. */
5336 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
5340 if(!irccasecmp(argv[i], "name"))
5341 search->name = argv[++i];
5342 else if(!irccasecmp(argv[i], "registrar"))
5343 search->registrar = argv[++i];
5344 else if(!irccasecmp(argv[i], "unvisited"))
5345 search->unvisited = ParseInterval(argv[++i]);
5346 else if(!irccasecmp(argv[i], "registered"))
5347 search->registered = ParseInterval(argv[++i]);
5348 else if(!irccasecmp(argv[i], "flags"))
5351 if(!irccasecmp(argv[i], "nodelete"))
5352 search->flags |= CHANNEL_NODELETE;
5353 else if(!irccasecmp(argv[i], "suspended"))
5354 search->flags |= CHANNEL_SUSPENDED;
5355 else if(!irccasecmp(argv[i], "unreviewed"))
5356 search->flags |= CHANNEL_UNREVIEWED;
5359 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
5363 else if(!irccasecmp(argv[i], "limit"))
5364 search->limit = strtoul(argv[++i], NULL, 10);
5367 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
5372 if(search->name && !strcmp(search->name, "*"))
5374 if(search->registrar && !strcmp(search->registrar, "*"))
5375 search->registrar = 0;
5384 chanserv_channel_match(struct chanData *channel, search_t search)
5386 const char *name = channel->channel->name;
5387 if((search->name && !match_ircglob(name, search->name)) ||
5388 (search->registrar && !channel->registrar) ||
5389 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
5390 (search->unvisited && (now - channel->visited) < search->unvisited) ||
5391 (search->registered && (now - channel->registered) > search->registered) ||
5392 (search->flags && ((search->flags & channel->flags) != search->flags)))
5399 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
5401 struct chanData *channel;
5402 unsigned int matches = 0;
5404 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
5406 if(!chanserv_channel_match(channel, search))
5416 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
5421 search_print(struct chanData *channel, void *data)
5423 send_message_type(4, data, chanserv, "%s", channel->channel->name);
5426 static CHANSERV_FUNC(cmd_search)
5429 unsigned int matches;
5430 channel_search_func action;
5434 if(!irccasecmp(argv[1], "count"))
5435 action = search_count;
5436 else if(!irccasecmp(argv[1], "print"))
5437 action = search_print;
5440 reply("CSMSG_ACTION_INVALID", argv[1]);
5444 search = chanserv_search_create(user, argc - 2, argv + 2);
5448 if(action == search_count)
5449 search->limit = INT_MAX;
5451 if(action == search_print)
5452 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
5454 matches = chanserv_channel_search(search, action, user);
5457 reply("MSG_MATCH_COUNT", matches);
5459 reply("MSG_NO_MATCHES");
5465 static CHANSERV_FUNC(cmd_unvisited)
5467 struct chanData *cData;
5468 unsigned long interval = chanserv_conf.channel_expire_delay;
5469 char buffer[INTERVALLEN];
5470 unsigned int limit = 25, matches = 0;
5474 interval = ParseInterval(argv[1]);
5476 limit = atoi(argv[2]);
5479 intervalString(buffer, interval, user->handle_info);
5480 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
5482 for(cData = channelList; cData && matches < limit; cData = cData->next)
5484 if((now - cData->visited) < interval)
5487 intervalString(buffer, now - cData->visited, user->handle_info);
5488 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
5495 static MODCMD_FUNC(chan_opt_defaulttopic)
5501 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5503 reply("CSMSG_TOPIC_LOCKED", channel->name);
5507 topic = unsplit_string(argv+1, argc-1, NULL);
5509 free(channel->channel_info->topic);
5510 if(topic[0] == '*' && topic[1] == 0)
5512 topic = channel->channel_info->topic = NULL;
5516 topic = channel->channel_info->topic = strdup(topic);
5517 if(channel->channel_info->topic_mask
5518 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
5519 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5521 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
5524 if(channel->channel_info->topic)
5525 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
5527 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
5531 static MODCMD_FUNC(chan_opt_topicmask)
5535 struct chanData *cData = channel->channel_info;
5538 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5540 reply("CSMSG_TOPIC_LOCKED", channel->name);
5544 mask = unsplit_string(argv+1, argc-1, NULL);
5546 if(cData->topic_mask)
5547 free(cData->topic_mask);
5548 if(mask[0] == '*' && mask[1] == 0)
5550 cData->topic_mask = 0;
5554 cData->topic_mask = strdup(mask);
5556 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
5557 else if(!match_ircglob(cData->topic, cData->topic_mask))
5558 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5562 if(channel->channel_info->topic_mask)
5563 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
5565 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
5569 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
5573 char *greeting = unsplit_string(argv+1, argc-1, NULL);
5577 if(greeting[0] == '*' && greeting[1] == 0)
5581 unsigned int length = strlen(greeting);
5582 if(length > chanserv_conf.greeting_length)
5584 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
5587 *data = strdup(greeting);
5596 reply(name, user_find_message(user, "MSG_NONE"));
5600 static MODCMD_FUNC(chan_opt_greeting)
5602 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
5605 static MODCMD_FUNC(chan_opt_usergreeting)
5607 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
5610 static MODCMD_FUNC(chan_opt_modes)
5612 struct mod_chanmode *new_modes;
5617 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
5619 reply("CSMSG_NO_ACCESS");
5622 if(argv[1][0] == '*' && argv[1][1] == 0)
5624 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
5626 else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED|MCP_NO_APASS, 0)))
5628 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5631 else if(new_modes->argc > 1)
5633 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5634 mod_chanmode_free(new_modes);
5639 channel->channel_info->modes = *new_modes;
5640 modcmd_chanmode_announce(new_modes);
5641 mod_chanmode_free(new_modes);
5645 mod_chanmode_format(&channel->channel_info->modes, modes);
5647 reply("CSMSG_SET_MODES", modes);
5649 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
5653 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5655 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5657 struct chanData *cData = channel->channel_info;
5662 /* Set flag according to value. */
5663 if(enabled_string(argv[1]))
5665 cData->flags |= mask;
5668 else if(disabled_string(argv[1]))
5670 cData->flags &= ~mask;
5675 reply("MSG_INVALID_BINARY", argv[1]);
5681 /* Find current option value. */
5682 value = (cData->flags & mask) ? 1 : 0;
5686 reply(name, user_find_message(user, "MSG_ON"));
5688 reply(name, user_find_message(user, "MSG_OFF"));
5692 static MODCMD_FUNC(chan_opt_nodelete)
5694 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
5696 reply("MSG_SETTING_PRIVILEGED", argv[0]);
5700 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
5703 static MODCMD_FUNC(chan_opt_dynlimit)
5705 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
5708 static MODCMD_FUNC(chan_opt_offchannel)
5710 struct chanData *cData = channel->channel_info;
5715 /* Set flag according to value. */
5716 if(enabled_string(argv[1]))
5718 if(!IsOffChannel(cData))
5719 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
5720 cData->flags |= CHANNEL_OFFCHANNEL;
5723 else if(disabled_string(argv[1]))
5725 if(IsOffChannel(cData))
5727 struct mod_chanmode change;
5728 mod_chanmode_init(&change);
5730 change.args[0].mode = MODE_CHANOP;
5731 change.args[0].u.member = AddChannelUser(chanserv, channel);
5732 mod_chanmode_announce(chanserv, channel, &change);
5734 cData->flags &= ~CHANNEL_OFFCHANNEL;
5739 reply("MSG_INVALID_BINARY", argv[1]);
5745 /* Find current option value. */
5746 value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
5750 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
5752 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
5756 static MODCMD_FUNC(chan_opt_unreviewed)
5758 struct chanData *cData = channel->channel_info;
5759 int value = (cData->flags & CHANNEL_UNREVIEWED) ? 1 : 0;
5765 /* The two directions can have different ACLs. */
5766 if(enabled_string(argv[1]))
5768 else if(disabled_string(argv[1]))
5772 reply("MSG_INVALID_BINARY", argv[1]);
5776 if (new_value != value)
5778 struct svccmd *subcmd;
5779 char subcmd_name[32];
5781 snprintf(subcmd_name, sizeof(subcmd_name), "%s %s", argv[0], (new_value ? "on" : "off"));
5782 subcmd = dict_find(cmd->parent->commands, subcmd_name, NULL);
5785 reply("MSG_COMMAND_DISABLED", subcmd_name);
5788 else if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
5792 cData->flags |= CHANNEL_UNREVIEWED;
5795 free(cData->registrar);
5796 cData->registrar = strdup(user->handle_info->handle);
5797 cData->flags &= ~CHANNEL_UNREVIEWED;
5804 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_ON"));
5806 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_OFF"));
5810 static MODCMD_FUNC(chan_opt_defaults)
5812 struct userData *uData;
5813 struct chanData *cData;
5814 const char *confirm;
5815 enum levelOption lvlOpt;
5816 enum charOption chOpt;
5818 cData = channel->channel_info;
5819 uData = GetChannelUser(cData, user->handle_info);
5820 if(!uData || (uData->access < UL_OWNER))
5822 reply("CSMSG_OWNER_DEFAULTS", channel->name);
5825 confirm = make_confirmation_string(uData);
5826 if((argc < 2) || strcmp(argv[1], confirm))
5828 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
5831 cData->flags = (CHANNEL_DEFAULT_FLAGS & ~CHANNEL_PRESERVED_FLAGS)
5832 | (cData->flags & CHANNEL_PRESERVED_FLAGS);
5833 cData->modes = chanserv_conf.default_modes;
5834 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
5835 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
5836 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
5837 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
5838 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
5843 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5845 struct chanData *cData = channel->channel_info;
5846 struct userData *uData;
5847 unsigned short value;
5851 if(!check_user_level(channel, user, option, 1, 1))
5853 reply("CSMSG_CANNOT_SET");
5856 value = user_level_from_name(argv[1], UL_OWNER+1);
5857 if(!value && strcmp(argv[1], "0"))
5859 reply("CSMSG_INVALID_ACCESS", argv[1]);
5862 uData = GetChannelUser(cData, user->handle_info);
5863 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
5865 reply("CSMSG_BAD_SETLEVEL");
5871 if(value > cData->lvlOpts[lvlGiveOps])
5873 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
5878 if(value < cData->lvlOpts[lvlGiveVoice])
5880 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
5885 /* This test only applies to owners, since non-owners
5886 * trying to set an option to above their level get caught
5887 * by the CSMSG_BAD_SETLEVEL test above.
5889 if(value > uData->access)
5891 reply("CSMSG_BAD_SETTERS");
5898 cData->lvlOpts[option] = value;
5900 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
5904 static MODCMD_FUNC(chan_opt_enfops)
5906 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
5909 static MODCMD_FUNC(chan_opt_giveops)
5911 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
5914 static MODCMD_FUNC(chan_opt_enfmodes)
5916 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
5919 static MODCMD_FUNC(chan_opt_enftopic)
5921 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
5924 static MODCMD_FUNC(chan_opt_pubcmd)
5926 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
5929 static MODCMD_FUNC(chan_opt_setters)
5931 return channel_level_option(lvlSetters, CSFUNC_ARGS);
5934 static MODCMD_FUNC(chan_opt_ctcpusers)
5936 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
5939 static MODCMD_FUNC(chan_opt_userinfo)
5941 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
5944 static MODCMD_FUNC(chan_opt_givevoice)
5946 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
5949 static MODCMD_FUNC(chan_opt_topicsnarf)
5951 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
5954 static MODCMD_FUNC(chan_opt_vote)
5956 return channel_level_option(lvlVote, CSFUNC_ARGS);
5959 static MODCMD_FUNC(chan_opt_inviteme)
5961 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
5965 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5967 struct chanData *cData = channel->channel_info;
5968 int count = charOptions[option].count, idx;
5972 idx = atoi(argv[1]);
5974 if(!isdigit(argv[1][0]) || (idx < 0) || (idx >= count))
5976 reply("CSMSG_INVALID_NUMERIC", idx);
5977 /* Show possible values. */
5978 for(idx = 0; idx < count; idx++)
5979 reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
5983 cData->chOpts[option] = charOptions[option].values[idx].value;
5987 /* Find current option value. */
5990 (idx < count) && (cData->chOpts[option] != charOptions[option].values[idx].value);
5994 /* Somehow, the option value is corrupt; reset it to the default. */
5995 cData->chOpts[option] = charOptions[option].default_value;
6000 reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
6004 static MODCMD_FUNC(chan_opt_protect)
6006 return channel_multiple_option(chProtect, CSFUNC_ARGS);
6009 static MODCMD_FUNC(chan_opt_toys)
6011 return channel_multiple_option(chToys, CSFUNC_ARGS);
6014 static MODCMD_FUNC(chan_opt_ctcpreaction)
6016 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
6019 static MODCMD_FUNC(chan_opt_topicrefresh)
6021 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
6024 static struct svccmd_list set_shows_list;
6027 handle_svccmd_unbind(struct svccmd *target) {
6029 for(ii=0; ii<set_shows_list.used; ++ii)
6030 if(target == set_shows_list.list[ii])
6031 set_shows_list.used = 0;
6034 static CHANSERV_FUNC(cmd_set)
6036 struct svccmd *subcmd;
6040 /* Check if we need to (re-)initialize set_shows_list. */
6041 if(!set_shows_list.used)
6043 if(!set_shows_list.size)
6045 set_shows_list.size = chanserv_conf.set_shows->used;
6046 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
6048 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
6050 const char *name = chanserv_conf.set_shows->list[ii];
6051 sprintf(buf, "%s %s", argv[0], name);
6052 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6055 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
6058 svccmd_list_append(&set_shows_list, subcmd);
6064 reply("CSMSG_CHANNEL_OPTIONS");
6065 for(ii = 0; ii < set_shows_list.used; ii++)
6067 subcmd = set_shows_list.list[ii];
6068 subcmd->command->func(user, channel, 1, argv+1, subcmd);
6073 sprintf(buf, "%s %s", argv[0], argv[1]);
6074 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6077 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
6080 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
6082 reply("CSMSG_NO_ACCESS");
6088 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
6092 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
6094 struct userData *uData;
6096 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6099 reply("CSMSG_NOT_USER", channel->name);
6105 /* Just show current option value. */
6107 else if(enabled_string(argv[1]))
6109 uData->flags |= mask;
6111 else if(disabled_string(argv[1]))
6113 uData->flags &= ~mask;
6117 reply("MSG_INVALID_BINARY", argv[1]);
6121 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
6125 static MODCMD_FUNC(user_opt_noautoop)
6127 struct userData *uData;
6129 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6132 reply("CSMSG_NOT_USER", channel->name);
6135 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
6136 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
6138 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
6141 static MODCMD_FUNC(user_opt_autoinvite)
6143 if((argc > 1) && !check_user_level(channel, user, lvlInviteMe, 1, 0))
6145 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
6147 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
6150 static MODCMD_FUNC(user_opt_info)
6152 struct userData *uData;
6155 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6159 /* If they got past the command restrictions (which require access)
6160 * but fail this test, we have some fool with security override on.
6162 reply("CSMSG_NOT_USER", channel->name);
6169 infoline = unsplit_string(argv + 1, argc - 1, NULL);
6170 if(strlen(infoline) > chanserv_conf.max_userinfo_length)
6172 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
6175 bp = strcspn(infoline, "\001");
6178 reply("CSMSG_BAD_INFOLINE", infoline[bp]);
6183 if(infoline[0] == '*' && infoline[1] == 0)
6186 uData->info = strdup(infoline);
6189 reply("CSMSG_USET_INFO", uData->info);
6191 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
6195 struct svccmd_list uset_shows_list;
6197 static CHANSERV_FUNC(cmd_uset)
6199 struct svccmd *subcmd;
6203 /* Check if we need to (re-)initialize uset_shows_list. */
6204 if(!uset_shows_list.used)
6208 "NoAutoOp", "AutoInvite", "Info"
6211 if(!uset_shows_list.size)
6213 uset_shows_list.size = ArrayLength(options);
6214 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
6216 for(ii = 0; ii < ArrayLength(options); ii++)
6218 const char *name = options[ii];
6219 sprintf(buf, "%s %s", argv[0], name);
6220 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6223 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
6226 svccmd_list_append(&uset_shows_list, subcmd);
6232 /* Do this so options are presented in a consistent order. */
6233 reply("CSMSG_USER_OPTIONS");
6234 for(ii = 0; ii < uset_shows_list.used; ii++)
6235 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
6239 sprintf(buf, "%s %s", argv[0], argv[1]);
6240 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6243 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
6247 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
6250 static CHANSERV_FUNC(cmd_giveownership)
6252 struct handle_info *new_owner_hi;
6253 struct userData *new_owner;
6254 struct userData *curr_user;
6255 struct userData *invoker;
6256 struct chanData *cData = channel->channel_info;
6257 struct do_not_register *dnr;
6258 const char *confirm;
6260 unsigned short co_access;
6261 char reason[MAXLEN];
6264 curr_user = GetChannelAccess(cData, user->handle_info);
6265 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
6266 if(!curr_user || (curr_user->access != UL_OWNER))
6268 struct userData *owner = NULL;
6269 for(curr_user = channel->channel_info->users;
6271 curr_user = curr_user->next)
6273 if(curr_user->access != UL_OWNER)
6277 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
6284 else if(!force && (now < cData->ownerTransfer + chanserv_conf.giveownership_period))
6286 char delay[INTERVALLEN];
6287 intervalString(delay, cData->ownerTransfer + chanserv_conf.giveownership_period - now, user->handle_info);
6288 reply("CSMSG_TRANSFER_WAIT", delay, channel->name);
6291 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
6293 if(new_owner_hi == user->handle_info)
6295 reply("CSMSG_NO_TRANSFER_SELF");
6298 new_owner = GetChannelAccess(cData, new_owner_hi);
6303 new_owner = add_channel_user(cData, new_owner_hi, UL_OWNER - 1, 0, NULL);
6307 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
6311 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
6313 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
6316 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
6317 if(!IsHelping(user))
6318 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
6320 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi->handle);
6323 invoker = GetChannelUser(cData, user->handle_info);
6324 if(invoker->access <= UL_OWNER)
6326 confirm = make_confirmation_string(curr_user);
6327 if((argc < 3) || strcmp(argv[2], confirm))
6329 reply("CSMSG_CONFIRM_GIVEOWNERSHIP", new_owner_hi->handle, confirm);
6333 if(new_owner->access >= UL_COOWNER)
6334 co_access = new_owner->access;
6336 co_access = UL_COOWNER;
6337 new_owner->access = UL_OWNER;
6339 curr_user->access = co_access;
6340 cData->ownerTransfer = now;
6341 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
6342 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
6343 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
6347 static CHANSERV_FUNC(cmd_suspend)
6349 struct handle_info *hi;
6350 struct userData *actor, *real_actor, *target;
6351 unsigned int override = 0;
6354 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6355 actor = GetChannelUser(channel->channel_info, user->handle_info);
6356 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
6357 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6359 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6362 if(target->access >= actor->access)
6364 reply("MSG_USER_OUTRANKED", hi->handle);
6367 if(target->flags & USER_SUSPENDED)
6369 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
6374 target->present = 0;
6377 if(!real_actor || target->access >= real_actor->access)
6378 override = CMD_LOG_OVERRIDE;
6379 target->flags |= USER_SUSPENDED;
6380 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
6381 return 1 | override;
6384 static CHANSERV_FUNC(cmd_unsuspend)
6386 struct handle_info *hi;
6387 struct userData *actor, *real_actor, *target;
6388 unsigned int override = 0;
6391 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6392 actor = GetChannelUser(channel->channel_info, user->handle_info);
6393 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
6394 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6396 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6399 if(target->access >= actor->access)
6401 reply("MSG_USER_OUTRANKED", hi->handle);
6404 if(!(target->flags & USER_SUSPENDED))
6406 reply("CSMSG_NOT_SUSPENDED", hi->handle);
6409 if(!real_actor || target->access >= real_actor->access)
6410 override = CMD_LOG_OVERRIDE;
6411 target->flags &= ~USER_SUSPENDED;
6412 scan_user_presence(target, NULL);
6413 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
6414 return 1 | override;
6417 static MODCMD_FUNC(cmd_deleteme)
6419 struct handle_info *hi;
6420 struct userData *target;
6421 const char *confirm_string;
6422 unsigned short access_level;
6425 hi = user->handle_info;
6426 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6428 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6431 if(target->access == UL_OWNER)
6433 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
6436 confirm_string = make_confirmation_string(target);
6437 if((argc < 2) || strcmp(argv[1], confirm_string))
6439 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
6442 access_level = target->access;
6443 channel_name = strdup(channel->name);
6444 del_channel_user(target, 1);
6445 reply("CSMSG_DELETED_YOU", access_level, channel_name);
6450 static CHANSERV_FUNC(cmd_addvote)
6452 struct chanData *cData = channel->channel_info;
6453 struct userData *uData, *target;
6454 struct handle_info *hi;
6455 if (!cData) return 0;
6457 hi = user->handle_info;
6458 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6460 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6463 if(target->access < 300) {
6464 reply("CSMSG_NO_ACCESS");
6468 reply("CSMSG_ADDVOTE_FULL");
6472 msg = unsplit_string(argv + 1, argc - 1, NULL);
6473 cData->vote = strdup(msg);
6474 cData->vote_start=0;
6475 dict_delete(cData->vote_options);
6476 cData->vote_options = dict_new();
6477 dict_set_free_data(cData->vote_options, free_vote_options);
6478 for(uData = channel->channel_info->users; uData; uData = uData->next)
6483 reply("CSMSG_ADDVOTE_DONE");
6487 static CHANSERV_FUNC(cmd_delvote)
6489 struct chanData *cData = channel->channel_info;
6490 struct userData *target;
6491 struct handle_info *hi;
6492 if (!cData) return 0;
6493 hi = user->handle_info;
6494 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6496 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6499 if(target->access < 300) {
6500 reply("CSMSG_NO_ACCESS");
6504 reply("CSMSG_NO_VOTE");
6509 reply("CSMSG_DELVOTE_DONE");
6513 static CHANSERV_FUNC(cmd_addoption)
6515 struct chanData *cData = channel->channel_info;
6516 struct userData *target;
6517 struct handle_info *hi;
6518 if (!cData) return 0;
6520 hi = user->handle_info;
6521 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6523 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6526 if(target->access < 300) {
6527 reply("CSMSG_NO_ACCESS");
6531 reply("CSMSG_NO_VOTE");
6537 msg = unsplit_string(argv + 1, argc - 1, NULL);
6540 unsigned int lastid = 1;
6541 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6542 struct vote_option *cvOpt = iter_data(it);
6543 if(cvOpt->option_id > lastid)
6544 lastid = cvOpt->option_id;
6546 struct vote_option *vOpt;
6547 vOpt = calloc(1, sizeof(*vOpt));
6548 vOpt->name = strdup(msg);
6549 vOpt->option_id = (lastid + 1);
6551 sprintf(str,"%i",(lastid + 1));
6552 vOpt->option_str = strdup(str);
6554 dict_insert(cData->vote_options,vOpt->option_str,vOpt);
6556 reply("CSMSG_ADDOPTION_DONE",dict_size(cData->vote_options),lastid,(lastid + 1));
6560 static CHANSERV_FUNC(cmd_deloption)
6562 struct chanData *cData = channel->channel_info;
6563 struct userData *uData, *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");
6581 if(cData->vote_start) {
6582 if(dict_size(cData->vote_options) < 3) {
6583 reply("CSMSG_VOTE_NEED_OPTIONS");
6588 int find_id = atoi(argv[1]);
6590 unsigned int found = 0;
6593 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6595 if (find_id == ii) {
6596 struct vote_option *vOpt = iter_data(it);
6597 found = vOpt->option_id;
6599 sprintf(str,"%i",vOpt->option_id);
6600 dict_remove(cData->vote_options, str);
6605 for(uData = channel->channel_info->users; uData; uData = uData->next) {
6606 if(uData->votefor == found) {
6611 reply("CSMSG_DELOPTION_DONE");
6614 reply("CSMSG_DELOPTION_NONE");
6619 static CHANSERV_FUNC(cmd_vote)
6621 struct chanData *cData = channel->channel_info;
6622 struct userData *target;
6623 struct handle_info *hi;
6624 unsigned int votedfor = 0;
6625 char *votedfor_str = NULL;
6627 if (!cData || !cData->vote) {
6628 reply("CSMSG_NO_VOTE");
6631 if(argc > 1 && cData->vote_start) {
6632 hi = user->handle_info;
6633 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6635 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6638 if(!check_user_level(channel, user, lvlVote, 1, 0)) {
6639 reply("CSMSG_NO_ACCESS");
6643 reply("CSMSG_VOTE_VOTED");
6646 int find_id = atoi(argv[1]);
6649 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6651 if (find_id == ii) {
6652 struct vote_option *vOpt = iter_data(it);
6655 target->votefor = vOpt->option_id;
6656 votedfor = vOpt->option_id;
6657 votedfor_str = vOpt->name;
6661 reply("CSMSG_VOTE_INVALID");
6665 if (!cData->vote_start) {
6666 reply("CSMSG_VOTE_NOT_STARTED");
6668 reply("CSMSG_VOTE_QUESTION",cData->vote);
6670 unsigned int voteid = 0;
6673 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6674 struct vote_option *vOpt = iter_data(it);
6676 reply("CSMSG_VOTE_OPTION",voteid,vOpt->name,vOpt->voted);
6678 if(argc > 1 && cData->vote_start && votedfor_str) {
6679 reply("CSMSG_VOTE_DONE",votedfor_str);
6684 static CHANSERV_FUNC(cmd_startvote)
6686 struct chanData *cData = channel->channel_info;
6687 struct userData *target;
6688 struct handle_info *hi;
6689 if (!cData) return 0;
6690 hi = user->handle_info;
6691 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6693 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6696 if(target->access < 300) {
6697 reply("CSMSG_NO_ACCESS");
6701 reply("CSMSG_NO_VOTE");
6704 if(cData->vote_start) {
6705 reply("CSMSG_STARTVOTE_RUNNING");
6708 if(dict_size(cData->vote_options) < 2) {
6709 reply("CSMSG_VOTE_NEED_OPTIONS");
6712 cData->vote_start = 1;
6713 char response[MAXLEN];
6714 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_TOP"), user->nick);
6715 irc_privmsg(cmd->parent->bot, channel->name, response);
6716 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_QUESTION"), cData->vote);
6717 irc_privmsg(cmd->parent->bot, channel->name, response);
6718 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 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_OPTION"), voteid, vOpt->name);
6724 irc_privmsg(cmd->parent->bot, channel->name, response);
6726 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_ACCESS"), cData->lvlOpts[lvlVote]); //Todo
6727 irc_privmsg(cmd->parent->bot, channel->name, response);
6728 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_HOWTO")); //Todo
6729 irc_privmsg(cmd->parent->bot, channel->name, response);
6733 static CHANSERV_FUNC(cmd_endvote)
6735 struct chanData *cData = channel->channel_info;
6736 struct userData *target;
6737 struct handle_info *hi;
6738 if (!cData) return 0;
6739 hi = user->handle_info;
6740 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6742 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6745 if(target->access < 300) {
6746 reply("CSMSG_NO_ACCESS");
6750 reply("CSMSG_NO_VOTE");
6753 if(!cData->vote_start) {
6754 reply("CSMSG_ENDVOTE_STOPPED");
6757 cData->vote_start = 0;
6758 reply("CSMSG_ENDVOTE_DONE");
6762 static CHANSERV_FUNC(cmd_voteresults)
6764 struct chanData *cData = channel->channel_info;
6765 struct userData *target;
6766 struct handle_info *hi;
6767 if (!cData) return 0;
6769 reply("CSMSG_NO_VOTE");
6772 if (argc > 1 && !irccasecmp(argv[1], "*")) {
6773 hi = user->handle_info;
6774 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6776 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6779 if(target->access < 300) {
6780 reply("CSMSG_NO_ACCESS");
6783 char response[MAXLEN];
6784 sprintf(response, user_find_message(user, "CSMSG_VOTERES_QUESTION"), cData->vote);
6785 irc_privmsg(cmd->parent->bot, channel->name, response);
6786 unsigned int voteid = 0;
6788 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6789 struct vote_option *vOpt = iter_data(it);
6791 sprintf(response, user_find_message(user, "CSMSG_VOTERES_OPTION"), voteid, vOpt->name, vOpt->voted);
6792 irc_privmsg(cmd->parent->bot, channel->name, response);
6795 reply("CSMSG_VOTE_QUESTION",cData->vote);
6796 unsigned int voteid = 0;
6798 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6799 struct vote_option *vOpt = iter_data(it);
6801 reply("CSMSG_VOTE_OPTION",voteid,vOpt->name,vOpt->voted);
6808 chanserv_refresh_topics(UNUSED_ARG(void *data))
6810 unsigned int refresh_num = (now - self->link_time) / chanserv_conf.refresh_period;
6811 struct chanData *cData;
6814 for(cData = channelList; cData; cData = cData->next)
6816 if(IsSuspended(cData))
6818 opt = cData->chOpts[chTopicRefresh];
6821 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
6824 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
6825 cData->last_refresh = refresh_num;
6827 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
6830 static CHANSERV_FUNC(cmd_unf)
6834 char response[MAXLEN];
6835 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
6836 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6837 irc_privmsg(cmd->parent->bot, channel->name, response);
6840 reply("CSMSG_UNF_RESPONSE");
6844 static CHANSERV_FUNC(cmd_ping)
6848 char response[MAXLEN];
6849 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
6850 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6851 irc_privmsg(cmd->parent->bot, channel->name, response);
6854 reply("CSMSG_PING_RESPONSE");
6858 static CHANSERV_FUNC(cmd_wut)
6862 char response[MAXLEN];
6863 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
6864 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6865 irc_privmsg(cmd->parent->bot, channel->name, response);
6868 reply("CSMSG_WUT_RESPONSE");
6872 static CHANSERV_FUNC(cmd_8ball)
6874 unsigned int i, j, accum;
6879 for(i=1; i<argc; i++)
6880 for(j=0; argv[i][j]; j++)
6881 accum = (accum << 5) - accum + toupper(argv[i][j]);
6882 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
6885 char response[MAXLEN];
6886 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
6887 irc_privmsg(cmd->parent->bot, channel->name, response);
6890 send_message_type(4, user, cmd->parent->bot, "%s", resp);
6894 static CHANSERV_FUNC(cmd_d)
6896 unsigned long sides, count, modifier, ii, total;
6897 char response[MAXLEN], *sep;
6901 if((count = strtoul(argv[1], &sep, 10)) < 1)
6911 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
6912 && (sides = strtoul(sep+1, &sep, 10)) > 1)
6916 else if((sep[0] == '-') && isdigit(sep[1]))
6917 modifier = strtoul(sep, NULL, 10);
6918 else if((sep[0] == '+') && isdigit(sep[1]))
6919 modifier = strtoul(sep+1, NULL, 10);
6926 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
6931 reply("CSMSG_BAD_DICE_COUNT", count, 10);
6934 for(total = ii = 0; ii < count; ++ii)
6935 total += (rand() % sides) + 1;
6938 if((count > 1) || modifier)
6940 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
6941 sprintf(response, fmt, total, count, sides, modifier);
6945 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
6946 sprintf(response, fmt, total, sides);
6949 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
6951 send_message_type(4, user, cmd->parent->bot, "%s", response);
6955 static CHANSERV_FUNC(cmd_huggle)
6957 /* CTCP must be via PRIVMSG, never notice */
6959 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
6961 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
6966 chanserv_adjust_limit(void *data)
6968 struct mod_chanmode change;
6969 struct chanData *cData = data;
6970 struct chanNode *channel = cData->channel;
6973 if(IsSuspended(cData))
6976 cData->limitAdjusted = now;
6977 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
6978 if(cData->modes.modes_set & MODE_LIMIT)
6980 if(limit > cData->modes.new_limit)
6981 limit = cData->modes.new_limit;
6982 else if(limit == cData->modes.new_limit)
6986 mod_chanmode_init(&change);
6987 change.modes_set = MODE_LIMIT;
6988 change.new_limit = limit;
6989 mod_chanmode_announce(chanserv, channel, &change);
6993 handle_new_channel(struct chanNode *channel)
6995 struct chanData *cData;
6997 if(!(cData = channel->channel_info))
7000 if(cData->modes.modes_set || cData->modes.modes_clear)
7001 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
7003 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
7004 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
7007 /* Welcome to my worst nightmare. Warning: Read (or modify)
7008 the code below at your own risk. */
7010 handle_join(struct modeNode *mNode)
7012 struct mod_chanmode change;
7013 struct userNode *user = mNode->user;
7014 struct chanNode *channel = mNode->channel;
7015 struct chanData *cData;
7016 struct userData *uData = NULL;
7017 struct banData *bData;
7018 struct handle_info *handle;
7019 unsigned int modes = 0, info = 0;
7023 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
7026 cData = channel->channel_info;
7027 if(channel->members.used > cData->max) {
7028 cData->max = channel->members.used;
7029 cData->max_time = now;
7032 for(i = 0; i < channel->invited.used; i++)
7034 if(channel->invited.list[i] == user) {
7035 userList_remove(&channel->invited, user);
7039 /* Check for bans. If they're joining through a ban, one of two
7041 * 1: Join during a netburst, by riding the break. Kick them
7042 * unless they have ops or voice in the channel.
7043 * 2: They're allowed to join through the ban (an invite in
7044 * ircu2.10, or a +e on Hybrid, or something).
7045 * If they're not joining through a ban, and the banlist is not
7046 * full, see if they're on the banlist for the channel. If so,
7049 if(user->uplink->burst && !mNode->modes)
7052 for(ii = 0; ii < channel->banlist.used; ii++)
7054 if(user_matches_glob(user, channel->banlist.list[ii]->ban, MATCH_USENICK))
7056 /* Riding a netburst. Naughty. */
7057 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
7063 mod_chanmode_init(&change);
7065 if(channel->banlist.used < MAXBANS)
7067 /* Not joining through a ban. */
7068 for(bData = cData->bans;
7069 bData && !user_matches_glob(user, bData->mask, MATCH_USENICK);
7070 bData = bData->next);
7074 char kick_reason[MAXLEN];
7075 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
7077 bData->triggered = now;
7078 if(bData != cData->bans)
7080 /* Shuffle the ban to the head of the list. */
7082 bData->next->prev = bData->prev;
7084 bData->prev->next = bData->next;
7087 bData->next = cData->bans;
7090 cData->bans->prev = bData;
7091 cData->bans = bData;
7094 change.args[0].mode = MODE_BAN;
7095 change.args[0].u.hostmask = bData->mask;
7096 mod_chanmode_announce(chanserv, channel, &change);
7097 KickChannelUser(user, channel, chanserv, kick_reason);
7102 /* ChanServ will not modify the limits in join-flooded channels,
7103 or when there are enough slots left below the limit. */
7104 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
7105 && !channel->join_flooded
7106 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
7108 /* The user count has begun "bumping" into the channel limit,
7109 so set a timer to raise the limit a bit. Any previous
7110 timers are removed so three incoming users within the delay
7111 results in one limit change, not three. */
7113 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7114 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7117 if(channel->join_flooded)
7119 /* don't automatically give ops or voice during a join flood */
7121 else if(cData->lvlOpts[lvlGiveOps] == 0)
7122 modes |= MODE_CHANOP;
7123 else if(cData->lvlOpts[lvlGiveVoice] == 0)
7124 modes |= MODE_VOICE;
7126 greeting = cData->greeting;
7127 if(user->handle_info)
7129 handle = user->handle_info;
7131 if(IsHelper(user) && !IsHelping(user))
7134 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7136 if(channel == chanserv_conf.support_channels.list[ii])
7138 HANDLE_SET_FLAG(user->handle_info, HELPING);
7144 uData = GetTrueChannelAccess(cData, handle);
7145 if(uData && !IsUserSuspended(uData))
7147 /* Ops and above were handled by the above case. */
7148 if(IsUserAutoOp(uData))
7150 if(uData->access >= cData->lvlOpts[lvlGiveOps])
7151 modes |= MODE_CHANOP;
7152 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
7153 modes |= MODE_VOICE;
7155 if(uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
7156 cData->visited = now;
7157 if(cData->user_greeting)
7158 greeting = cData->user_greeting;
7160 && (uData->access >= cData->lvlOpts[lvlUserInfo])
7161 && ((now - uData->seen) >= chanserv_conf.info_delay)
7169 /* If user joining normally (not during burst), apply op or voice,
7170 * and send greeting/userinfo as appropriate.
7172 if(!user->uplink->burst)
7176 if(modes & MODE_CHANOP)
7177 modes &= ~MODE_VOICE;
7178 change.args[0].mode = modes;
7179 change.args[0].u.member = mNode;
7180 mod_chanmode_announce(chanserv, channel, &change);
7183 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
7184 if(uData && info && (modes || !(channel->modes & MODE_DELAYJOINS)))
7185 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
7191 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
7193 struct mod_chanmode change;
7194 struct userData *channel;
7195 unsigned int ii, jj;
7197 if(!user->handle_info)
7200 mod_chanmode_init(&change);
7202 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
7204 struct chanNode *cn;
7205 struct modeNode *mn;
7206 if(IsUserSuspended(channel)
7207 || IsSuspended(channel->channel)
7208 || !(cn = channel->channel->channel))
7211 mn = GetUserMode(cn, user);
7214 if(!IsUserSuspended(channel)
7215 && IsUserAutoInvite(channel)
7216 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
7218 && !user->uplink->burst)
7219 irc_invite(chanserv, user, cn);
7223 if(channel->access >= UL_PRESENT && !HANDLE_FLAGGED(channel->handle, BOT))
7224 channel->channel->visited = now;
7226 if(IsUserAutoOp(channel))
7228 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
7229 change.args[0].mode = MODE_CHANOP;
7230 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
7231 change.args[0].mode = MODE_VOICE;
7233 change.args[0].mode = 0;
7234 change.args[0].u.member = mn;
7235 if(change.args[0].mode)
7236 mod_chanmode_announce(chanserv, cn, &change);
7239 channel->seen = now;
7240 channel->present = 1;
7243 for(ii = 0; ii < user->channels.used; ++ii)
7245 struct chanNode *chan = user->channels.list[ii]->channel;
7246 struct banData *ban;
7248 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
7249 || !chan->channel_info
7250 || IsSuspended(chan->channel_info))
7252 for(jj = 0; jj < chan->banlist.used; ++jj)
7253 if(user_matches_glob(user, chan->banlist.list[jj]->ban, MATCH_USENICK))
7255 if(jj < chan->banlist.used)
7257 for(ban = chan->channel_info->bans; ban; ban = ban->next)
7259 char kick_reason[MAXLEN];
7260 if(!user_matches_glob(user, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
7262 change.args[0].mode = MODE_BAN;
7263 change.args[0].u.hostmask = ban->mask;
7264 mod_chanmode_announce(chanserv, chan, &change);
7265 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
7266 KickChannelUser(user, chan, chanserv, kick_reason);
7267 ban->triggered = now;
7272 if(IsSupportHelper(user))
7274 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7276 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
7278 HANDLE_SET_FLAG(user->handle_info, HELPING);
7286 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
7288 struct chanData *cData;
7289 struct userData *uData;
7291 cData = mn->channel->channel_info;
7292 if(!cData || IsSuspended(cData) || IsLocal(mn->user))
7295 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
7297 /* Allow for a bit of padding so that the limit doesn't
7298 track the user count exactly, which could get annoying. */
7299 if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
7301 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7302 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7306 if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
7308 scan_user_presence(uData, mn->user);
7310 if (uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
7311 cData->visited = now;
7314 if(IsHelping(mn->user) && IsSupportHelper(mn->user))
7317 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii) {
7318 struct chanNode *channel;
7319 struct userNode *exclude;
7320 /* When looking at the channel that is being /part'ed, we
7321 * have to skip over the client that is leaving. For
7322 * other channels, we must not do that.
7324 channel = chanserv_conf.support_channels.list[ii];
7325 exclude = (channel == mn->channel) ? mn->user : NULL;
7326 if(find_handle_in_channel(channel, mn->user->handle_info, exclude))
7329 if(ii == chanserv_conf.support_channels.used)
7330 HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
7335 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
7337 struct userData *uData;
7339 if(!channel->channel_info || !kicker || IsService(kicker)
7340 || (kicker == victim) || IsSuspended(channel->channel_info)
7341 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
7344 if(protect_user(victim, kicker, channel->channel_info))
7346 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED_2");
7347 KickChannelUser(kicker, channel, chanserv, reason);
7350 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
7355 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
7357 struct chanData *cData;
7359 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
7362 cData = channel->channel_info;
7363 if(bad_topic(channel, user, channel->topic))
7365 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
7366 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
7367 SetChannelTopic(channel, chanserv, old_topic, 1);
7368 else if(cData->topic)
7369 SetChannelTopic(channel, chanserv, cData->topic, 1);
7372 /* With topicsnarf, grab the topic and save it as the default topic. */
7373 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
7376 cData->topic = strdup(channel->topic);
7382 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
7384 struct mod_chanmode *bounce = NULL;
7385 unsigned int bnc, ii;
7388 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
7391 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
7392 && mode_lock_violated(&channel->channel_info->modes, change))
7394 char correct[MAXLEN];
7395 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
7396 mod_chanmode_format(&channel->channel_info->modes, correct);
7397 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
7399 for(ii = bnc = 0; ii < change->argc; ++ii)
7401 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
7403 const struct userNode *victim = change->args[ii].u.member->user;
7404 if(!protect_user(victim, user, channel->channel_info))
7407 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7410 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
7411 bounce->args[bnc].u.member = GetUserMode(channel, user);
7412 if(bounce->args[bnc].u.member)
7416 bounce->args[bnc].mode = MODE_CHANOP;
7417 bounce->args[bnc].u.member = change->args[ii].u.member;
7419 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
7421 else if(change->args[ii].mode & MODE_CHANOP)
7423 const struct userNode *victim = change->args[ii].u.member->user;
7424 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
7427 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7428 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
7429 bounce->args[bnc].u.member = change->args[ii].u.member;
7432 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
7434 const char *ban = change->args[ii].u.hostmask;
7435 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
7438 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7439 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
7440 bounce->args[bnc].u.hostmask = strdup(ban);
7442 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
7447 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
7448 mod_chanmode_announce(chanserv, channel, bounce);
7449 for(ii = 0; ii < change->argc; ++ii)
7450 if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
7451 free((char*)bounce->args[ii].u.hostmask);
7452 mod_chanmode_free(bounce);
7457 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
7459 struct chanNode *channel;
7460 struct banData *bData;
7461 struct mod_chanmode change;
7462 unsigned int ii, jj;
7463 char kick_reason[MAXLEN];
7465 mod_chanmode_init(&change);
7467 change.args[0].mode = MODE_BAN;
7468 for(ii = 0; ii < user->channels.used; ++ii)
7470 channel = user->channels.list[ii]->channel;
7471 /* Need not check for bans if they're opped or voiced. */
7472 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
7474 /* Need not check for bans unless channel registration is active. */
7475 if(!channel->channel_info || IsSuspended(channel->channel_info))
7477 /* Look for a matching ban already on the channel. */
7478 for(jj = 0; jj < channel->banlist.used; ++jj)
7479 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
7481 /* Need not act if we found one. */
7482 if(jj < channel->banlist.used)
7484 /* Look for a matching ban in this channel. */
7485 for(bData = channel->channel_info->bans; bData; bData = bData->next)
7487 if(!user_matches_glob(user, bData->mask, MATCH_USENICK | MATCH_VISIBLE))
7489 change.args[0].u.hostmask = bData->mask;
7490 mod_chanmode_announce(chanserv, channel, &change);
7491 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
7492 KickChannelUser(user, channel, chanserv, kick_reason);
7493 bData->triggered = now;
7494 break; /* we don't need to check any more bans in the channel */
7499 static void handle_rename(struct handle_info *handle, const char *old_handle)
7501 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
7505 dict_remove2(handle_dnrs, old_handle, 1);
7506 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
7507 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
7512 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
7514 struct userNode *h_user;
7516 if(handle->channels)
7518 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
7519 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
7521 while(handle->channels)
7522 del_channel_user(handle->channels, 1);
7527 handle_server_link(UNUSED_ARG(struct server *server))
7529 struct chanData *cData;
7531 for(cData = channelList; cData; cData = cData->next)
7533 if(!IsSuspended(cData))
7534 cData->may_opchan = 1;
7535 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
7536 && !cData->channel->join_flooded
7537 && ((cData->channel->limit - cData->channel->members.used)
7538 < chanserv_conf.adjust_threshold))
7540 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7541 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7547 chanserv_conf_read(void)
7551 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
7552 struct mod_chanmode *change;
7553 struct string_list *strlist;
7554 struct chanNode *chan;
7557 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
7559 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
7562 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7563 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7564 chanserv_conf.support_channels.used = 0;
7565 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
7567 for(ii = 0; ii < strlist->used; ++ii)
7569 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7572 chan = AddChannel(strlist->list[ii], now, str2, NULL);
7574 channelList_append(&chanserv_conf.support_channels, chan);
7577 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
7580 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7583 chan = AddChannel(str, now, str2, NULL);
7585 channelList_append(&chanserv_conf.support_channels, chan);
7587 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
7588 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
7589 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
7590 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
7591 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
7592 chanserv_conf.greeting_length = str ? atoi(str) : 200;
7593 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
7594 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
7595 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
7596 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
7597 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
7598 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
7599 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
7600 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
7601 str = database_get_data(conf_node, KEY_DNR_EXPIRE_FREQ, RECDB_QSTRING);
7602 chanserv_conf.dnr_expire_frequency = str ? ParseInterval(str) : 3600;
7603 str = database_get_data(conf_node, KEY_INVITED_INTERVAL, RECDB_QSTRING);
7604 chanserv_conf.invited_timeout = str ? ParseInterval(str) : 600*2;
7605 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
7606 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
7607 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
7608 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
7609 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
7610 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
7611 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
7612 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
7613 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
7615 NickChange(chanserv, str, 0);
7616 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
7617 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
7618 str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
7619 chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
7620 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
7621 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
7622 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
7623 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
7624 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
7625 chanserv_conf.max_owned = str ? atoi(str) : 5;
7626 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
7627 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
7628 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
7629 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
7630 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
7631 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
7632 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
7635 safestrncpy(mode_line, str, sizeof(mode_line));
7636 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
7637 if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE|MCP_NO_APASS, 0))
7638 && (change->argc < 2))
7640 chanserv_conf.default_modes = *change;
7641 mod_chanmode_free(change);
7643 free_string_list(chanserv_conf.set_shows);
7644 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
7646 strlist = string_list_copy(strlist);
7649 static const char *list[] = {
7650 /* free form text */
7651 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
7652 /* options based on user level */
7653 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
7654 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
7655 /* multiple choice options */
7656 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
7657 /* binary options */
7658 "DynLimit", "NoDelete", "expire", "Vote",
7662 strlist = alloc_string_list(ArrayLength(list)-1);
7663 for(ii=0; list[ii]; ii++)
7664 string_list_append(strlist, strdup(list[ii]));
7666 chanserv_conf.set_shows = strlist;
7667 /* We don't look things up now, in case the list refers to options
7668 * defined by modules initialized after this point. Just mark the
7669 * function list as invalid, so it will be initialized.
7671 set_shows_list.used = 0;
7672 free_string_list(chanserv_conf.eightball);
7673 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
7676 strlist = string_list_copy(strlist);
7680 strlist = alloc_string_list(4);
7681 string_list_append(strlist, strdup("Yes."));
7682 string_list_append(strlist, strdup("No."));
7683 string_list_append(strlist, strdup("Maybe so."));
7685 chanserv_conf.eightball = strlist;
7686 free_string_list(chanserv_conf.old_ban_names);
7687 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
7689 strlist = string_list_copy(strlist);
7691 strlist = alloc_string_list(2);
7692 chanserv_conf.old_ban_names = strlist;
7693 str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
7694 off_channel = str ? atoi(str) : 0;
7698 chanserv_note_type_read(const char *key, struct record_data *rd)
7701 struct note_type *ntype;
7704 if(!(obj = GET_RECORD_OBJECT(rd)))
7706 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
7709 if(!(ntype = chanserv_create_note_type(key)))
7711 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
7715 /* Figure out set access */
7716 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
7718 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7719 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
7721 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
7723 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
7724 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
7726 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
7728 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
7732 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
7733 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7734 ntype->set_access.min_opserv = 0;
7737 /* Figure out visibility */
7738 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
7739 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7740 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
7741 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7742 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
7743 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
7744 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
7745 ntype->visible_type = NOTE_VIS_ALL;
7747 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7749 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
7750 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
7754 vote_option_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7756 struct vote_option *vOpt;
7759 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7761 log_module(CS_LOG, LOG_ERROR, "Invalid vote option in %s.", chan->channel->name);
7765 vOpt = calloc(1, sizeof(*vOpt));
7766 vOpt->name = strdup(database_get_data(rd->d.object, KEY_VOTE_OPTION_NAME, RECDB_QSTRING));
7767 str = database_get_data(rd->d.object, KEY_VOTE_OPTION_VOTED, RECDB_QSTRING);
7768 vOpt->voted = str ? atoi(str) : 0;
7769 vOpt->option_id = str ? atoi(key) : 0;
7770 vOpt->option_str = strdup(key);
7771 dict_insert(chan->vote_options,vOpt->option_str,vOpt);
7775 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7777 struct handle_info *handle;
7778 struct userData *uData;
7779 char *seen, *inf, *flags, *voted, *votefor;
7780 unsigned long last_seen;
7781 unsigned short access_level;
7783 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7785 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
7789 access_level = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
7790 if(access_level > UL_OWNER)
7792 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
7796 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
7797 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
7798 last_seen = seen ? strtoul(seen, NULL, 0) : now;
7799 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
7800 voted = database_get_data(rd->d.object, KEY_VOTE_VOTED, RECDB_QSTRING);
7801 votefor = database_get_data(rd->d.object, KEY_VOTE_VOTEDFOR, RECDB_QSTRING);
7802 handle = get_handle_info(key);
7805 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
7809 uData = add_channel_user(chan, handle, access_level, last_seen, inf);
7810 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
7812 uData->voted = voted ? strtoul(voted, NULL, 0) : 0;
7813 uData->votefor = votefor ? strtoul(votefor, NULL, 0) : 0;
7821 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7823 struct banData *bData;
7824 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
7825 unsigned long set_time, triggered_time, expires_time;
7827 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7829 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
7833 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
7834 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
7835 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
7836 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
7837 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
7838 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
7839 if (!reason || !owner)
7842 set_time = set ? strtoul(set, NULL, 0) : now;
7843 triggered_time = triggered ? strtoul(triggered, NULL, 0) : 0;
7845 expires_time = strtoul(s_expires, NULL, 0);
7847 expires_time = set_time + atoi(s_duration);
7851 if(!reason || (expires_time && (expires_time < now)))
7854 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
7857 static struct suspended *
7858 chanserv_read_suspended(dict_t obj)
7860 struct suspended *suspended = calloc(1, sizeof(*suspended));
7864 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
7865 suspended->expires = str ? strtoul(str, NULL, 0) : 0;
7866 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
7867 suspended->revoked = str ? strtoul(str, NULL, 0) : 0;
7868 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
7869 suspended->issued = str ? strtoul(str, NULL, 0) : 0;
7870 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
7871 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
7872 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
7873 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
7878 chanserv_channel_read(const char *key, struct record_data *hir)
7880 struct suspended *suspended;
7881 struct mod_chanmode *modes;
7882 struct chanNode *cNode;
7883 struct chanData *cData;
7884 struct dict *channel, *obj;
7885 char *str, *argv[10];
7889 channel = hir->d.object;
7891 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
7894 cNode = AddChannel(key, now, NULL, NULL);
7897 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
7900 cData = register_channel(cNode, str);
7903 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
7907 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
7909 enum levelOption lvlOpt;
7910 enum charOption chOpt;
7912 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
7913 cData->flags = atoi(str);
7915 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7917 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
7919 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
7920 else if(levelOptions[lvlOpt].old_flag)
7922 if(cData->flags & levelOptions[lvlOpt].old_flag)
7923 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
7925 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
7929 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7931 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
7933 cData->chOpts[chOpt] = str[0];
7936 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
7938 enum levelOption lvlOpt;
7939 enum charOption chOpt;
7942 cData->flags = base64toint(str, 5);
7943 count = strlen(str += 5);
7944 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7947 if(levelOptions[lvlOpt].old_flag)
7949 if(cData->flags & levelOptions[lvlOpt].old_flag)
7950 lvl = levelOptions[lvlOpt].flag_value;
7952 lvl = levelOptions[lvlOpt].default_value;
7954 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
7956 case 'c': lvl = UL_COOWNER; break;
7957 case 'm': lvl = UL_MASTER; break;
7958 case 'n': lvl = UL_OWNER+1; break;
7959 case 'o': lvl = UL_OP; break;
7960 case 'p': lvl = UL_PEON; break;
7961 case 'w': lvl = UL_OWNER; break;
7962 default: lvl = 0; break;
7964 cData->lvlOpts[lvlOpt] = lvl;
7966 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7967 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
7970 if((str = database_get_data(hir->d.object, KEY_EXPIRE, RECDB_QSTRING)))
7972 cData->expiry = atoi(str);
7973 if(cData->expiry > 0) {
7974 if(cData->expiry > now) {
7975 timeq_add(cData->expiry, chanserv_expire_channel, cData);
7977 timeq_add(1, chanserv_expire_channel, cData);
7984 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
7986 suspended = chanserv_read_suspended(obj);
7987 cData->suspended = suspended;
7988 suspended->cData = cData;
7989 /* We could use suspended->expires and suspended->revoked to
7990 * set the CHANNEL_SUSPENDED flag, but we don't. */
7992 else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
7994 suspended = calloc(1, sizeof(*suspended));
7995 suspended->issued = 0;
7996 suspended->revoked = 0;
7997 suspended->suspender = strdup(str);
7998 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
7999 suspended->expires = str ? atoi(str) : 0;
8000 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
8001 suspended->reason = strdup(str ? str : "No reason");
8002 suspended->previous = NULL;
8003 cData->suspended = suspended;
8004 suspended->cData = cData;
8008 cData->flags &= ~CHANNEL_SUSPENDED;
8009 suspended = NULL; /* to squelch a warning */
8012 if(IsSuspended(cData)) {
8013 if(suspended->expires > now)
8014 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
8015 else if(suspended->expires)
8016 cData->flags &= ~CHANNEL_SUSPENDED;
8019 if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
8020 struct mod_chanmode change;
8021 mod_chanmode_init(&change);
8023 change.args[0].mode = MODE_CHANOP;
8024 change.args[0].u.member = AddChannelUser(chanserv, cNode);
8025 mod_chanmode_announce(chanserv, cNode, &change);
8028 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
8029 cData->registered = str ? strtoul(str, NULL, 0) : now;
8030 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
8031 cData->visited = str ? strtoul(str, NULL, 0) : now;
8032 str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
8033 cData->ownerTransfer = str ? strtoul(str, NULL, 0) : 0;
8034 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
8035 cData->max = str ? atoi(str) : 0;
8036 str = database_get_data(channel, KEY_MAX_TIME, RECDB_QSTRING);
8037 cData->max_time = str ? atoi(str) : 0;
8038 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
8039 cData->greeting = str ? strdup(str) : NULL;
8040 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
8041 cData->user_greeting = str ? strdup(str) : NULL;
8042 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
8043 cData->topic_mask = str ? strdup(str) : NULL;
8044 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
8045 cData->topic = str ? strdup(str) : NULL;
8047 str = database_get_data(channel, KEY_VOTE, RECDB_QSTRING);
8049 cData->vote = str ? strdup(str) : NULL;
8050 dict_delete(cData->vote_options);
8051 cData->vote_options = dict_new();
8052 dict_set_free_data(cData->vote_options, free_vote_options);
8053 str = database_get_data(channel, KEY_VOTE_START, RECDB_QSTRING);
8054 cData->vote_start = str ? atoi(str) : 0;
8055 obj = database_get_data(channel, KEY_VOTE_OPTIONS, RECDB_OBJECT);
8056 for(it = dict_first(obj); it; it = iter_next(it)) {
8057 vote_option_read_helper(iter_key(it), iter_data(it), cData);
8061 if(!IsSuspended(cData)
8062 && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
8063 && (argc = split_line(str, 0, ArrayLength(argv), argv))
8064 && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE|MCP_NO_APASS, 0))) {
8065 cData->modes = *modes;
8067 cData->modes.modes_set |= MODE_REGISTERED;
8068 if(cData->modes.argc > 1)
8069 cData->modes.argc = 1;
8070 mod_chanmode_announce(chanserv, cNode, &cData->modes);
8071 mod_chanmode_free(modes);
8074 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
8075 for(it = dict_first(obj); it; it = iter_next(it))
8076 user_read_helper(iter_key(it), iter_data(it), cData);
8078 if(!cData->users && !IsProtected(cData))
8080 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
8081 unregister_channel(cData, "has empty user list.");
8085 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
8086 for(it = dict_first(obj); it; it = iter_next(it))
8087 ban_read_helper(iter_key(it), iter_data(it), cData);
8089 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
8090 for(it = dict_first(obj); it; it = iter_next(it))
8092 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
8093 struct record_data *rd = iter_data(it);
8094 const char *note, *setter;
8096 if(rd->type != RECDB_OBJECT)
8098 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
8102 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
8104 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
8106 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
8110 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
8111 if(!setter) setter = "<unknown>";
8112 chanserv_add_channel_note(cData, ntype, setter, note);
8120 chanserv_dnr_read(const char *key, struct record_data *hir)
8122 const char *setter, *reason, *str;
8123 struct do_not_register *dnr;
8124 unsigned long expiry;
8126 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
8129 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
8132 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
8135 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
8138 str = database_get_data(hir->d.object, KEY_EXPIRES, RECDB_QSTRING);
8139 expiry = str ? strtoul(str, NULL, 0) : 0;
8140 if(expiry && expiry <= now)
8142 dnr = chanserv_add_dnr(key, setter, expiry, reason);
8145 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
8147 dnr->set = atoi(str);
8153 chanserv_saxdb_read(struct dict *database)
8155 struct dict *section;
8158 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
8159 for(it = dict_first(section); it; it = iter_next(it))
8160 chanserv_note_type_read(iter_key(it), iter_data(it));
8162 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
8163 for(it = dict_first(section); it; it = iter_next(it))
8164 chanserv_channel_read(iter_key(it), iter_data(it));
8166 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
8167 for(it = dict_first(section); it; it = iter_next(it))
8168 chanserv_dnr_read(iter_key(it), iter_data(it));
8174 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
8176 int high_present = 0;
8177 saxdb_start_record(ctx, KEY_USERS, 1);
8178 for(; uData; uData = uData->next)
8180 if((uData->access >= UL_PRESENT) && uData->present && !HANDLE_FLAGGED(uData->handle, BOT))
8182 saxdb_start_record(ctx, uData->handle->handle, 0);
8183 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
8184 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
8186 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
8187 if(uData->channel->vote && uData->voted)
8188 saxdb_write_int(ctx, KEY_VOTE_VOTED, uData->voted);
8189 if(uData->channel->vote && uData->votefor)
8190 saxdb_write_int(ctx, KEY_VOTE_VOTEDFOR, uData->votefor);
8192 saxdb_write_string(ctx, KEY_INFO, uData->info);
8193 saxdb_end_record(ctx);
8195 saxdb_end_record(ctx);
8196 return high_present;
8200 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
8204 saxdb_start_record(ctx, KEY_BANS, 1);
8205 for(; bData; bData = bData->next)
8207 saxdb_start_record(ctx, bData->mask, 0);
8208 saxdb_write_int(ctx, KEY_SET, bData->set);
8209 if(bData->triggered)
8210 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
8212 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
8214 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
8216 saxdb_write_string(ctx, KEY_REASON, bData->reason);
8217 saxdb_end_record(ctx);
8219 saxdb_end_record(ctx);
8223 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
8225 saxdb_start_record(ctx, name, 0);
8226 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
8227 saxdb_write_string(ctx, KEY_REASON, susp->reason);
8229 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
8231 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
8233 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
8235 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
8236 saxdb_end_record(ctx);
8240 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
8244 enum levelOption lvlOpt;
8245 enum charOption chOpt;
8248 saxdb_start_record(ctx, channel->channel->name, 1);
8250 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
8251 saxdb_write_int(ctx, KEY_MAX, channel->max);
8252 saxdb_write_int(ctx, KEY_MAX_TIME, channel->max_time);
8254 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
8255 if(channel->registrar)
8256 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
8257 if(channel->greeting)
8258 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
8259 if(channel->user_greeting)
8260 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
8261 if(channel->topic_mask)
8262 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
8263 if(channel->suspended)
8264 chanserv_write_suspended(ctx, "suspended", channel->suspended);
8266 saxdb_write_int(ctx, KEY_EXPIRE, channel->expiry);
8269 saxdb_write_string(ctx, KEY_VOTE, channel->vote);
8270 if(channel->vote_start)
8271 saxdb_write_int(ctx, KEY_VOTE_START, channel->vote_start);
8272 if (dict_size(channel->vote_options)) {
8273 saxdb_start_record(ctx, KEY_VOTE_OPTIONS, 1);
8274 for (it = dict_first(channel->vote_options); it; it = iter_next(it)) {
8275 struct vote_option *vOpt = iter_data(it);
8277 sprintf(str,"%i",vOpt->option_id);
8278 saxdb_start_record(ctx, str, 0);
8280 saxdb_write_int(ctx, KEY_VOTE_OPTION_VOTED, vOpt->voted);
8282 saxdb_write_string(ctx, KEY_VOTE_OPTION_NAME, vOpt->name);
8283 saxdb_end_record(ctx);
8285 saxdb_end_record(ctx);
8289 saxdb_start_record(ctx, KEY_OPTIONS, 0);
8290 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
8291 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8292 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
8293 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8295 buf[0] = channel->chOpts[chOpt];
8297 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
8299 saxdb_end_record(ctx);
8301 if(channel->modes.modes_set || channel->modes.modes_clear)
8303 mod_chanmode_format(&channel->modes, buf);
8304 saxdb_write_string(ctx, KEY_MODES, buf);
8307 high_present = chanserv_write_users(ctx, channel->users);
8308 chanserv_write_bans(ctx, channel->bans);
8310 if(dict_size(channel->notes))
8314 saxdb_start_record(ctx, KEY_NOTES, 1);
8315 for(it = dict_first(channel->notes); it; it = iter_next(it))
8317 struct note *note = iter_data(it);
8318 saxdb_start_record(ctx, iter_key(it), 0);
8319 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
8320 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
8321 saxdb_end_record(ctx);
8323 saxdb_end_record(ctx);
8326 if(channel->ownerTransfer)
8327 saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
8328 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
8329 saxdb_end_record(ctx);
8333 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
8337 saxdb_start_record(ctx, ntype->name, 0);
8338 switch(ntype->set_access_type)
8340 case NOTE_SET_CHANNEL_ACCESS:
8341 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
8343 case NOTE_SET_CHANNEL_SETTER:
8344 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
8346 case NOTE_SET_PRIVILEGED: default:
8347 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
8350 switch(ntype->visible_type)
8352 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
8353 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
8354 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
8356 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
8357 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
8358 saxdb_end_record(ctx);
8362 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
8364 struct do_not_register *dnr;
8365 dict_iterator_t it, next;
8367 for(it = dict_first(dnrs); it; it = next)
8369 next = iter_next(it);
8370 dnr = iter_data(it);
8371 if(dnr->expires && dnr->expires <= now)
8373 dict_remove(dnrs, iter_key(it));
8376 saxdb_start_record(ctx, dnr->chan_name, 0);
8378 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
8380 saxdb_write_int(ctx, KEY_EXPIRES, dnr->expires);
8381 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
8382 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
8383 saxdb_end_record(ctx);
8388 chanserv_saxdb_write(struct saxdb_context *ctx)
8391 struct chanData *channel;
8394 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
8395 for(it = dict_first(note_types); it; it = iter_next(it))
8396 chanserv_write_note_type(ctx, iter_data(it));
8397 saxdb_end_record(ctx);
8400 saxdb_start_record(ctx, KEY_DNR, 1);
8401 write_dnrs_helper(ctx, handle_dnrs);
8402 write_dnrs_helper(ctx, plain_dnrs);
8403 write_dnrs_helper(ctx, mask_dnrs);
8404 saxdb_end_record(ctx);
8407 saxdb_start_record(ctx, KEY_CHANNELS, 1);
8408 for(channel = channelList; channel; channel = channel->next)
8409 chanserv_write_channel(ctx, channel);
8410 saxdb_end_record(ctx);
8416 chanserv_db_cleanup(void) {
8418 unreg_part_func(handle_part);
8420 unregister_channel(channelList, "terminating.");
8421 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
8422 UnlockChannel(chanserv_conf.support_channels.list[ii]);
8423 free(chanserv_conf.support_channels.list);
8424 dict_delete(handle_dnrs);
8425 dict_delete(plain_dnrs);
8426 dict_delete(mask_dnrs);
8427 dict_delete(note_types);
8428 free_string_list(chanserv_conf.eightball);
8429 free_string_list(chanserv_conf.old_ban_names);
8430 free_string_list(chanserv_conf.set_shows);
8431 free(set_shows_list.list);
8432 free(uset_shows_list.list);
8435 struct userData *helper = helperList;
8436 helperList = helperList->next;
8441 #if defined(GCC_VARMACROS)
8442 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ARGS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ARGS)
8443 #elif defined(C99_VARMACROS)
8444 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, __VA_ARGS__)
8446 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
8447 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
8450 init_chanserv(const char *nick)
8452 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
8453 conf_register_reload(chanserv_conf_read);
8457 reg_server_link_func(handle_server_link);
8458 reg_new_channel_func(handle_new_channel);
8459 reg_join_func(handle_join);
8460 reg_part_func(handle_part);
8461 reg_kick_func(handle_kick);
8462 reg_topic_func(handle_topic);
8463 reg_mode_change_func(handle_mode);
8464 reg_nick_change_func(handle_nick_change);
8465 reg_auth_func(handle_auth);
8468 reg_handle_rename_func(handle_rename);
8469 reg_unreg_func(handle_unreg);
8471 handle_dnrs = dict_new();
8472 dict_set_free_data(handle_dnrs, free);
8473 plain_dnrs = dict_new();
8474 dict_set_free_data(plain_dnrs, free);
8475 mask_dnrs = dict_new();
8476 dict_set_free_data(mask_dnrs, free);
8478 reg_svccmd_unbind_func(handle_svccmd_unbind);
8479 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
8480 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
8481 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
8482 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
8483 DEFINE_COMMAND(dnrsearch, 3, 0, "template", "noregister", NULL);
8484 modcmd_register(chanserv_module, "dnrsearch print", NULL, 0, 0, NULL);
8485 modcmd_register(chanserv_module, "dnrsearch remove", NULL, 0, 0, NULL);
8486 modcmd_register(chanserv_module, "dnrsearch count", NULL, 0, 0, NULL);
8487 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
8488 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
8489 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
8490 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
8491 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
8493 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
8494 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
8496 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8497 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8498 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8499 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8500 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
8502 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
8503 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
8504 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
8505 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8506 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8508 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8509 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
8510 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8511 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
8513 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
8514 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
8515 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8516 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8517 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
8518 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8519 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8520 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8522 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8523 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8524 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8525 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
8526 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
8527 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
8528 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
8529 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
8530 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8531 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
8532 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
8533 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
8534 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8535 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8537 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
8538 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8539 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8540 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8541 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
8543 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
8544 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
8546 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
8547 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8548 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8549 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8550 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8551 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8552 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8553 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8554 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8555 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8556 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8558 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
8559 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
8561 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
8562 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
8563 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
8564 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
8566 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
8567 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
8568 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
8569 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
8570 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
8572 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8573 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8574 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8575 DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8576 DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8577 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8579 DEFINE_COMMAND(addvote, 1, MODCMD_REQUIRE_AUTHED, NULL);
8580 DEFINE_COMMAND(delvote, 1, MODCMD_REQUIRE_AUTHED, NULL);
8581 DEFINE_COMMAND(addoption, 1, MODCMD_REQUIRE_AUTHED, NULL);
8582 DEFINE_COMMAND(deloption, 1, MODCMD_REQUIRE_AUTHED, NULL);
8583 DEFINE_COMMAND(vote, 1, MODCMD_REQUIRE_AUTHED, NULL);
8584 DEFINE_COMMAND(startvote, 1, MODCMD_REQUIRE_AUTHED, NULL);
8585 DEFINE_COMMAND(endvote, 1, MODCMD_REQUIRE_AUTHED, NULL);
8586 DEFINE_COMMAND(voteresults, 1, MODCMD_REQUIRE_AUTHED, NULL);
8588 /* Channel options */
8589 DEFINE_CHANNEL_OPTION(defaulttopic);
8590 DEFINE_CHANNEL_OPTION(topicmask);
8591 DEFINE_CHANNEL_OPTION(greeting);
8592 DEFINE_CHANNEL_OPTION(usergreeting);
8593 DEFINE_CHANNEL_OPTION(modes);
8594 DEFINE_CHANNEL_OPTION(enfops);
8595 DEFINE_CHANNEL_OPTION(giveops);
8596 DEFINE_CHANNEL_OPTION(protect);
8597 DEFINE_CHANNEL_OPTION(enfmodes);
8598 DEFINE_CHANNEL_OPTION(enftopic);
8599 DEFINE_CHANNEL_OPTION(pubcmd);
8600 DEFINE_CHANNEL_OPTION(givevoice);
8601 DEFINE_CHANNEL_OPTION(userinfo);
8602 DEFINE_CHANNEL_OPTION(dynlimit);
8603 DEFINE_CHANNEL_OPTION(topicsnarf);
8604 DEFINE_CHANNEL_OPTION(vote);
8605 DEFINE_CHANNEL_OPTION(nodelete);
8606 DEFINE_CHANNEL_OPTION(toys);
8607 DEFINE_CHANNEL_OPTION(setters);
8608 DEFINE_CHANNEL_OPTION(topicrefresh);
8609 DEFINE_CHANNEL_OPTION(ctcpusers);
8610 DEFINE_CHANNEL_OPTION(ctcpreaction);
8611 DEFINE_CHANNEL_OPTION(inviteme);
8612 DEFINE_CHANNEL_OPTION(unreviewed);
8613 modcmd_register(chanserv_module, "set expire", chan_opt_expire, 1, 0, "flags", "+helping", NULL);
8614 modcmd_register(chanserv_module, "set unreviewed on", NULL, 0, 0, "flags", "+helping", NULL);
8615 modcmd_register(chanserv_module, "set unreviewed off", NULL, 0, 0, "flags", "+oper", NULL);
8617 DEFINE_CHANNEL_OPTION(offchannel);
8618 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
8620 /* Alias set topic to set defaulttopic for compatibility. */
8621 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
8624 DEFINE_USER_OPTION(noautoop);
8625 DEFINE_USER_OPTION(autoinvite);
8626 DEFINE_USER_OPTION(info);
8628 /* Alias uset autovoice to uset autoop. */
8629 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
8631 note_types = dict_new();
8632 dict_set_free_data(note_types, chanserv_deref_note_type);
8635 const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
8636 chanserv = AddLocalUser(nick, nick, NULL, "Channel Services", modes);
8637 service_register(chanserv)->trigger = '!';
8638 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
8640 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
8642 if(chanserv_conf.channel_expire_frequency)
8643 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
8645 if(chanserv_conf.dnr_expire_frequency)
8646 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
8648 if(chanserv_conf.refresh_period)
8650 unsigned long next_refresh;
8651 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
8652 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
8655 reg_exit_func(chanserv_db_cleanup);
8656 message_register_table(msgtab);