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)
718 user_level_from_name(const char *name, unsigned short clamp_level)
720 unsigned int level = 0, ii;
722 level = strtoul(name, NULL, 10);
723 else for(ii = 0; (ii < ArrayLength(accessLevels)) && !level; ++ii)
724 if(!irccasecmp(name, accessLevels[ii].name))
725 level = accessLevels[ii].level;
726 if(level > clamp_level)
732 parse_level_range(unsigned short *minl, unsigned short *maxl, const char *arg)
735 *minl = strtoul(arg, &sep, 10);
743 *maxl = strtoul(sep+1, &sep, 10);
751 _GetChannelUser(struct chanData *channel, struct handle_info *handle, int override, int allow_suspended)
753 struct userData *uData, **head;
755 if(!channel || !handle)
758 if(override && HANDLE_FLAGGED(handle, HELPING)
759 && ((handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel)))
761 for(uData = helperList;
762 uData && uData->handle != handle;
763 uData = uData->next);
767 uData = calloc(1, sizeof(struct userData));
768 uData->handle = handle;
770 uData->access = UL_HELPER;
776 uData->next = helperList;
778 helperList->prev = uData;
786 for(uData = channel->users; uData; uData = uData->next)
787 if((uData->handle == handle) && (allow_suspended || !IsUserSuspended(uData)))
790 head = &(channel->users);
793 if(uData && (uData != *head))
795 /* Shuffle the user to the head of whatever list he was in. */
797 uData->next->prev = uData->prev;
799 uData->prev->next = uData->next;
805 (**head).prev = uData;
812 /* Returns non-zero if user has at least the minimum access.
813 * exempt_owner is set when handling !set, so the owner can set things
816 int check_user_level(struct chanNode *channel, struct userNode *user, enum levelOption opt, int allow_override, int exempt_owner)
818 struct userData *uData;
819 struct chanData *cData = channel->channel_info;
820 unsigned short minimum = cData->lvlOpts[opt];
823 uData = _GetChannelUser(cData, user->handle_info, allow_override, 0);
826 if(minimum <= uData->access)
828 if((minimum > UL_OWNER) && (uData->access == UL_OWNER) && exempt_owner)
833 /* Scan for other users authenticated to the same handle
834 still in the channel. If so, keep them listed as present.
836 user is optional, if not null, it skips checking that userNode
837 (for the handle_part function) */
839 scan_user_presence(struct userData *uData, struct userNode *user)
843 if(IsSuspended(uData->channel)
844 || IsUserSuspended(uData)
845 || !(mn = find_handle_in_channel(uData->channel->channel, uData->handle, user)))
857 chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, const char *text, UNUSED_ARG(struct userNode *bot), UNUSED_ARG(unsigned int is_notice))
859 unsigned int eflags, argc;
861 static char *bad_ctcp_reason = "CTCPs to this channel are forbidden.";
863 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
864 if(!channel->channel_info
865 || IsSuspended(channel->channel_info)
867 || !ircncasecmp(text, "ACTION ", 7))
869 /* Figure out the minimum level needed to CTCP the channel */
870 if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
872 /* We need to enforce against them; do so. */
874 argv[0] = (char*)text;
875 argv[1] = user->nick;
877 if(GetUserMode(channel, user))
878 eflags |= ACTION_KICK;
879 switch(channel->channel_info->chOpts[chCTCPReaction]) {
880 default: case 'k': /* just do the kick */ break;
882 eflags |= ACTION_BAN;
885 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
886 argv[argc++] = (char*)chanserv_conf.ctcp_short_ban_duration;
889 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
890 argv[argc++] = (char*)chanserv_conf.ctcp_long_ban_duration;
893 argv[argc++] = bad_ctcp_reason;
894 eject_user(chanserv, channel, argc, argv, NULL, eflags);
898 chanserv_create_note_type(const char *name)
900 struct note_type *ntype = calloc(1, sizeof(*ntype) + strlen(name));
901 strcpy(ntype->name, name);
903 dict_insert(note_types, ntype->name, ntype);
908 free_vote_options(void *data)
910 struct vote_option *vOpt = data;
912 free(vOpt->option_str);
917 chanserv_deref_note_type(void *data)
919 struct note_type *ntype = data;
921 if(--ntype->refs > 0)
927 chanserv_flush_note_type(struct note_type *ntype)
929 struct chanData *cData;
930 for(cData = channelList; cData; cData = cData->next)
931 dict_remove(cData->notes, ntype->name);
935 chanserv_truncate_notes(struct note_type *ntype)
937 struct chanData *cData;
939 unsigned int size = sizeof(*note) + ntype->max_length;
941 for(cData = channelList; cData; cData = cData->next) {
942 note = dict_find(cData->notes, ntype->name, NULL);
945 if(strlen(note->note) <= ntype->max_length)
947 dict_remove2(cData->notes, ntype->name, 1);
948 note = realloc(note, size);
949 note->note[ntype->max_length] = 0;
950 dict_insert(cData->notes, ntype->name, note);
954 static int note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user);
957 chanserv_add_channel_note(struct chanData *channel, struct note_type *type, const char *setter, const char *text)
960 unsigned int len = strlen(text);
962 if(len > type->max_length) len = type->max_length;
963 note = calloc(1, sizeof(*note) + len);
965 strncpy(note->setter, setter, sizeof(note->setter)-1);
966 memcpy(note->note, text, len);
968 dict_insert(channel->notes, type->name, note);
974 chanserv_free_note(void *data)
976 struct note *note = data;
978 chanserv_deref_note_type(note->type);
979 assert(note->type->refs > 0); /* must use delnote to remove the type */
983 static MODCMD_FUNC(cmd_createnote) {
984 struct note_type *ntype;
985 unsigned int arg = 1, existed = 0, max_length;
987 if((ntype = dict_find(note_types, argv[1], NULL)))
990 ntype = chanserv_create_note_type(argv[arg]);
991 if(!irccasecmp(argv[++arg], "privileged"))
994 ntype->set_access_type = NOTE_SET_PRIVILEGED;
995 ntype->set_access.min_opserv = strtoul(argv[arg], NULL, 0);
997 else if(!irccasecmp(argv[arg], "channel"))
999 unsigned short ulvl = user_level_from_name(argv[++arg], UL_OWNER);
1002 reply("CSMSG_INVALID_ACCESS", argv[arg]);
1005 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
1006 ntype->set_access.min_ulevel = ulvl;
1008 else if(!irccasecmp(argv[arg], "setter"))
1010 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
1014 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
1018 if(!irccasecmp(argv[++arg], "privileged"))
1019 ntype->visible_type = NOTE_VIS_PRIVILEGED;
1020 else if(!irccasecmp(argv[arg], "channel_users"))
1021 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
1022 else if(!irccasecmp(argv[arg], "all"))
1023 ntype->visible_type = NOTE_VIS_ALL;
1025 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
1029 if((arg+1) >= argc) {
1030 reply("MSG_MISSING_PARAMS", argv[0]);
1033 max_length = strtoul(argv[++arg], NULL, 0);
1034 if(max_length < 20 || max_length > 450)
1036 reply("CSMSG_BAD_MAX_LENGTH", argv[arg]);
1039 if(existed && (max_length < ntype->max_length))
1041 ntype->max_length = max_length;
1042 chanserv_truncate_notes(ntype);
1044 ntype->max_length = max_length;
1047 reply("CSMSG_NOTE_MODIFIED", ntype->name);
1049 reply("CSMSG_NOTE_CREATED", ntype->name);
1054 dict_remove(note_types, ntype->name);
1058 static MODCMD_FUNC(cmd_removenote) {
1059 struct note_type *ntype;
1062 ntype = dict_find(note_types, argv[1], NULL);
1063 force = (argc > 2) && !irccasecmp(argv[2], "force");
1066 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
1073 reply("CSMSG_NOTE_TYPE_USED", ntype->name);
1076 chanserv_flush_note_type(ntype);
1078 dict_remove(note_types, argv[1]);
1079 reply("CSMSG_NOTE_DELETED", argv[1]);
1084 chanserv_expire_channel(void *data)
1086 struct chanData *channel = data;
1087 char reason[MAXLEN];
1088 sprintf(reason, "channel expired.");
1089 channel->expiry = 0;
1090 spamserv_cs_unregister(NULL, channel->channel, expire, NULL);
1091 unregister_channel(channel, reason);
1094 static MODCMD_FUNC(chan_opt_expire)
1096 struct chanData *cData = channel->channel_info;
1097 unsigned long value = cData->expiry;
1101 if((!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
1103 reply("MSG_SETTING_PRIVILEGED", argv[0]);
1106 unsigned long expiry,duration;
1108 /* The two directions can have different ACLs. */
1109 if(!strcmp(argv[1], "0"))
1111 else if((duration = ParseInterval(argv[1])))
1112 expiry = now + duration;
1115 reply("MSG_INVALID_DURATION", argv[1]);
1119 if (expiry != value)
1123 timeq_del(value, chanserv_expire_channel, cData, 0);
1126 cData->expiry = value;
1129 timeq_add(expiry, chanserv_expire_channel, cData);
1134 if(cData->expiry > now) {
1135 char expirestr[INTERVALLEN];
1136 reply("CSMSG_SET_EXPIRE", intervalString(expirestr, cData->expiry - now, user->handle_info));
1138 reply("CSMSG_SET_EXPIRE_OFF");
1143 mode_lock_violated(const struct mod_chanmode *orig, const struct mod_chanmode *change)
1147 if(orig->modes_set & change->modes_clear)
1149 if(orig->modes_clear & change->modes_set)
1151 if((orig->modes_set & MODE_KEY) && (change->modes_set & MODE_KEY)
1152 && strcmp(orig->new_key, change->new_key))
1154 if((orig->modes_set & MODE_LIMIT) && (change->modes_set & MODE_LIMIT)
1155 && (orig->new_limit != change->new_limit))
1160 static char max_length_text[MAXLEN+1][16];
1162 static struct helpfile_expansion
1163 chanserv_expand_variable(const char *variable)
1165 struct helpfile_expansion exp;
1167 if(!irccasecmp(variable, "notes"))
1170 exp.type = HF_TABLE;
1171 exp.value.table.length = 1;
1172 exp.value.table.width = 3;
1173 exp.value.table.flags = 0;
1174 exp.value.table.contents = calloc(dict_size(note_types)+1, sizeof(char**));
1175 exp.value.table.contents[0] = calloc(exp.value.table.width, sizeof(char*));
1176 exp.value.table.contents[0][0] = "Note Type";
1177 exp.value.table.contents[0][1] = "Visibility";
1178 exp.value.table.contents[0][2] = "Max Length";
1179 for(it=dict_first(note_types); it; it=iter_next(it))
1181 struct note_type *ntype = iter_data(it);
1184 if(!note_type_visible_to_user(NULL, ntype, message_dest)) continue;
1185 row = exp.value.table.length++;
1186 exp.value.table.contents[row] = calloc(exp.value.table.width, sizeof(char*));
1187 exp.value.table.contents[row][0] = ntype->name;
1188 exp.value.table.contents[row][1] = (ntype->visible_type == NOTE_VIS_ALL) ? "all" :
1189 (ntype->visible_type == NOTE_VIS_CHANNEL_USERS) ? "chan users" :
1191 if(!max_length_text[ntype->max_length][0])
1192 snprintf(max_length_text[ntype->max_length], sizeof(max_length_text[ntype->max_length]), "%u", ntype->max_length);
1193 exp.value.table.contents[row][2] = max_length_text[ntype->max_length];
1198 exp.type = HF_STRING;
1199 exp.value.str = NULL;
1203 static struct chanData*
1204 register_channel(struct chanNode *cNode, char *registrar)
1206 struct chanData *channel;
1207 enum levelOption lvlOpt;
1208 enum charOption chOpt;
1210 channel = calloc(1, sizeof(struct chanData));
1212 channel->notes = dict_new();
1213 dict_set_free_data(channel->notes, chanserv_free_note);
1215 channel->registrar = strdup(registrar);
1216 channel->registered = now;
1217 channel->visited = now;
1218 channel->limitAdjusted = now;
1219 channel->ownerTransfer = now;
1220 channel->flags = CHANNEL_DEFAULT_FLAGS;
1221 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
1222 channel->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
1223 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
1224 channel->chOpts[chOpt] = charOptions[chOpt].default_value;
1226 channel->prev = NULL;
1227 channel->next = channelList;
1230 channelList->prev = channel;
1231 channelList = channel;
1232 registered_channels++;
1234 channel->channel = cNode;
1236 cNode->channel_info = channel;
1238 channel->vote = NULL;
1243 static struct userData*
1244 add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned short access_level, unsigned long seen, const char *info)
1246 struct userData *ud;
1248 if(access_level > UL_OWNER)
1251 ud = calloc(1, sizeof(*ud));
1252 ud->channel = channel;
1253 ud->handle = handle;
1255 ud->access = access_level;
1256 ud->info = info ? strdup(info) : NULL;
1259 ud->next = channel->users;
1261 channel->users->prev = ud;
1262 channel->users = ud;
1264 channel->userCount++;
1268 ud->u_next = ud->handle->channels;
1270 ud->u_next->u_prev = ud;
1271 ud->handle->channels = ud;
1277 del_channel_user(struct userData *user, int do_gc)
1279 struct chanData *channel = user->channel;
1281 channel->userCount--;
1285 user->prev->next = user->next;
1287 channel->users = user->next;
1289 user->next->prev = user->prev;
1292 user->u_prev->u_next = user->u_next;
1294 user->handle->channels = user->u_next;
1296 user->u_next->u_prev = user->u_prev;
1300 if(do_gc && !channel->users && !IsProtected(channel)) {
1301 spamserv_cs_unregister(NULL, channel->channel, lost_all_users, NULL);
1302 unregister_channel(channel, "lost all users.");
1306 static void expire_ban(void *data);
1309 add_channel_ban(struct chanData *channel, const char *mask, char *owner, unsigned long set, unsigned long triggered, unsigned long expires, char *reason)
1312 unsigned int ii, l1, l2;
1317 bd = malloc(sizeof(struct banData));
1319 bd->channel = channel;
1321 bd->triggered = triggered;
1322 bd->expires = expires;
1324 for(ii = 0; ii < chanserv_conf.old_ban_names->used; ++ii)
1326 extern const char *hidden_host_suffix;
1327 const char *old_name = chanserv_conf.old_ban_names->list[ii];
1331 l2 = strlen(old_name);
1334 if(irccasecmp(mask + l1 - l2, old_name))
1336 new_mask = alloca(MAXLEN);
1337 sprintf(new_mask, "%.*s%s", (int)(l1-l2), mask, hidden_host_suffix);
1340 safestrncpy(bd->mask, mask, sizeof(bd->mask));
1342 safestrncpy(bd->owner, owner, sizeof(bd->owner));
1343 bd->reason = strdup(reason);
1346 timeq_add(expires, expire_ban, bd);
1349 bd->next = channel->bans;
1351 channel->bans->prev = bd;
1353 channel->banCount++;
1360 del_channel_ban(struct banData *ban)
1362 ban->channel->banCount--;
1366 ban->prev->next = ban->next;
1368 ban->channel->bans = ban->next;
1371 ban->next->prev = ban->prev;
1374 timeq_del(0, expire_ban, ban, TIMEQ_IGNORE_WHEN);
1383 expire_ban(void *data)
1385 struct banData *bd = data;
1386 if(!IsSuspended(bd->channel))
1388 struct banList bans;
1389 struct mod_chanmode change;
1391 bans = bd->channel->channel->banlist;
1392 mod_chanmode_init(&change);
1393 for(ii=0; ii<bans.used; ii++)
1395 if(!strcmp(bans.list[ii]->ban, bd->mask))
1398 change.args[0].mode = MODE_REMOVE|MODE_BAN;
1399 change.args[0].u.hostmask = bd->mask;
1400 mod_chanmode_announce(chanserv, bd->channel->channel, &change);
1406 del_channel_ban(bd);
1409 static void chanserv_expire_suspension(void *data);
1412 unregister_channel(struct chanData *channel, const char *reason)
1414 struct mod_chanmode change;
1415 char msgbuf[MAXLEN];
1417 /* After channel unregistration, the following must be cleaned
1419 - Channel information.
1422 - Channel suspension data.
1423 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1429 timeq_del(0, NULL, channel, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
1433 mod_chanmode_init(&change);
1434 change.modes_clear |= MODE_REGISTERED;
1435 mod_chanmode_announce(chanserv, channel->channel, &change);
1438 while(channel->users)
1439 del_channel_user(channel->users, 0);
1441 while(channel->bans)
1442 del_channel_ban(channel->bans);
1444 free(channel->topic);
1445 free(channel->registrar);
1446 free(channel->greeting);
1447 free(channel->user_greeting);
1448 free(channel->topic_mask);
1451 channel->prev->next = channel->next;
1453 channelList = channel->next;
1456 channel->next->prev = channel->prev;
1458 if(channel->suspended)
1460 struct chanNode *cNode = channel->channel;
1461 struct suspended *suspended, *next_suspended;
1463 for(suspended = channel->suspended; suspended; suspended = next_suspended)
1465 next_suspended = suspended->previous;
1466 free(suspended->suspender);
1467 free(suspended->reason);
1468 if(suspended->expires)
1469 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
1474 cNode->channel_info = NULL;
1477 timeq_del(channel->expiry, chanserv_expire_channel, channel, 0);
1478 channel->channel->channel_info = NULL;
1480 dict_delete(channel->notes);
1481 sprintf(msgbuf, "%s %s", channel->channel->name, reason);
1482 if(!IsSuspended(channel))
1483 DelChannelUser(chanserv, channel->channel, msgbuf, 0);
1484 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, msgbuf);
1485 UnlockChannel(channel->channel);
1487 registered_channels--;
1491 expire_channels(UNUSED_ARG(void *data))
1493 struct chanData *channel, *next;
1494 struct userData *user;
1495 char delay[INTERVALLEN], reason[INTERVALLEN + 64];
1497 intervalString(delay, chanserv_conf.channel_expire_delay, NULL);
1498 sprintf(reason, "Channel registration automatically expired after %s of disuse.", delay);
1500 for(channel = channelList; channel; channel = next)
1502 next = channel->next;
1504 /* See if the channel can be expired. */
1505 if(((now - channel->visited) <= chanserv_conf.channel_expire_delay)
1506 || IsProtected(channel))
1509 /* Make sure there are no high-ranking users still in the channel. */
1510 for(user=channel->users; user; user=user->next)
1511 if(user->present && (user->access >= UL_PRESENT) && !HANDLE_FLAGGED(user->handle, BOT))
1516 /* Unregister the channel */
1517 log_module(CS_LOG, LOG_INFO, "(%s) Channel registration expired.", channel->channel->name);
1518 spamserv_cs_unregister(NULL, channel->channel, expire, NULL);
1519 unregister_channel(channel, "registration expired.");
1522 if(chanserv_conf.channel_expire_frequency)
1523 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
1527 expire_dnrs(UNUSED_ARG(void *data))
1529 dict_iterator_t it, next;
1530 struct do_not_register *dnr;
1532 for(it = dict_first(handle_dnrs); it; it = next)
1534 dnr = iter_data(it);
1535 next = iter_next(it);
1536 if(dnr->expires && dnr->expires <= now)
1537 dict_remove(handle_dnrs, dnr->chan_name + 1);
1539 for(it = dict_first(plain_dnrs); it; it = next)
1541 dnr = iter_data(it);
1542 next = iter_next(it);
1543 if(dnr->expires && dnr->expires <= now)
1544 dict_remove(plain_dnrs, dnr->chan_name + 1);
1546 for(it = dict_first(mask_dnrs); it; it = next)
1548 dnr = iter_data(it);
1549 next = iter_next(it);
1550 if(dnr->expires && dnr->expires <= now)
1551 dict_remove(mask_dnrs, dnr->chan_name + 1);
1554 if(chanserv_conf.dnr_expire_frequency)
1555 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
1559 protect_user(const struct userNode *victim, const struct userNode *aggressor, struct chanData *channel)
1561 char protect = channel->chOpts[chProtect];
1562 struct userData *cs_victim, *cs_aggressor;
1564 /* Don't protect if no one is to be protected, someone is attacking
1565 himself, or if the aggressor is an IRC Operator. */
1566 if(protect == 'n' || victim == aggressor || IsOper(aggressor))
1569 /* Don't protect if the victim isn't authenticated (because they
1570 can't be a channel user), unless we are to protect non-users
1572 cs_victim = GetChannelAccess(channel, victim->handle_info);
1573 if(protect != 'a' && !cs_victim)
1576 /* Protect if the aggressor isn't a user because at this point,
1577 the aggressor can only be less than or equal to the victim. */
1578 cs_aggressor = GetChannelAccess(channel, aggressor->handle_info);
1582 /* If the aggressor was a user, then the victim can't be helped. */
1589 if(cs_victim->access > cs_aggressor->access)
1594 if(cs_victim->access >= cs_aggressor->access)
1603 validate_op(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1605 struct chanData *cData = channel->channel_info;
1606 struct userData *cs_victim;
1608 if((!(cs_victim = GetChannelUser(cData, victim->handle_info))
1609 || (cs_victim->access < cData->lvlOpts[lvlGiveOps]))
1610 && !check_user_level(channel, user, lvlEnfOps, 0, 0))
1612 send_message(user, chanserv, "CSMSG_OPBY_LOCKED");
1620 validate_deop(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1622 if(IsService(victim))
1624 send_message(user, chanserv, "MSG_SERVICE_IMMUNE", victim->nick);
1628 if(protect_user(victim, user, channel->channel_info))
1630 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
1637 static struct do_not_register *
1638 chanserv_add_dnr(const char *chan_name, const char *setter, unsigned long expires, const char *reason)
1640 struct do_not_register *dnr = calloc(1, sizeof(*dnr)+strlen(reason));
1641 safestrncpy(dnr->chan_name, chan_name, sizeof(dnr->chan_name));
1642 safestrncpy(dnr->setter, setter, sizeof(dnr->setter));
1643 strcpy(dnr->reason, reason);
1645 dnr->expires = expires;
1646 if(dnr->chan_name[0] == '*')
1647 dict_insert(handle_dnrs, dnr->chan_name+1, dnr);
1648 else if(strpbrk(dnr->chan_name, "*?"))
1649 dict_insert(mask_dnrs, dnr->chan_name, dnr);
1651 dict_insert(plain_dnrs, dnr->chan_name, dnr);
1655 static struct dnrList
1656 chanserv_find_dnrs(const char *chan_name, const char *handle, unsigned int max)
1658 struct dnrList list;
1659 dict_iterator_t it, next;
1660 struct do_not_register *dnr;
1662 dnrList_init(&list);
1664 if(handle && (dnr = dict_find(handle_dnrs, handle, NULL)))
1666 if(dnr->expires && dnr->expires <= now)
1667 dict_remove(handle_dnrs, handle);
1668 else if(list.used < max)
1669 dnrList_append(&list, dnr);
1672 if(chan_name && (dnr = dict_find(plain_dnrs, chan_name, NULL)))
1674 if(dnr->expires && dnr->expires <= now)
1675 dict_remove(plain_dnrs, chan_name);
1676 else if(list.used < max)
1677 dnrList_append(&list, dnr);
1682 for(it = dict_first(mask_dnrs); it && list.used < max; it = next)
1684 next = iter_next(it);
1685 if(!match_ircglob(chan_name, iter_key(it)))
1687 dnr = iter_data(it);
1688 if(dnr->expires && dnr->expires <= now)
1689 dict_remove(mask_dnrs, iter_key(it));
1691 dnrList_append(&list, dnr);
1698 static int dnr_print_func(struct do_not_register *dnr, void *extra)
1700 struct userNode *user;
1701 char buf1[INTERVALLEN];
1702 char buf2[INTERVALLEN];
1709 strftime(buf1, sizeof(buf1), "%d %b %Y", localtime(&feh));
1714 strftime(buf2, sizeof(buf2), "%d %b %Y", localtime(&feh));
1715 send_message(user, chanserv, "CSMSG_DNR_INFO_SET_EXPIRES", dnr->chan_name, buf1, dnr->setter, buf2, dnr->reason);
1719 send_message(user, chanserv, "CSMSG_DNR_INFO_SET", dnr->chan_name, buf1, dnr->setter, dnr->reason);
1722 send_message(user, chanserv, "CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1727 chanserv_show_dnrs(struct userNode *user, struct svccmd *cmd, const char *chan_name, const char *handle)
1729 struct dnrList list;
1732 list = chanserv_find_dnrs(chan_name, handle, UINT_MAX);
1733 for(ii = 0; (ii < list.used) && (ii < 10); ++ii)
1734 dnr_print_func(list.list[ii], user);
1736 reply("CSMSG_MORE_DNRS", list.used - ii);
1741 struct do_not_register *
1742 chanserv_is_dnr(const char *chan_name, struct handle_info *handle)
1744 struct dnrList list;
1745 struct do_not_register *dnr;
1747 list = chanserv_find_dnrs(chan_name, handle ? handle->handle : NULL, 1);
1748 dnr = list.used ? list.list[0] : NULL;
1753 static unsigned int send_dnrs(struct userNode *user, dict_t dict)
1755 struct do_not_register *dnr;
1756 dict_iterator_t it, next;
1757 unsigned int matches = 0;
1759 for(it = dict_first(dict); it; it = next)
1761 dnr = iter_data(it);
1762 next = iter_next(it);
1763 if(dnr->expires && dnr->expires <= now)
1765 dict_remove(dict, iter_key(it));
1768 dnr_print_func(dnr, user);
1775 static CHANSERV_FUNC(cmd_noregister)
1779 unsigned long expiry, duration;
1780 unsigned int matches;
1784 reply("CSMSG_DNR_SEARCH_RESULTS");
1785 matches = send_dnrs(user, handle_dnrs);
1786 matches += send_dnrs(user, plain_dnrs);
1787 matches += send_dnrs(user, mask_dnrs);
1789 reply("MSG_MATCH_COUNT", matches);
1791 reply("MSG_NO_MATCHES");
1797 if(!IsChannelName(target) && (*target != '*'))
1799 reply("CSMSG_NOT_DNR", target);
1807 reply("MSG_INVALID_DURATION", argv[2]);
1811 if(!strcmp(argv[2], "0"))
1813 else if((duration = ParseInterval(argv[2])))
1814 expiry = now + duration;
1817 reply("MSG_INVALID_DURATION", argv[2]);
1821 reason = unsplit_string(argv + 3, argc - 3, NULL);
1822 if((*target == '*') && !get_handle_info(target + 1))
1824 reply("MSG_HANDLE_UNKNOWN", target + 1);
1827 chanserv_add_dnr(target, user->handle_info->handle, expiry, reason);
1828 reply("CSMSG_NOREGISTER_CHANNEL", target);
1832 reply("CSMSG_DNR_SEARCH_RESULTS");
1834 matches = chanserv_show_dnrs(user, cmd, NULL, target + 1);
1836 matches = chanserv_show_dnrs(user, cmd, target, NULL);
1838 reply("MSG_NO_MATCHES");
1842 static CHANSERV_FUNC(cmd_allowregister)
1844 const char *chan_name = argv[1];
1846 if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
1847 || dict_remove(plain_dnrs, chan_name)
1848 || dict_remove(mask_dnrs, chan_name))
1850 reply("CSMSG_DNR_REMOVED", chan_name);
1853 reply("CSMSG_NO_SUCH_DNR", chan_name);
1858 struct userNode *source;
1862 unsigned long min_set, max_set;
1863 unsigned long min_expires, max_expires;
1868 dnr_search_matches(const struct do_not_register *dnr, const struct dnr_search *search)
1870 return !((dnr->set < search->min_set)
1871 || (dnr->set > search->max_set)
1872 || (dnr->expires < search->min_expires)
1873 || (search->max_expires
1874 && ((dnr->expires == 0)
1875 || (dnr->expires > search->max_expires)))
1876 || (search->chan_mask
1877 && !match_ircglob(dnr->chan_name, search->chan_mask))
1878 || (search->setter_mask
1879 && !match_ircglob(dnr->setter, search->setter_mask))
1880 || (search->reason_mask
1881 && !match_ircglob(dnr->reason, search->reason_mask)));
1884 static struct dnr_search *
1885 dnr_search_create(struct userNode *user, struct svccmd *cmd, unsigned int argc, char *argv[])
1887 struct dnr_search *discrim;
1890 discrim = calloc(1, sizeof(*discrim));
1891 discrim->source = user;
1892 discrim->chan_mask = NULL;
1893 discrim->setter_mask = NULL;
1894 discrim->reason_mask = NULL;
1895 discrim->max_set = INT_MAX;
1896 discrim->limit = 50;
1898 for(ii=0; ii<argc; ++ii)
1902 reply("MSG_MISSING_PARAMS", argv[ii]);
1905 else if(0 == irccasecmp(argv[ii], "channel"))
1907 discrim->chan_mask = argv[++ii];
1909 else if(0 == irccasecmp(argv[ii], "setter"))
1911 discrim->setter_mask = argv[++ii];
1913 else if(0 == irccasecmp(argv[ii], "reason"))
1915 discrim->reason_mask = argv[++ii];
1917 else if(0 == irccasecmp(argv[ii], "limit"))
1919 discrim->limit = strtoul(argv[++ii], NULL, 0);
1921 else if(0 == irccasecmp(argv[ii], "set"))
1923 const char *cmp = argv[++ii];
1926 discrim->min_set = now - ParseInterval(cmp + 2);
1928 discrim->min_set = now - (ParseInterval(cmp + 1) - 1);
1929 } else if(cmp[0] == '=') {
1930 discrim->min_set = discrim->max_set = now - ParseInterval(cmp + 1);
1931 } else if(cmp[0] == '>') {
1933 discrim->max_set = now - ParseInterval(cmp + 2);
1935 discrim->max_set = now - (ParseInterval(cmp + 1) - 1);
1937 discrim->max_set = now - (ParseInterval(cmp) - 1);
1940 else if(0 == irccasecmp(argv[ii], "expires"))
1942 const char *cmp = argv[++ii];
1945 discrim->max_expires = now + ParseInterval(cmp + 2);
1947 discrim->max_expires = now + (ParseInterval(cmp + 1) - 1);
1948 } else if(cmp[0] == '=') {
1949 discrim->min_expires = discrim->max_expires = now + ParseInterval(cmp + 1);
1950 } else if(cmp[0] == '>') {
1952 discrim->min_expires = now + ParseInterval(cmp + 2);
1954 discrim->min_expires = now + (ParseInterval(cmp + 1) - 1);
1956 discrim->min_expires = now + (ParseInterval(cmp) - 1);
1961 reply("MSG_INVALID_CRITERIA", argv[ii]);
1972 typedef int (*dnr_search_func)(struct do_not_register *match, void *extra);
1975 dnr_search(struct dnr_search *discrim, dnr_search_func dsf, void *data)
1977 struct do_not_register *dnr;
1978 dict_iterator_t next;
1983 /* Initialize local variables. */
1986 if(discrim->chan_mask)
1988 int shift = (discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*') ? 2 : 0;
1989 if('\0' == discrim->chan_mask[shift + strcspn(discrim->chan_mask+shift, "*?")])
1993 if(target_fixed && discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*')
1995 /* Check against account-based DNRs. */
1996 dnr = dict_find(handle_dnrs, discrim->chan_mask + 2, NULL);
1997 if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
2000 else if(target_fixed)
2002 /* Check against channel-based DNRs. */
2003 dnr = dict_find(plain_dnrs, discrim->chan_mask, NULL);
2004 if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
2009 /* Exhaustively search account DNRs. */
2010 for(it = dict_first(handle_dnrs); it; it = next)
2012 next = iter_next(it);
2013 dnr = iter_data(it);
2014 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
2018 /* Do the same for channel DNRs. */
2019 for(it = dict_first(plain_dnrs); it; it = next)
2021 next = iter_next(it);
2022 dnr = iter_data(it);
2023 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
2027 /* Do the same for wildcarded channel DNRs. */
2028 for(it = dict_first(mask_dnrs); it; it = next)
2030 next = iter_next(it);
2031 dnr = iter_data(it);
2032 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
2040 dnr_remove_func(struct do_not_register *match, void *extra)
2042 struct userNode *user;
2045 chan_name = alloca(strlen(match->chan_name) + 1);
2046 strcpy(chan_name, match->chan_name);
2048 if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
2049 || dict_remove(plain_dnrs, chan_name)
2050 || dict_remove(mask_dnrs, chan_name))
2052 send_message(user, chanserv, "CSMSG_DNR_REMOVED", chan_name);
2058 dnr_count_func(struct do_not_register *match, void *extra)
2060 return 0; (void)match; (void)extra;
2063 static MODCMD_FUNC(cmd_dnrsearch)
2065 struct dnr_search *discrim;
2066 dnr_search_func action;
2067 struct svccmd *subcmd;
2068 unsigned int matches;
2071 sprintf(buf, "dnrsearch %s", argv[1]);
2072 subcmd = dict_find(cmd->parent->commands, buf, NULL);
2075 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
2078 if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
2080 if(!irccasecmp(argv[1], "print"))
2081 action = dnr_print_func;
2082 else if(!irccasecmp(argv[1], "remove"))
2083 action = dnr_remove_func;
2084 else if(!irccasecmp(argv[1], "count"))
2085 action = dnr_count_func;
2088 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
2092 discrim = dnr_search_create(user, cmd, argc-2, argv+2);
2096 if(action == dnr_print_func)
2097 reply("CSMSG_DNR_SEARCH_RESULTS");
2098 matches = dnr_search(discrim, action, user);
2100 reply("MSG_MATCH_COUNT", matches);
2102 reply("MSG_NO_MATCHES");
2108 chanserv_get_owned_count(struct handle_info *hi)
2110 struct userData *cList;
2113 for(owned=0, cList=hi->channels; cList; cList=cList->u_next)
2114 if(cList->access == UL_OWNER)
2119 static CHANSERV_FUNC(cmd_register)
2121 struct handle_info *handle;
2122 struct chanData *cData;
2123 struct modeNode *mn;
2124 char reason[MAXLEN];
2126 unsigned int new_channel, force=0;
2127 struct do_not_register *dnr;
2131 if(channel->channel_info)
2133 reply("CSMSG_ALREADY_REGGED", channel->name);
2137 if(channel->bad_channel)
2139 reply("CSMSG_ILLEGAL_CHANNEL", channel->name);
2144 && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
2146 reply("CSMSG_MUST_BE_OPPED", channel->name);
2151 chan_name = channel->name;
2155 if((argc < 2) || !IsChannelName(argv[1]))
2157 reply("MSG_NOT_CHANNEL_NAME");
2161 if(opserv_bad_channel(argv[1]))
2163 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2168 chan_name = argv[1];
2171 if(argc >= (new_channel+2))
2173 if(!IsHelping(user))
2175 reply("CSMSG_PROXY_FORBIDDEN");
2179 if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
2181 force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
2182 dnr = chanserv_is_dnr(chan_name, handle);
2186 handle = user->handle_info;
2187 dnr = chanserv_is_dnr(chan_name, handle);
2191 if(!IsHelping(user))
2192 reply("CSMSG_DNR_CHANNEL", chan_name);
2194 chanserv_show_dnrs(user, cmd, chan_name, handle->handle);
2198 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2200 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
2205 channel = AddChannel(argv[1], now, NULL, NULL);
2207 cData = register_channel(channel, user->handle_info->handle);
2208 scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL), NULL);
2209 cData->modes = chanserv_conf.default_modes;
2211 cData->modes.modes_set |= MODE_REGISTERED;
2212 if (IsOffChannel(cData))
2214 mod_chanmode_announce(chanserv, channel, &cData->modes);
2218 struct mod_chanmode *change = mod_chanmode_dup(&cData->modes, 1);
2219 change->args[change->argc].mode = MODE_CHANOP;
2220 change->args[change->argc].u.member = AddChannelUser(chanserv, channel);
2222 mod_chanmode_announce(chanserv, channel, change);
2223 mod_chanmode_free(change);
2226 /* Initialize the channel's max user record. */
2227 cData->max = channel->members.used;
2228 cData->max_time = 0;
2230 if(handle != user->handle_info)
2231 reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
2233 reply("CSMSG_REG_SUCCESS", channel->name);
2235 sprintf(reason, "%s registered to %s by %s.", channel->name, handle->handle, user->handle_info->handle);
2236 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2241 make_confirmation_string(struct userData *uData)
2243 static char strbuf[16];
2248 for(src = uData->handle->handle; *src; )
2249 accum = accum * 31 + toupper(*src++);
2251 for(src = uData->channel->channel->name; *src; )
2252 accum = accum * 31 + toupper(*src++);
2253 sprintf(strbuf, "%08x", accum);
2257 static CHANSERV_FUNC(cmd_unregister)
2260 char reason[MAXLEN];
2261 struct chanData *cData;
2262 struct userData *uData;
2264 cData = channel->channel_info;
2267 reply("CSMSG_NOT_REGISTERED", channel->name);
2271 uData = GetChannelUser(cData, user->handle_info);
2272 if(!uData || (uData->access < UL_OWNER))
2274 reply("CSMSG_NO_ACCESS");
2278 if(IsProtected(cData))
2280 reply("CSMSG_UNREG_NODELETE", channel->name);
2284 if(!IsHelping(user))
2286 const char *confirm_string;
2287 if(IsSuspended(cData))
2289 reply("CSMSG_CHAN_SUSPENDED", channel->name, cData->suspended->reason);
2292 confirm_string = make_confirmation_string(uData);
2293 if((argc < 2) || strcmp(argv[1], confirm_string))
2295 reply("CSMSG_CONFIRM_UNREG", confirm_string);
2300 sprintf(reason, "unregistered by %s.", user->handle_info->handle);
2301 name = strdup(channel->name);
2302 unregister_channel(cData, reason);
2303 spamserv_cs_unregister(user, channel, manually, "unregistered");
2304 reply("CSMSG_UNREG_SUCCESS", name);
2310 ss_cs_join_channel(struct chanNode *channel, int spamserv_join)
2312 extern struct userNode *spamserv;
2313 struct mod_chanmode *change;
2315 if(spamserv && spamserv_join && get_chanInfo(channel->name))
2317 change = mod_chanmode_alloc(2);
2319 change->args[0].mode = MODE_CHANOP;
2320 change->args[0].u.member = AddChannelUser(chanserv, channel);
2321 change->args[1].mode = MODE_CHANOP;
2322 change->args[1].u.member = AddChannelUser(spamserv, channel);
2326 change = mod_chanmode_alloc(1);
2328 change->args[0].mode = MODE_CHANOP;
2329 change->args[0].u.member = AddChannelUser(chanserv, channel);
2332 mod_chanmode_announce(chanserv, channel, change);
2333 mod_chanmode_free(change);
2336 static CHANSERV_FUNC(cmd_move)
2338 struct mod_chanmode change;
2339 struct chanNode *target;
2340 struct modeNode *mn;
2341 struct userData *uData;
2342 char reason[MAXLEN];
2343 struct do_not_register *dnr;
2344 int chanserv_join = 0, spamserv_join;
2348 if(IsProtected(channel->channel_info))
2350 reply("CSMSG_MOVE_NODELETE", channel->name);
2354 if(!IsChannelName(argv[1]))
2356 reply("MSG_NOT_CHANNEL_NAME");
2360 if(opserv_bad_channel(argv[1]))
2362 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2366 if(!IsHelping(user) || (argc < 3) || irccasecmp(argv[2], "force"))
2368 for(uData = channel->channel_info->users; uData; uData = uData->next)
2370 if((uData->access == UL_OWNER) && (dnr = chanserv_is_dnr(argv[1], uData->handle)))
2372 if(!IsHelping(user))
2373 reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]);
2375 chanserv_show_dnrs(user, cmd, argv[1], uData->handle->handle);
2381 mod_chanmode_init(&change);
2382 if(!(target = GetChannel(argv[1])))
2384 target = AddChannel(argv[1], now, NULL, NULL);
2385 if(!IsSuspended(channel->channel_info))
2388 else if(target->channel_info)
2390 reply("CSMSG_ALREADY_REGGED", target->name);
2393 else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
2394 && !IsHelping(user))
2396 reply("CSMSG_MUST_BE_OPPED", target->name);
2399 else if(!IsSuspended(channel->channel_info))
2404 /* Clear MODE_REGISTERED from old channel, add it to new. */
2406 change.modes_clear = MODE_REGISTERED;
2407 mod_chanmode_announce(chanserv, channel, &change);
2408 change.modes_clear = 0;
2409 change.modes_set = MODE_REGISTERED;
2410 mod_chanmode_announce(chanserv, target, &change);
2413 /* Move the channel_info to the target channel; it
2414 shouldn't be necessary to clear timeq callbacks
2415 for the old channel. */
2416 target->channel_info = channel->channel_info;
2417 target->channel_info->channel = target;
2418 channel->channel_info = NULL;
2420 /* Check whether users are present in the new channel. */
2421 for(uData = target->channel_info->users; uData; uData = uData->next)
2422 scan_user_presence(uData, NULL);
2424 spamserv_join = spamserv_cs_move_merge(user, channel, target, 1);
2427 ss_cs_join_channel(target, spamserv_join);
2429 sprintf(reason, "%s moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
2430 if(!IsSuspended(target->channel_info))
2432 char reason2[MAXLEN];
2433 sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
2434 DelChannelUser(chanserv, channel, reason2, 0);
2436 UnlockChannel(channel);
2437 LockChannel(target);
2438 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2439 reply("CSMSG_MOVE_SUCCESS", target->name);
2444 merge_users(struct chanData *source, struct chanData *target)
2446 struct userData *suData, *tuData, *next;
2452 /* Insert the source's users into the scratch area. */
2453 for(suData = source->users; suData; suData = suData->next)
2454 dict_insert(merge, suData->handle->handle, suData);
2456 /* Iterate through the target's users, looking for
2457 users common to both channels. The lower access is
2458 removed from either the scratch area or target user
2460 for(tuData = target->users; tuData; tuData = next)
2462 struct userData *choice;
2464 next = tuData->next;
2466 /* If a source user exists with the same handle as a target
2467 channel's user, resolve the conflict by removing one. */
2468 suData = dict_find(merge, tuData->handle->handle, NULL);
2472 /* Pick the data we want to keep. */
2473 /* If the access is the same, use the later seen time. */
2474 if(suData->access == tuData->access)
2475 choice = (suData->seen > tuData->seen) ? suData : tuData;
2476 else /* Otherwise, keep the higher access level. */
2477 choice = (suData->access > tuData->access) ? suData : tuData;
2478 /* Use the later seen time. */
2479 if(suData->seen < tuData->seen)
2480 suData->seen = tuData->seen;
2482 tuData->seen = suData->seen;
2484 /* Remove the user that wasn't picked. */
2485 if(choice == tuData)
2487 dict_remove(merge, suData->handle->handle);
2488 del_channel_user(suData, 0);
2491 del_channel_user(tuData, 0);
2494 /* Move the remaining users to the target channel. */
2495 for(it = dict_first(merge); it; it = iter_next(it))
2497 suData = iter_data(it);
2499 /* Insert the user into the target channel's linked list. */
2500 suData->prev = NULL;
2501 suData->next = target->users;
2502 suData->channel = target;
2505 target->users->prev = suData;
2506 target->users = suData;
2508 /* Update the user counts for the target channel; the
2509 source counts are left alone. */
2510 target->userCount++;
2512 /* Check whether the user is in the target channel. */
2513 scan_user_presence(suData, NULL);
2516 /* Possible to assert (source->users == NULL) here. */
2517 source->users = NULL;
2522 merge_bans(struct chanData *source, struct chanData *target)
2524 struct banData *sbData, *tbData, *sNext, *tNext, *tFront;
2526 /* Hold on to the original head of the target ban list
2527 to avoid comparing source bans with source bans. */
2528 tFront = target->bans;
2530 /* Perform a totally expensive O(n*m) merge, ick. */
2531 for(sbData = source->bans; sbData; sbData = sNext)
2533 /* Flag to track whether the ban's been moved
2534 to the destination yet. */
2537 /* Possible to assert (sbData->prev == NULL) here. */
2538 sNext = sbData->next;
2540 for(tbData = tFront; tbData; tbData = tNext)
2542 tNext = tbData->next;
2544 /* Perform two comparisons between each source
2545 and target ban, conflicts are resolved by
2546 keeping the broader ban and copying the later
2547 expiration and triggered time. */
2548 if(match_ircglobs(tbData->mask, sbData->mask))
2550 /* There is a broader ban in the target channel that
2551 overrides one in the source channel; remove the
2552 source ban and break. */
2553 if(sbData->expires > tbData->expires)
2554 tbData->expires = sbData->expires;
2555 if(sbData->triggered > tbData->triggered)
2556 tbData->triggered = sbData->triggered;
2557 del_channel_ban(sbData);
2560 else if(match_ircglobs(sbData->mask, tbData->mask))
2562 /* There is a broader ban in the source channel that
2563 overrides one in the target channel; remove the
2564 target ban, fall through and move the source over. */
2565 if(tbData->expires > sbData->expires)
2566 sbData->expires = tbData->expires;
2567 if(tbData->triggered > sbData->triggered)
2568 sbData->triggered = tbData->triggered;
2569 if(tbData == tFront)
2571 del_channel_ban(tbData);
2574 /* Source bans can override multiple target bans, so
2575 we allow a source to run through this loop multiple
2576 times, but we can only move it once. */
2581 /* Remove the source ban from the source ban list. */
2583 sbData->next->prev = sbData->prev;
2585 /* Modify the source ban's associated channel. */
2586 sbData->channel = target;
2588 /* Insert the ban into the target channel's linked list. */
2589 sbData->prev = NULL;
2590 sbData->next = target->bans;
2593 target->bans->prev = sbData;
2594 target->bans = sbData;
2596 /* Update the user counts for the target channel. */
2601 /* Possible to assert (source->bans == NULL) here. */
2602 source->bans = NULL;
2606 merge_data(struct chanData *source, struct chanData *target)
2608 /* Use more recent visited and owner-transfer time; use older
2609 * registered time. Bitwise or may_opchan. Use higher max.
2610 * Do not touch last_refresh, ban count or user counts.
2612 if(source->visited > target->visited)
2613 target->visited = source->visited;
2614 if(source->registered < target->registered)
2615 target->registered = source->registered;
2616 if(source->ownerTransfer > target->ownerTransfer)
2617 target->ownerTransfer = source->ownerTransfer;
2618 if(source->may_opchan)
2619 target->may_opchan = 1;
2620 if(source->max > target->max) {
2621 target->max = source->max;
2622 target->max_time = source->max_time;
2627 merge_channel(struct chanData *source, struct chanData *target)
2629 merge_users(source, target);
2630 merge_bans(source, target);
2631 merge_data(source, target);
2634 static CHANSERV_FUNC(cmd_merge)
2636 struct userData *target_user;
2637 struct chanNode *target;
2638 char reason[MAXLEN];
2642 /* Make sure the target channel exists and is registered to the user
2643 performing the command. */
2644 if(!(target = GetChannel(argv[1])))
2646 reply("MSG_INVALID_CHANNEL");
2650 if(!target->channel_info)
2652 reply("CSMSG_NOT_REGISTERED", target->name);
2656 if(IsProtected(channel->channel_info))
2658 reply("CSMSG_MERGE_NODELETE");
2662 if(IsSuspended(target->channel_info))
2664 reply("CSMSG_MERGE_SUSPENDED");
2668 if(channel == target)
2670 reply("CSMSG_MERGE_SELF");
2674 target_user = GetChannelUser(target->channel_info, user->handle_info);
2675 if(!target_user || (target_user->access < UL_OWNER))
2677 reply("CSMSG_MERGE_NOT_OWNER");
2681 /* Merge the channel structures and associated data. */
2682 merge_channel(channel->channel_info, target->channel_info);
2683 spamserv_cs_move_merge(user, channel, target, 0);
2684 sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
2685 unregister_channel(channel->channel_info, reason);
2686 reply("CSMSG_MERGE_SUCCESS", target->name);
2690 static CHANSERV_FUNC(cmd_opchan)
2692 struct mod_chanmode change;
2693 if(!IsHelping(user) && !channel->channel_info->may_opchan)
2695 reply("CSMSG_ALREADY_OPCHANNED", channel->name);
2698 channel->channel_info->may_opchan = 0;
2699 mod_chanmode_init(&change);
2701 change.args[0].mode = MODE_CHANOP;
2702 change.args[0].u.member = GetUserMode(channel, chanserv);
2703 if(!change.args[0].u.member)
2705 reply("CSMSG_OUT_OF_CHANNEL", channel->name);
2708 mod_chanmode_announce(chanserv, channel, &change);
2709 reply("CSMSG_OPCHAN_DONE", channel->name);
2713 static CHANSERV_FUNC(cmd_adduser)
2715 struct userData *actee;
2716 struct userData *actor, *real_actor;
2717 struct handle_info *handle;
2718 unsigned short access_level, override = 0;
2722 if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
2724 reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
2728 access_level = user_level_from_name(argv[2], UL_OWNER);
2731 reply("CSMSG_INVALID_ACCESS", argv[2]);
2735 actor = GetChannelUser(channel->channel_info, user->handle_info);
2736 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2738 if(actor->access <= access_level)
2740 reply("CSMSG_NO_BUMP_ACCESS");
2744 /* Trying to add someone with equal/more access? */
2745 if (!real_actor || real_actor->access <= access_level)
2746 override = CMD_LOG_OVERRIDE;
2748 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2751 if((actee = GetTrueChannelAccess(channel->channel_info, handle)))
2753 reply("CSMSG_USER_EXISTS", handle->handle, channel->name, actee->access);
2757 actee = add_channel_user(channel->channel_info, handle, access_level, 0, NULL);
2758 scan_user_presence(actee, NULL);
2759 reply("CSMSG_ADDED_USER", handle->handle, channel->name, access_level);
2760 return 1 | override;
2763 static CHANSERV_FUNC(cmd_clvl)
2765 struct handle_info *handle;
2766 struct userData *victim;
2767 struct userData *actor, *real_actor;
2768 unsigned short new_access, override = 0;
2769 int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
2773 actor = GetChannelUser(channel->channel_info, user->handle_info);
2774 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2776 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2779 if(handle == user->handle_info && !privileged)
2781 reply("CSMSG_NO_SELF_CLVL");
2785 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2787 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2791 if(actor->access <= victim->access && !privileged)
2793 reply("MSG_USER_OUTRANKED", handle->handle);
2797 new_access = user_level_from_name(argv[2], UL_OWNER);
2801 reply("CSMSG_INVALID_ACCESS", argv[2]);
2805 if(new_access >= actor->access && !privileged)
2807 reply("CSMSG_NO_BUMP_ACCESS");
2811 /* Trying to clvl a equal/higher user? */
2812 if(!real_actor || (real_actor->access <= victim->access && handle != user->handle_info))
2813 override = CMD_LOG_OVERRIDE;
2814 /* Trying to clvl someone to equal/higher access? */
2815 if(!real_actor || new_access >= real_actor->access)
2816 override = CMD_LOG_OVERRIDE;
2817 /* Helpers clvling themselves get caught by the "clvl someone to equal/higher access" check.
2818 * If they lower their own access it's not a big problem.
2821 victim->access = new_access;
2822 reply("CSMSG_CHANGED_ACCESS", handle->handle, new_access, channel->name);
2823 return 1 | override;
2826 static CHANSERV_FUNC(cmd_deluser)
2828 struct handle_info *handle;
2829 struct userData *victim;
2830 struct userData *actor, *real_actor;
2831 unsigned short access_level, override = 0;
2836 actor = GetChannelUser(channel->channel_info, user->handle_info);
2837 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2839 if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
2842 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2844 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2850 access_level = user_level_from_name(argv[1], UL_OWNER);
2853 reply("CSMSG_INVALID_ACCESS", argv[1]);
2856 if(access_level != victim->access)
2858 reply("CSMSG_INCORRECT_ACCESS", handle->handle, victim->access, argv[1]);
2864 access_level = victim->access;
2867 if((actor->access <= victim->access) && !IsHelping(user))
2869 reply("MSG_USER_OUTRANKED", victim->handle->handle);
2873 /* If people delete themselves it is an override, but they
2874 * could've used deleteme so we don't log it as an override
2876 if(!real_actor || (real_actor->access <= victim->access && real_actor != victim))
2877 override = CMD_LOG_OVERRIDE;
2879 chan_name = strdup(channel->name);
2880 del_channel_user(victim, 1);
2881 reply("CSMSG_DELETED_USER", handle->handle, access_level, chan_name);
2883 return 1 | override;
2887 cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
2889 struct userData *actor, *real_actor, *uData, *next;
2890 unsigned int override = 0;
2892 actor = GetChannelUser(channel->channel_info, user->handle_info);
2893 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2895 if(min_access > max_access)
2897 reply("CSMSG_BAD_RANGE", min_access, max_access);
2901 if(actor->access <= max_access)
2903 reply("CSMSG_NO_ACCESS");
2907 if(!real_actor || real_actor->access <= max_access)
2908 override = CMD_LOG_OVERRIDE;
2910 for(uData = channel->channel_info->users; uData; uData = next)
2914 if((uData->access >= min_access)
2915 && (uData->access <= max_access)
2916 && match_ircglob(uData->handle->handle, mask))
2917 del_channel_user(uData, 1);
2920 reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
2921 return 1 | override;
2924 static CHANSERV_FUNC(cmd_mdelowner)
2926 return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
2929 static CHANSERV_FUNC(cmd_mdelcoowner)
2931 return cmd_mdel_user(user, channel, UL_COOWNER, UL_COOWNER, argv[1], cmd);
2934 static CHANSERV_FUNC(cmd_mdelmaster)
2936 return cmd_mdel_user(user, channel, UL_MASTER, UL_MASTER, argv[1], cmd);
2939 static CHANSERV_FUNC(cmd_mdelop)
2941 return cmd_mdel_user(user, channel, UL_OP, UL_OP, argv[1], cmd);
2944 static CHANSERV_FUNC(cmd_mdelpeon)
2946 return cmd_mdel_user(user, channel, UL_PEON, UL_PEON, argv[1], cmd);
2950 cmd_trim_bans(struct userNode *user, struct chanNode *channel, unsigned long duration)
2952 struct banData *bData, *next;
2953 char interval[INTERVALLEN];
2955 unsigned long limit;
2958 limit = now - duration;
2959 for(bData = channel->channel_info->bans; bData; bData = next)
2963 if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
2966 del_channel_ban(bData);
2970 intervalString(interval, duration, user->handle_info);
2971 send_message(user, chanserv, "CSMSG_TRIMMED_BANS", count, channel->name, interval);
2976 cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration, int vacation)
2978 struct userData *actor, *uData, *next;
2979 char interval[INTERVALLEN];
2981 unsigned long limit;
2983 actor = GetChannelAccess(channel->channel_info, user->handle_info);
2984 if(min_access > max_access)
2986 send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
2990 if(!actor || actor->access <= max_access)
2992 send_message(user, chanserv, "CSMSG_NO_ACCESS");
2997 limit = now - duration;
2998 for(uData = channel->channel_info->users; uData; uData = next)
3002 if((uData->seen > limit)
3004 || (HANDLE_FLAGGED(uData->handle, FROZEN) && !vacation))
3007 if(((uData->access >= min_access) && (uData->access <= max_access))
3008 || (!max_access && (uData->access < actor->access)))
3010 del_channel_user(uData, 1);
3018 max_access = (actor->access > UL_OWNER) ? UL_OWNER : (actor->access - 1);
3020 send_message(user, chanserv, "CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration, user->handle_info));
3024 static CHANSERV_FUNC(cmd_trim)
3026 unsigned long duration;
3027 unsigned short min_level, max_level;
3032 vacation = argc > 3 && !strcmp(argv[3], "vacation");
3033 duration = ParseInterval(argv[2]);
3036 reply("CSMSG_CANNOT_TRIM");
3040 if(!irccasecmp(argv[1], "bans"))
3042 cmd_trim_bans(user, channel, duration);
3045 else if(!irccasecmp(argv[1], "users"))
3047 cmd_trim_users(user, channel, 0, 0, duration, vacation);
3050 else if(parse_level_range(&min_level, &max_level, argv[1]))
3052 cmd_trim_users(user, channel, min_level, max_level, duration, vacation);
3055 else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
3057 cmd_trim_users(user, channel, min_level, min_level, duration, vacation);
3062 reply("CSMSG_INVALID_TRIM", argv[1]);
3067 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
3068 to the user. cmd_all takes advantage of this. */
3069 static CHANSERV_FUNC(cmd_up)
3071 struct mod_chanmode change;
3072 struct userData *uData;
3075 mod_chanmode_init(&change);
3077 change.args[0].u.member = GetUserMode(channel, user);
3078 if(!change.args[0].u.member)
3081 reply("MSG_CHANNEL_ABSENT", channel->name);
3085 uData = GetChannelAccess(channel->channel_info, user->handle_info);
3089 reply("CSMSG_GODMODE_UP", argv[0]);
3092 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveOps])
3094 change.args[0].mode = MODE_CHANOP;
3095 errmsg = "CSMSG_ALREADY_OPPED";
3097 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveVoice])
3099 change.args[0].mode = MODE_VOICE;
3100 errmsg = "CSMSG_ALREADY_VOICED";
3105 reply("CSMSG_NO_ACCESS");
3108 change.args[0].mode &= ~change.args[0].u.member->modes;
3109 if(!change.args[0].mode)
3112 reply(errmsg, channel->name);
3115 modcmd_chanmode_announce(&change);
3119 static CHANSERV_FUNC(cmd_down)
3121 struct mod_chanmode change;
3123 mod_chanmode_init(&change);
3125 change.args[0].u.member = GetUserMode(channel, user);
3126 if(!change.args[0].u.member)
3129 reply("MSG_CHANNEL_ABSENT", channel->name);
3133 if(!change.args[0].u.member->modes)
3136 reply("CSMSG_ALREADY_DOWN", channel->name);
3140 change.args[0].mode = MODE_REMOVE | change.args[0].u.member->modes;
3141 modcmd_chanmode_announce(&change);
3145 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)
3147 struct userData *cList;
3149 for(cList = user->handle_info->channels; cList; cList = cList->u_next)
3151 if(IsSuspended(cList->channel)
3152 || IsUserSuspended(cList)
3153 || !GetUserMode(cList->channel->channel, user))
3156 mcmd(user, cList->channel->channel, 0, NULL, cmd);
3162 static CHANSERV_FUNC(cmd_upall)
3164 return cmd_all(CSFUNC_ARGS, cmd_up);
3167 static CHANSERV_FUNC(cmd_downall)
3169 return cmd_all(CSFUNC_ARGS, cmd_down);
3172 typedef int validate_func_t(struct userNode *user, struct chanNode *channel, struct userNode *victim);
3173 typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
3176 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)
3178 unsigned int ii, valid;
3179 struct userNode *victim;
3180 struct mod_chanmode *change;
3182 change = mod_chanmode_alloc(argc - 1);
3184 for(ii=valid=0; ++ii < argc; )
3186 if(!(victim = GetUserH(argv[ii])))
3188 change->args[valid].mode = mode;
3189 change->args[valid].u.member = GetUserMode(channel, victim);
3190 if(!change->args[valid].u.member)
3192 if(validate && !validate(user, channel, victim))
3197 change->argc = valid;
3198 if(valid < (argc-1))
3199 reply("CSMSG_PROCESS_FAILED");
3202 modcmd_chanmode_announce(change);
3203 reply(action, channel->name);
3205 mod_chanmode_free(change);
3209 static CHANSERV_FUNC(cmd_op)
3211 return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
3214 static CHANSERV_FUNC(cmd_deop)
3216 return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
3219 static CHANSERV_FUNC(cmd_voice)
3221 return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
3224 static CHANSERV_FUNC(cmd_devoice)
3226 return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
3230 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, unsigned int *victimCount, struct modeNode **victims)
3236 for(ii=0; ii<channel->members.used; ii++)
3238 struct modeNode *mn = channel->members.list[ii];
3240 if(IsService(mn->user))
3243 if(!user_matches_glob(mn->user, ban, MATCH_USENICK | MATCH_VISIBLE))
3246 if(protect_user(mn->user, user, channel->channel_info))
3250 victims[(*victimCount)++] = mn;
3256 eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3258 struct userNode *victim;
3259 struct modeNode **victims;
3260 unsigned int offset, n, victimCount, duration = 0;
3261 char *reason = "Bye.", *ban, *name;
3262 char interval[INTERVALLEN];
3264 offset = (action & ACTION_ADD_TIMED_BAN) ? 3 : 2;
3265 REQUIRE_PARAMS(offset);
3268 reason = unsplit_string(argv + offset, argc - offset, NULL);
3269 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
3271 /* Truncate the reason to a length of TOPICLEN, as
3272 the ircd does; however, leave room for an ellipsis
3273 and the kicker's nick. */
3274 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
3278 if((victim = GetUserH(argv[1])))
3280 victims = alloca(sizeof(victims[0]));
3281 victims[0] = GetUserMode(channel, victim);
3282 /* XXX: The comparison with ACTION_KICK is just because all
3283 * other actions can work on users outside the channel, and we
3284 * want to allow those (e.g. unbans) in that case. If we add
3285 * some other ejection action for in-channel users, change
3287 victimCount = victims[0] ? 1 : 0;
3289 if(IsService(victim))
3291 reply("MSG_SERVICE_IMMUNE", victim->nick);
3295 if((action == ACTION_KICK) && !victimCount)
3297 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
3301 if(protect_user(victim, user, channel->channel_info))
3303 reply("CSMSG_USER_PROTECTED", victim->nick);
3307 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
3308 name = victim->nick;
3310 else if(!is_ircmask(argv[1]) && (*argv[1] == '*'))
3312 struct handle_info *hi;
3313 extern const char *titlehost_suffix;
3314 char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
3315 const char *accountname = argv[1] + 1;
3317 if(!(hi = get_handle_info(accountname)))
3319 reply("MSG_HANDLE_UNKNOWN", accountname);
3323 snprintf(banmask, sizeof(banmask), "*!*@%s.*.%s", hi->handle, titlehost_suffix);
3324 victims = alloca(sizeof(victims[0]) * channel->members.used);
3326 if(bad_channel_ban(channel, user, banmask, &victimCount, victims))
3328 reply("CSMSG_MASK_PROTECTED", banmask);
3332 if((action == ACTION_KICK) && (victimCount == 0))
3334 reply("CSMSG_NO_MATCHING_USERS", channel->name, banmask);
3338 name = ban = strdup(banmask);
3342 if(!is_ircmask(argv[1]))
3344 reply("MSG_NICK_UNKNOWN", argv[1]);
3348 victims = alloca(sizeof(victims[0]) * channel->members.used);
3350 if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
3352 reply("CSMSG_MASK_PROTECTED", argv[1]);
3356 if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
3358 reply("CSMSG_LAME_MASK", argv[1]);
3362 if((action == ACTION_KICK) && (victimCount == 0))
3364 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
3368 name = ban = strdup(argv[1]);
3371 /* Truncate the ban in place if necessary; we must ensure
3372 that 'ban' is a valid ban mask before sanitizing it. */
3373 sanitize_ircmask(ban);
3375 if(action & ACTION_ADD_BAN)
3377 struct banData *bData, *next;
3379 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans)
3381 reply("CSMSG_MAXIMUM_BANS", chanserv_conf.max_chan_bans);
3386 if(action & ACTION_ADD_TIMED_BAN)
3388 duration = ParseInterval(argv[2]);
3392 reply("CSMSG_DURATION_TOO_LOW");
3396 else if(duration > (86400 * 365 * 2))
3398 reply("CSMSG_DURATION_TOO_HIGH");
3404 for(bData = channel->channel_info->bans; bData; bData = next)
3406 if(match_ircglobs(bData->mask, ban))
3408 int exact = !irccasecmp(bData->mask, ban);
3410 /* The ban is redundant; there is already a ban
3411 with the same effect in place. */
3415 free(bData->reason);
3416 bData->reason = strdup(reason);
3417 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
3419 reply("CSMSG_REASON_CHANGE", ban);
3423 if(exact && bData->expires)
3427 /* If the ban matches an existing one exactly,
3428 extend the expiration time if the provided
3429 duration is longer. */
3430 if(duration && (now + duration > bData->expires))
3432 bData->expires = now + duration;
3443 /* Delete the expiration timeq entry and
3444 requeue if necessary. */
3445 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
3448 timeq_add(bData->expires, expire_ban, bData);
3452 /* automated kickban */
3455 reply("CSMSG_BAN_EXTENDED", ban, intervalString(interval, duration, user->handle_info));
3457 reply("CSMSG_BAN_ADDED", name, channel->name);
3463 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3470 if(match_ircglobs(ban, bData->mask))
3472 /* The ban we are adding makes previously existing
3473 bans redundant; silently remove them. */
3474 del_channel_ban(bData);
3478 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);
3480 name = ban = strdup(bData->mask);
3484 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
3486 extern const char *hidden_host_suffix;
3487 const char *old_name = chanserv_conf.old_ban_names->list[n];
3489 unsigned int l1, l2;
3492 l2 = strlen(old_name);
3495 if(irccasecmp(ban + l1 - l2, old_name))
3497 new_mask = malloc(MAXLEN);
3498 sprintf(new_mask, "%.*s%s", (int)(l1-l2), ban, hidden_host_suffix);
3500 name = ban = new_mask;
3505 if(action & ACTION_BAN)
3507 unsigned int exists;
3508 struct mod_chanmode *change;
3510 if(channel->banlist.used >= MAXBANS)
3513 reply("CSMSG_BANLIST_FULL", channel->name);
3518 exists = ChannelBanExists(channel, ban);
3519 change = mod_chanmode_alloc(victimCount + 1);
3520 for(n = 0; n < victimCount; ++n)
3522 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_VOICE;
3523 change->args[n].u.member = victims[n];
3527 change->args[n].mode = MODE_BAN;
3528 change->args[n++].u.hostmask = ban;
3532 modcmd_chanmode_announce(change);
3534 mod_chanmode_announce(chanserv, channel, change);
3535 mod_chanmode_free(change);
3537 if(exists && (action == ACTION_BAN))
3540 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3546 if(action & ACTION_KICK)
3548 char kick_reason[MAXLEN];
3549 sprintf(kick_reason, "(%s) %s", user->nick, reason);
3551 for(n = 0; n < victimCount; n++)
3552 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
3557 /* No response, since it was automated. */
3559 else if(action & ACTION_ADD_BAN)
3562 reply("CSMSG_TIMED_BAN_ADDED", name, channel->name, intervalString(interval, duration, user->handle_info));
3564 reply("CSMSG_BAN_ADDED", name, channel->name);
3566 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
3567 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
3568 else if(action & ACTION_BAN)
3569 reply("CSMSG_BAN_DONE", name, channel->name);
3570 else if(action & ACTION_KICK && victimCount)
3571 reply("CSMSG_KICK_DONE", name, channel->name);
3577 static CHANSERV_FUNC(cmd_kickban)
3579 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
3582 static CHANSERV_FUNC(cmd_kick)
3584 return eject_user(CSFUNC_ARGS, ACTION_KICK);
3587 static CHANSERV_FUNC(cmd_ban)
3589 return eject_user(CSFUNC_ARGS, ACTION_BAN);
3592 static CHANSERV_FUNC(cmd_addban)
3594 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN);
3597 static CHANSERV_FUNC(cmd_addtimedban)
3599 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN);
3602 static struct mod_chanmode *
3603 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
3605 struct mod_chanmode *change;
3606 unsigned char *match;
3607 unsigned int ii, count;
3609 match = alloca(bans->used);
3612 for(ii = count = 0; ii < bans->used; ++ii)
3614 match[ii] = user_matches_glob(actee, bans->list[ii]->ban,
3615 MATCH_USENICK | MATCH_VISIBLE);
3622 for(ii = count = 0; ii < bans->used; ++ii)
3624 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
3631 change = mod_chanmode_alloc(count);
3632 for(ii = count = 0; ii < bans->used; ++ii)
3636 change->args[count].mode = MODE_REMOVE | MODE_BAN;
3637 change->args[count++].u.hostmask = strdup(bans->list[ii]->ban);
3639 assert(count == change->argc);
3644 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3646 struct userNode *actee;
3652 /* may want to allow a comma delimited list of users... */
3653 if(!(actee = GetUserH(argv[1])))
3655 if(!is_ircmask(argv[1]) && *argv[1] == '*')
3657 char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
3658 const char *accountname = argv[1] + 1;
3660 snprintf(banmask, sizeof(banmask), "*!*@%s.*", accountname);
3661 mask = strdup(banmask);
3663 else if(!is_ircmask(argv[1]))
3665 reply("MSG_NICK_UNKNOWN", argv[1]);
3670 mask = strdup(argv[1]);
3674 /* We don't sanitize the mask here because ircu
3676 if(action & ACTION_UNBAN)
3678 struct mod_chanmode *change;
3679 change = find_matching_bans(&channel->banlist, actee, mask);
3684 modcmd_chanmode_announce(change);
3685 for(ii = 0; ii < change->argc; ++ii)
3686 free((char*)change->args[ii].u.hostmask);
3687 mod_chanmode_free(change);
3692 if(action & ACTION_DEL_BAN)
3694 struct banData *ban, *next;
3696 ban = channel->channel_info->bans;
3700 for( ; ban && !user_matches_glob(actee, ban->mask,
3701 MATCH_USENICK | MATCH_VISIBLE);
3704 for( ; ban && !match_ircglobs(mask, ban->mask);
3709 del_channel_ban(ban);
3716 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3718 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3724 static CHANSERV_FUNC(cmd_unban)
3726 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3729 static CHANSERV_FUNC(cmd_delban)
3731 /* it doesn't necessarily have to remove the channel ban - may want
3732 to make that an option. */
3733 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_BAN);
3736 static CHANSERV_FUNC(cmd_unbanme)
3738 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3739 long flags = ACTION_UNBAN;
3741 /* remove permanent bans if the user has the proper access. */
3742 if(uData->access >= UL_MASTER)
3743 flags |= ACTION_DEL_BAN;
3745 argv[1] = user->nick;
3746 return unban_user(user, channel, 2, argv, cmd, flags);
3749 static CHANSERV_FUNC(cmd_unbanall)
3751 struct mod_chanmode *change;
3754 if(!channel->banlist.used)
3756 reply("CSMSG_NO_BANS", channel->name);
3760 change = mod_chanmode_alloc(channel->banlist.used);
3761 for(ii=0; ii<channel->banlist.used; ii++)
3763 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3764 change->args[ii].u.hostmask = strdup(channel->banlist.list[ii]->ban);
3766 modcmd_chanmode_announce(change);
3767 for(ii = 0; ii < change->argc; ++ii)
3768 free((char*)change->args[ii].u.hostmask);
3769 mod_chanmode_free(change);
3770 reply("CSMSG_BANS_REMOVED", channel->name);
3774 static CHANSERV_FUNC(cmd_open)
3776 struct mod_chanmode *change;
3779 change = find_matching_bans(&channel->banlist, user, NULL);
3781 change = mod_chanmode_alloc(0);
3782 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3783 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3784 && channel->channel_info->modes.modes_set)
3785 change->modes_clear &= ~channel->channel_info->modes.modes_set;
3786 modcmd_chanmode_announce(change);
3787 reply("CSMSG_CHANNEL_OPENED", channel->name);
3788 for(ii = 0; ii < change->argc; ++ii)
3789 free((char*)change->args[ii].u.hostmask);
3790 mod_chanmode_free(change);
3794 static CHANSERV_FUNC(cmd_myaccess)
3796 static struct string_buffer sbuf;
3797 struct handle_info *target_handle;
3798 struct userData *uData;
3802 target_handle = user->handle_info;
3803 else if(!IsStaff(user))
3805 reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
3808 else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3811 if(!oper_outranks(user, target_handle))
3814 if(!target_handle->channels)
3816 reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
3820 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3821 for(uData = target_handle->channels; uData; uData = uData->u_next)
3823 struct chanData *cData = uData->channel;
3826 if(uData->access > UL_OWNER)
3828 if(IsProtected(cData)
3829 && (target_handle != user->handle_info)
3830 && !GetTrueChannelAccess(cData, user->handle_info))
3833 string_buffer_append_printf(&sbuf, "[%s (%d", cData->channel->name, uData->access);
3834 if(uData->flags != USER_AUTO_OP)
3835 string_buffer_append(&sbuf, ',');
3836 if(IsUserSuspended(uData))
3837 string_buffer_append(&sbuf, 's');
3838 if(IsUserAutoOp(uData))
3840 if(uData->access >= cData->lvlOpts[lvlGiveOps])
3841 string_buffer_append(&sbuf, 'o');
3842 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
3843 string_buffer_append(&sbuf, 'v');
3845 if(IsUserAutoInvite(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe]))
3846 string_buffer_append(&sbuf, 'i');
3848 string_buffer_append_printf(&sbuf, ")] %s", uData->info);
3850 string_buffer_append_string(&sbuf, ")]");
3851 string_buffer_append(&sbuf, '\0');
3852 send_message_type(4, user, cmd->parent->bot, "%s", sbuf.list);
3856 reply("CSMSG_MYACCESS_COUNT_1", target_handle->handle, ccount);
3858 reply("CSMSG_MYACCESS_COUNT", target_handle->handle, ccount);
3864 static CHANSERV_FUNC(cmd_access)
3866 struct userNode *target;
3867 struct handle_info *target_handle;
3868 struct userData *uData;
3870 char prefix[MAXLEN];
3875 target_handle = target->handle_info;
3877 else if((target = GetUserH(argv[1])))
3879 target_handle = target->handle_info;
3881 else if(argv[1][0] == '*')
3883 if(!(target_handle = get_handle_info(argv[1]+1)))
3885 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3891 reply("MSG_NICK_UNKNOWN", argv[1]);
3895 assert(target || target_handle);
3897 if(target == chanserv)
3899 reply("CSMSG_IS_CHANSERV");
3907 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
3912 reply("MSG_USER_AUTHENTICATE", target->nick);
3915 reply("MSG_AUTHENTICATE");
3921 const char *epithet = NULL, *type = NULL;
3924 epithet = chanserv_conf.irc_operator_epithet;
3925 type = user_find_message(user, "CSMSG_OPERATOR_TITLE");
3927 else if(IsNetworkHelper(target))
3929 epithet = chanserv_conf.network_helper_epithet;
3930 type = user_find_message(user, "CSMSG_UC_H_TITLE");
3932 else if(IsSupportHelper(target))
3934 epithet = chanserv_conf.support_helper_epithet;
3935 type = user_find_message(user, "CSMSG_LC_H_TITLE");
3939 if(target_handle->epithet)
3940 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
3942 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
3944 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
3948 sprintf(prefix, "%s", target_handle->handle);
3951 if(!channel->channel_info)
3953 reply("CSMSG_NOT_REGISTERED", channel->name);
3957 helping = HANDLE_FLAGGED(target_handle, HELPING)
3958 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
3959 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
3961 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
3962 /* To prevent possible information leaks, only show infolines
3963 * if the requestor is in the channel or it's their own
3965 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
3967 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
3969 /* Likewise, only say it's suspended if the user has active
3970 * access in that channel or it's their own entry. */
3971 if(IsUserSuspended(uData)
3972 && (GetChannelUser(channel->channel_info, user->handle_info)
3973 || (user->handle_info == uData->handle)))
3975 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
3980 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
3987 zoot_list(struct listData *list)
3989 struct userData *uData;
3990 unsigned int start, curr, highest, lowest;
3991 struct helpfile_table tmp_table;
3992 const char **temp, *msg;
3994 if(list->table.length == 1)
3997 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3999 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
4000 msg = user_find_message(list->user, "MSG_NONE");
4001 send_message_type(4, list->user, list->bot, " %s", msg);
4003 tmp_table.width = list->table.width;
4004 tmp_table.flags = list->table.flags;
4005 list->table.contents[0][0] = " ";
4006 highest = list->highest;
4007 if(list->lowest != 0)
4008 lowest = list->lowest;
4009 else if(highest < 100)
4012 lowest = highest - 100;
4013 for(start = curr = 1; curr < list->table.length; )
4015 uData = list->users[curr-1];
4016 list->table.contents[curr++][0] = " ";
4017 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
4020 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, lowest, highest, list->search);
4022 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, lowest, highest);
4023 temp = list->table.contents[--start];
4024 list->table.contents[start] = list->table.contents[0];
4025 tmp_table.contents = list->table.contents + start;
4026 tmp_table.length = curr - start;
4027 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
4028 list->table.contents[start] = temp;
4030 highest = lowest - 1;
4031 lowest = (highest < 100) ? 0 : (highest - 99);
4037 def_list(struct listData *list)
4041 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
4043 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
4044 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4045 if(list->table.length == 1)
4047 msg = user_find_message(list->user, "MSG_NONE");
4048 send_message_type(4, list->user, list->bot, " %s", msg);
4053 userData_access_comp(const void *arg_a, const void *arg_b)
4055 const struct userData *a = *(struct userData**)arg_a;
4056 const struct userData *b = *(struct userData**)arg_b;
4058 if(a->access != b->access)
4059 res = b->access - a->access;
4061 res = irccasecmp(a->handle->handle, b->handle->handle);
4066 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
4068 void (*send_list)(struct listData *);
4069 struct userData *uData;
4070 struct listData lData;
4071 unsigned int matches;
4075 lData.bot = cmd->parent->bot;
4076 lData.channel = channel;
4077 lData.lowest = lowest;
4078 lData.highest = highest;
4079 lData.search = (argc > 1) ? argv[1] : NULL;
4080 send_list = def_list;
4081 (void)zoot_list; /* since it doesn't show user levels */
4083 if(user->handle_info)
4085 switch(user->handle_info->userlist_style)
4087 case HI_STYLE_DEF: send_list = def_list; break;
4088 case HI_STYLE_ZOOT: send_list = def_list; break;
4092 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
4094 for(uData = channel->channel_info->users; uData; uData = uData->next)
4096 if((uData->access < lowest)
4097 || (uData->access > highest)
4098 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
4100 lData.users[matches++] = uData;
4102 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
4104 lData.table.length = matches+1;
4105 lData.table.width = 4;
4106 lData.table.flags = TABLE_NO_FREE;
4107 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
4108 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
4109 lData.table.contents[0] = ary;
4112 ary[2] = "Last Seen";
4114 for(matches = 1; matches < lData.table.length; ++matches)
4116 char seen[INTERVALLEN];
4118 uData = lData.users[matches-1];
4119 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
4120 lData.table.contents[matches] = ary;
4121 ary[0] = strtab(uData->access);
4122 ary[1] = uData->handle->handle;
4125 else if(!uData->seen)
4128 ary[2] = intervalString(seen, now - uData->seen, user->handle_info);
4129 ary[2] = strdup(ary[2]);
4130 if(IsUserSuspended(uData))
4131 ary[3] = "Suspended";
4132 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
4133 ary[3] = "Vacation";
4134 else if(HANDLE_FLAGGED(uData->handle, BOT))
4140 for(matches = 1; matches < lData.table.length; ++matches)
4142 free((char*)lData.table.contents[matches][2]);
4143 free(lData.table.contents[matches]);
4145 free(lData.table.contents[0]);
4146 free(lData.table.contents);
4150 static CHANSERV_FUNC(cmd_users)
4152 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
4155 static CHANSERV_FUNC(cmd_wlist)
4157 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
4160 static CHANSERV_FUNC(cmd_clist)
4162 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
4165 static CHANSERV_FUNC(cmd_mlist)
4167 return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
4170 static CHANSERV_FUNC(cmd_olist)
4172 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
4175 static CHANSERV_FUNC(cmd_plist)
4177 return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
4180 static CHANSERV_FUNC(cmd_bans)
4182 struct userNode *search_u = NULL;
4183 struct helpfile_table tbl;
4184 unsigned int matches = 0, timed = 0, search_wilds = 0, ii;
4185 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
4186 const char *msg_never, *triggered, *expires;
4187 struct banData *ban, **bans;
4191 else if(strchr(search = argv[1], '!'))
4194 search_wilds = search[strcspn(search, "?*")];
4196 else if(!(search_u = GetUserH(search)))
4197 reply("MSG_NICK_UNKNOWN", search);
4199 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
4201 for(ban = channel->channel_info->bans; ban; ban = ban->next)
4205 if(!user_matches_glob(search_u, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
4210 if(search_wilds ? !match_ircglobs(search, ban->mask) : !match_ircglob(search, ban->mask))
4213 bans[matches++] = ban;
4218 tbl.length = matches + 1;
4219 tbl.width = 4 + timed;
4221 tbl.flags = TABLE_NO_FREE;
4222 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
4223 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4224 tbl.contents[0][0] = "Mask";
4225 tbl.contents[0][1] = "Set By";
4226 tbl.contents[0][2] = "Triggered";
4229 tbl.contents[0][3] = "Expires";
4230 tbl.contents[0][4] = "Reason";
4233 tbl.contents[0][3] = "Reason";
4236 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4238 free(tbl.contents[0]);
4243 msg_never = user_find_message(user, "MSG_NEVER");
4244 for(ii = 0; ii < matches; )
4250 else if(ban->expires)
4251 expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
4253 expires = msg_never;
4256 triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
4258 triggered = msg_never;
4260 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4261 tbl.contents[ii][0] = ban->mask;
4262 tbl.contents[ii][1] = ban->owner;
4263 tbl.contents[ii][2] = strdup(triggered);
4266 tbl.contents[ii][3] = strdup(expires);
4267 tbl.contents[ii][4] = ban->reason;
4270 tbl.contents[ii][3] = ban->reason;
4272 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4273 reply("MSG_MATCH_COUNT", matches);
4274 for(ii = 1; ii < tbl.length; ++ii)
4276 free((char*)tbl.contents[ii][2]);
4278 free((char*)tbl.contents[ii][3]);
4279 free(tbl.contents[ii]);
4281 free(tbl.contents[0]);
4287 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
4289 struct chanData *cData = channel->channel_info;
4290 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
4292 if(cData->topic_mask)
4293 return !match_ircglob(new_topic, cData->topic_mask);
4294 else if(cData->topic)
4295 return irccasecmp(new_topic, cData->topic);
4300 static CHANSERV_FUNC(cmd_topic)
4302 struct chanData *cData;
4305 cData = channel->channel_info;
4310 SetChannelTopic(channel, chanserv, cData->topic, 1);
4311 reply("CSMSG_TOPIC_SET", cData->topic);
4315 reply("CSMSG_NO_TOPIC", channel->name);
4319 topic = unsplit_string(argv + 1, argc - 1, NULL);
4320 /* If they say "!topic *", use an empty topic. */
4321 if((topic[0] == '*') && (topic[1] == 0))
4323 if(bad_topic(channel, user, topic))
4325 char *topic_mask = cData->topic_mask;
4328 char new_topic[TOPICLEN+1], tchar;
4329 int pos=0, starpos=-1, dpos=0, len;
4331 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
4338 len = strlen(topic);
4339 if((dpos + len) > TOPICLEN)
4340 len = TOPICLEN + 1 - dpos;
4341 memcpy(new_topic+dpos, topic, len);
4345 case '\\': tchar = topic_mask[pos++]; /* and fall through */
4346 default: new_topic[dpos++] = tchar; break;
4349 if((dpos > TOPICLEN) || tchar)
4352 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
4353 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
4356 new_topic[dpos] = 0;
4357 SetChannelTopic(channel, chanserv, new_topic, 1);
4359 reply("CSMSG_TOPIC_LOCKED", channel->name);
4364 SetChannelTopic(channel, chanserv, topic, 1);
4366 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
4368 /* Grab the topic and save it as the default topic. */
4370 cData->topic = strdup(channel->topic);
4376 static CHANSERV_FUNC(cmd_mode)
4378 struct userData *uData;
4379 struct mod_chanmode *change;
4385 change = &channel->channel_info->modes;
4386 if(change->modes_set || change->modes_clear) {
4387 modcmd_chanmode_announce(change);
4388 reply("CSMSG_DEFAULTED_MODES", channel->name);
4390 reply("CSMSG_NO_MODES", channel->name);
4394 uData = GetChannelUser(channel->channel_info, user->handle_info);
4396 base_oplevel = MAXOPLEVEL;
4397 else if (uData->access >= UL_OWNER)
4400 base_oplevel = 1 + UL_OWNER - uData->access;
4401 change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED|MCP_NO_APASS, base_oplevel);
4404 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
4408 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
4409 && mode_lock_violated(&channel->channel_info->modes, change))
4412 mod_chanmode_format(&channel->channel_info->modes, modes);
4413 reply("CSMSG_MODE_LOCKED", modes, channel->name);
4417 modcmd_chanmode_announce(change);
4418 mod_chanmode_format(change, fmt);
4419 mod_chanmode_free(change);
4420 reply("CSMSG_MODES_SET", fmt);
4425 chanserv_del_invite_mark(void *data)
4427 struct ChanUser *chanuser = data;
4428 struct chanNode *channel = chanuser->chan;
4430 if(!channel) return;
4431 for(i = 0; i < channel->invited.used; i++)
4433 if(channel->invited.list[i] == chanuser->user) {
4434 userList_remove(&channel->invited, chanuser->user);
4440 static CHANSERV_FUNC(cmd_invite)
4442 struct userData *uData;
4443 struct userNode *invite;
4444 struct ChanUser *chanuser;
4447 uData = GetChannelUser(channel->channel_info, user->handle_info);
4451 if(!(invite = GetUserH(argv[1])))
4453 reply("MSG_NICK_UNKNOWN", argv[1]);
4460 if(GetUserMode(channel, invite))
4462 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
4466 for(i = 0; i < channel->invited.used; i++)
4468 if(channel->invited.list[i] == invite) {
4469 reply("CSMSG_ALREADY_INVITED", invite->nick, channel->name);
4478 char *reason = unsplit_string(argv + 2, argc - 2, NULL);
4479 send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
4482 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
4484 irc_invite(chanserv, invite, channel);
4486 reply("CSMSG_INVITED_USER", argv[1], channel->name);
4488 userList_append(&channel->invited, invite);
4489 chanuser = calloc(1, sizeof(*chanuser));
4490 chanuser->user=invite;
4491 chanuser->chan=channel;
4492 timeq_add(now + chanserv_conf.invited_timeout, chanserv_del_invite_mark, chanuser);
4497 static CHANSERV_FUNC(cmd_inviteme)
4499 if(GetUserMode(channel, user))
4501 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
4504 if(channel->channel_info
4505 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
4507 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
4510 irc_invite(cmd->parent->bot, user, channel);
4515 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
4518 char buf1[INTERVALLEN], buf2[INTERVALLEN];
4520 /* We display things based on two dimensions:
4521 * - Issue time: present or absent
4522 * - Expiration: revoked, expired, expires in future, or indefinite expiration
4523 * (in order of precedence, so something both expired and revoked
4524 * only counts as revoked)
4526 combo = (suspended->issued ? 4 : 0)
4527 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
4529 case 0: /* no issue time, indefinite expiration */
4530 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
4532 case 1: /* no issue time, expires in future */
4533 intervalString(buf1, suspended->expires-now, user->handle_info);
4534 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
4536 case 2: /* no issue time, expired */
4537 intervalString(buf1, now-suspended->expires, user->handle_info);
4538 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
4540 case 3: /* no issue time, revoked */
4541 intervalString(buf1, now-suspended->revoked, user->handle_info);
4542 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
4544 case 4: /* issue time set, indefinite expiration */
4545 intervalString(buf1, now-suspended->issued, user->handle_info);
4546 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
4548 case 5: /* issue time set, expires in future */
4549 intervalString(buf1, now-suspended->issued, user->handle_info);
4550 intervalString(buf2, suspended->expires-now, user->handle_info);
4551 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
4553 case 6: /* issue time set, expired */
4554 intervalString(buf1, now-suspended->issued, user->handle_info);
4555 intervalString(buf2, now-suspended->expires, user->handle_info);
4556 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
4558 case 7: /* issue time set, revoked */
4559 intervalString(buf1, now-suspended->issued, user->handle_info);
4560 intervalString(buf2, now-suspended->revoked, user->handle_info);
4561 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
4564 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
4569 static CHANSERV_FUNC(cmd_info)
4571 char modes[MAXLEN], buffer[INTERVALLEN];
4572 struct userData *uData, *owner;
4573 struct chanData *cData;
4574 struct do_not_register *dnr;
4579 cData = channel->channel_info;
4580 reply("CSMSG_CHANNEL_INFO", channel->name);
4582 uData = GetChannelUser(cData, user->handle_info);
4583 if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
4585 mod_chanmode_format(&cData->modes, modes);
4586 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
4587 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
4590 for(it = dict_first(cData->notes); it; it = iter_next(it))
4594 note = iter_data(it);
4595 if(!note_type_visible_to_user(cData, note->type, user))
4598 padding = PADLEN - 1 - strlen(iter_key(it));
4599 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
4602 if(cData->max_time) {
4603 reply("CSMSG_CHANNEL_MAX_TIME", cData->max, intervalString(buffer, now - cData->max_time, user->handle_info));
4605 reply("CSMSG_CHANNEL_MAX", cData->max);
4607 for(owner = cData->users; owner; owner = owner->next)
4608 if(owner->access == UL_OWNER)
4609 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
4610 reply("CSMSG_CHANNEL_USERS", cData->userCount);
4611 reply("CSMSG_CHANNEL_BANS", cData->banCount);
4612 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
4614 privileged = IsStaff(user);
4616 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
4617 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
4618 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
4620 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
4621 chanserv_show_dnrs(user, cmd, channel->name, NULL);
4623 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
4625 struct suspended *suspended;
4626 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
4627 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
4628 show_suspension_info(cmd, user, suspended);
4630 else if(IsSuspended(cData))
4632 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
4633 show_suspension_info(cmd, user, cData->suspended);
4638 static CHANSERV_FUNC(cmd_netinfo)
4640 extern unsigned long boot_time;
4641 extern unsigned long burst_length;
4642 char interval[INTERVALLEN];
4644 reply("CSMSG_NETWORK_INFO");
4645 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
4646 reply("CSMSG_NETWORK_USERS", dict_size(clients));
4647 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
4648 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
4649 reply("CSMSG_NETWORK_BANS", banCount);
4650 reply("CSMSG_NETWORK_CHANUSERS", userCount);
4651 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
4652 reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
4657 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
4659 struct helpfile_table table;
4661 struct userNode *user;
4666 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4667 table.contents = alloca(list->used*sizeof(*table.contents));
4668 for(nn=0; nn<list->used; nn++)
4670 user = list->list[nn];
4671 if(user->modes & skip_flags)
4675 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
4678 nick = alloca(strlen(user->nick)+3);
4679 sprintf(nick, "(%s)", user->nick);
4683 table.contents[table.length][0] = nick;
4686 table_send(chanserv, to->nick, 0, NULL, table);
4689 static CHANSERV_FUNC(cmd_ircops)
4691 reply("CSMSG_STAFF_OPERS");
4692 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
4696 static CHANSERV_FUNC(cmd_helpers)
4698 reply("CSMSG_STAFF_HELPERS");
4699 send_staff_list(user, &curr_helpers, FLAGS_OPER);
4703 static CHANSERV_FUNC(cmd_staff)
4705 reply("CSMSG_NETWORK_STAFF");
4706 cmd_ircops(CSFUNC_ARGS);
4707 cmd_helpers(CSFUNC_ARGS);
4711 static CHANSERV_FUNC(cmd_peek)
4713 struct modeNode *mn;
4714 char modes[MODELEN];
4716 struct helpfile_table table;
4717 int opcount = 0, voicecount = 0, srvcount = 0;
4719 irc_make_chanmode(channel, modes);
4721 reply("CSMSG_PEEK_INFO", channel->name);
4722 reply("CSMSG_PEEK_TOPIC", channel->topic);
4723 reply("CSMSG_PEEK_MODES", modes);
4727 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4728 table.contents = alloca(channel->members.used*sizeof(*table.contents));
4729 for(n = 0; n < channel->members.used; n++)
4731 mn = channel->members.list[n];
4732 if(IsLocal(mn->user))
4734 else if(mn->modes & MODE_CHANOP)
4736 else if(mn->modes & MODE_VOICE)
4739 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
4741 table.contents[table.length] = alloca(sizeof(**table.contents));
4742 table.contents[table.length][0] = mn->user->nick;
4746 reply("CSMSG_PEEK_USERS", channel->members.used, opcount, voicecount,
4747 (channel->members.used - opcount - voicecount - srvcount));
4751 reply("CSMSG_PEEK_OPS");
4752 table_send(chanserv, user->nick, 0, NULL, table);
4755 reply("CSMSG_PEEK_NO_OPS");
4759 static MODCMD_FUNC(cmd_wipeinfo)
4761 struct handle_info *victim;
4762 struct userData *ud, *actor, *real_actor;
4763 unsigned int override = 0;
4766 actor = GetChannelUser(channel->channel_info, user->handle_info);
4767 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
4768 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4770 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4772 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4775 if((ud->access >= actor->access) && (ud != actor))
4777 reply("MSG_USER_OUTRANKED", victim->handle);
4780 if((ud != real_actor) && (!real_actor || (ud->access >= real_actor->access)))
4781 override = CMD_LOG_OVERRIDE;
4785 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4786 return 1 | override;
4789 static CHANSERV_FUNC(cmd_resync)
4791 struct mod_chanmode *changes;
4792 struct chanData *cData = channel->channel_info;
4793 unsigned int ii, used;
4795 changes = mod_chanmode_alloc(channel->members.used * 2);
4796 for(ii = used = 0; ii < channel->members.used; ++ii)
4798 struct modeNode *mn = channel->members.list[ii];
4799 struct userData *uData;
4801 if(IsService(mn->user))
4804 uData = GetChannelAccess(cData, mn->user->handle_info);
4805 if(!cData->lvlOpts[lvlGiveOps]
4806 || (uData && uData->access >= cData->lvlOpts[lvlGiveOps]))
4808 if(!(mn->modes & MODE_CHANOP))
4810 changes->args[used].mode = MODE_CHANOP;
4811 changes->args[used++].u.member = mn;
4814 else if(!cData->lvlOpts[lvlGiveVoice]
4815 || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice]))
4817 if(mn->modes & MODE_CHANOP)
4819 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4820 changes->args[used++].u.member = mn;
4822 if(!(mn->modes & MODE_VOICE))
4824 changes->args[used].mode = MODE_VOICE;
4825 changes->args[used++].u.member = mn;
4832 changes->args[used].mode = MODE_REMOVE | mn->modes;
4833 changes->args[used++].u.member = mn;
4837 changes->argc = used;
4838 modcmd_chanmode_announce(changes);
4839 mod_chanmode_free(changes);
4840 reply("CSMSG_RESYNCED_USERS", channel->name);
4844 static CHANSERV_FUNC(cmd_seen)
4846 struct userData *uData;
4847 struct handle_info *handle;
4848 char seen[INTERVALLEN];
4852 if(!irccasecmp(argv[1], chanserv->nick))
4854 reply("CSMSG_IS_CHANSERV");
4858 if(!(handle = get_handle_info(argv[1])))
4860 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4864 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4866 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4871 reply("CSMSG_USER_PRESENT", handle->handle);
4872 else if(uData->seen)
4873 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
4875 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4877 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4878 reply("CSMSG_USER_VACATION", handle->handle);
4883 static MODCMD_FUNC(cmd_names)
4885 struct userNode *targ;
4886 struct userData *targData;
4887 unsigned int ii, pos;
4890 for(ii=pos=0; ii<channel->members.used; ++ii)
4892 targ = channel->members.list[ii]->user;
4893 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4896 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
4899 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4903 if(IsUserSuspended(targData))
4905 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4908 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4909 reply("CSMSG_END_NAMES", channel->name);
4914 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
4916 switch(ntype->visible_type)
4918 case NOTE_VIS_ALL: return 1;
4919 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
4920 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
4925 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
4927 struct userData *uData;
4929 switch(ntype->set_access_type)
4931 case NOTE_SET_CHANNEL_ACCESS:
4932 if(!user->handle_info)
4934 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
4936 return uData->access >= ntype->set_access.min_ulevel;
4937 case NOTE_SET_CHANNEL_SETTER:
4938 return check_user_level(channel, user, lvlSetters, 1, 0);
4939 case NOTE_SET_PRIVILEGED: default:
4940 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
4944 static CHANSERV_FUNC(cmd_note)
4946 struct chanData *cData;
4948 struct note_type *ntype;
4950 cData = channel->channel_info;
4953 reply("CSMSG_NOT_REGISTERED", channel->name);
4957 /* If no arguments, show all visible notes for the channel. */
4963 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
4965 note = iter_data(it);
4966 if(!note_type_visible_to_user(cData, note->type, user))
4969 reply("CSMSG_NOTELIST_HEADER", channel->name);
4970 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
4973 reply("CSMSG_NOTELIST_END", channel->name);
4975 reply("CSMSG_NOTELIST_EMPTY", channel->name);
4977 /* If one argument, show the named note. */
4980 if((note = dict_find(cData->notes, argv[1], NULL))
4981 && note_type_visible_to_user(cData, note->type, user))
4983 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
4985 else if((ntype = dict_find(note_types, argv[1], NULL))
4986 && note_type_visible_to_user(NULL, ntype, user))
4988 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
4993 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4997 /* Assume they're trying to set a note. */
5001 ntype = dict_find(note_types, argv[1], NULL);
5004 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
5007 else if(note_type_settable_by_user(channel, ntype, user))
5009 note_text = unsplit_string(argv+2, argc-2, NULL);
5010 if((note = dict_find(cData->notes, argv[1], NULL)))
5011 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
5012 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
5013 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
5015 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
5017 /* The note is viewable to staff only, so return 0
5018 to keep the invocation from getting logged (or
5019 regular users can see it in !events). */
5025 reply("CSMSG_NO_ACCESS");
5032 static CHANSERV_FUNC(cmd_delnote)
5037 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
5038 || !note_type_settable_by_user(channel, note->type, user))
5040 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
5043 dict_remove(channel->channel_info->notes, note->type->name);
5044 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
5048 static CHANSERV_FUNC(cmd_events)
5050 struct logSearch discrim;
5051 struct logReport report;
5052 unsigned int matches, limit;
5054 limit = (argc > 1) ? atoi(argv[1]) : 10;
5055 if(limit < 1 || limit > 200)
5058 memset(&discrim, 0, sizeof(discrim));
5059 discrim.masks.bot = chanserv;
5060 discrim.masks.channel_name = channel->name;
5062 discrim.masks.command = argv[2];
5063 discrim.limit = limit;
5064 discrim.max_time = INT_MAX;
5065 discrim.severities = 1 << LOG_COMMAND;
5066 report.reporter = chanserv;
5068 reply("CSMSG_EVENT_SEARCH_RESULTS");
5069 matches = log_entry_search(&discrim, log_report_entry, &report);
5071 reply("MSG_MATCH_COUNT", matches);
5073 reply("MSG_NO_MATCHES");
5077 static CHANSERV_FUNC(cmd_say)
5083 msg = unsplit_string(argv + 1, argc - 1, NULL);
5084 send_channel_message(channel, cmd->parent->bot, "%s", msg);
5086 else if(*argv[1] == '*' && argv[1][1] != '\0')
5088 struct handle_info *hi;
5089 struct userNode *authed;
5092 msg = unsplit_string(argv + 2, argc - 2, NULL);
5094 if (!(hi = get_handle_info(argv[1] + 1)))
5096 reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
5100 for (authed = hi->users; authed; authed = authed->next_authed)
5101 send_target_message(5, authed->nick, cmd->parent->bot, "%s", msg);
5103 else if(GetUserH(argv[1]))
5106 msg = unsplit_string(argv + 2, argc - 2, NULL);
5107 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
5111 reply("MSG_NOT_TARGET_NAME");
5117 static CHANSERV_FUNC(cmd_emote)
5123 /* CTCP is so annoying. */
5124 msg = unsplit_string(argv + 1, argc - 1, NULL);
5125 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
5127 else if(*argv[1] == '*' && argv[1][1] != '\0')
5129 struct handle_info *hi;
5130 struct userNode *authed;
5133 msg = unsplit_string(argv + 2, argc - 2, NULL);
5135 if (!(hi = get_handle_info(argv[1] + 1)))
5137 reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
5141 for (authed = hi->users; authed; authed = authed->next_authed)
5142 send_target_message(5, authed->nick, cmd->parent->bot, "\001ACTION %s\001", msg);
5144 else if(GetUserH(argv[1]))
5146 msg = unsplit_string(argv + 2, argc - 2, NULL);
5147 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
5151 reply("MSG_NOT_TARGET_NAME");
5157 struct channelList *
5158 chanserv_support_channels(void)
5160 return &chanserv_conf.support_channels;
5163 static CHANSERV_FUNC(cmd_expire)
5165 int channel_count = registered_channels;
5166 expire_channels(NULL);
5167 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
5172 chanserv_expire_suspension(void *data)
5174 struct suspended *suspended = data;
5175 struct chanNode *channel;
5178 /* Update the channel registration data structure. */
5179 if(!suspended->expires || (now < suspended->expires))
5180 suspended->revoked = now;
5181 channel = suspended->cData->channel;
5182 suspended->cData->channel = channel;
5183 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
5185 /* If appropriate, re-join ChanServ to the channel. */
5186 if(!IsOffChannel(suspended->cData))
5188 spamserv_cs_suspend(channel, 0, 0, NULL);
5189 ss_cs_join_channel(channel, 1);
5192 /* Mark everyone currently in the channel as present. */
5193 for(ii = 0; ii < channel->members.used; ++ii)
5195 struct userData *uData = GetChannelAccess(suspended->cData, channel->members.list[ii]->user->handle_info);
5204 static CHANSERV_FUNC(cmd_csuspend)
5206 struct suspended *suspended;
5207 char reason[MAXLEN];
5208 unsigned long expiry, duration;
5209 struct userData *uData;
5213 if(IsProtected(channel->channel_info))
5215 reply("CSMSG_SUSPEND_NODELETE", channel->name);
5219 if(argv[1][0] == '!')
5221 else if(IsSuspended(channel->channel_info))
5223 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
5224 show_suspension_info(cmd, user, channel->channel_info->suspended);
5228 if(!strcmp(argv[1], "0"))
5230 else if((duration = ParseInterval(argv[1])))
5231 expiry = now + duration;
5234 reply("MSG_INVALID_DURATION", argv[1]);
5238 unsplit_string(argv + 2, argc - 2, reason);
5240 suspended = calloc(1, sizeof(*suspended));
5241 suspended->revoked = 0;
5242 suspended->issued = now;
5243 suspended->suspender = strdup(user->handle_info->handle);
5244 suspended->expires = expiry;
5245 suspended->reason = strdup(reason);
5246 suspended->cData = channel->channel_info;
5247 suspended->previous = suspended->cData->suspended;
5248 suspended->cData->suspended = suspended;
5250 if(suspended->expires)
5251 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
5253 if(IsSuspended(channel->channel_info))
5255 suspended->previous->revoked = now;
5256 if(suspended->previous->expires)
5257 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
5258 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
5259 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5263 /* Mark all users in channel as absent. */
5264 for(uData = channel->channel_info->users; uData; uData = uData->next)
5273 /* Mark the channel as suspended, then part. */
5274 channel->channel_info->flags |= CHANNEL_SUSPENDED;
5275 spamserv_cs_suspend(channel, expiry, 1, suspended->reason);
5276 DelChannelUser(chanserv, channel, suspended->reason, 0);
5277 reply("CSMSG_SUSPENDED", channel->name);
5278 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
5279 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5284 static CHANSERV_FUNC(cmd_cunsuspend)
5286 struct suspended *suspended;
5287 char message[MAXLEN];
5289 if(!IsSuspended(channel->channel_info))
5291 reply("CSMSG_NOT_SUSPENDED", channel->name);
5295 suspended = channel->channel_info->suspended;
5297 /* Expire the suspension and join ChanServ to the channel. */
5298 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
5299 chanserv_expire_suspension(suspended);
5300 reply("CSMSG_UNSUSPENDED", channel->name);
5301 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
5302 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
5306 typedef struct chanservSearch
5311 unsigned long unvisited;
5312 unsigned long registered;
5314 unsigned long flags;
5318 typedef void (*channel_search_func)(struct chanData *channel, void *data);
5321 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
5326 search = malloc(sizeof(struct chanservSearch));
5327 memset(search, 0, sizeof(*search));
5330 for(i = 0; i < argc; i++)
5332 /* Assume all criteria require arguments. */
5335 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
5339 if(!irccasecmp(argv[i], "name"))
5340 search->name = argv[++i];
5341 else if(!irccasecmp(argv[i], "registrar"))
5342 search->registrar = argv[++i];
5343 else if(!irccasecmp(argv[i], "unvisited"))
5344 search->unvisited = ParseInterval(argv[++i]);
5345 else if(!irccasecmp(argv[i], "registered"))
5346 search->registered = ParseInterval(argv[++i]);
5347 else if(!irccasecmp(argv[i], "flags"))
5350 if(!irccasecmp(argv[i], "nodelete"))
5351 search->flags |= CHANNEL_NODELETE;
5352 else if(!irccasecmp(argv[i], "suspended"))
5353 search->flags |= CHANNEL_SUSPENDED;
5354 else if(!irccasecmp(argv[i], "unreviewed"))
5355 search->flags |= CHANNEL_UNREVIEWED;
5358 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
5362 else if(!irccasecmp(argv[i], "limit"))
5363 search->limit = strtoul(argv[++i], NULL, 10);
5366 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
5371 if(search->name && !strcmp(search->name, "*"))
5373 if(search->registrar && !strcmp(search->registrar, "*"))
5374 search->registrar = 0;
5383 chanserv_channel_match(struct chanData *channel, search_t search)
5385 const char *name = channel->channel->name;
5386 if((search->name && !match_ircglob(name, search->name)) ||
5387 (search->registrar && !channel->registrar) ||
5388 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
5389 (search->unvisited && (now - channel->visited) < search->unvisited) ||
5390 (search->registered && (now - channel->registered) > search->registered) ||
5391 (search->flags && ((search->flags & channel->flags) != search->flags)))
5398 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
5400 struct chanData *channel;
5401 unsigned int matches = 0;
5403 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
5405 if(!chanserv_channel_match(channel, search))
5415 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
5420 search_print(struct chanData *channel, void *data)
5422 send_message_type(4, data, chanserv, "%s", channel->channel->name);
5425 static CHANSERV_FUNC(cmd_search)
5428 unsigned int matches;
5429 channel_search_func action;
5433 if(!irccasecmp(argv[1], "count"))
5434 action = search_count;
5435 else if(!irccasecmp(argv[1], "print"))
5436 action = search_print;
5439 reply("CSMSG_ACTION_INVALID", argv[1]);
5443 search = chanserv_search_create(user, argc - 2, argv + 2);
5447 if(action == search_count)
5448 search->limit = INT_MAX;
5450 if(action == search_print)
5451 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
5453 matches = chanserv_channel_search(search, action, user);
5456 reply("MSG_MATCH_COUNT", matches);
5458 reply("MSG_NO_MATCHES");
5464 static CHANSERV_FUNC(cmd_unvisited)
5466 struct chanData *cData;
5467 unsigned long interval = chanserv_conf.channel_expire_delay;
5468 char buffer[INTERVALLEN];
5469 unsigned int limit = 25, matches = 0;
5473 interval = ParseInterval(argv[1]);
5475 limit = atoi(argv[2]);
5478 intervalString(buffer, interval, user->handle_info);
5479 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
5481 for(cData = channelList; cData && matches < limit; cData = cData->next)
5483 if((now - cData->visited) < interval)
5486 intervalString(buffer, now - cData->visited, user->handle_info);
5487 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
5494 static MODCMD_FUNC(chan_opt_defaulttopic)
5500 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5502 reply("CSMSG_TOPIC_LOCKED", channel->name);
5506 topic = unsplit_string(argv+1, argc-1, NULL);
5508 free(channel->channel_info->topic);
5509 if(topic[0] == '*' && topic[1] == 0)
5511 topic = channel->channel_info->topic = NULL;
5515 topic = channel->channel_info->topic = strdup(topic);
5516 if(channel->channel_info->topic_mask
5517 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
5518 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5520 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
5523 if(channel->channel_info->topic)
5524 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
5526 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
5530 static MODCMD_FUNC(chan_opt_topicmask)
5534 struct chanData *cData = channel->channel_info;
5537 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5539 reply("CSMSG_TOPIC_LOCKED", channel->name);
5543 mask = unsplit_string(argv+1, argc-1, NULL);
5545 if(cData->topic_mask)
5546 free(cData->topic_mask);
5547 if(mask[0] == '*' && mask[1] == 0)
5549 cData->topic_mask = 0;
5553 cData->topic_mask = strdup(mask);
5555 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
5556 else if(!match_ircglob(cData->topic, cData->topic_mask))
5557 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5561 if(channel->channel_info->topic_mask)
5562 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
5564 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
5568 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
5572 char *greeting = unsplit_string(argv+1, argc-1, NULL);
5576 if(greeting[0] == '*' && greeting[1] == 0)
5580 unsigned int length = strlen(greeting);
5581 if(length > chanserv_conf.greeting_length)
5583 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
5586 *data = strdup(greeting);
5595 reply(name, user_find_message(user, "MSG_NONE"));
5599 static MODCMD_FUNC(chan_opt_greeting)
5601 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
5604 static MODCMD_FUNC(chan_opt_usergreeting)
5606 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
5609 static MODCMD_FUNC(chan_opt_modes)
5611 struct mod_chanmode *new_modes;
5616 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
5618 reply("CSMSG_NO_ACCESS");
5621 if(argv[1][0] == '*' && argv[1][1] == 0)
5623 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
5625 else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED|MCP_NO_APASS, 0)))
5627 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5630 else if(new_modes->argc > 1)
5632 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5633 mod_chanmode_free(new_modes);
5638 channel->channel_info->modes = *new_modes;
5639 modcmd_chanmode_announce(new_modes);
5640 mod_chanmode_free(new_modes);
5644 mod_chanmode_format(&channel->channel_info->modes, modes);
5646 reply("CSMSG_SET_MODES", modes);
5648 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
5652 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5654 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5656 struct chanData *cData = channel->channel_info;
5661 /* Set flag according to value. */
5662 if(enabled_string(argv[1]))
5664 cData->flags |= mask;
5667 else if(disabled_string(argv[1]))
5669 cData->flags &= ~mask;
5674 reply("MSG_INVALID_BINARY", argv[1]);
5680 /* Find current option value. */
5681 value = (cData->flags & mask) ? 1 : 0;
5685 reply(name, user_find_message(user, "MSG_ON"));
5687 reply(name, user_find_message(user, "MSG_OFF"));
5691 static MODCMD_FUNC(chan_opt_nodelete)
5693 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
5695 reply("MSG_SETTING_PRIVILEGED", argv[0]);
5699 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
5702 static MODCMD_FUNC(chan_opt_dynlimit)
5704 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
5707 static MODCMD_FUNC(chan_opt_offchannel)
5709 struct chanData *cData = channel->channel_info;
5714 /* Set flag according to value. */
5715 if(enabled_string(argv[1]))
5717 if(!IsOffChannel(cData))
5718 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
5719 cData->flags |= CHANNEL_OFFCHANNEL;
5722 else if(disabled_string(argv[1]))
5724 if(IsOffChannel(cData))
5726 struct mod_chanmode change;
5727 mod_chanmode_init(&change);
5729 change.args[0].mode = MODE_CHANOP;
5730 change.args[0].u.member = AddChannelUser(chanserv, channel);
5731 mod_chanmode_announce(chanserv, channel, &change);
5733 cData->flags &= ~CHANNEL_OFFCHANNEL;
5738 reply("MSG_INVALID_BINARY", argv[1]);
5744 /* Find current option value. */
5745 value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
5749 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
5751 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
5755 static MODCMD_FUNC(chan_opt_unreviewed)
5757 struct chanData *cData = channel->channel_info;
5758 int value = (cData->flags & CHANNEL_UNREVIEWED) ? 1 : 0;
5764 /* The two directions can have different ACLs. */
5765 if(enabled_string(argv[1]))
5767 else if(disabled_string(argv[1]))
5771 reply("MSG_INVALID_BINARY", argv[1]);
5775 if (new_value != value)
5777 struct svccmd *subcmd;
5778 char subcmd_name[32];
5780 snprintf(subcmd_name, sizeof(subcmd_name), "%s %s", argv[0], (new_value ? "on" : "off"));
5781 subcmd = dict_find(cmd->parent->commands, subcmd_name, NULL);
5784 reply("MSG_COMMAND_DISABLED", subcmd_name);
5787 else if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
5791 cData->flags |= CHANNEL_UNREVIEWED;
5794 free(cData->registrar);
5795 cData->registrar = strdup(user->handle_info->handle);
5796 cData->flags &= ~CHANNEL_UNREVIEWED;
5803 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_ON"));
5805 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_OFF"));
5809 static MODCMD_FUNC(chan_opt_defaults)
5811 struct userData *uData;
5812 struct chanData *cData;
5813 const char *confirm;
5814 enum levelOption lvlOpt;
5815 enum charOption chOpt;
5817 cData = channel->channel_info;
5818 uData = GetChannelUser(cData, user->handle_info);
5819 if(!uData || (uData->access < UL_OWNER))
5821 reply("CSMSG_OWNER_DEFAULTS", channel->name);
5824 confirm = make_confirmation_string(uData);
5825 if((argc < 2) || strcmp(argv[1], confirm))
5827 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
5830 cData->flags = (CHANNEL_DEFAULT_FLAGS & ~CHANNEL_PRESERVED_FLAGS)
5831 | (cData->flags & CHANNEL_PRESERVED_FLAGS);
5832 cData->modes = chanserv_conf.default_modes;
5833 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
5834 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
5835 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
5836 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
5837 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
5842 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5844 struct chanData *cData = channel->channel_info;
5845 struct userData *uData;
5846 unsigned short value;
5850 if(!check_user_level(channel, user, option, 1, 1))
5852 reply("CSMSG_CANNOT_SET");
5855 value = user_level_from_name(argv[1], UL_OWNER+1);
5856 if(!value && strcmp(argv[1], "0"))
5858 reply("CSMSG_INVALID_ACCESS", argv[1]);
5861 uData = GetChannelUser(cData, user->handle_info);
5862 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
5864 reply("CSMSG_BAD_SETLEVEL");
5870 if(value > cData->lvlOpts[lvlGiveOps])
5872 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
5877 if(value < cData->lvlOpts[lvlGiveVoice])
5879 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
5884 /* This test only applies to owners, since non-owners
5885 * trying to set an option to above their level get caught
5886 * by the CSMSG_BAD_SETLEVEL test above.
5888 if(value > uData->access)
5890 reply("CSMSG_BAD_SETTERS");
5897 cData->lvlOpts[option] = value;
5899 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
5903 static MODCMD_FUNC(chan_opt_enfops)
5905 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
5908 static MODCMD_FUNC(chan_opt_giveops)
5910 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
5913 static MODCMD_FUNC(chan_opt_enfmodes)
5915 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
5918 static MODCMD_FUNC(chan_opt_enftopic)
5920 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
5923 static MODCMD_FUNC(chan_opt_pubcmd)
5925 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
5928 static MODCMD_FUNC(chan_opt_setters)
5930 return channel_level_option(lvlSetters, CSFUNC_ARGS);
5933 static MODCMD_FUNC(chan_opt_ctcpusers)
5935 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
5938 static MODCMD_FUNC(chan_opt_userinfo)
5940 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
5943 static MODCMD_FUNC(chan_opt_givevoice)
5945 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
5948 static MODCMD_FUNC(chan_opt_topicsnarf)
5950 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
5953 static MODCMD_FUNC(chan_opt_vote)
5955 return channel_level_option(lvlVote, CSFUNC_ARGS);
5958 static MODCMD_FUNC(chan_opt_inviteme)
5960 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
5964 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5966 struct chanData *cData = channel->channel_info;
5967 int count = charOptions[option].count, idx;
5971 idx = atoi(argv[1]);
5973 if(!isdigit(argv[1][0]) || (idx < 0) || (idx >= count))
5975 reply("CSMSG_INVALID_NUMERIC", idx);
5976 /* Show possible values. */
5977 for(idx = 0; idx < count; idx++)
5978 reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
5982 cData->chOpts[option] = charOptions[option].values[idx].value;
5986 /* Find current option value. */
5989 (idx < count) && (cData->chOpts[option] != charOptions[option].values[idx].value);
5993 /* Somehow, the option value is corrupt; reset it to the default. */
5994 cData->chOpts[option] = charOptions[option].default_value;
5999 reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
6003 static MODCMD_FUNC(chan_opt_protect)
6005 return channel_multiple_option(chProtect, CSFUNC_ARGS);
6008 static MODCMD_FUNC(chan_opt_toys)
6010 return channel_multiple_option(chToys, CSFUNC_ARGS);
6013 static MODCMD_FUNC(chan_opt_ctcpreaction)
6015 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
6018 static MODCMD_FUNC(chan_opt_topicrefresh)
6020 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
6023 static struct svccmd_list set_shows_list;
6026 handle_svccmd_unbind(struct svccmd *target) {
6028 for(ii=0; ii<set_shows_list.used; ++ii)
6029 if(target == set_shows_list.list[ii])
6030 set_shows_list.used = 0;
6033 static CHANSERV_FUNC(cmd_set)
6035 struct svccmd *subcmd;
6039 /* Check if we need to (re-)initialize set_shows_list. */
6040 if(!set_shows_list.used)
6042 if(!set_shows_list.size)
6044 set_shows_list.size = chanserv_conf.set_shows->used;
6045 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
6047 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
6049 const char *name = chanserv_conf.set_shows->list[ii];
6050 sprintf(buf, "%s %s", argv[0], name);
6051 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6054 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
6057 svccmd_list_append(&set_shows_list, subcmd);
6063 reply("CSMSG_CHANNEL_OPTIONS");
6064 for(ii = 0; ii < set_shows_list.used; ii++)
6066 subcmd = set_shows_list.list[ii];
6067 subcmd->command->func(user, channel, 1, argv+1, subcmd);
6072 sprintf(buf, "%s %s", argv[0], argv[1]);
6073 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6076 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
6079 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
6081 reply("CSMSG_NO_ACCESS");
6087 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
6091 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
6093 struct userData *uData;
6095 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6098 reply("CSMSG_NOT_USER", channel->name);
6104 /* Just show current option value. */
6106 else if(enabled_string(argv[1]))
6108 uData->flags |= mask;
6110 else if(disabled_string(argv[1]))
6112 uData->flags &= ~mask;
6116 reply("MSG_INVALID_BINARY", argv[1]);
6120 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
6124 static MODCMD_FUNC(user_opt_noautoop)
6126 struct userData *uData;
6128 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6131 reply("CSMSG_NOT_USER", channel->name);
6134 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
6135 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
6137 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
6140 static MODCMD_FUNC(user_opt_autoinvite)
6142 if((argc > 1) && !check_user_level(channel, user, lvlInviteMe, 1, 0))
6144 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
6146 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
6149 static MODCMD_FUNC(user_opt_info)
6151 struct userData *uData;
6154 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6158 /* If they got past the command restrictions (which require access)
6159 * but fail this test, we have some fool with security override on.
6161 reply("CSMSG_NOT_USER", channel->name);
6168 infoline = unsplit_string(argv + 1, argc - 1, NULL);
6169 if(strlen(infoline) > chanserv_conf.max_userinfo_length)
6171 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
6174 bp = strcspn(infoline, "\001");
6177 reply("CSMSG_BAD_INFOLINE", infoline[bp]);
6182 if(infoline[0] == '*' && infoline[1] == 0)
6185 uData->info = strdup(infoline);
6188 reply("CSMSG_USET_INFO", uData->info);
6190 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
6194 struct svccmd_list uset_shows_list;
6196 static CHANSERV_FUNC(cmd_uset)
6198 struct svccmd *subcmd;
6202 /* Check if we need to (re-)initialize uset_shows_list. */
6203 if(!uset_shows_list.used)
6207 "NoAutoOp", "AutoInvite", "Info"
6210 if(!uset_shows_list.size)
6212 uset_shows_list.size = ArrayLength(options);
6213 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
6215 for(ii = 0; ii < ArrayLength(options); ii++)
6217 const char *name = options[ii];
6218 sprintf(buf, "%s %s", argv[0], name);
6219 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6222 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
6225 svccmd_list_append(&uset_shows_list, subcmd);
6231 /* Do this so options are presented in a consistent order. */
6232 reply("CSMSG_USER_OPTIONS");
6233 for(ii = 0; ii < uset_shows_list.used; ii++)
6234 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
6238 sprintf(buf, "%s %s", argv[0], argv[1]);
6239 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6242 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
6246 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
6249 static CHANSERV_FUNC(cmd_giveownership)
6251 struct handle_info *new_owner_hi;
6252 struct userData *new_owner;
6253 struct userData *curr_user;
6254 struct userData *invoker;
6255 struct chanData *cData = channel->channel_info;
6256 struct do_not_register *dnr;
6257 const char *confirm;
6259 unsigned short co_access;
6260 char reason[MAXLEN];
6263 curr_user = GetChannelAccess(cData, user->handle_info);
6264 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
6265 if(!curr_user || (curr_user->access != UL_OWNER))
6267 struct userData *owner = NULL;
6268 for(curr_user = channel->channel_info->users;
6270 curr_user = curr_user->next)
6272 if(curr_user->access != UL_OWNER)
6276 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
6283 else if(!force && (now < cData->ownerTransfer + chanserv_conf.giveownership_period))
6285 char delay[INTERVALLEN];
6286 intervalString(delay, cData->ownerTransfer + chanserv_conf.giveownership_period - now, user->handle_info);
6287 reply("CSMSG_TRANSFER_WAIT", delay, channel->name);
6290 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
6292 if(new_owner_hi == user->handle_info)
6294 reply("CSMSG_NO_TRANSFER_SELF");
6297 new_owner = GetChannelAccess(cData, new_owner_hi);
6302 new_owner = add_channel_user(cData, new_owner_hi, UL_OWNER - 1, 0, NULL);
6306 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
6310 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
6312 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
6315 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
6316 if(!IsHelping(user))
6317 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
6319 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi->handle);
6322 invoker = GetChannelUser(cData, user->handle_info);
6323 if(invoker->access <= UL_OWNER)
6325 confirm = make_confirmation_string(curr_user);
6326 if((argc < 3) || strcmp(argv[2], confirm))
6328 reply("CSMSG_CONFIRM_GIVEOWNERSHIP", new_owner_hi->handle, confirm);
6332 if(new_owner->access >= UL_COOWNER)
6333 co_access = new_owner->access;
6335 co_access = UL_COOWNER;
6336 new_owner->access = UL_OWNER;
6338 curr_user->access = co_access;
6339 cData->ownerTransfer = now;
6340 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
6341 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
6342 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
6346 static CHANSERV_FUNC(cmd_suspend)
6348 struct handle_info *hi;
6349 struct userData *actor, *real_actor, *target;
6350 unsigned int override = 0;
6353 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6354 actor = GetChannelUser(channel->channel_info, user->handle_info);
6355 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
6356 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6358 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6361 if(target->access >= actor->access)
6363 reply("MSG_USER_OUTRANKED", hi->handle);
6366 if(target->flags & USER_SUSPENDED)
6368 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
6373 target->present = 0;
6376 if(!real_actor || target->access >= real_actor->access)
6377 override = CMD_LOG_OVERRIDE;
6378 target->flags |= USER_SUSPENDED;
6379 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
6380 return 1 | override;
6383 static CHANSERV_FUNC(cmd_unsuspend)
6385 struct handle_info *hi;
6386 struct userData *actor, *real_actor, *target;
6387 unsigned int override = 0;
6390 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6391 actor = GetChannelUser(channel->channel_info, user->handle_info);
6392 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
6393 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6395 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6398 if(target->access >= actor->access)
6400 reply("MSG_USER_OUTRANKED", hi->handle);
6403 if(!(target->flags & USER_SUSPENDED))
6405 reply("CSMSG_NOT_SUSPENDED", hi->handle);
6408 if(!real_actor || target->access >= real_actor->access)
6409 override = CMD_LOG_OVERRIDE;
6410 target->flags &= ~USER_SUSPENDED;
6411 scan_user_presence(target, NULL);
6412 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
6413 return 1 | override;
6416 static MODCMD_FUNC(cmd_deleteme)
6418 struct handle_info *hi;
6419 struct userData *target;
6420 const char *confirm_string;
6421 unsigned short access_level;
6424 hi = user->handle_info;
6425 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6427 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6430 if(target->access == UL_OWNER)
6432 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
6435 confirm_string = make_confirmation_string(target);
6436 if((argc < 2) || strcmp(argv[1], confirm_string))
6438 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
6441 access_level = target->access;
6442 channel_name = strdup(channel->name);
6443 del_channel_user(target, 1);
6444 reply("CSMSG_DELETED_YOU", access_level, channel_name);
6449 static CHANSERV_FUNC(cmd_addvote)
6451 struct chanData *cData = channel->channel_info;
6452 struct userData *uData, *target;
6453 struct handle_info *hi;
6454 if (!cData) return 0;
6456 hi = user->handle_info;
6457 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6459 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6462 if(target->access < 300) {
6463 reply("CSMSG_NO_ACCESS");
6467 reply("CSMSG_ADDVOTE_FULL");
6471 msg = unsplit_string(argv + 1, argc - 1, NULL);
6472 cData->vote = strdup(msg);
6473 cData->vote_start=0;
6474 dict_delete(cData->vote_options);
6475 cData->vote_options = dict_new();
6476 dict_set_free_data(cData->vote_options, free_vote_options);
6477 for(uData = channel->channel_info->users; uData; uData = uData->next)
6482 reply("CSMSG_ADDVOTE_DONE");
6486 static CHANSERV_FUNC(cmd_delvote)
6488 struct chanData *cData = channel->channel_info;
6489 struct userData *target;
6490 struct handle_info *hi;
6491 if (!cData) return 0;
6492 hi = user->handle_info;
6493 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6495 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6498 if(target->access < 300) {
6499 reply("CSMSG_NO_ACCESS");
6503 reply("CSMSG_NO_VOTE");
6508 reply("CSMSG_DELVOTE_DONE");
6512 static CHANSERV_FUNC(cmd_addoption)
6514 struct chanData *cData = channel->channel_info;
6515 struct userData *target;
6516 struct handle_info *hi;
6517 if (!cData) return 0;
6519 hi = user->handle_info;
6520 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6522 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6525 if(target->access < 300) {
6526 reply("CSMSG_NO_ACCESS");
6530 reply("CSMSG_NO_VOTE");
6536 msg = unsplit_string(argv + 1, argc - 1, NULL);
6539 unsigned int lastid = 1;
6540 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6541 struct vote_option *cvOpt = iter_data(it);
6542 if(cvOpt->option_id > lastid)
6543 lastid = cvOpt->option_id;
6545 struct vote_option *vOpt;
6546 vOpt = calloc(1, sizeof(*vOpt));
6547 vOpt->name = strdup(msg);
6548 vOpt->option_id = (lastid + 1);
6550 sprintf(str,"%i",(lastid + 1));
6551 vOpt->option_str = strdup(str);
6553 dict_insert(cData->vote_options,vOpt->option_str,vOpt);
6555 reply("CSMSG_ADDOPTION_DONE",dict_size(cData->vote_options),lastid,(lastid + 1));
6559 static CHANSERV_FUNC(cmd_deloption)
6561 struct chanData *cData = channel->channel_info;
6562 struct userData *uData, *target;
6563 struct handle_info *hi;
6564 if (!cData) return 0;
6566 hi = user->handle_info;
6567 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6569 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6572 if(target->access < 300) {
6573 reply("CSMSG_NO_ACCESS");
6577 reply("CSMSG_NO_VOTE");
6580 if(cData->vote_start) {
6581 if(dict_size(cData->vote_options) < 3) {
6582 reply("CSMSG_VOTE_NEED_OPTIONS");
6587 int find_id = atoi(argv[1]);
6589 unsigned int found = 0;
6592 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6594 if (find_id == ii) {
6595 struct vote_option *vOpt = iter_data(it);
6596 found = vOpt->option_id;
6598 sprintf(str,"%i",vOpt->option_id);
6599 dict_remove(cData->vote_options, str);
6604 for(uData = channel->channel_info->users; uData; uData = uData->next) {
6605 if(uData->votefor == found) {
6610 reply("CSMSG_DELOPTION_DONE");
6613 reply("CSMSG_DELOPTION_NONE");
6618 static CHANSERV_FUNC(cmd_vote)
6620 struct chanData *cData = channel->channel_info;
6621 struct userData *target;
6622 struct handle_info *hi;
6623 unsigned int votedfor = 0;
6626 if (!cData || !cData->vote) {
6627 reply("CSMSG_NO_VOTE");
6630 if(argc > 1 && cData->vote_start) {
6631 hi = user->handle_info;
6632 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6634 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6637 if(!check_user_level(channel, user, lvlVote, 1, 0)) {
6638 reply("CSMSG_NO_ACCESS");
6642 reply("CSMSG_VOTE_VOTED");
6645 int find_id = atoi(argv[1]);
6648 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6650 if (find_id == ii) {
6651 struct vote_option *vOpt = iter_data(it);
6654 target->votefor = vOpt->option_id;
6655 votedfor = vOpt->option_id;
6656 votedfor_str = vOpt->name;
6660 reply("CSMSG_VOTE_INVALID");
6664 if (!cData->vote_start) {
6665 reply("CSMSG_VOTE_NOT_STARTED");
6667 reply("CSMSG_VOTE_QUESTION",cData->vote);
6669 unsigned int voteid = 0;
6672 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6673 struct vote_option *vOpt = iter_data(it);
6675 reply("CSMSG_VOTE_OPTION",voteid,vOpt->name,vOpt->voted);
6677 if(argc > 1 && cData->vote_start && votedfor_str) {
6678 reply("CSMSG_VOTE_DONE",votedfor_str);
6683 static CHANSERV_FUNC(cmd_startvote)
6685 struct chanData *cData = channel->channel_info;
6686 struct userData *target;
6687 struct handle_info *hi;
6688 if (!cData) return 0;
6689 hi = user->handle_info;
6690 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6692 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6695 if(target->access < 300) {
6696 reply("CSMSG_NO_ACCESS");
6700 reply("CSMSG_NO_VOTE");
6703 if(cData->vote_start) {
6704 reply("CSMSG_STARTVOTE_RUNNING");
6707 if(dict_size(cData->vote_options) < 2) {
6708 reply("CSMSG_VOTE_NEED_OPTIONS");
6711 cData->vote_start = 1;
6712 char response[MAXLEN];
6713 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_TOP"), user->nick);
6714 irc_privmsg(cmd->parent->bot, channel->name, response);
6715 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_QUESTION"), cData->vote);
6716 irc_privmsg(cmd->parent->bot, channel->name, response);
6717 unsigned int voteid = 0;
6719 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6720 struct vote_option *vOpt = iter_data(it);
6722 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_OPTION"), voteid, vOpt->name);
6723 irc_privmsg(cmd->parent->bot, channel->name, response);
6725 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_ACCESS"), cData->lvlOpts[lvlVote]); //Todo
6726 irc_privmsg(cmd->parent->bot, channel->name, response);
6727 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_HOWTO")); //Todo
6728 irc_privmsg(cmd->parent->bot, channel->name, response);
6731 static CHANSERV_FUNC(cmd_endvote)
6733 struct chanData *cData = channel->channel_info;
6734 struct userData *target;
6735 struct handle_info *hi;
6736 if (!cData) return 0;
6737 hi = user->handle_info;
6738 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6740 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6743 if(target->access < 300) {
6744 reply("CSMSG_NO_ACCESS");
6748 reply("CSMSG_NO_VOTE");
6751 if(!cData->vote_start) {
6752 reply("CSMSG_ENDVOTE_STOPPED");
6755 cData->vote_start = 0;
6756 reply("CSMSG_ENDVOTE_DONE");
6760 static CHANSERV_FUNC(cmd_voteresults)
6762 struct chanData *cData = channel->channel_info;
6763 struct userData *target;
6764 struct handle_info *hi;
6765 if (!cData) return 0;
6767 reply("CSMSG_NO_VOTE");
6770 if (argc > 1 && !irccasecmp(argv[1], "*")) {
6771 hi = user->handle_info;
6772 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6774 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6777 if(target->access < 300) {
6778 reply("CSMSG_NO_ACCESS");
6781 char response[MAXLEN];
6782 sprintf(response, user_find_message(user, "CSMSG_VOTERES_QUESTION"), cData->vote);
6783 irc_privmsg(cmd->parent->bot, channel->name, response);
6784 unsigned int voteid = 0;
6786 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6787 struct vote_option *vOpt = iter_data(it);
6789 sprintf(response, user_find_message(user, "CSMSG_VOTERES_OPTION"), voteid, vOpt->name, vOpt->voted);
6790 irc_privmsg(cmd->parent->bot, channel->name, response);
6793 reply("CSMSG_VOTE_QUESTION",cData->vote);
6794 unsigned int voteid = 0;
6796 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6797 struct vote_option *vOpt = iter_data(it);
6799 reply("CSMSG_VOTE_OPTION",voteid,vOpt->name,vOpt->voted);
6806 chanserv_refresh_topics(UNUSED_ARG(void *data))
6808 unsigned int refresh_num = (now - self->link_time) / chanserv_conf.refresh_period;
6809 struct chanData *cData;
6812 for(cData = channelList; cData; cData = cData->next)
6814 if(IsSuspended(cData))
6816 opt = cData->chOpts[chTopicRefresh];
6819 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
6822 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
6823 cData->last_refresh = refresh_num;
6825 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
6828 static CHANSERV_FUNC(cmd_unf)
6832 char response[MAXLEN];
6833 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
6834 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6835 irc_privmsg(cmd->parent->bot, channel->name, response);
6838 reply("CSMSG_UNF_RESPONSE");
6842 static CHANSERV_FUNC(cmd_ping)
6846 char response[MAXLEN];
6847 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
6848 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6849 irc_privmsg(cmd->parent->bot, channel->name, response);
6852 reply("CSMSG_PING_RESPONSE");
6856 static CHANSERV_FUNC(cmd_wut)
6860 char response[MAXLEN];
6861 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
6862 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6863 irc_privmsg(cmd->parent->bot, channel->name, response);
6866 reply("CSMSG_WUT_RESPONSE");
6870 static CHANSERV_FUNC(cmd_8ball)
6872 unsigned int i, j, accum;
6877 for(i=1; i<argc; i++)
6878 for(j=0; argv[i][j]; j++)
6879 accum = (accum << 5) - accum + toupper(argv[i][j]);
6880 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
6883 char response[MAXLEN];
6884 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
6885 irc_privmsg(cmd->parent->bot, channel->name, response);
6888 send_message_type(4, user, cmd->parent->bot, "%s", resp);
6892 static CHANSERV_FUNC(cmd_d)
6894 unsigned long sides, count, modifier, ii, total;
6895 char response[MAXLEN], *sep;
6899 if((count = strtoul(argv[1], &sep, 10)) < 1)
6909 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
6910 && (sides = strtoul(sep+1, &sep, 10)) > 1)
6914 else if((sep[0] == '-') && isdigit(sep[1]))
6915 modifier = strtoul(sep, NULL, 10);
6916 else if((sep[0] == '+') && isdigit(sep[1]))
6917 modifier = strtoul(sep+1, NULL, 10);
6924 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
6929 reply("CSMSG_BAD_DICE_COUNT", count, 10);
6932 for(total = ii = 0; ii < count; ++ii)
6933 total += (rand() % sides) + 1;
6936 if((count > 1) || modifier)
6938 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
6939 sprintf(response, fmt, total, count, sides, modifier);
6943 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
6944 sprintf(response, fmt, total, sides);
6947 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
6949 send_message_type(4, user, cmd->parent->bot, "%s", response);
6953 static CHANSERV_FUNC(cmd_huggle)
6955 /* CTCP must be via PRIVMSG, never notice */
6957 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
6959 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
6964 chanserv_adjust_limit(void *data)
6966 struct mod_chanmode change;
6967 struct chanData *cData = data;
6968 struct chanNode *channel = cData->channel;
6971 if(IsSuspended(cData))
6974 cData->limitAdjusted = now;
6975 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
6976 if(cData->modes.modes_set & MODE_LIMIT)
6978 if(limit > cData->modes.new_limit)
6979 limit = cData->modes.new_limit;
6980 else if(limit == cData->modes.new_limit)
6984 mod_chanmode_init(&change);
6985 change.modes_set = MODE_LIMIT;
6986 change.new_limit = limit;
6987 mod_chanmode_announce(chanserv, channel, &change);
6991 handle_new_channel(struct chanNode *channel)
6993 struct chanData *cData;
6995 if(!(cData = channel->channel_info))
6998 if(cData->modes.modes_set || cData->modes.modes_clear)
6999 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
7001 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
7002 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
7005 /* Welcome to my worst nightmare. Warning: Read (or modify)
7006 the code below at your own risk. */
7008 handle_join(struct modeNode *mNode)
7010 struct mod_chanmode change;
7011 struct userNode *user = mNode->user;
7012 struct chanNode *channel = mNode->channel;
7013 struct chanData *cData;
7014 struct userData *uData = NULL;
7015 struct banData *bData;
7016 struct handle_info *handle;
7017 unsigned int modes = 0, info = 0;
7020 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
7023 cData = channel->channel_info;
7024 if(channel->members.used > cData->max) {
7025 cData->max = channel->members.used;
7026 cData->max_time = now;
7029 for(i = 0; i < channel->invited.used; i++)
7031 if(channel->invited.list[i] == user) {
7032 userList_remove(&channel->invited, user);
7036 /* Check for bans. If they're joining through a ban, one of two
7038 * 1: Join during a netburst, by riding the break. Kick them
7039 * unless they have ops or voice in the channel.
7040 * 2: They're allowed to join through the ban (an invite in
7041 * ircu2.10, or a +e on Hybrid, or something).
7042 * If they're not joining through a ban, and the banlist is not
7043 * full, see if they're on the banlist for the channel. If so,
7046 if(user->uplink->burst && !mNode->modes)
7049 for(ii = 0; ii < channel->banlist.used; ii++)
7051 if(user_matches_glob(user, channel->banlist.list[ii]->ban, MATCH_USENICK))
7053 /* Riding a netburst. Naughty. */
7054 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
7060 mod_chanmode_init(&change);
7062 if(channel->banlist.used < MAXBANS)
7064 /* Not joining through a ban. */
7065 for(bData = cData->bans;
7066 bData && !user_matches_glob(user, bData->mask, MATCH_USENICK);
7067 bData = bData->next);
7071 char kick_reason[MAXLEN];
7072 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
7074 bData->triggered = now;
7075 if(bData != cData->bans)
7077 /* Shuffle the ban to the head of the list. */
7079 bData->next->prev = bData->prev;
7081 bData->prev->next = bData->next;
7084 bData->next = cData->bans;
7087 cData->bans->prev = bData;
7088 cData->bans = bData;
7091 change.args[0].mode = MODE_BAN;
7092 change.args[0].u.hostmask = bData->mask;
7093 mod_chanmode_announce(chanserv, channel, &change);
7094 KickChannelUser(user, channel, chanserv, kick_reason);
7099 /* ChanServ will not modify the limits in join-flooded channels,
7100 or when there are enough slots left below the limit. */
7101 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
7102 && !channel->join_flooded
7103 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
7105 /* The user count has begun "bumping" into the channel limit,
7106 so set a timer to raise the limit a bit. Any previous
7107 timers are removed so three incoming users within the delay
7108 results in one limit change, not three. */
7110 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7111 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7114 if(channel->join_flooded)
7116 /* don't automatically give ops or voice during a join flood */
7118 else if(cData->lvlOpts[lvlGiveOps] == 0)
7119 modes |= MODE_CHANOP;
7120 else if(cData->lvlOpts[lvlGiveVoice] == 0)
7121 modes |= MODE_VOICE;
7123 greeting = cData->greeting;
7124 if(user->handle_info)
7126 handle = user->handle_info;
7128 if(IsHelper(user) && !IsHelping(user))
7131 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7133 if(channel == chanserv_conf.support_channels.list[ii])
7135 HANDLE_SET_FLAG(user->handle_info, HELPING);
7141 uData = GetTrueChannelAccess(cData, handle);
7142 if(uData && !IsUserSuspended(uData))
7144 /* Ops and above were handled by the above case. */
7145 if(IsUserAutoOp(uData))
7147 if(uData->access >= cData->lvlOpts[lvlGiveOps])
7148 modes |= MODE_CHANOP;
7149 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
7150 modes |= MODE_VOICE;
7152 if(uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
7153 cData->visited = now;
7154 if(cData->user_greeting)
7155 greeting = cData->user_greeting;
7157 && (uData->access >= cData->lvlOpts[lvlUserInfo])
7158 && ((now - uData->seen) >= chanserv_conf.info_delay)
7166 /* If user joining normally (not during burst), apply op or voice,
7167 * and send greeting/userinfo as appropriate.
7169 if(!user->uplink->burst)
7173 if(modes & MODE_CHANOP)
7174 modes &= ~MODE_VOICE;
7175 change.args[0].mode = modes;
7176 change.args[0].u.member = mNode;
7177 mod_chanmode_announce(chanserv, channel, &change);
7180 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
7181 if(uData && info && (modes || !(channel->modes & MODE_DELAYJOINS)))
7182 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
7188 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
7190 struct mod_chanmode change;
7191 struct userData *channel;
7192 unsigned int ii, jj;
7194 if(!user->handle_info)
7197 mod_chanmode_init(&change);
7199 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
7201 struct chanNode *cn;
7202 struct modeNode *mn;
7203 if(IsUserSuspended(channel)
7204 || IsSuspended(channel->channel)
7205 || !(cn = channel->channel->channel))
7208 mn = GetUserMode(cn, user);
7211 if(!IsUserSuspended(channel)
7212 && IsUserAutoInvite(channel)
7213 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
7215 && !user->uplink->burst)
7216 irc_invite(chanserv, user, cn);
7220 if(channel->access >= UL_PRESENT && !HANDLE_FLAGGED(channel->handle, BOT))
7221 channel->channel->visited = now;
7223 if(IsUserAutoOp(channel))
7225 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
7226 change.args[0].mode = MODE_CHANOP;
7227 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
7228 change.args[0].mode = MODE_VOICE;
7230 change.args[0].mode = 0;
7231 change.args[0].u.member = mn;
7232 if(change.args[0].mode)
7233 mod_chanmode_announce(chanserv, cn, &change);
7236 channel->seen = now;
7237 channel->present = 1;
7240 for(ii = 0; ii < user->channels.used; ++ii)
7242 struct chanNode *chan = user->channels.list[ii]->channel;
7243 struct banData *ban;
7245 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
7246 || !chan->channel_info
7247 || IsSuspended(chan->channel_info))
7249 for(jj = 0; jj < chan->banlist.used; ++jj)
7250 if(user_matches_glob(user, chan->banlist.list[jj]->ban, MATCH_USENICK))
7252 if(jj < chan->banlist.used)
7254 for(ban = chan->channel_info->bans; ban; ban = ban->next)
7256 char kick_reason[MAXLEN];
7257 if(!user_matches_glob(user, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
7259 change.args[0].mode = MODE_BAN;
7260 change.args[0].u.hostmask = ban->mask;
7261 mod_chanmode_announce(chanserv, chan, &change);
7262 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
7263 KickChannelUser(user, chan, chanserv, kick_reason);
7264 ban->triggered = now;
7269 if(IsSupportHelper(user))
7271 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7273 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
7275 HANDLE_SET_FLAG(user->handle_info, HELPING);
7283 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
7285 struct chanData *cData;
7286 struct userData *uData;
7288 cData = mn->channel->channel_info;
7289 if(!cData || IsSuspended(cData) || IsLocal(mn->user))
7292 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
7294 /* Allow for a bit of padding so that the limit doesn't
7295 track the user count exactly, which could get annoying. */
7296 if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
7298 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7299 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7303 if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
7305 scan_user_presence(uData, mn->user);
7307 if (uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
7308 cData->visited = now;
7311 if(IsHelping(mn->user) && IsSupportHelper(mn->user))
7314 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii) {
7315 struct chanNode *channel;
7316 struct userNode *exclude;
7317 /* When looking at the channel that is being /part'ed, we
7318 * have to skip over the client that is leaving. For
7319 * other channels, we must not do that.
7321 channel = chanserv_conf.support_channels.list[ii];
7322 exclude = (channel == mn->channel) ? mn->user : NULL;
7323 if(find_handle_in_channel(channel, mn->user->handle_info, exclude))
7326 if(ii == chanserv_conf.support_channels.used)
7327 HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
7332 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
7334 struct userData *uData;
7336 if(!channel->channel_info || !kicker || IsService(kicker)
7337 || (kicker == victim) || IsSuspended(channel->channel_info)
7338 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
7341 if(protect_user(victim, kicker, channel->channel_info))
7343 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED_2");
7344 KickChannelUser(kicker, channel, chanserv, reason);
7347 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
7352 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
7354 struct chanData *cData;
7356 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
7359 cData = channel->channel_info;
7360 if(bad_topic(channel, user, channel->topic))
7362 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
7363 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
7364 SetChannelTopic(channel, chanserv, old_topic, 1);
7365 else if(cData->topic)
7366 SetChannelTopic(channel, chanserv, cData->topic, 1);
7369 /* With topicsnarf, grab the topic and save it as the default topic. */
7370 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
7373 cData->topic = strdup(channel->topic);
7379 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
7381 struct mod_chanmode *bounce = NULL;
7382 unsigned int bnc, ii;
7385 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
7388 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
7389 && mode_lock_violated(&channel->channel_info->modes, change))
7391 char correct[MAXLEN];
7392 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
7393 mod_chanmode_format(&channel->channel_info->modes, correct);
7394 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
7396 for(ii = bnc = 0; ii < change->argc; ++ii)
7398 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
7400 const struct userNode *victim = change->args[ii].u.member->user;
7401 if(!protect_user(victim, user, channel->channel_info))
7404 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7407 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
7408 bounce->args[bnc].u.member = GetUserMode(channel, user);
7409 if(bounce->args[bnc].u.member)
7413 bounce->args[bnc].mode = MODE_CHANOP;
7414 bounce->args[bnc].u.member = change->args[ii].u.member;
7416 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
7418 else if(change->args[ii].mode & MODE_CHANOP)
7420 const struct userNode *victim = change->args[ii].u.member->user;
7421 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
7424 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7425 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
7426 bounce->args[bnc].u.member = change->args[ii].u.member;
7429 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
7431 const char *ban = change->args[ii].u.hostmask;
7432 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
7435 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7436 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
7437 bounce->args[bnc].u.hostmask = strdup(ban);
7439 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
7444 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
7445 mod_chanmode_announce(chanserv, channel, bounce);
7446 for(ii = 0; ii < change->argc; ++ii)
7447 if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
7448 free((char*)bounce->args[ii].u.hostmask);
7449 mod_chanmode_free(bounce);
7454 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
7456 struct chanNode *channel;
7457 struct banData *bData;
7458 struct mod_chanmode change;
7459 unsigned int ii, jj;
7460 char kick_reason[MAXLEN];
7462 mod_chanmode_init(&change);
7464 change.args[0].mode = MODE_BAN;
7465 for(ii = 0; ii < user->channels.used; ++ii)
7467 channel = user->channels.list[ii]->channel;
7468 /* Need not check for bans if they're opped or voiced. */
7469 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
7471 /* Need not check for bans unless channel registration is active. */
7472 if(!channel->channel_info || IsSuspended(channel->channel_info))
7474 /* Look for a matching ban already on the channel. */
7475 for(jj = 0; jj < channel->banlist.used; ++jj)
7476 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
7478 /* Need not act if we found one. */
7479 if(jj < channel->banlist.used)
7481 /* Look for a matching ban in this channel. */
7482 for(bData = channel->channel_info->bans; bData; bData = bData->next)
7484 if(!user_matches_glob(user, bData->mask, MATCH_USENICK | MATCH_VISIBLE))
7486 change.args[0].u.hostmask = bData->mask;
7487 mod_chanmode_announce(chanserv, channel, &change);
7488 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
7489 KickChannelUser(user, channel, chanserv, kick_reason);
7490 bData->triggered = now;
7491 break; /* we don't need to check any more bans in the channel */
7496 static void handle_rename(struct handle_info *handle, const char *old_handle)
7498 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
7502 dict_remove2(handle_dnrs, old_handle, 1);
7503 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
7504 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
7509 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
7511 struct userNode *h_user;
7513 if(handle->channels)
7515 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
7516 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
7518 while(handle->channels)
7519 del_channel_user(handle->channels, 1);
7524 handle_server_link(UNUSED_ARG(struct server *server))
7526 struct chanData *cData;
7528 for(cData = channelList; cData; cData = cData->next)
7530 if(!IsSuspended(cData))
7531 cData->may_opchan = 1;
7532 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
7533 && !cData->channel->join_flooded
7534 && ((cData->channel->limit - cData->channel->members.used)
7535 < chanserv_conf.adjust_threshold))
7537 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7538 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7544 chanserv_conf_read(void)
7548 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
7549 struct mod_chanmode *change;
7550 struct string_list *strlist;
7551 struct chanNode *chan;
7554 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
7556 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
7559 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7560 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7561 chanserv_conf.support_channels.used = 0;
7562 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
7564 for(ii = 0; ii < strlist->used; ++ii)
7566 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7569 chan = AddChannel(strlist->list[ii], now, str2, NULL);
7571 channelList_append(&chanserv_conf.support_channels, chan);
7574 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
7577 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7580 chan = AddChannel(str, now, str2, NULL);
7582 channelList_append(&chanserv_conf.support_channels, chan);
7584 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
7585 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
7586 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
7587 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
7588 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
7589 chanserv_conf.greeting_length = str ? atoi(str) : 200;
7590 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
7591 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
7592 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
7593 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
7594 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
7595 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
7596 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
7597 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
7598 str = database_get_data(conf_node, KEY_DNR_EXPIRE_FREQ, RECDB_QSTRING);
7599 chanserv_conf.dnr_expire_frequency = str ? ParseInterval(str) : 3600;
7600 str = database_get_data(conf_node, KEY_INVITED_INTERVAL, RECDB_QSTRING);
7601 chanserv_conf.invited_timeout = str ? ParseInterval(str) : 600*2;
7602 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
7603 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
7604 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
7605 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
7606 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
7607 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
7608 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
7609 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
7610 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
7612 NickChange(chanserv, str, 0);
7613 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
7614 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
7615 str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
7616 chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
7617 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
7618 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
7619 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
7620 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
7621 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
7622 chanserv_conf.max_owned = str ? atoi(str) : 5;
7623 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
7624 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
7625 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
7626 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
7627 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
7628 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
7629 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
7632 safestrncpy(mode_line, str, sizeof(mode_line));
7633 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
7634 if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE|MCP_NO_APASS, 0))
7635 && (change->argc < 2))
7637 chanserv_conf.default_modes = *change;
7638 mod_chanmode_free(change);
7640 free_string_list(chanserv_conf.set_shows);
7641 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
7643 strlist = string_list_copy(strlist);
7646 static const char *list[] = {
7647 /* free form text */
7648 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
7649 /* options based on user level */
7650 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
7651 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
7652 /* multiple choice options */
7653 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
7654 /* binary options */
7655 "DynLimit", "NoDelete", "expire", "Vote",
7659 strlist = alloc_string_list(ArrayLength(list)-1);
7660 for(ii=0; list[ii]; ii++)
7661 string_list_append(strlist, strdup(list[ii]));
7663 chanserv_conf.set_shows = strlist;
7664 /* We don't look things up now, in case the list refers to options
7665 * defined by modules initialized after this point. Just mark the
7666 * function list as invalid, so it will be initialized.
7668 set_shows_list.used = 0;
7669 free_string_list(chanserv_conf.eightball);
7670 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
7673 strlist = string_list_copy(strlist);
7677 strlist = alloc_string_list(4);
7678 string_list_append(strlist, strdup("Yes."));
7679 string_list_append(strlist, strdup("No."));
7680 string_list_append(strlist, strdup("Maybe so."));
7682 chanserv_conf.eightball = strlist;
7683 free_string_list(chanserv_conf.old_ban_names);
7684 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
7686 strlist = string_list_copy(strlist);
7688 strlist = alloc_string_list(2);
7689 chanserv_conf.old_ban_names = strlist;
7690 str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
7691 off_channel = str ? atoi(str) : 0;
7695 chanserv_note_type_read(const char *key, struct record_data *rd)
7698 struct note_type *ntype;
7701 if(!(obj = GET_RECORD_OBJECT(rd)))
7703 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
7706 if(!(ntype = chanserv_create_note_type(key)))
7708 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
7712 /* Figure out set access */
7713 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
7715 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7716 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
7718 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
7720 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
7721 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
7723 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
7725 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
7729 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
7730 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7731 ntype->set_access.min_opserv = 0;
7734 /* Figure out visibility */
7735 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
7736 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7737 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
7738 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7739 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
7740 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
7741 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
7742 ntype->visible_type = NOTE_VIS_ALL;
7744 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7746 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
7747 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
7751 vote_option_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7753 struct vote_option *vOpt;
7756 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7758 log_module(CS_LOG, LOG_ERROR, "Invalid vote option in %s.", chan->channel->name);
7762 vOpt = calloc(1, sizeof(*vOpt));
7763 vOpt->name = strdup(database_get_data(rd->d.object, KEY_VOTE_OPTION_NAME, RECDB_QSTRING));
7764 str = database_get_data(rd->d.object, KEY_VOTE_OPTION_VOTED, RECDB_QSTRING);
7765 vOpt->voted = str ? atoi(str) : 0;
7766 vOpt->option_id = str ? atoi(key) : 0;
7767 vOpt->option_str = strdup(key);
7768 dict_insert(chan->vote_options,vOpt->option_str,vOpt);
7772 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7774 struct handle_info *handle;
7775 struct userData *uData;
7776 char *seen, *inf, *flags, *voted, *votefor;
7777 unsigned long last_seen;
7778 unsigned short access_level;
7780 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7782 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
7786 access_level = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
7787 if(access_level > UL_OWNER)
7789 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
7793 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
7794 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
7795 last_seen = seen ? strtoul(seen, NULL, 0) : now;
7796 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
7797 voted = database_get_data(rd->d.object, KEY_VOTE_VOTED, RECDB_QSTRING);
7798 votefor = database_get_data(rd->d.object, KEY_VOTE_VOTEDFOR, RECDB_QSTRING);
7799 handle = get_handle_info(key);
7802 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
7806 uData = add_channel_user(chan, handle, access_level, last_seen, inf);
7807 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
7809 uData->voted = voted ? strtoul(voted, NULL, 0) : 0;
7810 uData->votefor = votefor ? strtoul(votefor, NULL, 0) : 0;
7818 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7820 struct banData *bData;
7821 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
7822 unsigned long set_time, triggered_time, expires_time;
7824 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7826 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
7830 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
7831 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
7832 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
7833 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
7834 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
7835 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
7836 if (!reason || !owner)
7839 set_time = set ? strtoul(set, NULL, 0) : now;
7840 triggered_time = triggered ? strtoul(triggered, NULL, 0) : 0;
7842 expires_time = strtoul(s_expires, NULL, 0);
7844 expires_time = set_time + atoi(s_duration);
7848 if(!reason || (expires_time && (expires_time < now)))
7851 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
7854 static struct suspended *
7855 chanserv_read_suspended(dict_t obj)
7857 struct suspended *suspended = calloc(1, sizeof(*suspended));
7861 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
7862 suspended->expires = str ? strtoul(str, NULL, 0) : 0;
7863 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
7864 suspended->revoked = str ? strtoul(str, NULL, 0) : 0;
7865 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
7866 suspended->issued = str ? strtoul(str, NULL, 0) : 0;
7867 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
7868 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
7869 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
7870 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
7875 chanserv_channel_read(const char *key, struct record_data *hir)
7877 struct suspended *suspended;
7878 struct mod_chanmode *modes;
7879 struct chanNode *cNode;
7880 struct chanData *cData;
7881 struct dict *channel, *obj;
7882 char *str, *argv[10];
7886 channel = hir->d.object;
7888 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
7891 cNode = AddChannel(key, now, NULL, NULL);
7894 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
7897 cData = register_channel(cNode, str);
7900 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
7904 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
7906 enum levelOption lvlOpt;
7907 enum charOption chOpt;
7909 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
7910 cData->flags = atoi(str);
7912 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7914 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
7916 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
7917 else if(levelOptions[lvlOpt].old_flag)
7919 if(cData->flags & levelOptions[lvlOpt].old_flag)
7920 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
7922 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
7926 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7928 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
7930 cData->chOpts[chOpt] = str[0];
7933 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
7935 enum levelOption lvlOpt;
7936 enum charOption chOpt;
7939 cData->flags = base64toint(str, 5);
7940 count = strlen(str += 5);
7941 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7944 if(levelOptions[lvlOpt].old_flag)
7946 if(cData->flags & levelOptions[lvlOpt].old_flag)
7947 lvl = levelOptions[lvlOpt].flag_value;
7949 lvl = levelOptions[lvlOpt].default_value;
7951 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
7953 case 'c': lvl = UL_COOWNER; break;
7954 case 'm': lvl = UL_MASTER; break;
7955 case 'n': lvl = UL_OWNER+1; break;
7956 case 'o': lvl = UL_OP; break;
7957 case 'p': lvl = UL_PEON; break;
7958 case 'w': lvl = UL_OWNER; break;
7959 default: lvl = 0; break;
7961 cData->lvlOpts[lvlOpt] = lvl;
7963 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7964 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
7967 if((str = database_get_data(hir->d.object, KEY_EXPIRE, RECDB_QSTRING)))
7969 cData->expiry = atoi(str);
7970 if(cData->expiry > 0) {
7971 if(cData->expiry > now) {
7972 timeq_add(cData->expiry, chanserv_expire_channel, cData);
7974 timeq_add(1, chanserv_expire_channel, cData);
7981 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
7983 suspended = chanserv_read_suspended(obj);
7984 cData->suspended = suspended;
7985 suspended->cData = cData;
7986 /* We could use suspended->expires and suspended->revoked to
7987 * set the CHANNEL_SUSPENDED flag, but we don't. */
7989 else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
7991 suspended = calloc(1, sizeof(*suspended));
7992 suspended->issued = 0;
7993 suspended->revoked = 0;
7994 suspended->suspender = strdup(str);
7995 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
7996 suspended->expires = str ? atoi(str) : 0;
7997 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
7998 suspended->reason = strdup(str ? str : "No reason");
7999 suspended->previous = NULL;
8000 cData->suspended = suspended;
8001 suspended->cData = cData;
8005 cData->flags &= ~CHANNEL_SUSPENDED;
8006 suspended = NULL; /* to squelch a warning */
8009 if(IsSuspended(cData)) {
8010 if(suspended->expires > now)
8011 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
8012 else if(suspended->expires)
8013 cData->flags &= ~CHANNEL_SUSPENDED;
8016 if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
8017 struct mod_chanmode change;
8018 mod_chanmode_init(&change);
8020 change.args[0].mode = MODE_CHANOP;
8021 change.args[0].u.member = AddChannelUser(chanserv, cNode);
8022 mod_chanmode_announce(chanserv, cNode, &change);
8025 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
8026 cData->registered = str ? strtoul(str, NULL, 0) : now;
8027 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
8028 cData->visited = str ? strtoul(str, NULL, 0) : now;
8029 str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
8030 cData->ownerTransfer = str ? strtoul(str, NULL, 0) : 0;
8031 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
8032 cData->max = str ? atoi(str) : 0;
8033 str = database_get_data(channel, KEY_MAX_TIME, RECDB_QSTRING);
8034 cData->max_time = str ? atoi(str) : 0;
8035 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
8036 cData->greeting = str ? strdup(str) : NULL;
8037 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
8038 cData->user_greeting = str ? strdup(str) : NULL;
8039 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
8040 cData->topic_mask = str ? strdup(str) : NULL;
8041 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
8042 cData->topic = str ? strdup(str) : NULL;
8044 str = database_get_data(channel, KEY_VOTE, RECDB_QSTRING);
8046 cData->vote = str ? strdup(str) : NULL;
8047 dict_delete(cData->vote_options);
8048 cData->vote_options = dict_new();
8049 dict_set_free_data(cData->vote_options, free_vote_options);
8050 str = database_get_data(channel, KEY_VOTE_START, RECDB_QSTRING);
8051 cData->vote_start = str ? atoi(str) : 0;
8052 obj = database_get_data(channel, KEY_VOTE_OPTIONS, RECDB_OBJECT);
8053 for(it = dict_first(obj); it; it = iter_next(it)) {
8054 vote_option_read_helper(iter_key(it), iter_data(it), cData);
8058 if(!IsSuspended(cData)
8059 && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
8060 && (argc = split_line(str, 0, ArrayLength(argv), argv))
8061 && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE|MCP_NO_APASS, 0))) {
8062 cData->modes = *modes;
8064 cData->modes.modes_set |= MODE_REGISTERED;
8065 if(cData->modes.argc > 1)
8066 cData->modes.argc = 1;
8067 mod_chanmode_announce(chanserv, cNode, &cData->modes);
8068 mod_chanmode_free(modes);
8071 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
8072 for(it = dict_first(obj); it; it = iter_next(it))
8073 user_read_helper(iter_key(it), iter_data(it), cData);
8075 if(!cData->users && !IsProtected(cData))
8077 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
8078 unregister_channel(cData, "has empty user list.");
8082 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
8083 for(it = dict_first(obj); it; it = iter_next(it))
8084 ban_read_helper(iter_key(it), iter_data(it), cData);
8086 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
8087 for(it = dict_first(obj); it; it = iter_next(it))
8089 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
8090 struct record_data *rd = iter_data(it);
8091 const char *note, *setter;
8093 if(rd->type != RECDB_OBJECT)
8095 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
8099 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
8101 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
8103 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
8107 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
8108 if(!setter) setter = "<unknown>";
8109 chanserv_add_channel_note(cData, ntype, setter, note);
8117 chanserv_dnr_read(const char *key, struct record_data *hir)
8119 const char *setter, *reason, *str;
8120 struct do_not_register *dnr;
8121 unsigned long expiry;
8123 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
8126 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
8129 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
8132 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
8135 str = database_get_data(hir->d.object, KEY_EXPIRES, RECDB_QSTRING);
8136 expiry = str ? strtoul(str, NULL, 0) : 0;
8137 if(expiry && expiry <= now)
8139 dnr = chanserv_add_dnr(key, setter, expiry, reason);
8142 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
8144 dnr->set = atoi(str);
8150 chanserv_saxdb_read(struct dict *database)
8152 struct dict *section;
8155 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
8156 for(it = dict_first(section); it; it = iter_next(it))
8157 chanserv_note_type_read(iter_key(it), iter_data(it));
8159 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
8160 for(it = dict_first(section); it; it = iter_next(it))
8161 chanserv_channel_read(iter_key(it), iter_data(it));
8163 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
8164 for(it = dict_first(section); it; it = iter_next(it))
8165 chanserv_dnr_read(iter_key(it), iter_data(it));
8171 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
8173 int high_present = 0;
8174 saxdb_start_record(ctx, KEY_USERS, 1);
8175 for(; uData; uData = uData->next)
8177 if((uData->access >= UL_PRESENT) && uData->present && !HANDLE_FLAGGED(uData->handle, BOT))
8179 saxdb_start_record(ctx, uData->handle->handle, 0);
8180 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
8181 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
8183 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
8184 if(uData->channel->vote && uData->voted)
8185 saxdb_write_int(ctx, KEY_VOTE_VOTED, uData->voted);
8186 if(uData->channel->vote && uData->votefor)
8187 saxdb_write_int(ctx, KEY_VOTE_VOTEDFOR, uData->votefor);
8189 saxdb_write_string(ctx, KEY_INFO, uData->info);
8190 saxdb_end_record(ctx);
8192 saxdb_end_record(ctx);
8193 return high_present;
8197 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
8201 saxdb_start_record(ctx, KEY_BANS, 1);
8202 for(; bData; bData = bData->next)
8204 saxdb_start_record(ctx, bData->mask, 0);
8205 saxdb_write_int(ctx, KEY_SET, bData->set);
8206 if(bData->triggered)
8207 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
8209 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
8211 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
8213 saxdb_write_string(ctx, KEY_REASON, bData->reason);
8214 saxdb_end_record(ctx);
8216 saxdb_end_record(ctx);
8220 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
8222 saxdb_start_record(ctx, name, 0);
8223 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
8224 saxdb_write_string(ctx, KEY_REASON, susp->reason);
8226 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
8228 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
8230 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
8232 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
8233 saxdb_end_record(ctx);
8237 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
8241 enum levelOption lvlOpt;
8242 enum charOption chOpt;
8245 saxdb_start_record(ctx, channel->channel->name, 1);
8247 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
8248 saxdb_write_int(ctx, KEY_MAX, channel->max);
8249 saxdb_write_int(ctx, KEY_MAX_TIME, channel->max_time);
8251 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
8252 if(channel->registrar)
8253 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
8254 if(channel->greeting)
8255 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
8256 if(channel->user_greeting)
8257 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
8258 if(channel->topic_mask)
8259 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
8260 if(channel->suspended)
8261 chanserv_write_suspended(ctx, "suspended", channel->suspended);
8263 saxdb_write_int(ctx, KEY_EXPIRE, channel->expiry);
8266 saxdb_write_string(ctx, KEY_VOTE, channel->vote);
8267 if(channel->vote_start)
8268 saxdb_write_int(ctx, KEY_VOTE_START, channel->vote_start);
8269 if (dict_size(channel->vote_options)) {
8270 saxdb_start_record(ctx, KEY_VOTE_OPTIONS, 1);
8271 for (it = dict_first(channel->vote_options); it; it = iter_next(it)) {
8272 struct vote_option *vOpt = iter_data(it);
8274 sprintf(str,"%i",vOpt->option_id);
8275 saxdb_start_record(ctx, str, 0);
8277 saxdb_write_int(ctx, KEY_VOTE_OPTION_VOTED, vOpt->voted);
8279 saxdb_write_string(ctx, KEY_VOTE_OPTION_NAME, vOpt->name);
8280 saxdb_end_record(ctx);
8282 saxdb_end_record(ctx);
8286 saxdb_start_record(ctx, KEY_OPTIONS, 0);
8287 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
8288 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8289 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
8290 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8292 buf[0] = channel->chOpts[chOpt];
8294 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
8296 saxdb_end_record(ctx);
8298 if(channel->modes.modes_set || channel->modes.modes_clear)
8300 mod_chanmode_format(&channel->modes, buf);
8301 saxdb_write_string(ctx, KEY_MODES, buf);
8304 high_present = chanserv_write_users(ctx, channel->users);
8305 chanserv_write_bans(ctx, channel->bans);
8307 if(dict_size(channel->notes))
8311 saxdb_start_record(ctx, KEY_NOTES, 1);
8312 for(it = dict_first(channel->notes); it; it = iter_next(it))
8314 struct note *note = iter_data(it);
8315 saxdb_start_record(ctx, iter_key(it), 0);
8316 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
8317 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
8318 saxdb_end_record(ctx);
8320 saxdb_end_record(ctx);
8323 if(channel->ownerTransfer)
8324 saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
8325 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
8326 saxdb_end_record(ctx);
8330 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
8334 saxdb_start_record(ctx, ntype->name, 0);
8335 switch(ntype->set_access_type)
8337 case NOTE_SET_CHANNEL_ACCESS:
8338 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
8340 case NOTE_SET_CHANNEL_SETTER:
8341 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
8343 case NOTE_SET_PRIVILEGED: default:
8344 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
8347 switch(ntype->visible_type)
8349 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
8350 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
8351 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
8353 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
8354 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
8355 saxdb_end_record(ctx);
8359 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
8361 struct do_not_register *dnr;
8362 dict_iterator_t it, next;
8364 for(it = dict_first(dnrs); it; it = next)
8366 next = iter_next(it);
8367 dnr = iter_data(it);
8368 if(dnr->expires && dnr->expires <= now)
8370 dict_remove(dnrs, iter_key(it));
8373 saxdb_start_record(ctx, dnr->chan_name, 0);
8375 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
8377 saxdb_write_int(ctx, KEY_EXPIRES, dnr->expires);
8378 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
8379 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
8380 saxdb_end_record(ctx);
8385 chanserv_saxdb_write(struct saxdb_context *ctx)
8388 struct chanData *channel;
8391 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
8392 for(it = dict_first(note_types); it; it = iter_next(it))
8393 chanserv_write_note_type(ctx, iter_data(it));
8394 saxdb_end_record(ctx);
8397 saxdb_start_record(ctx, KEY_DNR, 1);
8398 write_dnrs_helper(ctx, handle_dnrs);
8399 write_dnrs_helper(ctx, plain_dnrs);
8400 write_dnrs_helper(ctx, mask_dnrs);
8401 saxdb_end_record(ctx);
8404 saxdb_start_record(ctx, KEY_CHANNELS, 1);
8405 for(channel = channelList; channel; channel = channel->next)
8406 chanserv_write_channel(ctx, channel);
8407 saxdb_end_record(ctx);
8413 chanserv_db_cleanup(void) {
8415 unreg_part_func(handle_part);
8417 unregister_channel(channelList, "terminating.");
8418 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
8419 UnlockChannel(chanserv_conf.support_channels.list[ii]);
8420 free(chanserv_conf.support_channels.list);
8421 dict_delete(handle_dnrs);
8422 dict_delete(plain_dnrs);
8423 dict_delete(mask_dnrs);
8424 dict_delete(note_types);
8425 free_string_list(chanserv_conf.eightball);
8426 free_string_list(chanserv_conf.old_ban_names);
8427 free_string_list(chanserv_conf.set_shows);
8428 free(set_shows_list.list);
8429 free(uset_shows_list.list);
8432 struct userData *helper = helperList;
8433 helperList = helperList->next;
8438 #if defined(GCC_VARMACROS)
8439 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ARGS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ARGS)
8440 #elif defined(C99_VARMACROS)
8441 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, __VA_ARGS__)
8443 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
8444 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
8447 init_chanserv(const char *nick)
8449 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
8450 conf_register_reload(chanserv_conf_read);
8454 reg_server_link_func(handle_server_link);
8455 reg_new_channel_func(handle_new_channel);
8456 reg_join_func(handle_join);
8457 reg_part_func(handle_part);
8458 reg_kick_func(handle_kick);
8459 reg_topic_func(handle_topic);
8460 reg_mode_change_func(handle_mode);
8461 reg_nick_change_func(handle_nick_change);
8462 reg_auth_func(handle_auth);
8465 reg_handle_rename_func(handle_rename);
8466 reg_unreg_func(handle_unreg);
8468 handle_dnrs = dict_new();
8469 dict_set_free_data(handle_dnrs, free);
8470 plain_dnrs = dict_new();
8471 dict_set_free_data(plain_dnrs, free);
8472 mask_dnrs = dict_new();
8473 dict_set_free_data(mask_dnrs, free);
8475 reg_svccmd_unbind_func(handle_svccmd_unbind);
8476 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
8477 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
8478 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
8479 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
8480 DEFINE_COMMAND(dnrsearch, 3, 0, "template", "noregister", NULL);
8481 modcmd_register(chanserv_module, "dnrsearch print", NULL, 0, 0, NULL);
8482 modcmd_register(chanserv_module, "dnrsearch remove", NULL, 0, 0, NULL);
8483 modcmd_register(chanserv_module, "dnrsearch count", NULL, 0, 0, NULL);
8484 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
8485 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
8486 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
8487 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
8488 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
8490 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
8491 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
8493 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8494 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8495 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8496 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8497 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
8499 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
8500 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
8501 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
8502 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8503 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8505 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8506 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
8507 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8508 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
8510 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
8511 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
8512 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8513 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8514 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
8515 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8516 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8517 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8519 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8520 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8521 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8522 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
8523 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
8524 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
8525 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
8526 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
8527 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8528 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
8529 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
8530 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
8531 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8532 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8534 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
8535 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8536 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8537 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8538 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
8540 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
8541 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
8543 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
8544 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8545 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8546 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8547 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8548 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8549 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8550 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8551 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8552 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8553 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8555 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
8556 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
8558 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
8559 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
8560 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
8561 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
8563 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
8564 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
8565 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
8566 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
8567 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
8569 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8570 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8571 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8572 DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8573 DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8574 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8576 DEFINE_COMMAND(addvote, 1, MODCMD_REQUIRE_AUTHED, NULL);
8577 DEFINE_COMMAND(delvote, 1, MODCMD_REQUIRE_AUTHED, NULL);
8578 DEFINE_COMMAND(addoption, 1, MODCMD_REQUIRE_AUTHED, NULL);
8579 DEFINE_COMMAND(deloption, 1, MODCMD_REQUIRE_AUTHED, NULL);
8580 DEFINE_COMMAND(vote, 1, MODCMD_REQUIRE_AUTHED, NULL);
8581 DEFINE_COMMAND(startvote, 1, MODCMD_REQUIRE_AUTHED, NULL);
8582 DEFINE_COMMAND(endvote, 1, MODCMD_REQUIRE_AUTHED, NULL);
8583 DEFINE_COMMAND(voteresults, 1, MODCMD_REQUIRE_AUTHED, NULL);
8585 /* Channel options */
8586 DEFINE_CHANNEL_OPTION(defaulttopic);
8587 DEFINE_CHANNEL_OPTION(topicmask);
8588 DEFINE_CHANNEL_OPTION(greeting);
8589 DEFINE_CHANNEL_OPTION(usergreeting);
8590 DEFINE_CHANNEL_OPTION(modes);
8591 DEFINE_CHANNEL_OPTION(enfops);
8592 DEFINE_CHANNEL_OPTION(giveops);
8593 DEFINE_CHANNEL_OPTION(protect);
8594 DEFINE_CHANNEL_OPTION(enfmodes);
8595 DEFINE_CHANNEL_OPTION(enftopic);
8596 DEFINE_CHANNEL_OPTION(pubcmd);
8597 DEFINE_CHANNEL_OPTION(givevoice);
8598 DEFINE_CHANNEL_OPTION(userinfo);
8599 DEFINE_CHANNEL_OPTION(dynlimit);
8600 DEFINE_CHANNEL_OPTION(topicsnarf);
8601 DEFINE_CHANNEL_OPTION(vote);
8602 DEFINE_CHANNEL_OPTION(nodelete);
8603 DEFINE_CHANNEL_OPTION(toys);
8604 DEFINE_CHANNEL_OPTION(setters);
8605 DEFINE_CHANNEL_OPTION(topicrefresh);
8606 DEFINE_CHANNEL_OPTION(ctcpusers);
8607 DEFINE_CHANNEL_OPTION(ctcpreaction);
8608 DEFINE_CHANNEL_OPTION(inviteme);
8609 DEFINE_CHANNEL_OPTION(unreviewed);
8610 modcmd_register(chanserv_module, "set expire", chan_opt_expire, 1, 0, "flags", "+helping", NULL);
8611 modcmd_register(chanserv_module, "set unreviewed on", NULL, 0, 0, "flags", "+helping", NULL);
8612 modcmd_register(chanserv_module, "set unreviewed off", NULL, 0, 0, "flags", "+oper", NULL);
8614 DEFINE_CHANNEL_OPTION(offchannel);
8615 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
8617 /* Alias set topic to set defaulttopic for compatibility. */
8618 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
8621 DEFINE_USER_OPTION(noautoop);
8622 DEFINE_USER_OPTION(autoinvite);
8623 DEFINE_USER_OPTION(info);
8625 /* Alias uset autovoice to uset autoop. */
8626 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
8628 note_types = dict_new();
8629 dict_set_free_data(note_types, chanserv_deref_note_type);
8632 const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
8633 chanserv = AddLocalUser(nick, nick, NULL, "Channel Services", modes);
8634 service_register(chanserv)->trigger = '!';
8635 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
8637 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
8639 if(chanserv_conf.channel_expire_frequency)
8640 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
8642 if(chanserv_conf.dnr_expire_frequency)
8643 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
8645 if(chanserv_conf.refresh_period)
8647 unsigned long next_refresh;
8648 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
8649 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
8652 reg_exit_func(chanserv_db_cleanup);
8653 message_register_table(msgtab);