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." },
388 /* Seen information */
389 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
390 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
391 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
392 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
394 /* Names information */
395 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
396 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
398 /* Channel information */
399 { "CSMSG_CHANNEL_INFO", "$b%s$b Information:" },
400 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
401 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
402 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
403 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
404 { "CSMSG_CHANNEL_MAX_TIME", "$bRecord Visitors: $b%i (%s ago.)" },
405 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
406 { "CSMSG_CHANNEL_BANS", "$bBan Count: $b%i" },
407 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
408 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
409 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
410 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
411 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
412 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
413 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
414 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
415 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
416 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
417 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
418 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
419 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
420 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
422 { "CSMSG_PEEK_INFO", "$b%s$b Status:" },
423 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
424 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
425 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d (%d ops, %d voices, %d regulars)" },
426 { "CSMSG_PEEK_OPS", "$bOps:$b" },
427 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
429 /* Network information */
430 { "CSMSG_NETWORK_INFO", "Network Information:" },
431 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
432 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
433 { "CSMSG_NETWORK_BANS", "$bTotal Ban Count: $b%i" },
434 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
435 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
436 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
437 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
438 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
441 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
442 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
443 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
445 /* Channel searches */
446 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
447 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
448 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
449 { "CSMSG_CHANNEL_SEARCH_RESULTS", "The following channels were found:" },
451 /* Channel configuration */
452 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
453 { "CSMSG_INVALID_CFLAG", "$b%s$b is not a recognized channel flag." },
454 { "CSMSG_CHANNEL_OPTIONS", "Channel Options:" },
455 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
458 { "CSMSG_USER_OPTIONS", "User Options:" },
459 { "CSMSG_USER_PROTECTED_2", "That user is protected." },
462 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
463 { "CSMSG_PING_RESPONSE", "Pong!" },
464 { "CSMSG_WUT_RESPONSE", "wut" },
465 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
466 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
467 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
468 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
469 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
470 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
471 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
474 { "CSMSG_ADDVOTE_DONE", "Vote added. Use $baddoption$b to add at least 2 vote options and then $bstartvote$b to start the voting." },
475 { "CSMSG_ADDVOTE_FULL", "There is already a vote in this channel. Use $bdelvote$b to delete it." },
476 { "CSMSG_DELVOTE_DONE", "Vote deleted." },
477 { "CSMSG_NO_VOTE", "There is no vote in this channel." },
478 { "CSMSG_ADDOPTION_DONE", "Vote option added with id %i (%i - %i)." },
479 { "CSMSG_DELOPTION_DONE", "Vote option deleted." },
480 { "CSMSG_DELOPTION_NONE", "Vote option does not exist." },
481 { "CSMSG_VOTE_NEED_OPTIONS", "There must be at least 2 options in a vote." },
482 { "CSMSG_VOTE_NOT_STARTED", "The vote is not started. Use $bstartvote$b to allow voting." },
483 { "CSMSG_VOTE_QUESTION", "Question: %s" },
484 { "CSMSG_VOTE_OPTION", "$b%i$b: %s ($b%i$b votes)" },
485 { "CSMSG_VOTERES_QUESTION", "Question: %s" },
486 { "CSMSG_VOTERES_OPTION", "
\ 2%i
\ 2: %s (
\ 2%i
\ 2 votes)" },
487 { "CSMSG_STARTVOTE_TOP", "
\ 2%s
\ 2 has started a voting:" },
488 { "CSMSG_STARTVOTE_QUESTION", "
\ 2Question:
\ 2 %s" },
489 { "CSMSG_STARTVOTE_OPTION", "
\ 2%i:
\ 2 %s" },
490 { "CSMSG_STARTVOTE_ACCESS", "All channel users with at least
\ 2%i
\ 2 access can vote." },
491 { "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." },
492 { "CSMSG_STARTVOTE_RUNNING", "The vote is already running." },
493 { "CSMSG_VOTE_VOTED", "You have already voted." },
494 { "CSMSG_VOTE_INVALID", "Vote option does not exist." },
495 { "CSMSG_VOTE_DONE", "You voted for $b%s$b." },
496 { "CSMSG_ENDVOTE_DONE", "The vote has been finished." },
497 { "CSMSG_ENDVOTE_STOPPED", "The vote is not running." },
500 { "CSMSG_EVENT_SEARCH_RESULTS", "The following channel events were found:" },
504 /* eject_user and unban_user flags */
505 #define ACTION_KICK 0x0001
506 #define ACTION_BAN 0x0002
507 #define ACTION_ADD_BAN 0x0004
508 #define ACTION_ADD_TIMED_BAN 0x0008
509 #define ACTION_UNBAN 0x0010
510 #define ACTION_DEL_BAN 0x0020
512 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
513 #define MODELEN 40 + KEYLEN
517 #define CSFUNC_ARGS user, channel, argc, argv, cmd
519 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
520 #define CHANSERV_SYNTAX() svccmd_send_help(user, chanserv, cmd)
521 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
522 reply("MSG_MISSING_PARAMS", argv[0]); \
526 DECLARE_LIST(dnrList, struct do_not_register *);
527 DEFINE_LIST(dnrList, struct do_not_register *)
529 static int eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action);
531 struct userNode *chanserv;
534 static dict_t plain_dnrs, mask_dnrs, handle_dnrs;
535 static struct log_type *CS_LOG;
539 struct channelList support_channels;
540 struct mod_chanmode default_modes;
542 unsigned long db_backup_frequency;
543 unsigned long channel_expire_frequency;
544 unsigned long dnr_expire_frequency;
546 unsigned long invited_timeout;
548 unsigned long info_delay;
549 unsigned long adjust_delay;
550 unsigned long channel_expire_delay;
551 unsigned int nodelete_level;
553 unsigned int adjust_threshold;
554 int join_flood_threshold;
556 unsigned int greeting_length;
557 unsigned int refresh_period;
558 unsigned int giveownership_period;
560 unsigned int max_owned;
561 unsigned int max_chan_users;
562 unsigned int max_chan_bans;
563 unsigned int max_userinfo_length;
565 struct string_list *set_shows;
566 struct string_list *eightball;
567 struct string_list *old_ban_names;
569 const char *ctcp_short_ban_duration;
570 const char *ctcp_long_ban_duration;
572 const char *irc_operator_epithet;
573 const char *network_helper_epithet;
574 const char *support_helper_epithet;
579 struct userNode *user;
580 struct userNode *bot;
581 struct chanNode *channel;
583 unsigned short lowest;
584 unsigned short highest;
585 struct userData **users;
586 struct helpfile_table table;
591 struct userNode *user;
592 struct chanNode *chan;
595 enum note_access_type
597 NOTE_SET_CHANNEL_ACCESS,
598 NOTE_SET_CHANNEL_SETTER,
602 enum note_visible_type
605 NOTE_VIS_CHANNEL_USERS,
611 enum note_access_type set_access_type;
613 unsigned int min_opserv;
614 unsigned short min_ulevel;
616 enum note_visible_type visible_type;
617 unsigned int max_length;
624 struct note_type *type;
625 char setter[NICKSERV_HANDLE_LEN+1];
629 static unsigned int registered_channels;
630 static unsigned int banCount;
632 static const struct {
635 unsigned short level;
638 { "peon", "Peon", UL_PEON, '+' },
639 { "op", "Op", UL_OP, '@' },
640 { "master", "Master", UL_MASTER, '%' },
641 { "coowner", "Coowner", UL_COOWNER, '*' },
642 { "owner", "Owner", UL_OWNER, '!' },
643 { "helper", "BUG:", UL_HELPER, 'X' }
646 static const struct {
649 unsigned short default_value;
650 unsigned int old_idx;
651 unsigned int old_flag;
652 unsigned short flag_value;
654 { "CSMSG_SET_GIVE_VOICE", "givevoice", 100, ~0, CHANNEL_VOICE_ALL, 0 },
655 { "CSMSG_SET_GIVE_OPS", "giveops", 200, 2, 0, 0 },
656 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
657 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
658 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
659 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
660 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
661 { "CSMSG_SET_CTCPUSERS", "ctcpusers", 0, 9, 0, 0 },
662 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES, 1 },
663 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE, 200 },
664 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF, 1 },
665 { "CSMSG_SET_VOTE", "vote", 100, ~0, 0, 0 }
668 struct charOptionValues {
671 } protectValues[] = {
672 { 'a', "CSMSG_PROTECT_ALL" },
673 { 'e', "CSMSG_PROTECT_EQUAL" },
674 { 'l', "CSMSG_PROTECT_LOWER" },
675 { 'n', "CSMSG_PROTECT_NONE" }
677 { 'd', "CSMSG_TOYS_DISABLED" },
678 { 'n', "CSMSG_TOYS_PRIVATE" },
679 { 'p', "CSMSG_TOYS_PUBLIC" }
680 }, topicRefreshValues[] = {
681 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
682 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
683 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
684 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
685 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
686 }, ctcpReactionValues[] = {
687 { 'k', "CSMSG_CTCPREACTION_KICK" },
688 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
689 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
690 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
693 static const struct {
697 unsigned int old_idx;
699 struct charOptionValues *values;
701 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues), protectValues },
702 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues), toysValues },
703 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues), topicRefreshValues },
704 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 't', 10, ArrayLength(ctcpReactionValues), ctcpReactionValues }
707 struct userData *helperList;
708 struct chanData *channelList;
709 static struct module *chanserv_module;
710 static unsigned int userCount;
712 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
713 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
716 user_level_from_name(const char *name, unsigned short clamp_level)
718 unsigned int level = 0, ii;
720 level = strtoul(name, NULL, 10);
721 else for(ii = 0; (ii < ArrayLength(accessLevels)) && !level; ++ii)
722 if(!irccasecmp(name, accessLevels[ii].name))
723 level = accessLevels[ii].level;
724 if(level > clamp_level)
730 parse_level_range(unsigned short *minl, unsigned short *maxl, const char *arg)
733 *minl = strtoul(arg, &sep, 10);
741 *maxl = strtoul(sep+1, &sep, 10);
749 _GetChannelUser(struct chanData *channel, struct handle_info *handle, int override, int allow_suspended)
751 struct userData *uData, **head;
753 if(!channel || !handle)
756 if(override && HANDLE_FLAGGED(handle, HELPING)
757 && ((handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel)))
759 for(uData = helperList;
760 uData && uData->handle != handle;
761 uData = uData->next);
765 uData = calloc(1, sizeof(struct userData));
766 uData->handle = handle;
768 uData->access = UL_HELPER;
774 uData->next = helperList;
776 helperList->prev = uData;
784 for(uData = channel->users; uData; uData = uData->next)
785 if((uData->handle == handle) && (allow_suspended || !IsUserSuspended(uData)))
788 head = &(channel->users);
791 if(uData && (uData != *head))
793 /* Shuffle the user to the head of whatever list he was in. */
795 uData->next->prev = uData->prev;
797 uData->prev->next = uData->next;
803 (**head).prev = uData;
810 /* Returns non-zero if user has at least the minimum access.
811 * exempt_owner is set when handling !set, so the owner can set things
814 int check_user_level(struct chanNode *channel, struct userNode *user, enum levelOption opt, int allow_override, int exempt_owner)
816 struct userData *uData;
817 struct chanData *cData = channel->channel_info;
818 unsigned short minimum = cData->lvlOpts[opt];
821 uData = _GetChannelUser(cData, user->handle_info, allow_override, 0);
824 if(minimum <= uData->access)
826 if((minimum > UL_OWNER) && (uData->access == UL_OWNER) && exempt_owner)
831 /* Scan for other users authenticated to the same handle
832 still in the channel. If so, keep them listed as present.
834 user is optional, if not null, it skips checking that userNode
835 (for the handle_part function) */
837 scan_user_presence(struct userData *uData, struct userNode *user)
841 if(IsSuspended(uData->channel)
842 || IsUserSuspended(uData)
843 || !(mn = find_handle_in_channel(uData->channel->channel, uData->handle, user)))
855 chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, const char *text, UNUSED_ARG(struct userNode *bot), UNUSED_ARG(unsigned int is_notice))
857 unsigned int eflags, argc;
859 static char *bad_ctcp_reason = "CTCPs to this channel are forbidden.";
861 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
862 if(!channel->channel_info
863 || IsSuspended(channel->channel_info)
865 || !ircncasecmp(text, "ACTION ", 7))
867 /* Figure out the minimum level needed to CTCP the channel */
868 if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
870 /* We need to enforce against them; do so. */
872 argv[0] = (char*)text;
873 argv[1] = user->nick;
875 if(GetUserMode(channel, user))
876 eflags |= ACTION_KICK;
877 switch(channel->channel_info->chOpts[chCTCPReaction]) {
878 default: case 'k': /* just do the kick */ break;
880 eflags |= ACTION_BAN;
883 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
884 argv[argc++] = (char*)chanserv_conf.ctcp_short_ban_duration;
887 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
888 argv[argc++] = (char*)chanserv_conf.ctcp_long_ban_duration;
891 argv[argc++] = bad_ctcp_reason;
892 eject_user(chanserv, channel, argc, argv, NULL, eflags);
896 chanserv_create_note_type(const char *name)
898 struct note_type *ntype = calloc(1, sizeof(*ntype) + strlen(name));
899 strcpy(ntype->name, name);
901 dict_insert(note_types, ntype->name, ntype);
906 free_vote_options(void *data)
908 struct vote_option *vOpt = data;
910 free(vOpt->option_str);
915 chanserv_deref_note_type(void *data)
917 struct note_type *ntype = data;
919 if(--ntype->refs > 0)
925 chanserv_flush_note_type(struct note_type *ntype)
927 struct chanData *cData;
928 for(cData = channelList; cData; cData = cData->next)
929 dict_remove(cData->notes, ntype->name);
933 chanserv_truncate_notes(struct note_type *ntype)
935 struct chanData *cData;
937 unsigned int size = sizeof(*note) + ntype->max_length;
939 for(cData = channelList; cData; cData = cData->next) {
940 note = dict_find(cData->notes, ntype->name, NULL);
943 if(strlen(note->note) <= ntype->max_length)
945 dict_remove2(cData->notes, ntype->name, 1);
946 note = realloc(note, size);
947 note->note[ntype->max_length] = 0;
948 dict_insert(cData->notes, ntype->name, note);
952 static int note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user);
955 chanserv_add_channel_note(struct chanData *channel, struct note_type *type, const char *setter, const char *text)
958 unsigned int len = strlen(text);
960 if(len > type->max_length) len = type->max_length;
961 note = calloc(1, sizeof(*note) + len);
963 strncpy(note->setter, setter, sizeof(note->setter)-1);
964 memcpy(note->note, text, len);
966 dict_insert(channel->notes, type->name, note);
972 chanserv_free_note(void *data)
974 struct note *note = data;
976 chanserv_deref_note_type(note->type);
977 assert(note->type->refs > 0); /* must use delnote to remove the type */
981 static MODCMD_FUNC(cmd_createnote) {
982 struct note_type *ntype;
983 unsigned int arg = 1, existed = 0, max_length;
985 if((ntype = dict_find(note_types, argv[1], NULL)))
988 ntype = chanserv_create_note_type(argv[arg]);
989 if(!irccasecmp(argv[++arg], "privileged"))
992 ntype->set_access_type = NOTE_SET_PRIVILEGED;
993 ntype->set_access.min_opserv = strtoul(argv[arg], NULL, 0);
995 else if(!irccasecmp(argv[arg], "channel"))
997 unsigned short ulvl = user_level_from_name(argv[++arg], UL_OWNER);
1000 reply("CSMSG_INVALID_ACCESS", argv[arg]);
1003 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
1004 ntype->set_access.min_ulevel = ulvl;
1006 else if(!irccasecmp(argv[arg], "setter"))
1008 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
1012 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
1016 if(!irccasecmp(argv[++arg], "privileged"))
1017 ntype->visible_type = NOTE_VIS_PRIVILEGED;
1018 else if(!irccasecmp(argv[arg], "channel_users"))
1019 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
1020 else if(!irccasecmp(argv[arg], "all"))
1021 ntype->visible_type = NOTE_VIS_ALL;
1023 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
1027 if((arg+1) >= argc) {
1028 reply("MSG_MISSING_PARAMS", argv[0]);
1031 max_length = strtoul(argv[++arg], NULL, 0);
1032 if(max_length < 20 || max_length > 450)
1034 reply("CSMSG_BAD_MAX_LENGTH", argv[arg]);
1037 if(existed && (max_length < ntype->max_length))
1039 ntype->max_length = max_length;
1040 chanserv_truncate_notes(ntype);
1042 ntype->max_length = max_length;
1045 reply("CSMSG_NOTE_MODIFIED", ntype->name);
1047 reply("CSMSG_NOTE_CREATED", ntype->name);
1052 dict_remove(note_types, ntype->name);
1056 static MODCMD_FUNC(cmd_removenote) {
1057 struct note_type *ntype;
1060 ntype = dict_find(note_types, argv[1], NULL);
1061 force = (argc > 2) && !irccasecmp(argv[2], "force");
1064 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
1071 reply("CSMSG_NOTE_TYPE_USED", ntype->name);
1074 chanserv_flush_note_type(ntype);
1076 dict_remove(note_types, argv[1]);
1077 reply("CSMSG_NOTE_DELETED", argv[1]);
1082 chanserv_expire_channel(void *data)
1084 struct chanData *channel = data;
1085 char reason[MAXLEN];
1086 sprintf(reason, "channel expired.");
1087 channel->expiry = 0;
1088 spamserv_cs_unregister(NULL, channel->channel, expire, NULL);
1089 unregister_channel(channel, reason);
1092 static MODCMD_FUNC(chan_opt_expire)
1094 struct chanData *cData = channel->channel_info;
1095 unsigned long value = cData->expiry;
1099 if((!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
1101 reply("MSG_SETTING_PRIVILEGED", argv[0]);
1104 unsigned long expiry,duration;
1106 /* The two directions can have different ACLs. */
1107 if(!strcmp(argv[1], "0"))
1109 else if((duration = ParseInterval(argv[1])))
1110 expiry = now + duration;
1113 reply("MSG_INVALID_DURATION", argv[1]);
1117 if (expiry != value)
1121 timeq_del(value, chanserv_expire_channel, cData, 0);
1124 cData->expiry = value;
1127 timeq_add(expiry, chanserv_expire_channel, cData);
1132 if(cData->expiry > now) {
1133 char expirestr[INTERVALLEN];
1134 reply("CSMSG_SET_EXPIRE", intervalString(expirestr, cData->expiry - now, user->handle_info));
1136 reply("CSMSG_SET_EXPIRE_OFF");
1141 mode_lock_violated(const struct mod_chanmode *orig, const struct mod_chanmode *change)
1145 if(orig->modes_set & change->modes_clear)
1147 if(orig->modes_clear & change->modes_set)
1149 if((orig->modes_set & MODE_KEY) && (change->modes_set & MODE_KEY)
1150 && strcmp(orig->new_key, change->new_key))
1152 if((orig->modes_set & MODE_LIMIT) && (change->modes_set & MODE_LIMIT)
1153 && (orig->new_limit != change->new_limit))
1158 static char max_length_text[MAXLEN+1][16];
1160 static struct helpfile_expansion
1161 chanserv_expand_variable(const char *variable)
1163 struct helpfile_expansion exp;
1165 if(!irccasecmp(variable, "notes"))
1168 exp.type = HF_TABLE;
1169 exp.value.table.length = 1;
1170 exp.value.table.width = 3;
1171 exp.value.table.flags = 0;
1172 exp.value.table.contents = calloc(dict_size(note_types)+1, sizeof(char**));
1173 exp.value.table.contents[0] = calloc(exp.value.table.width, sizeof(char*));
1174 exp.value.table.contents[0][0] = "Note Type";
1175 exp.value.table.contents[0][1] = "Visibility";
1176 exp.value.table.contents[0][2] = "Max Length";
1177 for(it=dict_first(note_types); it; it=iter_next(it))
1179 struct note_type *ntype = iter_data(it);
1182 if(!note_type_visible_to_user(NULL, ntype, message_dest)) continue;
1183 row = exp.value.table.length++;
1184 exp.value.table.contents[row] = calloc(exp.value.table.width, sizeof(char*));
1185 exp.value.table.contents[row][0] = ntype->name;
1186 exp.value.table.contents[row][1] = (ntype->visible_type == NOTE_VIS_ALL) ? "all" :
1187 (ntype->visible_type == NOTE_VIS_CHANNEL_USERS) ? "chan users" :
1189 if(!max_length_text[ntype->max_length][0])
1190 snprintf(max_length_text[ntype->max_length], sizeof(max_length_text[ntype->max_length]), "%u", ntype->max_length);
1191 exp.value.table.contents[row][2] = max_length_text[ntype->max_length];
1196 exp.type = HF_STRING;
1197 exp.value.str = NULL;
1201 static struct chanData*
1202 register_channel(struct chanNode *cNode, char *registrar)
1204 struct chanData *channel;
1205 enum levelOption lvlOpt;
1206 enum charOption chOpt;
1208 channel = calloc(1, sizeof(struct chanData));
1210 channel->notes = dict_new();
1211 dict_set_free_data(channel->notes, chanserv_free_note);
1213 channel->registrar = strdup(registrar);
1214 channel->registered = now;
1215 channel->visited = now;
1216 channel->limitAdjusted = now;
1217 channel->ownerTransfer = now;
1218 channel->flags = CHANNEL_DEFAULT_FLAGS;
1219 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
1220 channel->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
1221 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
1222 channel->chOpts[chOpt] = charOptions[chOpt].default_value;
1224 channel->prev = NULL;
1225 channel->next = channelList;
1228 channelList->prev = channel;
1229 channelList = channel;
1230 registered_channels++;
1232 channel->channel = cNode;
1234 cNode->channel_info = channel;
1236 channel->vote = NULL;
1241 static struct userData*
1242 add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned short access_level, unsigned long seen, const char *info)
1244 struct userData *ud;
1246 if(access_level > UL_OWNER)
1249 ud = calloc(1, sizeof(*ud));
1250 ud->channel = channel;
1251 ud->handle = handle;
1253 ud->access = access_level;
1254 ud->info = info ? strdup(info) : NULL;
1257 ud->next = channel->users;
1259 channel->users->prev = ud;
1260 channel->users = ud;
1262 channel->userCount++;
1266 ud->u_next = ud->handle->channels;
1268 ud->u_next->u_prev = ud;
1269 ud->handle->channels = ud;
1275 del_channel_user(struct userData *user, int do_gc)
1277 struct chanData *channel = user->channel;
1279 channel->userCount--;
1283 user->prev->next = user->next;
1285 channel->users = user->next;
1287 user->next->prev = user->prev;
1290 user->u_prev->u_next = user->u_next;
1292 user->handle->channels = user->u_next;
1294 user->u_next->u_prev = user->u_prev;
1298 if(do_gc && !channel->users && !IsProtected(channel)) {
1299 spamserv_cs_unregister(NULL, channel->channel, lost_all_users, NULL);
1300 unregister_channel(channel, "lost all users.");
1304 static void expire_ban(void *data);
1307 add_channel_ban(struct chanData *channel, const char *mask, char *owner, unsigned long set, unsigned long triggered, unsigned long expires, char *reason)
1310 unsigned int ii, l1, l2;
1315 bd = malloc(sizeof(struct banData));
1317 bd->channel = channel;
1319 bd->triggered = triggered;
1320 bd->expires = expires;
1322 for(ii = 0; ii < chanserv_conf.old_ban_names->used; ++ii)
1324 extern const char *hidden_host_suffix;
1325 const char *old_name = chanserv_conf.old_ban_names->list[ii];
1329 l2 = strlen(old_name);
1332 if(irccasecmp(mask + l1 - l2, old_name))
1334 new_mask = alloca(MAXLEN);
1335 sprintf(new_mask, "%.*s%s", (int)(l1-l2), mask, hidden_host_suffix);
1338 safestrncpy(bd->mask, mask, sizeof(bd->mask));
1340 safestrncpy(bd->owner, owner, sizeof(bd->owner));
1341 bd->reason = strdup(reason);
1344 timeq_add(expires, expire_ban, bd);
1347 bd->next = channel->bans;
1349 channel->bans->prev = bd;
1351 channel->banCount++;
1358 del_channel_ban(struct banData *ban)
1360 ban->channel->banCount--;
1364 ban->prev->next = ban->next;
1366 ban->channel->bans = ban->next;
1369 ban->next->prev = ban->prev;
1372 timeq_del(0, expire_ban, ban, TIMEQ_IGNORE_WHEN);
1381 expire_ban(void *data)
1383 struct banData *bd = data;
1384 if(!IsSuspended(bd->channel))
1386 struct banList bans;
1387 struct mod_chanmode change;
1389 bans = bd->channel->channel->banlist;
1390 mod_chanmode_init(&change);
1391 for(ii=0; ii<bans.used; ii++)
1393 if(!strcmp(bans.list[ii]->ban, bd->mask))
1396 change.args[0].mode = MODE_REMOVE|MODE_BAN;
1397 change.args[0].u.hostmask = bd->mask;
1398 mod_chanmode_announce(chanserv, bd->channel->channel, &change);
1404 del_channel_ban(bd);
1407 static void chanserv_expire_suspension(void *data);
1410 unregister_channel(struct chanData *channel, const char *reason)
1412 struct mod_chanmode change;
1413 char msgbuf[MAXLEN];
1415 /* After channel unregistration, the following must be cleaned
1417 - Channel information.
1420 - Channel suspension data.
1421 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1427 timeq_del(0, NULL, channel, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
1431 mod_chanmode_init(&change);
1432 change.modes_clear |= MODE_REGISTERED;
1433 mod_chanmode_announce(chanserv, channel->channel, &change);
1436 while(channel->users)
1437 del_channel_user(channel->users, 0);
1439 while(channel->bans)
1440 del_channel_ban(channel->bans);
1442 free(channel->topic);
1443 free(channel->registrar);
1444 free(channel->greeting);
1445 free(channel->user_greeting);
1446 free(channel->topic_mask);
1449 channel->prev->next = channel->next;
1451 channelList = channel->next;
1454 channel->next->prev = channel->prev;
1456 if(channel->suspended)
1458 struct chanNode *cNode = channel->channel;
1459 struct suspended *suspended, *next_suspended;
1461 for(suspended = channel->suspended; suspended; suspended = next_suspended)
1463 next_suspended = suspended->previous;
1464 free(suspended->suspender);
1465 free(suspended->reason);
1466 if(suspended->expires)
1467 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
1472 cNode->channel_info = NULL;
1475 timeq_del(channel->expiry, chanserv_expire_channel, channel, 0);
1476 channel->channel->channel_info = NULL;
1478 dict_delete(channel->notes);
1479 sprintf(msgbuf, "%s %s", channel->channel->name, reason);
1480 if(!IsSuspended(channel))
1481 DelChannelUser(chanserv, channel->channel, msgbuf, 0);
1482 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, msgbuf);
1483 UnlockChannel(channel->channel);
1485 registered_channels--;
1489 expire_channels(UNUSED_ARG(void *data))
1491 struct chanData *channel, *next;
1492 struct userData *user;
1493 char delay[INTERVALLEN], reason[INTERVALLEN + 64];
1495 intervalString(delay, chanserv_conf.channel_expire_delay, NULL);
1496 sprintf(reason, "Channel registration automatically expired after %s of disuse.", delay);
1498 for(channel = channelList; channel; channel = next)
1500 next = channel->next;
1502 /* See if the channel can be expired. */
1503 if(((now - channel->visited) <= chanserv_conf.channel_expire_delay)
1504 || IsProtected(channel))
1507 /* Make sure there are no high-ranking users still in the channel. */
1508 for(user=channel->users; user; user=user->next)
1509 if(user->present && (user->access >= UL_PRESENT) && !HANDLE_FLAGGED(user->handle, BOT))
1514 /* Unregister the channel */
1515 log_module(CS_LOG, LOG_INFO, "(%s) Channel registration expired.", channel->channel->name);
1516 spamserv_cs_unregister(NULL, channel->channel, expire, NULL);
1517 unregister_channel(channel, "registration expired.");
1520 if(chanserv_conf.channel_expire_frequency)
1521 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
1525 expire_dnrs(UNUSED_ARG(void *data))
1527 dict_iterator_t it, next;
1528 struct do_not_register *dnr;
1530 for(it = dict_first(handle_dnrs); it; it = next)
1532 dnr = iter_data(it);
1533 next = iter_next(it);
1534 if(dnr->expires && dnr->expires <= now)
1535 dict_remove(handle_dnrs, dnr->chan_name + 1);
1537 for(it = dict_first(plain_dnrs); it; it = next)
1539 dnr = iter_data(it);
1540 next = iter_next(it);
1541 if(dnr->expires && dnr->expires <= now)
1542 dict_remove(plain_dnrs, dnr->chan_name + 1);
1544 for(it = dict_first(mask_dnrs); it; it = next)
1546 dnr = iter_data(it);
1547 next = iter_next(it);
1548 if(dnr->expires && dnr->expires <= now)
1549 dict_remove(mask_dnrs, dnr->chan_name + 1);
1552 if(chanserv_conf.dnr_expire_frequency)
1553 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
1557 protect_user(const struct userNode *victim, const struct userNode *aggressor, struct chanData *channel)
1559 char protect = channel->chOpts[chProtect];
1560 struct userData *cs_victim, *cs_aggressor;
1562 /* Don't protect if no one is to be protected, someone is attacking
1563 himself, or if the aggressor is an IRC Operator. */
1564 if(protect == 'n' || victim == aggressor || IsOper(aggressor))
1567 /* Don't protect if the victim isn't authenticated (because they
1568 can't be a channel user), unless we are to protect non-users
1570 cs_victim = GetChannelAccess(channel, victim->handle_info);
1571 if(protect != 'a' && !cs_victim)
1574 /* Protect if the aggressor isn't a user because at this point,
1575 the aggressor can only be less than or equal to the victim. */
1576 cs_aggressor = GetChannelAccess(channel, aggressor->handle_info);
1580 /* If the aggressor was a user, then the victim can't be helped. */
1587 if(cs_victim->access > cs_aggressor->access)
1592 if(cs_victim->access >= cs_aggressor->access)
1601 validate_op(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1603 struct chanData *cData = channel->channel_info;
1604 struct userData *cs_victim;
1606 if((!(cs_victim = GetChannelUser(cData, victim->handle_info))
1607 || (cs_victim->access < cData->lvlOpts[lvlGiveOps]))
1608 && !check_user_level(channel, user, lvlEnfOps, 0, 0))
1610 send_message(user, chanserv, "CSMSG_OPBY_LOCKED");
1618 validate_deop(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1620 if(IsService(victim))
1622 send_message(user, chanserv, "MSG_SERVICE_IMMUNE", victim->nick);
1626 if(protect_user(victim, user, channel->channel_info))
1628 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
1635 static struct do_not_register *
1636 chanserv_add_dnr(const char *chan_name, const char *setter, unsigned long expires, const char *reason)
1638 struct do_not_register *dnr = calloc(1, sizeof(*dnr)+strlen(reason));
1639 safestrncpy(dnr->chan_name, chan_name, sizeof(dnr->chan_name));
1640 safestrncpy(dnr->setter, setter, sizeof(dnr->setter));
1641 strcpy(dnr->reason, reason);
1643 dnr->expires = expires;
1644 if(dnr->chan_name[0] == '*')
1645 dict_insert(handle_dnrs, dnr->chan_name+1, dnr);
1646 else if(strpbrk(dnr->chan_name, "*?"))
1647 dict_insert(mask_dnrs, dnr->chan_name, dnr);
1649 dict_insert(plain_dnrs, dnr->chan_name, dnr);
1653 static struct dnrList
1654 chanserv_find_dnrs(const char *chan_name, const char *handle, unsigned int max)
1656 struct dnrList list;
1657 dict_iterator_t it, next;
1658 struct do_not_register *dnr;
1660 dnrList_init(&list);
1662 if(handle && (dnr = dict_find(handle_dnrs, handle, NULL)))
1664 if(dnr->expires && dnr->expires <= now)
1665 dict_remove(handle_dnrs, handle);
1666 else if(list.used < max)
1667 dnrList_append(&list, dnr);
1670 if(chan_name && (dnr = dict_find(plain_dnrs, chan_name, NULL)))
1672 if(dnr->expires && dnr->expires <= now)
1673 dict_remove(plain_dnrs, chan_name);
1674 else if(list.used < max)
1675 dnrList_append(&list, dnr);
1680 for(it = dict_first(mask_dnrs); it && list.used < max; it = next)
1682 next = iter_next(it);
1683 if(!match_ircglob(chan_name, iter_key(it)))
1685 dnr = iter_data(it);
1686 if(dnr->expires && dnr->expires <= now)
1687 dict_remove(mask_dnrs, iter_key(it));
1689 dnrList_append(&list, dnr);
1696 static int dnr_print_func(struct do_not_register *dnr, void *extra)
1698 struct userNode *user;
1699 char buf1[INTERVALLEN];
1700 char buf2[INTERVALLEN];
1707 strftime(buf1, sizeof(buf1), "%d %b %Y", localtime(&feh));
1712 strftime(buf2, sizeof(buf2), "%d %b %Y", localtime(&feh));
1713 send_message(user, chanserv, "CSMSG_DNR_INFO_SET_EXPIRES", dnr->chan_name, buf1, dnr->setter, buf2, dnr->reason);
1717 send_message(user, chanserv, "CSMSG_DNR_INFO_SET", dnr->chan_name, buf1, dnr->setter, dnr->reason);
1720 send_message(user, chanserv, "CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1725 chanserv_show_dnrs(struct userNode *user, struct svccmd *cmd, const char *chan_name, const char *handle)
1727 struct dnrList list;
1730 list = chanserv_find_dnrs(chan_name, handle, UINT_MAX);
1731 for(ii = 0; (ii < list.used) && (ii < 10); ++ii)
1732 dnr_print_func(list.list[ii], user);
1734 reply("CSMSG_MORE_DNRS", list.used - ii);
1739 struct do_not_register *
1740 chanserv_is_dnr(const char *chan_name, struct handle_info *handle)
1742 struct dnrList list;
1743 struct do_not_register *dnr;
1745 list = chanserv_find_dnrs(chan_name, handle ? handle->handle : NULL, 1);
1746 dnr = list.used ? list.list[0] : NULL;
1751 static unsigned int send_dnrs(struct userNode *user, dict_t dict)
1753 struct do_not_register *dnr;
1754 dict_iterator_t it, next;
1755 unsigned int matches = 0;
1757 for(it = dict_first(dict); it; it = next)
1759 dnr = iter_data(it);
1760 next = iter_next(it);
1761 if(dnr->expires && dnr->expires <= now)
1763 dict_remove(dict, iter_key(it));
1766 dnr_print_func(dnr, user);
1773 static CHANSERV_FUNC(cmd_noregister)
1777 unsigned long expiry, duration;
1778 unsigned int matches;
1782 reply("CSMSG_DNR_SEARCH_RESULTS");
1783 matches = send_dnrs(user, handle_dnrs);
1784 matches += send_dnrs(user, plain_dnrs);
1785 matches += send_dnrs(user, mask_dnrs);
1787 reply("MSG_MATCH_COUNT", matches);
1789 reply("MSG_NO_MATCHES");
1795 if(!IsChannelName(target) && (*target != '*'))
1797 reply("CSMSG_NOT_DNR", target);
1805 reply("MSG_INVALID_DURATION", argv[2]);
1809 if(!strcmp(argv[2], "0"))
1811 else if((duration = ParseInterval(argv[2])))
1812 expiry = now + duration;
1815 reply("MSG_INVALID_DURATION", argv[2]);
1819 reason = unsplit_string(argv + 3, argc - 3, NULL);
1820 if((*target == '*') && !get_handle_info(target + 1))
1822 reply("MSG_HANDLE_UNKNOWN", target + 1);
1825 chanserv_add_dnr(target, user->handle_info->handle, expiry, reason);
1826 reply("CSMSG_NOREGISTER_CHANNEL", target);
1830 reply("CSMSG_DNR_SEARCH_RESULTS");
1832 matches = chanserv_show_dnrs(user, cmd, NULL, target + 1);
1834 matches = chanserv_show_dnrs(user, cmd, target, NULL);
1836 reply("MSG_NO_MATCHES");
1840 static CHANSERV_FUNC(cmd_allowregister)
1842 const char *chan_name = argv[1];
1844 if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
1845 || dict_remove(plain_dnrs, chan_name)
1846 || dict_remove(mask_dnrs, chan_name))
1848 reply("CSMSG_DNR_REMOVED", chan_name);
1851 reply("CSMSG_NO_SUCH_DNR", chan_name);
1856 struct userNode *source;
1860 unsigned long min_set, max_set;
1861 unsigned long min_expires, max_expires;
1866 dnr_search_matches(const struct do_not_register *dnr, const struct dnr_search *search)
1868 return !((dnr->set < search->min_set)
1869 || (dnr->set > search->max_set)
1870 || (dnr->expires < search->min_expires)
1871 || (search->max_expires
1872 && ((dnr->expires == 0)
1873 || (dnr->expires > search->max_expires)))
1874 || (search->chan_mask
1875 && !match_ircglob(dnr->chan_name, search->chan_mask))
1876 || (search->setter_mask
1877 && !match_ircglob(dnr->setter, search->setter_mask))
1878 || (search->reason_mask
1879 && !match_ircglob(dnr->reason, search->reason_mask)));
1882 static struct dnr_search *
1883 dnr_search_create(struct userNode *user, struct svccmd *cmd, unsigned int argc, char *argv[])
1885 struct dnr_search *discrim;
1888 discrim = calloc(1, sizeof(*discrim));
1889 discrim->source = user;
1890 discrim->chan_mask = NULL;
1891 discrim->setter_mask = NULL;
1892 discrim->reason_mask = NULL;
1893 discrim->max_set = INT_MAX;
1894 discrim->limit = 50;
1896 for(ii=0; ii<argc; ++ii)
1900 reply("MSG_MISSING_PARAMS", argv[ii]);
1903 else if(0 == irccasecmp(argv[ii], "channel"))
1905 discrim->chan_mask = argv[++ii];
1907 else if(0 == irccasecmp(argv[ii], "setter"))
1909 discrim->setter_mask = argv[++ii];
1911 else if(0 == irccasecmp(argv[ii], "reason"))
1913 discrim->reason_mask = argv[++ii];
1915 else if(0 == irccasecmp(argv[ii], "limit"))
1917 discrim->limit = strtoul(argv[++ii], NULL, 0);
1919 else if(0 == irccasecmp(argv[ii], "set"))
1921 const char *cmp = argv[++ii];
1924 discrim->min_set = now - ParseInterval(cmp + 2);
1926 discrim->min_set = now - (ParseInterval(cmp + 1) - 1);
1927 } else if(cmp[0] == '=') {
1928 discrim->min_set = discrim->max_set = now - ParseInterval(cmp + 1);
1929 } else if(cmp[0] == '>') {
1931 discrim->max_set = now - ParseInterval(cmp + 2);
1933 discrim->max_set = now - (ParseInterval(cmp + 1) - 1);
1935 discrim->max_set = now - (ParseInterval(cmp) - 1);
1938 else if(0 == irccasecmp(argv[ii], "expires"))
1940 const char *cmp = argv[++ii];
1943 discrim->max_expires = now + ParseInterval(cmp + 2);
1945 discrim->max_expires = now + (ParseInterval(cmp + 1) - 1);
1946 } else if(cmp[0] == '=') {
1947 discrim->min_expires = discrim->max_expires = now + ParseInterval(cmp + 1);
1948 } else if(cmp[0] == '>') {
1950 discrim->min_expires = now + ParseInterval(cmp + 2);
1952 discrim->min_expires = now + (ParseInterval(cmp + 1) - 1);
1954 discrim->min_expires = now + (ParseInterval(cmp) - 1);
1959 reply("MSG_INVALID_CRITERIA", argv[ii]);
1970 typedef int (*dnr_search_func)(struct do_not_register *match, void *extra);
1973 dnr_search(struct dnr_search *discrim, dnr_search_func dsf, void *data)
1975 struct do_not_register *dnr;
1976 dict_iterator_t next;
1981 /* Initialize local variables. */
1984 if(discrim->chan_mask)
1986 int shift = (discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*') ? 2 : 0;
1987 if('\0' == discrim->chan_mask[shift + strcspn(discrim->chan_mask+shift, "*?")])
1991 if(target_fixed && discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*')
1993 /* Check against account-based DNRs. */
1994 dnr = dict_find(handle_dnrs, discrim->chan_mask + 2, NULL);
1995 if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
1998 else if(target_fixed)
2000 /* Check against channel-based DNRs. */
2001 dnr = dict_find(plain_dnrs, discrim->chan_mask, NULL);
2002 if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
2007 /* Exhaustively search account DNRs. */
2008 for(it = dict_first(handle_dnrs); it; it = next)
2010 next = iter_next(it);
2011 dnr = iter_data(it);
2012 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
2016 /* Do the same for channel DNRs. */
2017 for(it = dict_first(plain_dnrs); it; it = next)
2019 next = iter_next(it);
2020 dnr = iter_data(it);
2021 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
2025 /* Do the same for wildcarded channel DNRs. */
2026 for(it = dict_first(mask_dnrs); it; it = next)
2028 next = iter_next(it);
2029 dnr = iter_data(it);
2030 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
2038 dnr_remove_func(struct do_not_register *match, void *extra)
2040 struct userNode *user;
2043 chan_name = alloca(strlen(match->chan_name) + 1);
2044 strcpy(chan_name, match->chan_name);
2046 if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
2047 || dict_remove(plain_dnrs, chan_name)
2048 || dict_remove(mask_dnrs, chan_name))
2050 send_message(user, chanserv, "CSMSG_DNR_REMOVED", chan_name);
2056 dnr_count_func(struct do_not_register *match, void *extra)
2058 return 0; (void)match; (void)extra;
2061 static MODCMD_FUNC(cmd_dnrsearch)
2063 struct dnr_search *discrim;
2064 dnr_search_func action;
2065 struct svccmd *subcmd;
2066 unsigned int matches;
2069 sprintf(buf, "dnrsearch %s", argv[1]);
2070 subcmd = dict_find(cmd->parent->commands, buf, NULL);
2073 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
2076 if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
2078 if(!irccasecmp(argv[1], "print"))
2079 action = dnr_print_func;
2080 else if(!irccasecmp(argv[1], "remove"))
2081 action = dnr_remove_func;
2082 else if(!irccasecmp(argv[1], "count"))
2083 action = dnr_count_func;
2086 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
2090 discrim = dnr_search_create(user, cmd, argc-2, argv+2);
2094 if(action == dnr_print_func)
2095 reply("CSMSG_DNR_SEARCH_RESULTS");
2096 matches = dnr_search(discrim, action, user);
2098 reply("MSG_MATCH_COUNT", matches);
2100 reply("MSG_NO_MATCHES");
2106 chanserv_get_owned_count(struct handle_info *hi)
2108 struct userData *cList;
2111 for(owned=0, cList=hi->channels; cList; cList=cList->u_next)
2112 if(cList->access == UL_OWNER)
2117 static CHANSERV_FUNC(cmd_register)
2119 struct handle_info *handle;
2120 struct chanData *cData;
2121 struct modeNode *mn;
2122 char reason[MAXLEN];
2124 unsigned int new_channel, force=0;
2125 struct do_not_register *dnr;
2129 if(channel->channel_info)
2131 reply("CSMSG_ALREADY_REGGED", channel->name);
2135 if(channel->bad_channel)
2137 reply("CSMSG_ILLEGAL_CHANNEL", channel->name);
2142 && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
2144 reply("CSMSG_MUST_BE_OPPED", channel->name);
2149 chan_name = channel->name;
2153 if((argc < 2) || !IsChannelName(argv[1]))
2155 reply("MSG_NOT_CHANNEL_NAME");
2159 if(opserv_bad_channel(argv[1]))
2161 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2166 chan_name = argv[1];
2169 if(argc >= (new_channel+2))
2171 if(!IsHelping(user))
2173 reply("CSMSG_PROXY_FORBIDDEN");
2177 if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
2179 force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
2180 dnr = chanserv_is_dnr(chan_name, handle);
2184 handle = user->handle_info;
2185 dnr = chanserv_is_dnr(chan_name, handle);
2189 if(!IsHelping(user))
2190 reply("CSMSG_DNR_CHANNEL", chan_name);
2192 chanserv_show_dnrs(user, cmd, chan_name, handle->handle);
2196 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2198 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
2203 channel = AddChannel(argv[1], now, NULL, NULL);
2205 cData = register_channel(channel, user->handle_info->handle);
2206 scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL), NULL);
2207 cData->modes = chanserv_conf.default_modes;
2209 cData->modes.modes_set |= MODE_REGISTERED;
2210 if (IsOffChannel(cData))
2212 mod_chanmode_announce(chanserv, channel, &cData->modes);
2216 struct mod_chanmode *change = mod_chanmode_dup(&cData->modes, 1);
2217 change->args[change->argc].mode = MODE_CHANOP;
2218 change->args[change->argc].u.member = AddChannelUser(chanserv, channel);
2220 mod_chanmode_announce(chanserv, channel, change);
2221 mod_chanmode_free(change);
2224 /* Initialize the channel's max user record. */
2225 cData->max = channel->members.used;
2226 cData->max_time = 0;
2228 if(handle != user->handle_info)
2229 reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
2231 reply("CSMSG_REG_SUCCESS", channel->name);
2233 sprintf(reason, "%s registered to %s by %s.", channel->name, handle->handle, user->handle_info->handle);
2234 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2239 make_confirmation_string(struct userData *uData)
2241 static char strbuf[16];
2246 for(src = uData->handle->handle; *src; )
2247 accum = accum * 31 + toupper(*src++);
2249 for(src = uData->channel->channel->name; *src; )
2250 accum = accum * 31 + toupper(*src++);
2251 sprintf(strbuf, "%08x", accum);
2255 static CHANSERV_FUNC(cmd_unregister)
2258 char reason[MAXLEN];
2259 struct chanData *cData;
2260 struct userData *uData;
2262 cData = channel->channel_info;
2265 reply("CSMSG_NOT_REGISTERED", channel->name);
2269 uData = GetChannelUser(cData, user->handle_info);
2270 if(!uData || (uData->access < UL_OWNER))
2272 reply("CSMSG_NO_ACCESS");
2276 if(IsProtected(cData))
2278 reply("CSMSG_UNREG_NODELETE", channel->name);
2282 if(!IsHelping(user))
2284 const char *confirm_string;
2285 if(IsSuspended(cData))
2287 reply("CSMSG_CHAN_SUSPENDED", channel->name, cData->suspended->reason);
2290 confirm_string = make_confirmation_string(uData);
2291 if((argc < 2) || strcmp(argv[1], confirm_string))
2293 reply("CSMSG_CONFIRM_UNREG", confirm_string);
2298 sprintf(reason, "unregistered by %s.", user->handle_info->handle);
2299 name = strdup(channel->name);
2300 unregister_channel(cData, reason);
2301 spamserv_cs_unregister(user, channel, manually, "unregistered");
2302 reply("CSMSG_UNREG_SUCCESS", name);
2308 ss_cs_join_channel(struct chanNode *channel, int spamserv_join)
2310 extern struct userNode *spamserv;
2311 struct mod_chanmode *change;
2313 if(spamserv && spamserv_join && get_chanInfo(channel->name))
2315 change = mod_chanmode_alloc(2);
2317 change->args[0].mode = MODE_CHANOP;
2318 change->args[0].u.member = AddChannelUser(chanserv, channel);
2319 change->args[1].mode = MODE_CHANOP;
2320 change->args[1].u.member = AddChannelUser(spamserv, channel);
2324 change = mod_chanmode_alloc(1);
2326 change->args[0].mode = MODE_CHANOP;
2327 change->args[0].u.member = AddChannelUser(chanserv, channel);
2330 mod_chanmode_announce(chanserv, channel, change);
2331 mod_chanmode_free(change);
2334 static CHANSERV_FUNC(cmd_move)
2336 struct mod_chanmode change;
2337 struct chanNode *target;
2338 struct modeNode *mn;
2339 struct userData *uData;
2340 char reason[MAXLEN];
2341 struct do_not_register *dnr;
2342 int chanserv_join = 0, spamserv_join;
2346 if(IsProtected(channel->channel_info))
2348 reply("CSMSG_MOVE_NODELETE", channel->name);
2352 if(!IsChannelName(argv[1]))
2354 reply("MSG_NOT_CHANNEL_NAME");
2358 if(opserv_bad_channel(argv[1]))
2360 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2364 if(!IsHelping(user) || (argc < 3) || irccasecmp(argv[2], "force"))
2366 for(uData = channel->channel_info->users; uData; uData = uData->next)
2368 if((uData->access == UL_OWNER) && (dnr = chanserv_is_dnr(argv[1], uData->handle)))
2370 if(!IsHelping(user))
2371 reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]);
2373 chanserv_show_dnrs(user, cmd, argv[1], uData->handle->handle);
2379 mod_chanmode_init(&change);
2380 if(!(target = GetChannel(argv[1])))
2382 target = AddChannel(argv[1], now, NULL, NULL);
2383 if(!IsSuspended(channel->channel_info))
2386 else if(target->channel_info)
2388 reply("CSMSG_ALREADY_REGGED", target->name);
2391 else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
2392 && !IsHelping(user))
2394 reply("CSMSG_MUST_BE_OPPED", target->name);
2397 else if(!IsSuspended(channel->channel_info))
2402 /* Clear MODE_REGISTERED from old channel, add it to new. */
2404 change.modes_clear = MODE_REGISTERED;
2405 mod_chanmode_announce(chanserv, channel, &change);
2406 change.modes_clear = 0;
2407 change.modes_set = MODE_REGISTERED;
2408 mod_chanmode_announce(chanserv, target, &change);
2411 /* Move the channel_info to the target channel; it
2412 shouldn't be necessary to clear timeq callbacks
2413 for the old channel. */
2414 target->channel_info = channel->channel_info;
2415 target->channel_info->channel = target;
2416 channel->channel_info = NULL;
2418 /* Check whether users are present in the new channel. */
2419 for(uData = target->channel_info->users; uData; uData = uData->next)
2420 scan_user_presence(uData, NULL);
2422 spamserv_join = spamserv_cs_move_merge(user, channel, target, 1);
2425 ss_cs_join_channel(target, spamserv_join);
2427 sprintf(reason, "%s moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
2428 if(!IsSuspended(target->channel_info))
2430 char reason2[MAXLEN];
2431 sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
2432 DelChannelUser(chanserv, channel, reason2, 0);
2434 UnlockChannel(channel);
2435 LockChannel(target);
2436 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2437 reply("CSMSG_MOVE_SUCCESS", target->name);
2442 merge_users(struct chanData *source, struct chanData *target)
2444 struct userData *suData, *tuData, *next;
2450 /* Insert the source's users into the scratch area. */
2451 for(suData = source->users; suData; suData = suData->next)
2452 dict_insert(merge, suData->handle->handle, suData);
2454 /* Iterate through the target's users, looking for
2455 users common to both channels. The lower access is
2456 removed from either the scratch area or target user
2458 for(tuData = target->users; tuData; tuData = next)
2460 struct userData *choice;
2462 next = tuData->next;
2464 /* If a source user exists with the same handle as a target
2465 channel's user, resolve the conflict by removing one. */
2466 suData = dict_find(merge, tuData->handle->handle, NULL);
2470 /* Pick the data we want to keep. */
2471 /* If the access is the same, use the later seen time. */
2472 if(suData->access == tuData->access)
2473 choice = (suData->seen > tuData->seen) ? suData : tuData;
2474 else /* Otherwise, keep the higher access level. */
2475 choice = (suData->access > tuData->access) ? suData : tuData;
2476 /* Use the later seen time. */
2477 if(suData->seen < tuData->seen)
2478 suData->seen = tuData->seen;
2480 tuData->seen = suData->seen;
2482 /* Remove the user that wasn't picked. */
2483 if(choice == tuData)
2485 dict_remove(merge, suData->handle->handle);
2486 del_channel_user(suData, 0);
2489 del_channel_user(tuData, 0);
2492 /* Move the remaining users to the target channel. */
2493 for(it = dict_first(merge); it; it = iter_next(it))
2495 suData = iter_data(it);
2497 /* Insert the user into the target channel's linked list. */
2498 suData->prev = NULL;
2499 suData->next = target->users;
2500 suData->channel = target;
2503 target->users->prev = suData;
2504 target->users = suData;
2506 /* Update the user counts for the target channel; the
2507 source counts are left alone. */
2508 target->userCount++;
2510 /* Check whether the user is in the target channel. */
2511 scan_user_presence(suData, NULL);
2514 /* Possible to assert (source->users == NULL) here. */
2515 source->users = NULL;
2520 merge_bans(struct chanData *source, struct chanData *target)
2522 struct banData *sbData, *tbData, *sNext, *tNext, *tFront;
2524 /* Hold on to the original head of the target ban list
2525 to avoid comparing source bans with source bans. */
2526 tFront = target->bans;
2528 /* Perform a totally expensive O(n*m) merge, ick. */
2529 for(sbData = source->bans; sbData; sbData = sNext)
2531 /* Flag to track whether the ban's been moved
2532 to the destination yet. */
2535 /* Possible to assert (sbData->prev == NULL) here. */
2536 sNext = sbData->next;
2538 for(tbData = tFront; tbData; tbData = tNext)
2540 tNext = tbData->next;
2542 /* Perform two comparisons between each source
2543 and target ban, conflicts are resolved by
2544 keeping the broader ban and copying the later
2545 expiration and triggered time. */
2546 if(match_ircglobs(tbData->mask, sbData->mask))
2548 /* There is a broader ban in the target channel that
2549 overrides one in the source channel; remove the
2550 source ban and break. */
2551 if(sbData->expires > tbData->expires)
2552 tbData->expires = sbData->expires;
2553 if(sbData->triggered > tbData->triggered)
2554 tbData->triggered = sbData->triggered;
2555 del_channel_ban(sbData);
2558 else if(match_ircglobs(sbData->mask, tbData->mask))
2560 /* There is a broader ban in the source channel that
2561 overrides one in the target channel; remove the
2562 target ban, fall through and move the source over. */
2563 if(tbData->expires > sbData->expires)
2564 sbData->expires = tbData->expires;
2565 if(tbData->triggered > sbData->triggered)
2566 sbData->triggered = tbData->triggered;
2567 if(tbData == tFront)
2569 del_channel_ban(tbData);
2572 /* Source bans can override multiple target bans, so
2573 we allow a source to run through this loop multiple
2574 times, but we can only move it once. */
2579 /* Remove the source ban from the source ban list. */
2581 sbData->next->prev = sbData->prev;
2583 /* Modify the source ban's associated channel. */
2584 sbData->channel = target;
2586 /* Insert the ban into the target channel's linked list. */
2587 sbData->prev = NULL;
2588 sbData->next = target->bans;
2591 target->bans->prev = sbData;
2592 target->bans = sbData;
2594 /* Update the user counts for the target channel. */
2599 /* Possible to assert (source->bans == NULL) here. */
2600 source->bans = NULL;
2604 merge_data(struct chanData *source, struct chanData *target)
2606 /* Use more recent visited and owner-transfer time; use older
2607 * registered time. Bitwise or may_opchan. Use higher max.
2608 * Do not touch last_refresh, ban count or user counts.
2610 if(source->visited > target->visited)
2611 target->visited = source->visited;
2612 if(source->registered < target->registered)
2613 target->registered = source->registered;
2614 if(source->ownerTransfer > target->ownerTransfer)
2615 target->ownerTransfer = source->ownerTransfer;
2616 if(source->may_opchan)
2617 target->may_opchan = 1;
2618 if(source->max > target->max) {
2619 target->max = source->max;
2620 target->max_time = source->max_time;
2625 merge_channel(struct chanData *source, struct chanData *target)
2627 merge_users(source, target);
2628 merge_bans(source, target);
2629 merge_data(source, target);
2632 static CHANSERV_FUNC(cmd_merge)
2634 struct userData *target_user;
2635 struct chanNode *target;
2636 char reason[MAXLEN];
2640 /* Make sure the target channel exists and is registered to the user
2641 performing the command. */
2642 if(!(target = GetChannel(argv[1])))
2644 reply("MSG_INVALID_CHANNEL");
2648 if(!target->channel_info)
2650 reply("CSMSG_NOT_REGISTERED", target->name);
2654 if(IsProtected(channel->channel_info))
2656 reply("CSMSG_MERGE_NODELETE");
2660 if(IsSuspended(target->channel_info))
2662 reply("CSMSG_MERGE_SUSPENDED");
2666 if(channel == target)
2668 reply("CSMSG_MERGE_SELF");
2672 target_user = GetChannelUser(target->channel_info, user->handle_info);
2673 if(!target_user || (target_user->access < UL_OWNER))
2675 reply("CSMSG_MERGE_NOT_OWNER");
2679 /* Merge the channel structures and associated data. */
2680 merge_channel(channel->channel_info, target->channel_info);
2681 spamserv_cs_move_merge(user, channel, target, 0);
2682 sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
2683 unregister_channel(channel->channel_info, reason);
2684 reply("CSMSG_MERGE_SUCCESS", target->name);
2688 static CHANSERV_FUNC(cmd_opchan)
2690 struct mod_chanmode change;
2691 if(!IsHelping(user) && !channel->channel_info->may_opchan)
2693 reply("CSMSG_ALREADY_OPCHANNED", channel->name);
2696 channel->channel_info->may_opchan = 0;
2697 mod_chanmode_init(&change);
2699 change.args[0].mode = MODE_CHANOP;
2700 change.args[0].u.member = GetUserMode(channel, chanserv);
2701 if(!change.args[0].u.member)
2703 reply("CSMSG_OUT_OF_CHANNEL", channel->name);
2706 mod_chanmode_announce(chanserv, channel, &change);
2707 reply("CSMSG_OPCHAN_DONE", channel->name);
2711 static CHANSERV_FUNC(cmd_adduser)
2713 struct userData *actee;
2714 struct userData *actor, *real_actor;
2715 struct handle_info *handle;
2716 unsigned short access_level, override = 0;
2720 if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
2722 reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
2726 access_level = user_level_from_name(argv[2], UL_OWNER);
2729 reply("CSMSG_INVALID_ACCESS", argv[2]);
2733 actor = GetChannelUser(channel->channel_info, user->handle_info);
2734 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2736 if(actor->access <= access_level)
2738 reply("CSMSG_NO_BUMP_ACCESS");
2742 /* Trying to add someone with equal/more access? */
2743 if (!real_actor || real_actor->access <= access_level)
2744 override = CMD_LOG_OVERRIDE;
2746 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2749 if((actee = GetTrueChannelAccess(channel->channel_info, handle)))
2751 reply("CSMSG_USER_EXISTS", handle->handle, channel->name, actee->access);
2755 actee = add_channel_user(channel->channel_info, handle, access_level, 0, NULL);
2756 scan_user_presence(actee, NULL);
2757 reply("CSMSG_ADDED_USER", handle->handle, channel->name, access_level);
2758 return 1 | override;
2761 static CHANSERV_FUNC(cmd_clvl)
2763 struct handle_info *handle;
2764 struct userData *victim;
2765 struct userData *actor, *real_actor;
2766 unsigned short new_access, override = 0;
2767 int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
2771 actor = GetChannelUser(channel->channel_info, user->handle_info);
2772 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2774 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2777 if(handle == user->handle_info && !privileged)
2779 reply("CSMSG_NO_SELF_CLVL");
2783 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2785 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2789 if(actor->access <= victim->access && !privileged)
2791 reply("MSG_USER_OUTRANKED", handle->handle);
2795 new_access = user_level_from_name(argv[2], UL_OWNER);
2799 reply("CSMSG_INVALID_ACCESS", argv[2]);
2803 if(new_access >= actor->access && !privileged)
2805 reply("CSMSG_NO_BUMP_ACCESS");
2809 /* Trying to clvl a equal/higher user? */
2810 if(!real_actor || (real_actor->access <= victim->access && handle != user->handle_info))
2811 override = CMD_LOG_OVERRIDE;
2812 /* Trying to clvl someone to equal/higher access? */
2813 if(!real_actor || new_access >= real_actor->access)
2814 override = CMD_LOG_OVERRIDE;
2815 /* Helpers clvling themselves get caught by the "clvl someone to equal/higher access" check.
2816 * If they lower their own access it's not a big problem.
2819 victim->access = new_access;
2820 reply("CSMSG_CHANGED_ACCESS", handle->handle, new_access, channel->name);
2821 return 1 | override;
2824 static CHANSERV_FUNC(cmd_deluser)
2826 struct handle_info *handle;
2827 struct userData *victim;
2828 struct userData *actor, *real_actor;
2829 unsigned short access_level, override = 0;
2834 actor = GetChannelUser(channel->channel_info, user->handle_info);
2835 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2837 if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
2840 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2842 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2848 access_level = user_level_from_name(argv[1], UL_OWNER);
2851 reply("CSMSG_INVALID_ACCESS", argv[1]);
2854 if(access_level != victim->access)
2856 reply("CSMSG_INCORRECT_ACCESS", handle->handle, victim->access, argv[1]);
2862 access_level = victim->access;
2865 if((actor->access <= victim->access) && !IsHelping(user))
2867 reply("MSG_USER_OUTRANKED", victim->handle->handle);
2871 /* If people delete themselves it is an override, but they
2872 * could've used deleteme so we don't log it as an override
2874 if(!real_actor || (real_actor->access <= victim->access && real_actor != victim))
2875 override = CMD_LOG_OVERRIDE;
2877 chan_name = strdup(channel->name);
2878 del_channel_user(victim, 1);
2879 reply("CSMSG_DELETED_USER", handle->handle, access_level, chan_name);
2881 return 1 | override;
2885 cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
2887 struct userData *actor, *real_actor, *uData, *next;
2888 unsigned int override = 0;
2890 actor = GetChannelUser(channel->channel_info, user->handle_info);
2891 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2893 if(min_access > max_access)
2895 reply("CSMSG_BAD_RANGE", min_access, max_access);
2899 if(actor->access <= max_access)
2901 reply("CSMSG_NO_ACCESS");
2905 if(!real_actor || real_actor->access <= max_access)
2906 override = CMD_LOG_OVERRIDE;
2908 for(uData = channel->channel_info->users; uData; uData = next)
2912 if((uData->access >= min_access)
2913 && (uData->access <= max_access)
2914 && match_ircglob(uData->handle->handle, mask))
2915 del_channel_user(uData, 1);
2918 reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
2919 return 1 | override;
2922 static CHANSERV_FUNC(cmd_mdelowner)
2924 return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
2927 static CHANSERV_FUNC(cmd_mdelcoowner)
2929 return cmd_mdel_user(user, channel, UL_COOWNER, UL_COOWNER, argv[1], cmd);
2932 static CHANSERV_FUNC(cmd_mdelmaster)
2934 return cmd_mdel_user(user, channel, UL_MASTER, UL_MASTER, argv[1], cmd);
2937 static CHANSERV_FUNC(cmd_mdelop)
2939 return cmd_mdel_user(user, channel, UL_OP, UL_OP, argv[1], cmd);
2942 static CHANSERV_FUNC(cmd_mdelpeon)
2944 return cmd_mdel_user(user, channel, UL_PEON, UL_PEON, argv[1], cmd);
2948 cmd_trim_bans(struct userNode *user, struct chanNode *channel, unsigned long duration)
2950 struct banData *bData, *next;
2951 char interval[INTERVALLEN];
2953 unsigned long limit;
2956 limit = now - duration;
2957 for(bData = channel->channel_info->bans; bData; bData = next)
2961 if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
2964 del_channel_ban(bData);
2968 intervalString(interval, duration, user->handle_info);
2969 send_message(user, chanserv, "CSMSG_TRIMMED_BANS", count, channel->name, interval);
2974 cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration, int vacation)
2976 struct userData *actor, *uData, *next;
2977 char interval[INTERVALLEN];
2979 unsigned long limit;
2981 actor = GetChannelAccess(channel->channel_info, user->handle_info);
2982 if(min_access > max_access)
2984 send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
2988 if(!actor || actor->access <= max_access)
2990 send_message(user, chanserv, "CSMSG_NO_ACCESS");
2995 limit = now - duration;
2996 for(uData = channel->channel_info->users; uData; uData = next)
3000 if((uData->seen > limit)
3002 || (HANDLE_FLAGGED(uData->handle, FROZEN) && !vacation))
3005 if(((uData->access >= min_access) && (uData->access <= max_access))
3006 || (!max_access && (uData->access < actor->access)))
3008 del_channel_user(uData, 1);
3016 max_access = (actor->access > UL_OWNER) ? UL_OWNER : (actor->access - 1);
3018 send_message(user, chanserv, "CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration, user->handle_info));
3022 static CHANSERV_FUNC(cmd_trim)
3024 unsigned long duration;
3025 unsigned short min_level, max_level;
3030 vacation = argc > 3 && !strcmp(argv[3], "vacation");
3031 duration = ParseInterval(argv[2]);
3034 reply("CSMSG_CANNOT_TRIM");
3038 if(!irccasecmp(argv[1], "bans"))
3040 cmd_trim_bans(user, channel, duration);
3043 else if(!irccasecmp(argv[1], "users"))
3045 cmd_trim_users(user, channel, 0, 0, duration, vacation);
3048 else if(parse_level_range(&min_level, &max_level, argv[1]))
3050 cmd_trim_users(user, channel, min_level, max_level, duration, vacation);
3053 else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
3055 cmd_trim_users(user, channel, min_level, min_level, duration, vacation);
3060 reply("CSMSG_INVALID_TRIM", argv[1]);
3065 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
3066 to the user. cmd_all takes advantage of this. */
3067 static CHANSERV_FUNC(cmd_up)
3069 struct mod_chanmode change;
3070 struct userData *uData;
3073 mod_chanmode_init(&change);
3075 change.args[0].u.member = GetUserMode(channel, user);
3076 if(!change.args[0].u.member)
3079 reply("MSG_CHANNEL_ABSENT", channel->name);
3083 uData = GetChannelAccess(channel->channel_info, user->handle_info);
3087 reply("CSMSG_GODMODE_UP", argv[0]);
3090 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveOps])
3092 change.args[0].mode = MODE_CHANOP;
3093 errmsg = "CSMSG_ALREADY_OPPED";
3095 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveVoice])
3097 change.args[0].mode = MODE_VOICE;
3098 errmsg = "CSMSG_ALREADY_VOICED";
3103 reply("CSMSG_NO_ACCESS");
3106 change.args[0].mode &= ~change.args[0].u.member->modes;
3107 if(!change.args[0].mode)
3110 reply(errmsg, channel->name);
3113 modcmd_chanmode_announce(&change);
3117 static CHANSERV_FUNC(cmd_down)
3119 struct mod_chanmode change;
3121 mod_chanmode_init(&change);
3123 change.args[0].u.member = GetUserMode(channel, user);
3124 if(!change.args[0].u.member)
3127 reply("MSG_CHANNEL_ABSENT", channel->name);
3131 if(!change.args[0].u.member->modes)
3134 reply("CSMSG_ALREADY_DOWN", channel->name);
3138 change.args[0].mode = MODE_REMOVE | change.args[0].u.member->modes;
3139 modcmd_chanmode_announce(&change);
3143 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)
3145 struct userData *cList;
3147 for(cList = user->handle_info->channels; cList; cList = cList->u_next)
3149 if(IsSuspended(cList->channel)
3150 || IsUserSuspended(cList)
3151 || !GetUserMode(cList->channel->channel, user))
3154 mcmd(user, cList->channel->channel, 0, NULL, cmd);
3160 static CHANSERV_FUNC(cmd_upall)
3162 return cmd_all(CSFUNC_ARGS, cmd_up);
3165 static CHANSERV_FUNC(cmd_downall)
3167 return cmd_all(CSFUNC_ARGS, cmd_down);
3170 typedef int validate_func_t(struct userNode *user, struct chanNode *channel, struct userNode *victim);
3171 typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
3174 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)
3176 unsigned int ii, valid;
3177 struct userNode *victim;
3178 struct mod_chanmode *change;
3180 change = mod_chanmode_alloc(argc - 1);
3182 for(ii=valid=0; ++ii < argc; )
3184 if(!(victim = GetUserH(argv[ii])))
3186 change->args[valid].mode = mode;
3187 change->args[valid].u.member = GetUserMode(channel, victim);
3188 if(!change->args[valid].u.member)
3190 if(validate && !validate(user, channel, victim))
3195 change->argc = valid;
3196 if(valid < (argc-1))
3197 reply("CSMSG_PROCESS_FAILED");
3200 modcmd_chanmode_announce(change);
3201 reply(action, channel->name);
3203 mod_chanmode_free(change);
3207 static CHANSERV_FUNC(cmd_op)
3209 return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
3212 static CHANSERV_FUNC(cmd_deop)
3214 return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
3217 static CHANSERV_FUNC(cmd_voice)
3219 return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
3222 static CHANSERV_FUNC(cmd_devoice)
3224 return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
3228 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, unsigned int *victimCount, struct modeNode **victims)
3234 for(ii=0; ii<channel->members.used; ii++)
3236 struct modeNode *mn = channel->members.list[ii];
3238 if(IsService(mn->user))
3241 if(!user_matches_glob(mn->user, ban, MATCH_USENICK | MATCH_VISIBLE))
3244 if(protect_user(mn->user, user, channel->channel_info))
3248 victims[(*victimCount)++] = mn;
3254 eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3256 struct userNode *victim;
3257 struct modeNode **victims;
3258 unsigned int offset, n, victimCount, duration = 0;
3259 char *reason = "Bye.", *ban, *name;
3260 char interval[INTERVALLEN];
3262 offset = (action & ACTION_ADD_TIMED_BAN) ? 3 : 2;
3263 REQUIRE_PARAMS(offset);
3266 reason = unsplit_string(argv + offset, argc - offset, NULL);
3267 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
3269 /* Truncate the reason to a length of TOPICLEN, as
3270 the ircd does; however, leave room for an ellipsis
3271 and the kicker's nick. */
3272 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
3276 if((victim = GetUserH(argv[1])))
3278 victims = alloca(sizeof(victims[0]));
3279 victims[0] = GetUserMode(channel, victim);
3280 /* XXX: The comparison with ACTION_KICK is just because all
3281 * other actions can work on users outside the channel, and we
3282 * want to allow those (e.g. unbans) in that case. If we add
3283 * some other ejection action for in-channel users, change
3285 victimCount = victims[0] ? 1 : 0;
3287 if(IsService(victim))
3289 reply("MSG_SERVICE_IMMUNE", victim->nick);
3293 if((action == ACTION_KICK) && !victimCount)
3295 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
3299 if(protect_user(victim, user, channel->channel_info))
3301 reply("CSMSG_USER_PROTECTED", victim->nick);
3305 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
3306 name = victim->nick;
3308 else if(!is_ircmask(argv[1]) && (*argv[1] == '*'))
3310 struct handle_info *hi;
3311 extern const char *titlehost_suffix;
3312 char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
3313 const char *accountname = argv[1] + 1;
3315 if(!(hi = get_handle_info(accountname)))
3317 reply("MSG_HANDLE_UNKNOWN", accountname);
3321 snprintf(banmask, sizeof(banmask), "*!*@%s.*.%s", hi->handle, titlehost_suffix);
3322 victims = alloca(sizeof(victims[0]) * channel->members.used);
3324 if(bad_channel_ban(channel, user, banmask, &victimCount, victims))
3326 reply("CSMSG_MASK_PROTECTED", banmask);
3330 if((action == ACTION_KICK) && (victimCount == 0))
3332 reply("CSMSG_NO_MATCHING_USERS", channel->name, banmask);
3336 name = ban = strdup(banmask);
3340 if(!is_ircmask(argv[1]))
3342 reply("MSG_NICK_UNKNOWN", argv[1]);
3346 victims = alloca(sizeof(victims[0]) * channel->members.used);
3348 if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
3350 reply("CSMSG_MASK_PROTECTED", argv[1]);
3354 if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
3356 reply("CSMSG_LAME_MASK", argv[1]);
3360 if((action == ACTION_KICK) && (victimCount == 0))
3362 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
3366 name = ban = strdup(argv[1]);
3369 /* Truncate the ban in place if necessary; we must ensure
3370 that 'ban' is a valid ban mask before sanitizing it. */
3371 sanitize_ircmask(ban);
3373 if(action & ACTION_ADD_BAN)
3375 struct banData *bData, *next;
3377 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans)
3379 reply("CSMSG_MAXIMUM_BANS", chanserv_conf.max_chan_bans);
3384 if(action & ACTION_ADD_TIMED_BAN)
3386 duration = ParseInterval(argv[2]);
3390 reply("CSMSG_DURATION_TOO_LOW");
3394 else if(duration > (86400 * 365 * 2))
3396 reply("CSMSG_DURATION_TOO_HIGH");
3402 for(bData = channel->channel_info->bans; bData; bData = next)
3404 if(match_ircglobs(bData->mask, ban))
3406 int exact = !irccasecmp(bData->mask, ban);
3408 /* The ban is redundant; there is already a ban
3409 with the same effect in place. */
3413 free(bData->reason);
3414 bData->reason = strdup(reason);
3415 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
3417 reply("CSMSG_REASON_CHANGE", ban);
3421 if(exact && bData->expires)
3425 /* If the ban matches an existing one exactly,
3426 extend the expiration time if the provided
3427 duration is longer. */
3428 if(duration && (now + duration > bData->expires))
3430 bData->expires = now + duration;
3441 /* Delete the expiration timeq entry and
3442 requeue if necessary. */
3443 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
3446 timeq_add(bData->expires, expire_ban, bData);
3450 /* automated kickban */
3453 reply("CSMSG_BAN_EXTENDED", ban, intervalString(interval, duration, user->handle_info));
3455 reply("CSMSG_BAN_ADDED", name, channel->name);
3461 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3468 if(match_ircglobs(ban, bData->mask))
3470 /* The ban we are adding makes previously existing
3471 bans redundant; silently remove them. */
3472 del_channel_ban(bData);
3476 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);
3478 name = ban = strdup(bData->mask);
3482 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
3484 extern const char *hidden_host_suffix;
3485 const char *old_name = chanserv_conf.old_ban_names->list[n];
3487 unsigned int l1, l2;
3490 l2 = strlen(old_name);
3493 if(irccasecmp(ban + l1 - l2, old_name))
3495 new_mask = malloc(MAXLEN);
3496 sprintf(new_mask, "%.*s%s", (int)(l1-l2), ban, hidden_host_suffix);
3498 name = ban = new_mask;
3503 if(action & ACTION_BAN)
3505 unsigned int exists;
3506 struct mod_chanmode *change;
3508 if(channel->banlist.used >= MAXBANS)
3511 reply("CSMSG_BANLIST_FULL", channel->name);
3516 exists = ChannelBanExists(channel, ban);
3517 change = mod_chanmode_alloc(victimCount + 1);
3518 for(n = 0; n < victimCount; ++n)
3520 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_VOICE;
3521 change->args[n].u.member = victims[n];
3525 change->args[n].mode = MODE_BAN;
3526 change->args[n++].u.hostmask = ban;
3530 modcmd_chanmode_announce(change);
3532 mod_chanmode_announce(chanserv, channel, change);
3533 mod_chanmode_free(change);
3535 if(exists && (action == ACTION_BAN))
3538 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3544 if(action & ACTION_KICK)
3546 char kick_reason[MAXLEN];
3547 sprintf(kick_reason, "(%s) %s", user->nick, reason);
3549 for(n = 0; n < victimCount; n++)
3550 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
3555 /* No response, since it was automated. */
3557 else if(action & ACTION_ADD_BAN)
3560 reply("CSMSG_TIMED_BAN_ADDED", name, channel->name, intervalString(interval, duration, user->handle_info));
3562 reply("CSMSG_BAN_ADDED", name, channel->name);
3564 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
3565 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
3566 else if(action & ACTION_BAN)
3567 reply("CSMSG_BAN_DONE", name, channel->name);
3568 else if(action & ACTION_KICK && victimCount)
3569 reply("CSMSG_KICK_DONE", name, channel->name);
3575 static CHANSERV_FUNC(cmd_kickban)
3577 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
3580 static CHANSERV_FUNC(cmd_kick)
3582 return eject_user(CSFUNC_ARGS, ACTION_KICK);
3585 static CHANSERV_FUNC(cmd_ban)
3587 return eject_user(CSFUNC_ARGS, ACTION_BAN);
3590 static CHANSERV_FUNC(cmd_addban)
3592 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN);
3595 static CHANSERV_FUNC(cmd_addtimedban)
3597 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN);
3600 static struct mod_chanmode *
3601 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
3603 struct mod_chanmode *change;
3604 unsigned char *match;
3605 unsigned int ii, count;
3607 match = alloca(bans->used);
3610 for(ii = count = 0; ii < bans->used; ++ii)
3612 match[ii] = user_matches_glob(actee, bans->list[ii]->ban,
3613 MATCH_USENICK | MATCH_VISIBLE);
3620 for(ii = count = 0; ii < bans->used; ++ii)
3622 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
3629 change = mod_chanmode_alloc(count);
3630 for(ii = count = 0; ii < bans->used; ++ii)
3634 change->args[count].mode = MODE_REMOVE | MODE_BAN;
3635 change->args[count++].u.hostmask = strdup(bans->list[ii]->ban);
3637 assert(count == change->argc);
3642 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3644 struct userNode *actee;
3650 /* may want to allow a comma delimited list of users... */
3651 if(!(actee = GetUserH(argv[1])))
3653 if(!is_ircmask(argv[1]) && *argv[1] == '*')
3655 char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
3656 const char *accountname = argv[1] + 1;
3658 snprintf(banmask, sizeof(banmask), "*!*@%s.*", accountname);
3659 mask = strdup(banmask);
3661 else if(!is_ircmask(argv[1]))
3663 reply("MSG_NICK_UNKNOWN", argv[1]);
3668 mask = strdup(argv[1]);
3672 /* We don't sanitize the mask here because ircu
3674 if(action & ACTION_UNBAN)
3676 struct mod_chanmode *change;
3677 change = find_matching_bans(&channel->banlist, actee, mask);
3682 modcmd_chanmode_announce(change);
3683 for(ii = 0; ii < change->argc; ++ii)
3684 free((char*)change->args[ii].u.hostmask);
3685 mod_chanmode_free(change);
3690 if(action & ACTION_DEL_BAN)
3692 struct banData *ban, *next;
3694 ban = channel->channel_info->bans;
3698 for( ; ban && !user_matches_glob(actee, ban->mask,
3699 MATCH_USENICK | MATCH_VISIBLE);
3702 for( ; ban && !match_ircglobs(mask, ban->mask);
3707 del_channel_ban(ban);
3714 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3716 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3722 static CHANSERV_FUNC(cmd_unban)
3724 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3727 static CHANSERV_FUNC(cmd_delban)
3729 /* it doesn't necessarily have to remove the channel ban - may want
3730 to make that an option. */
3731 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_BAN);
3734 static CHANSERV_FUNC(cmd_unbanme)
3736 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3737 long flags = ACTION_UNBAN;
3739 /* remove permanent bans if the user has the proper access. */
3740 if(uData->access >= UL_MASTER)
3741 flags |= ACTION_DEL_BAN;
3743 argv[1] = user->nick;
3744 return unban_user(user, channel, 2, argv, cmd, flags);
3747 static CHANSERV_FUNC(cmd_unbanall)
3749 struct mod_chanmode *change;
3752 if(!channel->banlist.used)
3754 reply("CSMSG_NO_BANS", channel->name);
3758 change = mod_chanmode_alloc(channel->banlist.used);
3759 for(ii=0; ii<channel->banlist.used; ii++)
3761 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3762 change->args[ii].u.hostmask = strdup(channel->banlist.list[ii]->ban);
3764 modcmd_chanmode_announce(change);
3765 for(ii = 0; ii < change->argc; ++ii)
3766 free((char*)change->args[ii].u.hostmask);
3767 mod_chanmode_free(change);
3768 reply("CSMSG_BANS_REMOVED", channel->name);
3772 static CHANSERV_FUNC(cmd_open)
3774 struct mod_chanmode *change;
3777 change = find_matching_bans(&channel->banlist, user, NULL);
3779 change = mod_chanmode_alloc(0);
3780 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3781 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3782 && channel->channel_info->modes.modes_set)
3783 change->modes_clear &= ~channel->channel_info->modes.modes_set;
3784 modcmd_chanmode_announce(change);
3785 reply("CSMSG_CHANNEL_OPENED", channel->name);
3786 for(ii = 0; ii < change->argc; ++ii)
3787 free((char*)change->args[ii].u.hostmask);
3788 mod_chanmode_free(change);
3792 static CHANSERV_FUNC(cmd_myaccess)
3794 static struct string_buffer sbuf;
3795 struct handle_info *target_handle;
3796 struct userData *uData;
3799 target_handle = user->handle_info;
3800 else if(!IsStaff(user))
3802 reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
3805 else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3808 if(!oper_outranks(user, target_handle))
3811 if(!target_handle->channels)
3813 reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
3817 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3818 for(uData = target_handle->channels; uData; uData = uData->u_next)
3820 struct chanData *cData = uData->channel;
3822 if(uData->access > UL_OWNER)
3824 if(IsProtected(cData)
3825 && (target_handle != user->handle_info)
3826 && !GetTrueChannelAccess(cData, user->handle_info))
3829 string_buffer_append_printf(&sbuf, "[%s (%d", cData->channel->name, uData->access);
3830 if(uData->flags != USER_AUTO_OP)
3831 string_buffer_append(&sbuf, ',');
3832 if(IsUserSuspended(uData))
3833 string_buffer_append(&sbuf, 's');
3834 if(IsUserAutoOp(uData))
3836 if(uData->access >= cData->lvlOpts[lvlGiveOps])
3837 string_buffer_append(&sbuf, 'o');
3838 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
3839 string_buffer_append(&sbuf, 'v');
3841 if(IsUserAutoInvite(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe]))
3842 string_buffer_append(&sbuf, 'i');
3844 string_buffer_append_printf(&sbuf, ")] %s", uData->info);
3846 string_buffer_append_string(&sbuf, ")]");
3847 string_buffer_append(&sbuf, '\0');
3848 send_message_type(4, user, cmd->parent->bot, "%s", sbuf.list);
3854 static CHANSERV_FUNC(cmd_access)
3856 struct userNode *target;
3857 struct handle_info *target_handle;
3858 struct userData *uData;
3860 char prefix[MAXLEN];
3865 target_handle = target->handle_info;
3867 else if((target = GetUserH(argv[1])))
3869 target_handle = target->handle_info;
3871 else if(argv[1][0] == '*')
3873 if(!(target_handle = get_handle_info(argv[1]+1)))
3875 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3881 reply("MSG_NICK_UNKNOWN", argv[1]);
3885 assert(target || target_handle);
3887 if(target == chanserv)
3889 reply("CSMSG_IS_CHANSERV");
3897 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
3902 reply("MSG_USER_AUTHENTICATE", target->nick);
3905 reply("MSG_AUTHENTICATE");
3911 const char *epithet = NULL, *type = NULL;
3914 epithet = chanserv_conf.irc_operator_epithet;
3915 type = user_find_message(user, "CSMSG_OPERATOR_TITLE");
3917 else if(IsNetworkHelper(target))
3919 epithet = chanserv_conf.network_helper_epithet;
3920 type = user_find_message(user, "CSMSG_UC_H_TITLE");
3922 else if(IsSupportHelper(target))
3924 epithet = chanserv_conf.support_helper_epithet;
3925 type = user_find_message(user, "CSMSG_LC_H_TITLE");
3929 if(target_handle->epithet)
3930 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
3932 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
3934 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
3938 sprintf(prefix, "%s", target_handle->handle);
3941 if(!channel->channel_info)
3943 reply("CSMSG_NOT_REGISTERED", channel->name);
3947 helping = HANDLE_FLAGGED(target_handle, HELPING)
3948 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
3949 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
3951 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
3952 /* To prevent possible information leaks, only show infolines
3953 * if the requestor is in the channel or it's their own
3955 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
3957 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
3959 /* Likewise, only say it's suspended if the user has active
3960 * access in that channel or it's their own entry. */
3961 if(IsUserSuspended(uData)
3962 && (GetChannelUser(channel->channel_info, user->handle_info)
3963 || (user->handle_info == uData->handle)))
3965 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
3970 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
3977 zoot_list(struct listData *list)
3979 struct userData *uData;
3980 unsigned int start, curr, highest, lowest;
3981 struct helpfile_table tmp_table;
3982 const char **temp, *msg;
3984 if(list->table.length == 1)
3987 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3989 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3990 msg = user_find_message(list->user, "MSG_NONE");
3991 send_message_type(4, list->user, list->bot, " %s", msg);
3993 tmp_table.width = list->table.width;
3994 tmp_table.flags = list->table.flags;
3995 list->table.contents[0][0] = " ";
3996 highest = list->highest;
3997 if(list->lowest != 0)
3998 lowest = list->lowest;
3999 else if(highest < 100)
4002 lowest = highest - 100;
4003 for(start = curr = 1; curr < list->table.length; )
4005 uData = list->users[curr-1];
4006 list->table.contents[curr++][0] = " ";
4007 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
4010 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, lowest, highest, list->search);
4012 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, lowest, highest);
4013 temp = list->table.contents[--start];
4014 list->table.contents[start] = list->table.contents[0];
4015 tmp_table.contents = list->table.contents + start;
4016 tmp_table.length = curr - start;
4017 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
4018 list->table.contents[start] = temp;
4020 highest = lowest - 1;
4021 lowest = (highest < 100) ? 0 : (highest - 99);
4027 def_list(struct listData *list)
4031 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
4033 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
4034 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4035 if(list->table.length == 1)
4037 msg = user_find_message(list->user, "MSG_NONE");
4038 send_message_type(4, list->user, list->bot, " %s", msg);
4043 userData_access_comp(const void *arg_a, const void *arg_b)
4045 const struct userData *a = *(struct userData**)arg_a;
4046 const struct userData *b = *(struct userData**)arg_b;
4048 if(a->access != b->access)
4049 res = b->access - a->access;
4051 res = irccasecmp(a->handle->handle, b->handle->handle);
4056 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
4058 void (*send_list)(struct listData *);
4059 struct userData *uData;
4060 struct listData lData;
4061 unsigned int matches;
4065 lData.bot = cmd->parent->bot;
4066 lData.channel = channel;
4067 lData.lowest = lowest;
4068 lData.highest = highest;
4069 lData.search = (argc > 1) ? argv[1] : NULL;
4070 send_list = def_list;
4071 (void)zoot_list; /* since it doesn't show user levels */
4073 if(user->handle_info)
4075 switch(user->handle_info->userlist_style)
4077 case HI_STYLE_DEF: send_list = def_list; break;
4078 case HI_STYLE_ZOOT: send_list = def_list; break;
4082 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
4084 for(uData = channel->channel_info->users; uData; uData = uData->next)
4086 if((uData->access < lowest)
4087 || (uData->access > highest)
4088 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
4090 lData.users[matches++] = uData;
4092 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
4094 lData.table.length = matches+1;
4095 lData.table.width = 4;
4096 lData.table.flags = TABLE_NO_FREE;
4097 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
4098 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
4099 lData.table.contents[0] = ary;
4102 ary[2] = "Last Seen";
4104 for(matches = 1; matches < lData.table.length; ++matches)
4106 char seen[INTERVALLEN];
4108 uData = lData.users[matches-1];
4109 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
4110 lData.table.contents[matches] = ary;
4111 ary[0] = strtab(uData->access);
4112 ary[1] = uData->handle->handle;
4115 else if(!uData->seen)
4118 ary[2] = intervalString(seen, now - uData->seen, user->handle_info);
4119 ary[2] = strdup(ary[2]);
4120 if(IsUserSuspended(uData))
4121 ary[3] = "Suspended";
4122 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
4123 ary[3] = "Vacation";
4124 else if(HANDLE_FLAGGED(uData->handle, BOT))
4130 for(matches = 1; matches < lData.table.length; ++matches)
4132 free((char*)lData.table.contents[matches][2]);
4133 free(lData.table.contents[matches]);
4135 free(lData.table.contents[0]);
4136 free(lData.table.contents);
4140 static CHANSERV_FUNC(cmd_users)
4142 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
4145 static CHANSERV_FUNC(cmd_wlist)
4147 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
4150 static CHANSERV_FUNC(cmd_clist)
4152 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
4155 static CHANSERV_FUNC(cmd_mlist)
4157 return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
4160 static CHANSERV_FUNC(cmd_olist)
4162 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
4165 static CHANSERV_FUNC(cmd_plist)
4167 return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
4170 static CHANSERV_FUNC(cmd_bans)
4172 struct userNode *search_u = NULL;
4173 struct helpfile_table tbl;
4174 unsigned int matches = 0, timed = 0, search_wilds = 0, ii;
4175 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
4176 const char *msg_never, *triggered, *expires;
4177 struct banData *ban, **bans;
4181 else if(strchr(search = argv[1], '!'))
4184 search_wilds = search[strcspn(search, "?*")];
4186 else if(!(search_u = GetUserH(search)))
4187 reply("MSG_NICK_UNKNOWN", search);
4189 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
4191 for(ban = channel->channel_info->bans; ban; ban = ban->next)
4195 if(!user_matches_glob(search_u, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
4200 if(search_wilds ? !match_ircglobs(search, ban->mask) : !match_ircglob(search, ban->mask))
4203 bans[matches++] = ban;
4208 tbl.length = matches + 1;
4209 tbl.width = 4 + timed;
4211 tbl.flags = TABLE_NO_FREE;
4212 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
4213 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4214 tbl.contents[0][0] = "Mask";
4215 tbl.contents[0][1] = "Set By";
4216 tbl.contents[0][2] = "Triggered";
4219 tbl.contents[0][3] = "Expires";
4220 tbl.contents[0][4] = "Reason";
4223 tbl.contents[0][3] = "Reason";
4226 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4228 free(tbl.contents[0]);
4233 msg_never = user_find_message(user, "MSG_NEVER");
4234 for(ii = 0; ii < matches; )
4240 else if(ban->expires)
4241 expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
4243 expires = msg_never;
4246 triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
4248 triggered = msg_never;
4250 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4251 tbl.contents[ii][0] = ban->mask;
4252 tbl.contents[ii][1] = ban->owner;
4253 tbl.contents[ii][2] = strdup(triggered);
4256 tbl.contents[ii][3] = strdup(expires);
4257 tbl.contents[ii][4] = ban->reason;
4260 tbl.contents[ii][3] = ban->reason;
4262 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4263 reply("MSG_MATCH_COUNT", matches);
4264 for(ii = 1; ii < tbl.length; ++ii)
4266 free((char*)tbl.contents[ii][2]);
4268 free((char*)tbl.contents[ii][3]);
4269 free(tbl.contents[ii]);
4271 free(tbl.contents[0]);
4277 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
4279 struct chanData *cData = channel->channel_info;
4280 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
4282 if(cData->topic_mask)
4283 return !match_ircglob(new_topic, cData->topic_mask);
4284 else if(cData->topic)
4285 return irccasecmp(new_topic, cData->topic);
4290 static CHANSERV_FUNC(cmd_topic)
4292 struct chanData *cData;
4295 cData = channel->channel_info;
4300 SetChannelTopic(channel, chanserv, cData->topic, 1);
4301 reply("CSMSG_TOPIC_SET", cData->topic);
4305 reply("CSMSG_NO_TOPIC", channel->name);
4309 topic = unsplit_string(argv + 1, argc - 1, NULL);
4310 /* If they say "!topic *", use an empty topic. */
4311 if((topic[0] == '*') && (topic[1] == 0))
4313 if(bad_topic(channel, user, topic))
4315 char *topic_mask = cData->topic_mask;
4318 char new_topic[TOPICLEN+1], tchar;
4319 int pos=0, starpos=-1, dpos=0, len;
4321 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
4328 len = strlen(topic);
4329 if((dpos + len) > TOPICLEN)
4330 len = TOPICLEN + 1 - dpos;
4331 memcpy(new_topic+dpos, topic, len);
4335 case '\\': tchar = topic_mask[pos++]; /* and fall through */
4336 default: new_topic[dpos++] = tchar; break;
4339 if((dpos > TOPICLEN) || tchar)
4342 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
4343 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
4346 new_topic[dpos] = 0;
4347 SetChannelTopic(channel, chanserv, new_topic, 1);
4349 reply("CSMSG_TOPIC_LOCKED", channel->name);
4354 SetChannelTopic(channel, chanserv, topic, 1);
4356 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
4358 /* Grab the topic and save it as the default topic. */
4360 cData->topic = strdup(channel->topic);
4366 static CHANSERV_FUNC(cmd_mode)
4368 struct userData *uData;
4369 struct mod_chanmode *change;
4375 change = &channel->channel_info->modes;
4376 if(change->modes_set || change->modes_clear) {
4377 modcmd_chanmode_announce(change);
4378 reply("CSMSG_DEFAULTED_MODES", channel->name);
4380 reply("CSMSG_NO_MODES", channel->name);
4384 uData = GetChannelUser(channel->channel_info, user->handle_info);
4386 base_oplevel = MAXOPLEVEL;
4387 else if (uData->access >= UL_OWNER)
4390 base_oplevel = 1 + UL_OWNER - uData->access;
4391 change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED|MCP_NO_APASS, base_oplevel);
4394 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
4398 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
4399 && mode_lock_violated(&channel->channel_info->modes, change))
4402 mod_chanmode_format(&channel->channel_info->modes, modes);
4403 reply("CSMSG_MODE_LOCKED", modes, channel->name);
4407 modcmd_chanmode_announce(change);
4408 mod_chanmode_format(change, fmt);
4409 mod_chanmode_free(change);
4410 reply("CSMSG_MODES_SET", fmt);
4415 chanserv_del_invite_mark(void *data)
4417 struct ChanUser *chanuser = data;
4418 struct chanNode *channel = chanuser->chan;
4420 if(!channel) return;
4421 for(i = 0; i < channel->invited.used; i++)
4423 if(channel->invited.list[i] == chanuser->user) {
4424 userList_remove(&channel->invited, chanuser->user);
4430 static CHANSERV_FUNC(cmd_invite)
4432 struct userData *uData;
4433 struct userNode *invite;
4434 struct ChanUser *chanuser;
4437 uData = GetChannelUser(channel->channel_info, user->handle_info);
4441 if(!(invite = GetUserH(argv[1])))
4443 reply("MSG_NICK_UNKNOWN", argv[1]);
4450 if(GetUserMode(channel, invite))
4452 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
4456 for(i = 0; i < channel->invited.used; i++)
4458 if(channel->invited.list[i] == invite) {
4459 reply("CSMSG_ALREADY_INVITED", invite->nick, channel->name);
4468 char *reason = unsplit_string(argv + 2, argc - 2, NULL);
4469 send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
4472 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
4474 irc_invite(chanserv, invite, channel);
4476 reply("CSMSG_INVITED_USER", argv[1], channel->name);
4478 userList_append(&channel->invited, invite);
4479 chanuser = calloc(1, sizeof(*chanuser));
4480 chanuser->user=invite;
4481 chanuser->chan=channel;
4482 timeq_add(now + chanserv_conf.invited_timeout, chanserv_del_invite_mark, chanuser);
4487 static CHANSERV_FUNC(cmd_inviteme)
4489 if(GetUserMode(channel, user))
4491 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
4494 if(channel->channel_info
4495 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
4497 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
4500 irc_invite(cmd->parent->bot, user, channel);
4505 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
4508 char buf1[INTERVALLEN], buf2[INTERVALLEN];
4510 /* We display things based on two dimensions:
4511 * - Issue time: present or absent
4512 * - Expiration: revoked, expired, expires in future, or indefinite expiration
4513 * (in order of precedence, so something both expired and revoked
4514 * only counts as revoked)
4516 combo = (suspended->issued ? 4 : 0)
4517 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
4519 case 0: /* no issue time, indefinite expiration */
4520 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
4522 case 1: /* no issue time, expires in future */
4523 intervalString(buf1, suspended->expires-now, user->handle_info);
4524 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
4526 case 2: /* no issue time, expired */
4527 intervalString(buf1, now-suspended->expires, user->handle_info);
4528 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
4530 case 3: /* no issue time, revoked */
4531 intervalString(buf1, now-suspended->revoked, user->handle_info);
4532 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
4534 case 4: /* issue time set, indefinite expiration */
4535 intervalString(buf1, now-suspended->issued, user->handle_info);
4536 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
4538 case 5: /* issue time set, expires in future */
4539 intervalString(buf1, now-suspended->issued, user->handle_info);
4540 intervalString(buf2, suspended->expires-now, user->handle_info);
4541 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
4543 case 6: /* issue time set, expired */
4544 intervalString(buf1, now-suspended->issued, user->handle_info);
4545 intervalString(buf2, now-suspended->expires, user->handle_info);
4546 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
4548 case 7: /* issue time set, revoked */
4549 intervalString(buf1, now-suspended->issued, user->handle_info);
4550 intervalString(buf2, now-suspended->revoked, user->handle_info);
4551 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
4554 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
4559 static CHANSERV_FUNC(cmd_info)
4561 char modes[MAXLEN], buffer[INTERVALLEN];
4562 struct userData *uData, *owner;
4563 struct chanData *cData;
4564 struct do_not_register *dnr;
4569 cData = channel->channel_info;
4570 reply("CSMSG_CHANNEL_INFO", channel->name);
4572 uData = GetChannelUser(cData, user->handle_info);
4573 if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
4575 mod_chanmode_format(&cData->modes, modes);
4576 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
4577 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
4580 for(it = dict_first(cData->notes); it; it = iter_next(it))
4584 note = iter_data(it);
4585 if(!note_type_visible_to_user(cData, note->type, user))
4588 padding = PADLEN - 1 - strlen(iter_key(it));
4589 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
4592 if(cData->max_time) {
4593 reply("CSMSG_CHANNEL_MAX_TIME", cData->max, intervalString(buffer, now - cData->max_time, user->handle_info));
4595 reply("CSMSG_CHANNEL_MAX", cData->max);
4597 for(owner = cData->users; owner; owner = owner->next)
4598 if(owner->access == UL_OWNER)
4599 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
4600 reply("CSMSG_CHANNEL_USERS", cData->userCount);
4601 reply("CSMSG_CHANNEL_BANS", cData->banCount);
4602 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
4604 privileged = IsStaff(user);
4606 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
4607 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
4608 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
4610 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
4611 chanserv_show_dnrs(user, cmd, channel->name, NULL);
4613 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
4615 struct suspended *suspended;
4616 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
4617 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
4618 show_suspension_info(cmd, user, suspended);
4620 else if(IsSuspended(cData))
4622 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
4623 show_suspension_info(cmd, user, cData->suspended);
4628 static CHANSERV_FUNC(cmd_netinfo)
4630 extern unsigned long boot_time;
4631 extern unsigned long burst_length;
4632 char interval[INTERVALLEN];
4634 reply("CSMSG_NETWORK_INFO");
4635 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
4636 reply("CSMSG_NETWORK_USERS", dict_size(clients));
4637 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
4638 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
4639 reply("CSMSG_NETWORK_BANS", banCount);
4640 reply("CSMSG_NETWORK_CHANUSERS", userCount);
4641 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
4642 reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
4647 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
4649 struct helpfile_table table;
4651 struct userNode *user;
4656 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4657 table.contents = alloca(list->used*sizeof(*table.contents));
4658 for(nn=0; nn<list->used; nn++)
4660 user = list->list[nn];
4661 if(user->modes & skip_flags)
4665 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
4668 nick = alloca(strlen(user->nick)+3);
4669 sprintf(nick, "(%s)", user->nick);
4673 table.contents[table.length][0] = nick;
4676 table_send(chanserv, to->nick, 0, NULL, table);
4679 static CHANSERV_FUNC(cmd_ircops)
4681 reply("CSMSG_STAFF_OPERS");
4682 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
4686 static CHANSERV_FUNC(cmd_helpers)
4688 reply("CSMSG_STAFF_HELPERS");
4689 send_staff_list(user, &curr_helpers, FLAGS_OPER);
4693 static CHANSERV_FUNC(cmd_staff)
4695 reply("CSMSG_NETWORK_STAFF");
4696 cmd_ircops(CSFUNC_ARGS);
4697 cmd_helpers(CSFUNC_ARGS);
4701 static CHANSERV_FUNC(cmd_peek)
4703 struct modeNode *mn;
4704 char modes[MODELEN];
4706 struct helpfile_table table;
4707 int opcount = 0, voicecount = 0, srvcount = 0;
4709 irc_make_chanmode(channel, modes);
4711 reply("CSMSG_PEEK_INFO", channel->name);
4712 reply("CSMSG_PEEK_TOPIC", channel->topic);
4713 reply("CSMSG_PEEK_MODES", modes);
4717 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4718 table.contents = alloca(channel->members.used*sizeof(*table.contents));
4719 for(n = 0; n < channel->members.used; n++)
4721 mn = channel->members.list[n];
4722 if(IsLocal(mn->user))
4724 else if(mn->modes & MODE_CHANOP)
4726 else if(mn->modes & MODE_VOICE)
4729 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
4731 table.contents[table.length] = alloca(sizeof(**table.contents));
4732 table.contents[table.length][0] = mn->user->nick;
4736 reply("CSMSG_PEEK_USERS", channel->members.used, opcount, voicecount,
4737 (channel->members.used - opcount - voicecount - srvcount));
4741 reply("CSMSG_PEEK_OPS");
4742 table_send(chanserv, user->nick, 0, NULL, table);
4745 reply("CSMSG_PEEK_NO_OPS");
4749 static MODCMD_FUNC(cmd_wipeinfo)
4751 struct handle_info *victim;
4752 struct userData *ud, *actor, *real_actor;
4753 unsigned int override = 0;
4756 actor = GetChannelUser(channel->channel_info, user->handle_info);
4757 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
4758 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4760 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4762 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4765 if((ud->access >= actor->access) && (ud != actor))
4767 reply("MSG_USER_OUTRANKED", victim->handle);
4770 if((ud != real_actor) && (!real_actor || (ud->access >= real_actor->access)))
4771 override = CMD_LOG_OVERRIDE;
4775 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4776 return 1 | override;
4779 static CHANSERV_FUNC(cmd_resync)
4781 struct mod_chanmode *changes;
4782 struct chanData *cData = channel->channel_info;
4783 unsigned int ii, used;
4785 changes = mod_chanmode_alloc(channel->members.used * 2);
4786 for(ii = used = 0; ii < channel->members.used; ++ii)
4788 struct modeNode *mn = channel->members.list[ii];
4789 struct userData *uData;
4791 if(IsService(mn->user))
4794 uData = GetChannelAccess(cData, mn->user->handle_info);
4795 if(!cData->lvlOpts[lvlGiveOps]
4796 || (uData && uData->access >= cData->lvlOpts[lvlGiveOps]))
4798 if(!(mn->modes & MODE_CHANOP))
4800 changes->args[used].mode = MODE_CHANOP;
4801 changes->args[used++].u.member = mn;
4804 else if(!cData->lvlOpts[lvlGiveVoice]
4805 || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice]))
4807 if(mn->modes & MODE_CHANOP)
4809 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4810 changes->args[used++].u.member = mn;
4812 if(!(mn->modes & MODE_VOICE))
4814 changes->args[used].mode = MODE_VOICE;
4815 changes->args[used++].u.member = mn;
4822 changes->args[used].mode = MODE_REMOVE | mn->modes;
4823 changes->args[used++].u.member = mn;
4827 changes->argc = used;
4828 modcmd_chanmode_announce(changes);
4829 mod_chanmode_free(changes);
4830 reply("CSMSG_RESYNCED_USERS", channel->name);
4834 static CHANSERV_FUNC(cmd_seen)
4836 struct userData *uData;
4837 struct handle_info *handle;
4838 char seen[INTERVALLEN];
4842 if(!irccasecmp(argv[1], chanserv->nick))
4844 reply("CSMSG_IS_CHANSERV");
4848 if(!(handle = get_handle_info(argv[1])))
4850 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4854 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4856 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4861 reply("CSMSG_USER_PRESENT", handle->handle);
4862 else if(uData->seen)
4863 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
4865 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4867 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4868 reply("CSMSG_USER_VACATION", handle->handle);
4873 static MODCMD_FUNC(cmd_names)
4875 struct userNode *targ;
4876 struct userData *targData;
4877 unsigned int ii, pos;
4880 for(ii=pos=0; ii<channel->members.used; ++ii)
4882 targ = channel->members.list[ii]->user;
4883 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4886 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
4889 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4893 if(IsUserSuspended(targData))
4895 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4898 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4899 reply("CSMSG_END_NAMES", channel->name);
4904 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
4906 switch(ntype->visible_type)
4908 case NOTE_VIS_ALL: return 1;
4909 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
4910 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
4915 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
4917 struct userData *uData;
4919 switch(ntype->set_access_type)
4921 case NOTE_SET_CHANNEL_ACCESS:
4922 if(!user->handle_info)
4924 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
4926 return uData->access >= ntype->set_access.min_ulevel;
4927 case NOTE_SET_CHANNEL_SETTER:
4928 return check_user_level(channel, user, lvlSetters, 1, 0);
4929 case NOTE_SET_PRIVILEGED: default:
4930 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
4934 static CHANSERV_FUNC(cmd_note)
4936 struct chanData *cData;
4938 struct note_type *ntype;
4940 cData = channel->channel_info;
4943 reply("CSMSG_NOT_REGISTERED", channel->name);
4947 /* If no arguments, show all visible notes for the channel. */
4953 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
4955 note = iter_data(it);
4956 if(!note_type_visible_to_user(cData, note->type, user))
4959 reply("CSMSG_NOTELIST_HEADER", channel->name);
4960 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
4963 reply("CSMSG_NOTELIST_END", channel->name);
4965 reply("CSMSG_NOTELIST_EMPTY", channel->name);
4967 /* If one argument, show the named note. */
4970 if((note = dict_find(cData->notes, argv[1], NULL))
4971 && note_type_visible_to_user(cData, note->type, user))
4973 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
4975 else if((ntype = dict_find(note_types, argv[1], NULL))
4976 && note_type_visible_to_user(NULL, ntype, user))
4978 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
4983 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4987 /* Assume they're trying to set a note. */
4991 ntype = dict_find(note_types, argv[1], NULL);
4994 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4997 else if(note_type_settable_by_user(channel, ntype, user))
4999 note_text = unsplit_string(argv+2, argc-2, NULL);
5000 if((note = dict_find(cData->notes, argv[1], NULL)))
5001 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
5002 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
5003 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
5005 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
5007 /* The note is viewable to staff only, so return 0
5008 to keep the invocation from getting logged (or
5009 regular users can see it in !events). */
5015 reply("CSMSG_NO_ACCESS");
5022 static CHANSERV_FUNC(cmd_delnote)
5027 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
5028 || !note_type_settable_by_user(channel, note->type, user))
5030 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
5033 dict_remove(channel->channel_info->notes, note->type->name);
5034 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
5038 static CHANSERV_FUNC(cmd_events)
5040 struct logSearch discrim;
5041 struct logReport report;
5042 unsigned int matches, limit;
5044 limit = (argc > 1) ? atoi(argv[1]) : 10;
5045 if(limit < 1 || limit > 200)
5048 memset(&discrim, 0, sizeof(discrim));
5049 discrim.masks.bot = chanserv;
5050 discrim.masks.channel_name = channel->name;
5052 discrim.masks.command = argv[2];
5053 discrim.limit = limit;
5054 discrim.max_time = INT_MAX;
5055 discrim.severities = 1 << LOG_COMMAND;
5056 report.reporter = chanserv;
5058 reply("CSMSG_EVENT_SEARCH_RESULTS");
5059 matches = log_entry_search(&discrim, log_report_entry, &report);
5061 reply("MSG_MATCH_COUNT", matches);
5063 reply("MSG_NO_MATCHES");
5067 static CHANSERV_FUNC(cmd_say)
5073 msg = unsplit_string(argv + 1, argc - 1, NULL);
5074 send_channel_message(channel, cmd->parent->bot, "%s", msg);
5076 else if(*argv[1] == '*' && argv[1][1] != '\0')
5078 struct handle_info *hi;
5079 struct userNode *authed;
5082 msg = unsplit_string(argv + 2, argc - 2, NULL);
5084 if (!(hi = get_handle_info(argv[1] + 1)))
5086 reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
5090 for (authed = hi->users; authed; authed = authed->next_authed)
5091 send_target_message(5, authed->nick, cmd->parent->bot, "%s", msg);
5093 else if(GetUserH(argv[1]))
5096 msg = unsplit_string(argv + 2, argc - 2, NULL);
5097 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
5101 reply("MSG_NOT_TARGET_NAME");
5107 static CHANSERV_FUNC(cmd_emote)
5113 /* CTCP is so annoying. */
5114 msg = unsplit_string(argv + 1, argc - 1, NULL);
5115 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
5117 else if(*argv[1] == '*' && argv[1][1] != '\0')
5119 struct handle_info *hi;
5120 struct userNode *authed;
5123 msg = unsplit_string(argv + 2, argc - 2, NULL);
5125 if (!(hi = get_handle_info(argv[1] + 1)))
5127 reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
5131 for (authed = hi->users; authed; authed = authed->next_authed)
5132 send_target_message(5, authed->nick, cmd->parent->bot, "\001ACTION %s\001", msg);
5134 else if(GetUserH(argv[1]))
5136 msg = unsplit_string(argv + 2, argc - 2, NULL);
5137 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
5141 reply("MSG_NOT_TARGET_NAME");
5147 struct channelList *
5148 chanserv_support_channels(void)
5150 return &chanserv_conf.support_channels;
5153 static CHANSERV_FUNC(cmd_expire)
5155 int channel_count = registered_channels;
5156 expire_channels(NULL);
5157 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
5162 chanserv_expire_suspension(void *data)
5164 struct suspended *suspended = data;
5165 struct chanNode *channel;
5168 /* Update the channel registration data structure. */
5169 if(!suspended->expires || (now < suspended->expires))
5170 suspended->revoked = now;
5171 channel = suspended->cData->channel;
5172 suspended->cData->channel = channel;
5173 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
5175 /* If appropriate, re-join ChanServ to the channel. */
5176 if(!IsOffChannel(suspended->cData))
5178 spamserv_cs_suspend(channel, 0, 0, NULL);
5179 ss_cs_join_channel(channel, 1);
5182 /* Mark everyone currently in the channel as present. */
5183 for(ii = 0; ii < channel->members.used; ++ii)
5185 struct userData *uData = GetChannelAccess(suspended->cData, channel->members.list[ii]->user->handle_info);
5194 static CHANSERV_FUNC(cmd_csuspend)
5196 struct suspended *suspended;
5197 char reason[MAXLEN];
5198 unsigned long expiry, duration;
5199 struct userData *uData;
5203 if(IsProtected(channel->channel_info))
5205 reply("CSMSG_SUSPEND_NODELETE", channel->name);
5209 if(argv[1][0] == '!')
5211 else if(IsSuspended(channel->channel_info))
5213 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
5214 show_suspension_info(cmd, user, channel->channel_info->suspended);
5218 if(!strcmp(argv[1], "0"))
5220 else if((duration = ParseInterval(argv[1])))
5221 expiry = now + duration;
5224 reply("MSG_INVALID_DURATION", argv[1]);
5228 unsplit_string(argv + 2, argc - 2, reason);
5230 suspended = calloc(1, sizeof(*suspended));
5231 suspended->revoked = 0;
5232 suspended->issued = now;
5233 suspended->suspender = strdup(user->handle_info->handle);
5234 suspended->expires = expiry;
5235 suspended->reason = strdup(reason);
5236 suspended->cData = channel->channel_info;
5237 suspended->previous = suspended->cData->suspended;
5238 suspended->cData->suspended = suspended;
5240 if(suspended->expires)
5241 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
5243 if(IsSuspended(channel->channel_info))
5245 suspended->previous->revoked = now;
5246 if(suspended->previous->expires)
5247 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
5248 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
5249 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5253 /* Mark all users in channel as absent. */
5254 for(uData = channel->channel_info->users; uData; uData = uData->next)
5263 /* Mark the channel as suspended, then part. */
5264 channel->channel_info->flags |= CHANNEL_SUSPENDED;
5265 spamserv_cs_suspend(channel, expiry, 1, suspended->reason);
5266 DelChannelUser(chanserv, channel, suspended->reason, 0);
5267 reply("CSMSG_SUSPENDED", channel->name);
5268 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
5269 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5274 static CHANSERV_FUNC(cmd_cunsuspend)
5276 struct suspended *suspended;
5277 char message[MAXLEN];
5279 if(!IsSuspended(channel->channel_info))
5281 reply("CSMSG_NOT_SUSPENDED", channel->name);
5285 suspended = channel->channel_info->suspended;
5287 /* Expire the suspension and join ChanServ to the channel. */
5288 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
5289 chanserv_expire_suspension(suspended);
5290 reply("CSMSG_UNSUSPENDED", channel->name);
5291 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
5292 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
5296 typedef struct chanservSearch
5301 unsigned long unvisited;
5302 unsigned long registered;
5304 unsigned long flags;
5308 typedef void (*channel_search_func)(struct chanData *channel, void *data);
5311 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
5316 search = malloc(sizeof(struct chanservSearch));
5317 memset(search, 0, sizeof(*search));
5320 for(i = 0; i < argc; i++)
5322 /* Assume all criteria require arguments. */
5325 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
5329 if(!irccasecmp(argv[i], "name"))
5330 search->name = argv[++i];
5331 else if(!irccasecmp(argv[i], "registrar"))
5332 search->registrar = argv[++i];
5333 else if(!irccasecmp(argv[i], "unvisited"))
5334 search->unvisited = ParseInterval(argv[++i]);
5335 else if(!irccasecmp(argv[i], "registered"))
5336 search->registered = ParseInterval(argv[++i]);
5337 else if(!irccasecmp(argv[i], "flags"))
5340 if(!irccasecmp(argv[i], "nodelete"))
5341 search->flags |= CHANNEL_NODELETE;
5342 else if(!irccasecmp(argv[i], "suspended"))
5343 search->flags |= CHANNEL_SUSPENDED;
5344 else if(!irccasecmp(argv[i], "unreviewed"))
5345 search->flags |= CHANNEL_UNREVIEWED;
5348 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
5352 else if(!irccasecmp(argv[i], "limit"))
5353 search->limit = strtoul(argv[++i], NULL, 10);
5356 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
5361 if(search->name && !strcmp(search->name, "*"))
5363 if(search->registrar && !strcmp(search->registrar, "*"))
5364 search->registrar = 0;
5373 chanserv_channel_match(struct chanData *channel, search_t search)
5375 const char *name = channel->channel->name;
5376 if((search->name && !match_ircglob(name, search->name)) ||
5377 (search->registrar && !channel->registrar) ||
5378 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
5379 (search->unvisited && (now - channel->visited) < search->unvisited) ||
5380 (search->registered && (now - channel->registered) > search->registered) ||
5381 (search->flags && ((search->flags & channel->flags) != search->flags)))
5388 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
5390 struct chanData *channel;
5391 unsigned int matches = 0;
5393 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
5395 if(!chanserv_channel_match(channel, search))
5405 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
5410 search_print(struct chanData *channel, void *data)
5412 send_message_type(4, data, chanserv, "%s", channel->channel->name);
5415 static CHANSERV_FUNC(cmd_search)
5418 unsigned int matches;
5419 channel_search_func action;
5423 if(!irccasecmp(argv[1], "count"))
5424 action = search_count;
5425 else if(!irccasecmp(argv[1], "print"))
5426 action = search_print;
5429 reply("CSMSG_ACTION_INVALID", argv[1]);
5433 search = chanserv_search_create(user, argc - 2, argv + 2);
5437 if(action == search_count)
5438 search->limit = INT_MAX;
5440 if(action == search_print)
5441 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
5443 matches = chanserv_channel_search(search, action, user);
5446 reply("MSG_MATCH_COUNT", matches);
5448 reply("MSG_NO_MATCHES");
5454 static CHANSERV_FUNC(cmd_unvisited)
5456 struct chanData *cData;
5457 unsigned long interval = chanserv_conf.channel_expire_delay;
5458 char buffer[INTERVALLEN];
5459 unsigned int limit = 25, matches = 0;
5463 interval = ParseInterval(argv[1]);
5465 limit = atoi(argv[2]);
5468 intervalString(buffer, interval, user->handle_info);
5469 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
5471 for(cData = channelList; cData && matches < limit; cData = cData->next)
5473 if((now - cData->visited) < interval)
5476 intervalString(buffer, now - cData->visited, user->handle_info);
5477 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
5484 static MODCMD_FUNC(chan_opt_defaulttopic)
5490 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5492 reply("CSMSG_TOPIC_LOCKED", channel->name);
5496 topic = unsplit_string(argv+1, argc-1, NULL);
5498 free(channel->channel_info->topic);
5499 if(topic[0] == '*' && topic[1] == 0)
5501 topic = channel->channel_info->topic = NULL;
5505 topic = channel->channel_info->topic = strdup(topic);
5506 if(channel->channel_info->topic_mask
5507 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
5508 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5510 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
5513 if(channel->channel_info->topic)
5514 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
5516 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
5520 static MODCMD_FUNC(chan_opt_topicmask)
5524 struct chanData *cData = channel->channel_info;
5527 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5529 reply("CSMSG_TOPIC_LOCKED", channel->name);
5533 mask = unsplit_string(argv+1, argc-1, NULL);
5535 if(cData->topic_mask)
5536 free(cData->topic_mask);
5537 if(mask[0] == '*' && mask[1] == 0)
5539 cData->topic_mask = 0;
5543 cData->topic_mask = strdup(mask);
5545 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
5546 else if(!match_ircglob(cData->topic, cData->topic_mask))
5547 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5551 if(channel->channel_info->topic_mask)
5552 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
5554 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
5558 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
5562 char *greeting = unsplit_string(argv+1, argc-1, NULL);
5566 if(greeting[0] == '*' && greeting[1] == 0)
5570 unsigned int length = strlen(greeting);
5571 if(length > chanserv_conf.greeting_length)
5573 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
5576 *data = strdup(greeting);
5585 reply(name, user_find_message(user, "MSG_NONE"));
5589 static MODCMD_FUNC(chan_opt_greeting)
5591 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
5594 static MODCMD_FUNC(chan_opt_usergreeting)
5596 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
5599 static MODCMD_FUNC(chan_opt_modes)
5601 struct mod_chanmode *new_modes;
5606 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
5608 reply("CSMSG_NO_ACCESS");
5611 if(argv[1][0] == '*' && argv[1][1] == 0)
5613 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
5615 else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED|MCP_NO_APASS, 0)))
5617 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5620 else if(new_modes->argc > 1)
5622 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5623 mod_chanmode_free(new_modes);
5628 channel->channel_info->modes = *new_modes;
5629 modcmd_chanmode_announce(new_modes);
5630 mod_chanmode_free(new_modes);
5634 mod_chanmode_format(&channel->channel_info->modes, modes);
5636 reply("CSMSG_SET_MODES", modes);
5638 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
5642 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5644 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5646 struct chanData *cData = channel->channel_info;
5651 /* Set flag according to value. */
5652 if(enabled_string(argv[1]))
5654 cData->flags |= mask;
5657 else if(disabled_string(argv[1]))
5659 cData->flags &= ~mask;
5664 reply("MSG_INVALID_BINARY", argv[1]);
5670 /* Find current option value. */
5671 value = (cData->flags & mask) ? 1 : 0;
5675 reply(name, user_find_message(user, "MSG_ON"));
5677 reply(name, user_find_message(user, "MSG_OFF"));
5681 static MODCMD_FUNC(chan_opt_nodelete)
5683 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
5685 reply("MSG_SETTING_PRIVILEGED", argv[0]);
5689 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
5692 static MODCMD_FUNC(chan_opt_dynlimit)
5694 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
5697 static MODCMD_FUNC(chan_opt_offchannel)
5699 struct chanData *cData = channel->channel_info;
5704 /* Set flag according to value. */
5705 if(enabled_string(argv[1]))
5707 if(!IsOffChannel(cData))
5708 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
5709 cData->flags |= CHANNEL_OFFCHANNEL;
5712 else if(disabled_string(argv[1]))
5714 if(IsOffChannel(cData))
5716 struct mod_chanmode change;
5717 mod_chanmode_init(&change);
5719 change.args[0].mode = MODE_CHANOP;
5720 change.args[0].u.member = AddChannelUser(chanserv, channel);
5721 mod_chanmode_announce(chanserv, channel, &change);
5723 cData->flags &= ~CHANNEL_OFFCHANNEL;
5728 reply("MSG_INVALID_BINARY", argv[1]);
5734 /* Find current option value. */
5735 value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
5739 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
5741 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
5745 static MODCMD_FUNC(chan_opt_unreviewed)
5747 struct chanData *cData = channel->channel_info;
5748 int value = (cData->flags & CHANNEL_UNREVIEWED) ? 1 : 0;
5754 /* The two directions can have different ACLs. */
5755 if(enabled_string(argv[1]))
5757 else if(disabled_string(argv[1]))
5761 reply("MSG_INVALID_BINARY", argv[1]);
5765 if (new_value != value)
5767 struct svccmd *subcmd;
5768 char subcmd_name[32];
5770 snprintf(subcmd_name, sizeof(subcmd_name), "%s %s", argv[0], (new_value ? "on" : "off"));
5771 subcmd = dict_find(cmd->parent->commands, subcmd_name, NULL);
5774 reply("MSG_COMMAND_DISABLED", subcmd_name);
5777 else if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
5781 cData->flags |= CHANNEL_UNREVIEWED;
5784 free(cData->registrar);
5785 cData->registrar = strdup(user->handle_info->handle);
5786 cData->flags &= ~CHANNEL_UNREVIEWED;
5793 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_ON"));
5795 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_OFF"));
5799 static MODCMD_FUNC(chan_opt_defaults)
5801 struct userData *uData;
5802 struct chanData *cData;
5803 const char *confirm;
5804 enum levelOption lvlOpt;
5805 enum charOption chOpt;
5807 cData = channel->channel_info;
5808 uData = GetChannelUser(cData, user->handle_info);
5809 if(!uData || (uData->access < UL_OWNER))
5811 reply("CSMSG_OWNER_DEFAULTS", channel->name);
5814 confirm = make_confirmation_string(uData);
5815 if((argc < 2) || strcmp(argv[1], confirm))
5817 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
5820 cData->flags = (CHANNEL_DEFAULT_FLAGS & ~CHANNEL_PRESERVED_FLAGS)
5821 | (cData->flags & CHANNEL_PRESERVED_FLAGS);
5822 cData->modes = chanserv_conf.default_modes;
5823 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
5824 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
5825 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
5826 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
5827 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
5832 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5834 struct chanData *cData = channel->channel_info;
5835 struct userData *uData;
5836 unsigned short value;
5840 if(!check_user_level(channel, user, option, 1, 1))
5842 reply("CSMSG_CANNOT_SET");
5845 value = user_level_from_name(argv[1], UL_OWNER+1);
5846 if(!value && strcmp(argv[1], "0"))
5848 reply("CSMSG_INVALID_ACCESS", argv[1]);
5851 uData = GetChannelUser(cData, user->handle_info);
5852 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
5854 reply("CSMSG_BAD_SETLEVEL");
5860 if(value > cData->lvlOpts[lvlGiveOps])
5862 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
5867 if(value < cData->lvlOpts[lvlGiveVoice])
5869 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
5874 /* This test only applies to owners, since non-owners
5875 * trying to set an option to above their level get caught
5876 * by the CSMSG_BAD_SETLEVEL test above.
5878 if(value > uData->access)
5880 reply("CSMSG_BAD_SETTERS");
5887 cData->lvlOpts[option] = value;
5889 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
5893 static MODCMD_FUNC(chan_opt_enfops)
5895 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
5898 static MODCMD_FUNC(chan_opt_giveops)
5900 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
5903 static MODCMD_FUNC(chan_opt_enfmodes)
5905 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
5908 static MODCMD_FUNC(chan_opt_enftopic)
5910 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
5913 static MODCMD_FUNC(chan_opt_pubcmd)
5915 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
5918 static MODCMD_FUNC(chan_opt_setters)
5920 return channel_level_option(lvlSetters, CSFUNC_ARGS);
5923 static MODCMD_FUNC(chan_opt_ctcpusers)
5925 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
5928 static MODCMD_FUNC(chan_opt_userinfo)
5930 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
5933 static MODCMD_FUNC(chan_opt_givevoice)
5935 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
5938 static MODCMD_FUNC(chan_opt_topicsnarf)
5940 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
5943 static MODCMD_FUNC(chan_opt_vote)
5945 return channel_level_option(lvlVote, CSFUNC_ARGS);
5948 static MODCMD_FUNC(chan_opt_inviteme)
5950 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
5954 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5956 struct chanData *cData = channel->channel_info;
5957 int count = charOptions[option].count, idx;
5961 idx = atoi(argv[1]);
5963 if(!isdigit(argv[1][0]) || (idx < 0) || (idx >= count))
5965 reply("CSMSG_INVALID_NUMERIC", idx);
5966 /* Show possible values. */
5967 for(idx = 0; idx < count; idx++)
5968 reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
5972 cData->chOpts[option] = charOptions[option].values[idx].value;
5976 /* Find current option value. */
5979 (idx < count) && (cData->chOpts[option] != charOptions[option].values[idx].value);
5983 /* Somehow, the option value is corrupt; reset it to the default. */
5984 cData->chOpts[option] = charOptions[option].default_value;
5989 reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
5993 static MODCMD_FUNC(chan_opt_protect)
5995 return channel_multiple_option(chProtect, CSFUNC_ARGS);
5998 static MODCMD_FUNC(chan_opt_toys)
6000 return channel_multiple_option(chToys, CSFUNC_ARGS);
6003 static MODCMD_FUNC(chan_opt_ctcpreaction)
6005 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
6008 static MODCMD_FUNC(chan_opt_topicrefresh)
6010 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
6013 static struct svccmd_list set_shows_list;
6016 handle_svccmd_unbind(struct svccmd *target) {
6018 for(ii=0; ii<set_shows_list.used; ++ii)
6019 if(target == set_shows_list.list[ii])
6020 set_shows_list.used = 0;
6023 static CHANSERV_FUNC(cmd_set)
6025 struct svccmd *subcmd;
6029 /* Check if we need to (re-)initialize set_shows_list. */
6030 if(!set_shows_list.used)
6032 if(!set_shows_list.size)
6034 set_shows_list.size = chanserv_conf.set_shows->used;
6035 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
6037 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
6039 const char *name = chanserv_conf.set_shows->list[ii];
6040 sprintf(buf, "%s %s", argv[0], name);
6041 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6044 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
6047 svccmd_list_append(&set_shows_list, subcmd);
6053 reply("CSMSG_CHANNEL_OPTIONS");
6054 for(ii = 0; ii < set_shows_list.used; ii++)
6056 subcmd = set_shows_list.list[ii];
6057 subcmd->command->func(user, channel, 1, argv+1, subcmd);
6062 sprintf(buf, "%s %s", argv[0], argv[1]);
6063 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6066 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
6069 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
6071 reply("CSMSG_NO_ACCESS");
6077 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
6081 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
6083 struct userData *uData;
6085 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6088 reply("CSMSG_NOT_USER", channel->name);
6094 /* Just show current option value. */
6096 else if(enabled_string(argv[1]))
6098 uData->flags |= mask;
6100 else if(disabled_string(argv[1]))
6102 uData->flags &= ~mask;
6106 reply("MSG_INVALID_BINARY", argv[1]);
6110 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
6114 static MODCMD_FUNC(user_opt_noautoop)
6116 struct userData *uData;
6118 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6121 reply("CSMSG_NOT_USER", channel->name);
6124 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
6125 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
6127 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
6130 static MODCMD_FUNC(user_opt_autoinvite)
6132 if((argc > 1) && !check_user_level(channel, user, lvlInviteMe, 1, 0))
6134 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
6136 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
6139 static MODCMD_FUNC(user_opt_info)
6141 struct userData *uData;
6144 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6148 /* If they got past the command restrictions (which require access)
6149 * but fail this test, we have some fool with security override on.
6151 reply("CSMSG_NOT_USER", channel->name);
6158 infoline = unsplit_string(argv + 1, argc - 1, NULL);
6159 if(strlen(infoline) > chanserv_conf.max_userinfo_length)
6161 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
6164 bp = strcspn(infoline, "\001");
6167 reply("CSMSG_BAD_INFOLINE", infoline[bp]);
6172 if(infoline[0] == '*' && infoline[1] == 0)
6175 uData->info = strdup(infoline);
6178 reply("CSMSG_USET_INFO", uData->info);
6180 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
6184 struct svccmd_list uset_shows_list;
6186 static CHANSERV_FUNC(cmd_uset)
6188 struct svccmd *subcmd;
6192 /* Check if we need to (re-)initialize uset_shows_list. */
6193 if(!uset_shows_list.used)
6197 "NoAutoOp", "AutoInvite", "Info"
6200 if(!uset_shows_list.size)
6202 uset_shows_list.size = ArrayLength(options);
6203 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
6205 for(ii = 0; ii < ArrayLength(options); ii++)
6207 const char *name = options[ii];
6208 sprintf(buf, "%s %s", argv[0], name);
6209 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6212 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
6215 svccmd_list_append(&uset_shows_list, subcmd);
6221 /* Do this so options are presented in a consistent order. */
6222 reply("CSMSG_USER_OPTIONS");
6223 for(ii = 0; ii < uset_shows_list.used; ii++)
6224 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
6228 sprintf(buf, "%s %s", argv[0], argv[1]);
6229 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6232 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
6236 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
6239 static CHANSERV_FUNC(cmd_giveownership)
6241 struct handle_info *new_owner_hi;
6242 struct userData *new_owner;
6243 struct userData *curr_user;
6244 struct userData *invoker;
6245 struct chanData *cData = channel->channel_info;
6246 struct do_not_register *dnr;
6247 const char *confirm;
6249 unsigned short co_access;
6250 char reason[MAXLEN];
6253 curr_user = GetChannelAccess(cData, user->handle_info);
6254 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
6255 if(!curr_user || (curr_user->access != UL_OWNER))
6257 struct userData *owner = NULL;
6258 for(curr_user = channel->channel_info->users;
6260 curr_user = curr_user->next)
6262 if(curr_user->access != UL_OWNER)
6266 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
6273 else if(!force && (now < cData->ownerTransfer + chanserv_conf.giveownership_period))
6275 char delay[INTERVALLEN];
6276 intervalString(delay, cData->ownerTransfer + chanserv_conf.giveownership_period - now, user->handle_info);
6277 reply("CSMSG_TRANSFER_WAIT", delay, channel->name);
6280 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
6282 if(new_owner_hi == user->handle_info)
6284 reply("CSMSG_NO_TRANSFER_SELF");
6287 new_owner = GetChannelAccess(cData, new_owner_hi);
6292 new_owner = add_channel_user(cData, new_owner_hi, UL_OWNER - 1, 0, NULL);
6296 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
6300 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
6302 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
6305 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
6306 if(!IsHelping(user))
6307 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
6309 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi->handle);
6312 invoker = GetChannelUser(cData, user->handle_info);
6313 if(invoker->access <= UL_OWNER)
6315 confirm = make_confirmation_string(curr_user);
6316 if((argc < 3) || strcmp(argv[2], confirm))
6318 reply("CSMSG_CONFIRM_GIVEOWNERSHIP", new_owner_hi->handle, confirm);
6322 if(new_owner->access >= UL_COOWNER)
6323 co_access = new_owner->access;
6325 co_access = UL_COOWNER;
6326 new_owner->access = UL_OWNER;
6328 curr_user->access = co_access;
6329 cData->ownerTransfer = now;
6330 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
6331 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
6332 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
6336 static CHANSERV_FUNC(cmd_suspend)
6338 struct handle_info *hi;
6339 struct userData *actor, *real_actor, *target;
6340 unsigned int override = 0;
6343 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6344 actor = GetChannelUser(channel->channel_info, user->handle_info);
6345 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
6346 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6348 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6351 if(target->access >= actor->access)
6353 reply("MSG_USER_OUTRANKED", hi->handle);
6356 if(target->flags & USER_SUSPENDED)
6358 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
6363 target->present = 0;
6366 if(!real_actor || target->access >= real_actor->access)
6367 override = CMD_LOG_OVERRIDE;
6368 target->flags |= USER_SUSPENDED;
6369 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
6370 return 1 | override;
6373 static CHANSERV_FUNC(cmd_unsuspend)
6375 struct handle_info *hi;
6376 struct userData *actor, *real_actor, *target;
6377 unsigned int override = 0;
6380 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6381 actor = GetChannelUser(channel->channel_info, user->handle_info);
6382 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
6383 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6385 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6388 if(target->access >= actor->access)
6390 reply("MSG_USER_OUTRANKED", hi->handle);
6393 if(!(target->flags & USER_SUSPENDED))
6395 reply("CSMSG_NOT_SUSPENDED", hi->handle);
6398 if(!real_actor || target->access >= real_actor->access)
6399 override = CMD_LOG_OVERRIDE;
6400 target->flags &= ~USER_SUSPENDED;
6401 scan_user_presence(target, NULL);
6402 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
6403 return 1 | override;
6406 static MODCMD_FUNC(cmd_deleteme)
6408 struct handle_info *hi;
6409 struct userData *target;
6410 const char *confirm_string;
6411 unsigned short access_level;
6414 hi = user->handle_info;
6415 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6417 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6420 if(target->access == UL_OWNER)
6422 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
6425 confirm_string = make_confirmation_string(target);
6426 if((argc < 2) || strcmp(argv[1], confirm_string))
6428 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
6431 access_level = target->access;
6432 channel_name = strdup(channel->name);
6433 del_channel_user(target, 1);
6434 reply("CSMSG_DELETED_YOU", access_level, channel_name);
6439 static CHANSERV_FUNC(cmd_addvote)
6441 struct chanData *cData = channel->channel_info;
6442 struct userData *uData, *target;
6443 struct handle_info *hi;
6444 if (!cData) return 0;
6446 hi = user->handle_info;
6447 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6449 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6452 if(target->access < 300) {
6453 reply("CSMSG_NO_ACCESS");
6457 reply("CSMSG_ADDVOTE_FULL");
6461 msg = unsplit_string(argv + 1, argc - 1, NULL);
6462 cData->vote = strdup(msg);
6463 cData->vote_start=0;
6464 dict_delete(cData->vote_options);
6465 cData->vote_options = dict_new();
6466 dict_set_free_data(cData->vote_options, free_vote_options);
6467 for(uData = channel->channel_info->users; uData; uData = uData->next)
6472 reply("CSMSG_ADDVOTE_DONE");
6476 static CHANSERV_FUNC(cmd_delvote)
6478 struct chanData *cData = channel->channel_info;
6479 struct userData *target;
6480 struct handle_info *hi;
6481 if (!cData) return 0;
6482 hi = user->handle_info;
6483 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6485 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6488 if(target->access < 300) {
6489 reply("CSMSG_NO_ACCESS");
6493 reply("CSMSG_NO_VOTE");
6498 reply("CSMSG_DELVOTE_DONE");
6502 static CHANSERV_FUNC(cmd_addoption)
6504 struct chanData *cData = channel->channel_info;
6505 struct userData *target;
6506 struct handle_info *hi;
6507 if (!cData) return 0;
6509 hi = user->handle_info;
6510 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6512 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6515 if(target->access < 300) {
6516 reply("CSMSG_NO_ACCESS");
6520 reply("CSMSG_NO_VOTE");
6526 msg = unsplit_string(argv + 1, argc - 1, NULL);
6529 unsigned int lastid = 1;
6530 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6531 struct vote_option *cvOpt = iter_data(it);
6532 if(cvOpt->option_id > lastid)
6533 lastid = cvOpt->option_id;
6535 struct vote_option *vOpt;
6536 vOpt = calloc(1, sizeof(*vOpt));
6537 vOpt->name = strdup(msg);
6538 vOpt->option_id = (lastid + 1);
6540 sprintf(str,"%i",(lastid + 1));
6541 vOpt->option_str = strdup(str);
6543 dict_insert(cData->vote_options,vOpt->option_str,vOpt);
6545 reply("CSMSG_ADDOPTION_DONE",dict_size(cData->vote_options),lastid,(lastid + 1));
6549 static CHANSERV_FUNC(cmd_deloption)
6551 struct chanData *cData = channel->channel_info;
6552 struct userData *uData, *target;
6553 struct handle_info *hi;
6554 if (!cData) return 0;
6556 hi = user->handle_info;
6557 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6559 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6562 if(target->access < 300) {
6563 reply("CSMSG_NO_ACCESS");
6567 reply("CSMSG_NO_VOTE");
6570 if(cData->vote_start) {
6571 if(dict_size(cData->vote_options) < 3) {
6572 reply("CSMSG_VOTE_NEED_OPTIONS");
6577 int find_id = atoi(argv[1]);
6579 unsigned int found = 0;
6582 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6584 if (find_id == ii) {
6585 struct vote_option *vOpt = iter_data(it);
6586 found = vOpt->option_id;
6588 sprintf(str,"%i",vOpt->option_id);
6589 dict_remove(cData->vote_options, str);
6594 for(uData = channel->channel_info->users; uData; uData = uData->next) {
6595 if(uData->votefor == found) {
6600 reply("CSMSG_DELOPTION_DONE");
6603 reply("CSMSG_DELOPTION_NONE");
6608 static CHANSERV_FUNC(cmd_vote)
6610 struct chanData *cData = channel->channel_info;
6611 struct userData *target;
6612 struct handle_info *hi;
6613 unsigned int votedfor = 0;
6616 if (!cData || !cData->vote) {
6617 reply("CSMSG_NO_VOTE");
6620 if(argc > 1 && cData->vote_start) {
6621 hi = user->handle_info;
6622 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6624 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6627 if(!check_user_level(channel, user, lvlVote, 1, 0)) {
6628 reply("CSMSG_NO_ACCESS");
6632 reply("CSMSG_VOTE_VOTED");
6635 int find_id = atoi(argv[1]);
6638 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6640 if (find_id == ii) {
6641 struct vote_option *vOpt = iter_data(it);
6644 target->votefor = vOpt->option_id;
6645 votedfor = vOpt->option_id;
6646 votedfor_str = vOpt->name;
6650 reply("CSMSG_VOTE_INVALID");
6654 if (!cData->vote_start) {
6655 reply("CSMSG_VOTE_NOT_STARTED");
6657 reply("CSMSG_VOTE_QUESTION",cData->vote);
6659 unsigned int voteid = 0;
6662 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6663 struct vote_option *vOpt = iter_data(it);
6665 reply("CSMSG_VOTE_OPTION",voteid,vOpt->name,vOpt->voted);
6667 if(argc > 1 && cData->vote_start && votedfor_str) {
6668 reply("CSMSG_VOTE_DONE",votedfor_str);
6673 static CHANSERV_FUNC(cmd_startvote)
6675 struct chanData *cData = channel->channel_info;
6676 struct userData *target;
6677 struct handle_info *hi;
6678 if (!cData) return 0;
6679 hi = user->handle_info;
6680 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6682 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6685 if(target->access < 300) {
6686 reply("CSMSG_NO_ACCESS");
6690 reply("CSMSG_NO_VOTE");
6693 if(cData->vote_start) {
6694 reply("CSMSG_STARTVOTE_RUNNING");
6697 if(dict_size(cData->vote_options) < 2) {
6698 reply("CSMSG_VOTE_NEED_OPTIONS");
6701 cData->vote_start = 1;
6702 char response[MAXLEN];
6703 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_TOP"), user->nick);
6704 irc_privmsg(cmd->parent->bot, channel->name, response);
6705 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_QUESTION"), cData->vote);
6706 irc_privmsg(cmd->parent->bot, channel->name, response);
6707 unsigned int voteid = 0;
6709 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6710 struct vote_option *vOpt = iter_data(it);
6712 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_OPTION"), voteid, vOpt->name);
6713 irc_privmsg(cmd->parent->bot, channel->name, response);
6715 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_ACCESS"), cData->lvlOpts[lvlVote]); //Todo
6716 irc_privmsg(cmd->parent->bot, channel->name, response);
6717 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_HOWTO")); //Todo
6718 irc_privmsg(cmd->parent->bot, channel->name, response);
6721 static CHANSERV_FUNC(cmd_endvote)
6723 struct chanData *cData = channel->channel_info;
6724 struct userData *target;
6725 struct handle_info *hi;
6726 if (!cData) return 0;
6727 hi = user->handle_info;
6728 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6730 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6733 if(target->access < 300) {
6734 reply("CSMSG_NO_ACCESS");
6738 reply("CSMSG_NO_VOTE");
6741 if(!cData->vote_start) {
6742 reply("CSMSG_ENDVOTE_STOPPED");
6745 cData->vote_start = 0;
6746 reply("CSMSG_ENDVOTE_DONE");
6750 static CHANSERV_FUNC(cmd_voteresults)
6752 struct chanData *cData = channel->channel_info;
6753 struct userData *target;
6754 struct handle_info *hi;
6755 if (!cData) return 0;
6757 reply("CSMSG_NO_VOTE");
6760 if (argc > 1 && !irccasecmp(argv[1], "*")) {
6761 hi = user->handle_info;
6762 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6764 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6767 if(target->access < 300) {
6768 reply("CSMSG_NO_ACCESS");
6771 char response[MAXLEN];
6772 sprintf(response, user_find_message(user, "CSMSG_VOTERES_QUESTION"), cData->vote);
6773 irc_privmsg(cmd->parent->bot, channel->name, response);
6774 unsigned int voteid = 0;
6776 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6777 struct vote_option *vOpt = iter_data(it);
6779 sprintf(response, user_find_message(user, "CSMSG_VOTERES_OPTION"), voteid, vOpt->name, vOpt->voted);
6780 irc_privmsg(cmd->parent->bot, channel->name, response);
6783 reply("CSMSG_VOTE_QUESTION",cData->vote);
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 reply("CSMSG_VOTE_OPTION",voteid,vOpt->name,vOpt->voted);
6796 chanserv_refresh_topics(UNUSED_ARG(void *data))
6798 unsigned int refresh_num = (now - self->link_time) / chanserv_conf.refresh_period;
6799 struct chanData *cData;
6802 for(cData = channelList; cData; cData = cData->next)
6804 if(IsSuspended(cData))
6806 opt = cData->chOpts[chTopicRefresh];
6809 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
6812 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
6813 cData->last_refresh = refresh_num;
6815 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
6818 static CHANSERV_FUNC(cmd_unf)
6822 char response[MAXLEN];
6823 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
6824 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6825 irc_privmsg(cmd->parent->bot, channel->name, response);
6828 reply("CSMSG_UNF_RESPONSE");
6832 static CHANSERV_FUNC(cmd_ping)
6836 char response[MAXLEN];
6837 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
6838 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6839 irc_privmsg(cmd->parent->bot, channel->name, response);
6842 reply("CSMSG_PING_RESPONSE");
6846 static CHANSERV_FUNC(cmd_wut)
6850 char response[MAXLEN];
6851 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
6852 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6853 irc_privmsg(cmd->parent->bot, channel->name, response);
6856 reply("CSMSG_WUT_RESPONSE");
6860 static CHANSERV_FUNC(cmd_8ball)
6862 unsigned int i, j, accum;
6867 for(i=1; i<argc; i++)
6868 for(j=0; argv[i][j]; j++)
6869 accum = (accum << 5) - accum + toupper(argv[i][j]);
6870 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
6873 char response[MAXLEN];
6874 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
6875 irc_privmsg(cmd->parent->bot, channel->name, response);
6878 send_message_type(4, user, cmd->parent->bot, "%s", resp);
6882 static CHANSERV_FUNC(cmd_d)
6884 unsigned long sides, count, modifier, ii, total;
6885 char response[MAXLEN], *sep;
6889 if((count = strtoul(argv[1], &sep, 10)) < 1)
6899 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
6900 && (sides = strtoul(sep+1, &sep, 10)) > 1)
6904 else if((sep[0] == '-') && isdigit(sep[1]))
6905 modifier = strtoul(sep, NULL, 10);
6906 else if((sep[0] == '+') && isdigit(sep[1]))
6907 modifier = strtoul(sep+1, NULL, 10);
6914 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
6919 reply("CSMSG_BAD_DICE_COUNT", count, 10);
6922 for(total = ii = 0; ii < count; ++ii)
6923 total += (rand() % sides) + 1;
6926 if((count > 1) || modifier)
6928 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
6929 sprintf(response, fmt, total, count, sides, modifier);
6933 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
6934 sprintf(response, fmt, total, sides);
6937 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
6939 send_message_type(4, user, cmd->parent->bot, "%s", response);
6943 static CHANSERV_FUNC(cmd_huggle)
6945 /* CTCP must be via PRIVMSG, never notice */
6947 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
6949 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
6954 chanserv_adjust_limit(void *data)
6956 struct mod_chanmode change;
6957 struct chanData *cData = data;
6958 struct chanNode *channel = cData->channel;
6961 if(IsSuspended(cData))
6964 cData->limitAdjusted = now;
6965 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
6966 if(cData->modes.modes_set & MODE_LIMIT)
6968 if(limit > cData->modes.new_limit)
6969 limit = cData->modes.new_limit;
6970 else if(limit == cData->modes.new_limit)
6974 mod_chanmode_init(&change);
6975 change.modes_set = MODE_LIMIT;
6976 change.new_limit = limit;
6977 mod_chanmode_announce(chanserv, channel, &change);
6981 handle_new_channel(struct chanNode *channel)
6983 struct chanData *cData;
6985 if(!(cData = channel->channel_info))
6988 if(cData->modes.modes_set || cData->modes.modes_clear)
6989 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
6991 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
6992 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
6995 /* Welcome to my worst nightmare. Warning: Read (or modify)
6996 the code below at your own risk. */
6998 handle_join(struct modeNode *mNode)
7000 struct mod_chanmode change;
7001 struct userNode *user = mNode->user;
7002 struct chanNode *channel = mNode->channel;
7003 struct chanData *cData;
7004 struct userData *uData = NULL;
7005 struct banData *bData;
7006 struct handle_info *handle;
7007 unsigned int modes = 0, info = 0;
7010 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
7013 cData = channel->channel_info;
7014 if(channel->members.used > cData->max) {
7015 cData->max = channel->members.used;
7016 cData->max_time = now;
7019 for(i = 0; i < channel->invited.used; i++)
7021 if(channel->invited.list[i] == user) {
7022 userList_remove(&channel->invited, user);
7026 /* Check for bans. If they're joining through a ban, one of two
7028 * 1: Join during a netburst, by riding the break. Kick them
7029 * unless they have ops or voice in the channel.
7030 * 2: They're allowed to join through the ban (an invite in
7031 * ircu2.10, or a +e on Hybrid, or something).
7032 * If they're not joining through a ban, and the banlist is not
7033 * full, see if they're on the banlist for the channel. If so,
7036 if(user->uplink->burst && !mNode->modes)
7039 for(ii = 0; ii < channel->banlist.used; ii++)
7041 if(user_matches_glob(user, channel->banlist.list[ii]->ban, MATCH_USENICK))
7043 /* Riding a netburst. Naughty. */
7044 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
7050 mod_chanmode_init(&change);
7052 if(channel->banlist.used < MAXBANS)
7054 /* Not joining through a ban. */
7055 for(bData = cData->bans;
7056 bData && !user_matches_glob(user, bData->mask, MATCH_USENICK);
7057 bData = bData->next);
7061 char kick_reason[MAXLEN];
7062 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
7064 bData->triggered = now;
7065 if(bData != cData->bans)
7067 /* Shuffle the ban to the head of the list. */
7069 bData->next->prev = bData->prev;
7071 bData->prev->next = bData->next;
7074 bData->next = cData->bans;
7077 cData->bans->prev = bData;
7078 cData->bans = bData;
7081 change.args[0].mode = MODE_BAN;
7082 change.args[0].u.hostmask = bData->mask;
7083 mod_chanmode_announce(chanserv, channel, &change);
7084 KickChannelUser(user, channel, chanserv, kick_reason);
7089 /* ChanServ will not modify the limits in join-flooded channels,
7090 or when there are enough slots left below the limit. */
7091 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
7092 && !channel->join_flooded
7093 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
7095 /* The user count has begun "bumping" into the channel limit,
7096 so set a timer to raise the limit a bit. Any previous
7097 timers are removed so three incoming users within the delay
7098 results in one limit change, not three. */
7100 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7101 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7104 if(channel->join_flooded)
7106 /* don't automatically give ops or voice during a join flood */
7108 else if(cData->lvlOpts[lvlGiveOps] == 0)
7109 modes |= MODE_CHANOP;
7110 else if(cData->lvlOpts[lvlGiveVoice] == 0)
7111 modes |= MODE_VOICE;
7113 greeting = cData->greeting;
7114 if(user->handle_info)
7116 handle = user->handle_info;
7118 if(IsHelper(user) && !IsHelping(user))
7121 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7123 if(channel == chanserv_conf.support_channels.list[ii])
7125 HANDLE_SET_FLAG(user->handle_info, HELPING);
7131 uData = GetTrueChannelAccess(cData, handle);
7132 if(uData && !IsUserSuspended(uData))
7134 /* Ops and above were handled by the above case. */
7135 if(IsUserAutoOp(uData))
7137 if(uData->access >= cData->lvlOpts[lvlGiveOps])
7138 modes |= MODE_CHANOP;
7139 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
7140 modes |= MODE_VOICE;
7142 if(uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
7143 cData->visited = now;
7144 if(cData->user_greeting)
7145 greeting = cData->user_greeting;
7147 && (uData->access >= cData->lvlOpts[lvlUserInfo])
7148 && ((now - uData->seen) >= chanserv_conf.info_delay)
7156 /* If user joining normally (not during burst), apply op or voice,
7157 * and send greeting/userinfo as appropriate.
7159 if(!user->uplink->burst)
7163 if(modes & MODE_CHANOP)
7164 modes &= ~MODE_VOICE;
7165 change.args[0].mode = modes;
7166 change.args[0].u.member = mNode;
7167 mod_chanmode_announce(chanserv, channel, &change);
7170 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
7171 if(uData && info && (modes || !(channel->modes & MODE_DELAYJOINS)))
7172 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
7178 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
7180 struct mod_chanmode change;
7181 struct userData *channel;
7182 unsigned int ii, jj;
7184 if(!user->handle_info)
7187 mod_chanmode_init(&change);
7189 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
7191 struct chanNode *cn;
7192 struct modeNode *mn;
7193 if(IsUserSuspended(channel)
7194 || IsSuspended(channel->channel)
7195 || !(cn = channel->channel->channel))
7198 mn = GetUserMode(cn, user);
7201 if(!IsUserSuspended(channel)
7202 && IsUserAutoInvite(channel)
7203 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
7205 && !user->uplink->burst)
7206 irc_invite(chanserv, user, cn);
7210 if(channel->access >= UL_PRESENT && !HANDLE_FLAGGED(channel->handle, BOT))
7211 channel->channel->visited = now;
7213 if(IsUserAutoOp(channel))
7215 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
7216 change.args[0].mode = MODE_CHANOP;
7217 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
7218 change.args[0].mode = MODE_VOICE;
7220 change.args[0].mode = 0;
7221 change.args[0].u.member = mn;
7222 if(change.args[0].mode)
7223 mod_chanmode_announce(chanserv, cn, &change);
7226 channel->seen = now;
7227 channel->present = 1;
7230 for(ii = 0; ii < user->channels.used; ++ii)
7232 struct chanNode *chan = user->channels.list[ii]->channel;
7233 struct banData *ban;
7235 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
7236 || !chan->channel_info
7237 || IsSuspended(chan->channel_info))
7239 for(jj = 0; jj < chan->banlist.used; ++jj)
7240 if(user_matches_glob(user, chan->banlist.list[jj]->ban, MATCH_USENICK))
7242 if(jj < chan->banlist.used)
7244 for(ban = chan->channel_info->bans; ban; ban = ban->next)
7246 char kick_reason[MAXLEN];
7247 if(!user_matches_glob(user, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
7249 change.args[0].mode = MODE_BAN;
7250 change.args[0].u.hostmask = ban->mask;
7251 mod_chanmode_announce(chanserv, chan, &change);
7252 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
7253 KickChannelUser(user, chan, chanserv, kick_reason);
7254 ban->triggered = now;
7259 if(IsSupportHelper(user))
7261 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7263 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
7265 HANDLE_SET_FLAG(user->handle_info, HELPING);
7273 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
7275 struct chanData *cData;
7276 struct userData *uData;
7278 cData = mn->channel->channel_info;
7279 if(!cData || IsSuspended(cData) || IsLocal(mn->user))
7282 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
7284 /* Allow for a bit of padding so that the limit doesn't
7285 track the user count exactly, which could get annoying. */
7286 if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
7288 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7289 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7293 if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
7295 scan_user_presence(uData, mn->user);
7297 if (uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
7298 cData->visited = now;
7301 if(IsHelping(mn->user) && IsSupportHelper(mn->user))
7304 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii) {
7305 struct chanNode *channel;
7306 struct userNode *exclude;
7307 /* When looking at the channel that is being /part'ed, we
7308 * have to skip over the client that is leaving. For
7309 * other channels, we must not do that.
7311 channel = chanserv_conf.support_channels.list[ii];
7312 exclude = (channel == mn->channel) ? mn->user : NULL;
7313 if(find_handle_in_channel(channel, mn->user->handle_info, exclude))
7316 if(ii == chanserv_conf.support_channels.used)
7317 HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
7322 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
7324 struct userData *uData;
7326 if(!channel->channel_info || !kicker || IsService(kicker)
7327 || (kicker == victim) || IsSuspended(channel->channel_info)
7328 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
7331 if(protect_user(victim, kicker, channel->channel_info))
7333 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED_2");
7334 KickChannelUser(kicker, channel, chanserv, reason);
7337 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
7342 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
7344 struct chanData *cData;
7346 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
7349 cData = channel->channel_info;
7350 if(bad_topic(channel, user, channel->topic))
7352 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
7353 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
7354 SetChannelTopic(channel, chanserv, old_topic, 1);
7355 else if(cData->topic)
7356 SetChannelTopic(channel, chanserv, cData->topic, 1);
7359 /* With topicsnarf, grab the topic and save it as the default topic. */
7360 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
7363 cData->topic = strdup(channel->topic);
7369 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
7371 struct mod_chanmode *bounce = NULL;
7372 unsigned int bnc, ii;
7375 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
7378 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
7379 && mode_lock_violated(&channel->channel_info->modes, change))
7381 char correct[MAXLEN];
7382 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
7383 mod_chanmode_format(&channel->channel_info->modes, correct);
7384 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
7386 for(ii = bnc = 0; ii < change->argc; ++ii)
7388 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
7390 const struct userNode *victim = change->args[ii].u.member->user;
7391 if(!protect_user(victim, user, channel->channel_info))
7394 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7397 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
7398 bounce->args[bnc].u.member = GetUserMode(channel, user);
7399 if(bounce->args[bnc].u.member)
7403 bounce->args[bnc].mode = MODE_CHANOP;
7404 bounce->args[bnc].u.member = change->args[ii].u.member;
7406 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
7408 else if(change->args[ii].mode & MODE_CHANOP)
7410 const struct userNode *victim = change->args[ii].u.member->user;
7411 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
7414 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7415 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
7416 bounce->args[bnc].u.member = change->args[ii].u.member;
7419 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
7421 const char *ban = change->args[ii].u.hostmask;
7422 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
7425 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7426 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
7427 bounce->args[bnc].u.hostmask = strdup(ban);
7429 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
7434 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
7435 mod_chanmode_announce(chanserv, channel, bounce);
7436 for(ii = 0; ii < change->argc; ++ii)
7437 if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
7438 free((char*)bounce->args[ii].u.hostmask);
7439 mod_chanmode_free(bounce);
7444 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
7446 struct chanNode *channel;
7447 struct banData *bData;
7448 struct mod_chanmode change;
7449 unsigned int ii, jj;
7450 char kick_reason[MAXLEN];
7452 mod_chanmode_init(&change);
7454 change.args[0].mode = MODE_BAN;
7455 for(ii = 0; ii < user->channels.used; ++ii)
7457 channel = user->channels.list[ii]->channel;
7458 /* Need not check for bans if they're opped or voiced. */
7459 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
7461 /* Need not check for bans unless channel registration is active. */
7462 if(!channel->channel_info || IsSuspended(channel->channel_info))
7464 /* Look for a matching ban already on the channel. */
7465 for(jj = 0; jj < channel->banlist.used; ++jj)
7466 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
7468 /* Need not act if we found one. */
7469 if(jj < channel->banlist.used)
7471 /* Look for a matching ban in this channel. */
7472 for(bData = channel->channel_info->bans; bData; bData = bData->next)
7474 if(!user_matches_glob(user, bData->mask, MATCH_USENICK | MATCH_VISIBLE))
7476 change.args[0].u.hostmask = bData->mask;
7477 mod_chanmode_announce(chanserv, channel, &change);
7478 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
7479 KickChannelUser(user, channel, chanserv, kick_reason);
7480 bData->triggered = now;
7481 break; /* we don't need to check any more bans in the channel */
7486 static void handle_rename(struct handle_info *handle, const char *old_handle)
7488 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
7492 dict_remove2(handle_dnrs, old_handle, 1);
7493 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
7494 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
7499 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
7501 struct userNode *h_user;
7503 if(handle->channels)
7505 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
7506 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
7508 while(handle->channels)
7509 del_channel_user(handle->channels, 1);
7514 handle_server_link(UNUSED_ARG(struct server *server))
7516 struct chanData *cData;
7518 for(cData = channelList; cData; cData = cData->next)
7520 if(!IsSuspended(cData))
7521 cData->may_opchan = 1;
7522 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
7523 && !cData->channel->join_flooded
7524 && ((cData->channel->limit - cData->channel->members.used)
7525 < chanserv_conf.adjust_threshold))
7527 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7528 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7534 chanserv_conf_read(void)
7538 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
7539 struct mod_chanmode *change;
7540 struct string_list *strlist;
7541 struct chanNode *chan;
7544 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
7546 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
7549 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7550 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7551 chanserv_conf.support_channels.used = 0;
7552 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
7554 for(ii = 0; ii < strlist->used; ++ii)
7556 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7559 chan = AddChannel(strlist->list[ii], now, str2, NULL);
7561 channelList_append(&chanserv_conf.support_channels, chan);
7564 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
7567 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7570 chan = AddChannel(str, now, str2, NULL);
7572 channelList_append(&chanserv_conf.support_channels, chan);
7574 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
7575 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
7576 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
7577 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
7578 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
7579 chanserv_conf.greeting_length = str ? atoi(str) : 200;
7580 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
7581 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
7582 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
7583 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
7584 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
7585 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
7586 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
7587 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
7588 str = database_get_data(conf_node, KEY_DNR_EXPIRE_FREQ, RECDB_QSTRING);
7589 chanserv_conf.dnr_expire_frequency = str ? ParseInterval(str) : 3600;
7590 str = database_get_data(conf_node, KEY_INVITED_INTERVAL, RECDB_QSTRING);
7591 chanserv_conf.invited_timeout = str ? ParseInterval(str) : 600*2;
7592 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
7593 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
7594 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
7595 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
7596 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
7597 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
7598 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
7599 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
7600 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
7602 NickChange(chanserv, str, 0);
7603 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
7604 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
7605 str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
7606 chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
7607 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
7608 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
7609 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
7610 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
7611 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
7612 chanserv_conf.max_owned = str ? atoi(str) : 5;
7613 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
7614 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
7615 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
7616 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
7617 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
7618 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
7619 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
7622 safestrncpy(mode_line, str, sizeof(mode_line));
7623 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
7624 if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE|MCP_NO_APASS, 0))
7625 && (change->argc < 2))
7627 chanserv_conf.default_modes = *change;
7628 mod_chanmode_free(change);
7630 free_string_list(chanserv_conf.set_shows);
7631 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
7633 strlist = string_list_copy(strlist);
7636 static const char *list[] = {
7637 /* free form text */
7638 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
7639 /* options based on user level */
7640 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
7641 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
7642 /* multiple choice options */
7643 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
7644 /* binary options */
7645 "DynLimit", "NoDelete", "expire", "Vote",
7649 strlist = alloc_string_list(ArrayLength(list)-1);
7650 for(ii=0; list[ii]; ii++)
7651 string_list_append(strlist, strdup(list[ii]));
7653 chanserv_conf.set_shows = strlist;
7654 /* We don't look things up now, in case the list refers to options
7655 * defined by modules initialized after this point. Just mark the
7656 * function list as invalid, so it will be initialized.
7658 set_shows_list.used = 0;
7659 free_string_list(chanserv_conf.eightball);
7660 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
7663 strlist = string_list_copy(strlist);
7667 strlist = alloc_string_list(4);
7668 string_list_append(strlist, strdup("Yes."));
7669 string_list_append(strlist, strdup("No."));
7670 string_list_append(strlist, strdup("Maybe so."));
7672 chanserv_conf.eightball = strlist;
7673 free_string_list(chanserv_conf.old_ban_names);
7674 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
7676 strlist = string_list_copy(strlist);
7678 strlist = alloc_string_list(2);
7679 chanserv_conf.old_ban_names = strlist;
7680 str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
7681 off_channel = str ? atoi(str) : 0;
7685 chanserv_note_type_read(const char *key, struct record_data *rd)
7688 struct note_type *ntype;
7691 if(!(obj = GET_RECORD_OBJECT(rd)))
7693 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
7696 if(!(ntype = chanserv_create_note_type(key)))
7698 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
7702 /* Figure out set access */
7703 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
7705 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7706 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
7708 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
7710 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
7711 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
7713 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
7715 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
7719 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
7720 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7721 ntype->set_access.min_opserv = 0;
7724 /* Figure out visibility */
7725 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
7726 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7727 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
7728 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7729 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
7730 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
7731 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
7732 ntype->visible_type = NOTE_VIS_ALL;
7734 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7736 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
7737 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
7741 vote_option_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7743 struct vote_option *vOpt;
7746 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7748 log_module(CS_LOG, LOG_ERROR, "Invalid vote option in %s.", chan->channel->name);
7752 vOpt = calloc(1, sizeof(*vOpt));
7753 vOpt->name = strdup(database_get_data(rd->d.object, KEY_VOTE_OPTION_NAME, RECDB_QSTRING));
7754 str = database_get_data(rd->d.object, KEY_VOTE_OPTION_VOTED, RECDB_QSTRING);
7755 vOpt->voted = str ? atoi(str) : 0;
7756 vOpt->option_id = str ? atoi(key) : 0;
7757 vOpt->option_str = strdup(key);
7758 dict_insert(chan->vote_options,vOpt->option_str,vOpt);
7762 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7764 struct handle_info *handle;
7765 struct userData *uData;
7766 char *seen, *inf, *flags, *voted, *votefor;
7767 unsigned long last_seen;
7768 unsigned short access_level;
7770 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7772 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
7776 access_level = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
7777 if(access_level > UL_OWNER)
7779 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
7783 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
7784 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
7785 last_seen = seen ? strtoul(seen, NULL, 0) : now;
7786 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
7787 voted = database_get_data(rd->d.object, KEY_VOTE_VOTED, RECDB_QSTRING);
7788 votefor = database_get_data(rd->d.object, KEY_VOTE_VOTEDFOR, RECDB_QSTRING);
7789 handle = get_handle_info(key);
7792 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
7796 uData = add_channel_user(chan, handle, access_level, last_seen, inf);
7797 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
7799 uData->voted = voted ? strtoul(voted, NULL, 0) : 0;
7800 uData->votefor = votefor ? strtoul(votefor, NULL, 0) : 0;
7808 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7810 struct banData *bData;
7811 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
7812 unsigned long set_time, triggered_time, expires_time;
7814 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7816 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
7820 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
7821 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
7822 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
7823 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
7824 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
7825 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
7826 if (!reason || !owner)
7829 set_time = set ? strtoul(set, NULL, 0) : now;
7830 triggered_time = triggered ? strtoul(triggered, NULL, 0) : 0;
7832 expires_time = strtoul(s_expires, NULL, 0);
7834 expires_time = set_time + atoi(s_duration);
7838 if(!reason || (expires_time && (expires_time < now)))
7841 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
7844 static struct suspended *
7845 chanserv_read_suspended(dict_t obj)
7847 struct suspended *suspended = calloc(1, sizeof(*suspended));
7851 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
7852 suspended->expires = str ? strtoul(str, NULL, 0) : 0;
7853 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
7854 suspended->revoked = str ? strtoul(str, NULL, 0) : 0;
7855 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
7856 suspended->issued = str ? strtoul(str, NULL, 0) : 0;
7857 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
7858 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
7859 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
7860 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
7865 chanserv_channel_read(const char *key, struct record_data *hir)
7867 struct suspended *suspended;
7868 struct mod_chanmode *modes;
7869 struct chanNode *cNode;
7870 struct chanData *cData;
7871 struct dict *channel, *obj;
7872 char *str, *argv[10];
7876 channel = hir->d.object;
7878 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
7881 cNode = AddChannel(key, now, NULL, NULL);
7884 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
7887 cData = register_channel(cNode, str);
7890 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
7894 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
7896 enum levelOption lvlOpt;
7897 enum charOption chOpt;
7899 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
7900 cData->flags = atoi(str);
7902 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7904 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
7906 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
7907 else if(levelOptions[lvlOpt].old_flag)
7909 if(cData->flags & levelOptions[lvlOpt].old_flag)
7910 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
7912 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
7916 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7918 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
7920 cData->chOpts[chOpt] = str[0];
7923 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
7925 enum levelOption lvlOpt;
7926 enum charOption chOpt;
7929 cData->flags = base64toint(str, 5);
7930 count = strlen(str += 5);
7931 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7934 if(levelOptions[lvlOpt].old_flag)
7936 if(cData->flags & levelOptions[lvlOpt].old_flag)
7937 lvl = levelOptions[lvlOpt].flag_value;
7939 lvl = levelOptions[lvlOpt].default_value;
7941 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
7943 case 'c': lvl = UL_COOWNER; break;
7944 case 'm': lvl = UL_MASTER; break;
7945 case 'n': lvl = UL_OWNER+1; break;
7946 case 'o': lvl = UL_OP; break;
7947 case 'p': lvl = UL_PEON; break;
7948 case 'w': lvl = UL_OWNER; break;
7949 default: lvl = 0; break;
7951 cData->lvlOpts[lvlOpt] = lvl;
7953 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7954 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
7957 if((str = database_get_data(hir->d.object, KEY_EXPIRE, RECDB_QSTRING)))
7959 cData->expiry = atoi(str);
7960 if(cData->expiry > 0) {
7961 if(cData->expiry > now) {
7962 timeq_add(cData->expiry, chanserv_expire_channel, cData);
7964 timeq_add(1, chanserv_expire_channel, cData);
7971 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
7973 suspended = chanserv_read_suspended(obj);
7974 cData->suspended = suspended;
7975 suspended->cData = cData;
7976 /* We could use suspended->expires and suspended->revoked to
7977 * set the CHANNEL_SUSPENDED flag, but we don't. */
7979 else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
7981 suspended = calloc(1, sizeof(*suspended));
7982 suspended->issued = 0;
7983 suspended->revoked = 0;
7984 suspended->suspender = strdup(str);
7985 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
7986 suspended->expires = str ? atoi(str) : 0;
7987 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
7988 suspended->reason = strdup(str ? str : "No reason");
7989 suspended->previous = NULL;
7990 cData->suspended = suspended;
7991 suspended->cData = cData;
7995 cData->flags &= ~CHANNEL_SUSPENDED;
7996 suspended = NULL; /* to squelch a warning */
7999 if(IsSuspended(cData)) {
8000 if(suspended->expires > now)
8001 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
8002 else if(suspended->expires)
8003 cData->flags &= ~CHANNEL_SUSPENDED;
8006 if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
8007 struct mod_chanmode change;
8008 mod_chanmode_init(&change);
8010 change.args[0].mode = MODE_CHANOP;
8011 change.args[0].u.member = AddChannelUser(chanserv, cNode);
8012 mod_chanmode_announce(chanserv, cNode, &change);
8015 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
8016 cData->registered = str ? strtoul(str, NULL, 0) : now;
8017 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
8018 cData->visited = str ? strtoul(str, NULL, 0) : now;
8019 str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
8020 cData->ownerTransfer = str ? strtoul(str, NULL, 0) : 0;
8021 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
8022 cData->max = str ? atoi(str) : 0;
8023 str = database_get_data(channel, KEY_MAX_TIME, RECDB_QSTRING);
8024 cData->max_time = str ? atoi(str) : 0;
8025 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
8026 cData->greeting = str ? strdup(str) : NULL;
8027 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
8028 cData->user_greeting = str ? strdup(str) : NULL;
8029 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
8030 cData->topic_mask = str ? strdup(str) : NULL;
8031 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
8032 cData->topic = str ? strdup(str) : NULL;
8034 str = database_get_data(channel, KEY_VOTE, RECDB_QSTRING);
8036 cData->vote = str ? strdup(str) : NULL;
8037 dict_delete(cData->vote_options);
8038 cData->vote_options = dict_new();
8039 dict_set_free_data(cData->vote_options, free_vote_options);
8040 str = database_get_data(channel, KEY_VOTE_START, RECDB_QSTRING);
8041 cData->vote_start = str ? atoi(str) : 0;
8042 obj = database_get_data(channel, KEY_VOTE_OPTIONS, RECDB_OBJECT);
8043 for(it = dict_first(obj); it; it = iter_next(it)) {
8044 vote_option_read_helper(iter_key(it), iter_data(it), cData);
8048 if(!IsSuspended(cData)
8049 && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
8050 && (argc = split_line(str, 0, ArrayLength(argv), argv))
8051 && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE|MCP_NO_APASS, 0))) {
8052 cData->modes = *modes;
8054 cData->modes.modes_set |= MODE_REGISTERED;
8055 if(cData->modes.argc > 1)
8056 cData->modes.argc = 1;
8057 mod_chanmode_announce(chanserv, cNode, &cData->modes);
8058 mod_chanmode_free(modes);
8061 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
8062 for(it = dict_first(obj); it; it = iter_next(it))
8063 user_read_helper(iter_key(it), iter_data(it), cData);
8065 if(!cData->users && !IsProtected(cData))
8067 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
8068 unregister_channel(cData, "has empty user list.");
8072 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
8073 for(it = dict_first(obj); it; it = iter_next(it))
8074 ban_read_helper(iter_key(it), iter_data(it), cData);
8076 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
8077 for(it = dict_first(obj); it; it = iter_next(it))
8079 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
8080 struct record_data *rd = iter_data(it);
8081 const char *note, *setter;
8083 if(rd->type != RECDB_OBJECT)
8085 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
8089 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
8091 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
8093 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
8097 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
8098 if(!setter) setter = "<unknown>";
8099 chanserv_add_channel_note(cData, ntype, setter, note);
8107 chanserv_dnr_read(const char *key, struct record_data *hir)
8109 const char *setter, *reason, *str;
8110 struct do_not_register *dnr;
8111 unsigned long expiry;
8113 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
8116 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
8119 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
8122 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
8125 str = database_get_data(hir->d.object, KEY_EXPIRES, RECDB_QSTRING);
8126 expiry = str ? strtoul(str, NULL, 0) : 0;
8127 if(expiry && expiry <= now)
8129 dnr = chanserv_add_dnr(key, setter, expiry, reason);
8132 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
8134 dnr->set = atoi(str);
8140 chanserv_saxdb_read(struct dict *database)
8142 struct dict *section;
8145 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
8146 for(it = dict_first(section); it; it = iter_next(it))
8147 chanserv_note_type_read(iter_key(it), iter_data(it));
8149 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
8150 for(it = dict_first(section); it; it = iter_next(it))
8151 chanserv_channel_read(iter_key(it), iter_data(it));
8153 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
8154 for(it = dict_first(section); it; it = iter_next(it))
8155 chanserv_dnr_read(iter_key(it), iter_data(it));
8161 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
8163 int high_present = 0;
8164 saxdb_start_record(ctx, KEY_USERS, 1);
8165 for(; uData; uData = uData->next)
8167 if((uData->access >= UL_PRESENT) && uData->present && !HANDLE_FLAGGED(uData->handle, BOT))
8169 saxdb_start_record(ctx, uData->handle->handle, 0);
8170 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
8171 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
8173 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
8174 if(uData->channel->vote && uData->voted)
8175 saxdb_write_int(ctx, KEY_VOTE_VOTED, uData->voted);
8176 if(uData->channel->vote && uData->votefor)
8177 saxdb_write_int(ctx, KEY_VOTE_VOTEDFOR, uData->votefor);
8179 saxdb_write_string(ctx, KEY_INFO, uData->info);
8180 saxdb_end_record(ctx);
8182 saxdb_end_record(ctx);
8183 return high_present;
8187 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
8191 saxdb_start_record(ctx, KEY_BANS, 1);
8192 for(; bData; bData = bData->next)
8194 saxdb_start_record(ctx, bData->mask, 0);
8195 saxdb_write_int(ctx, KEY_SET, bData->set);
8196 if(bData->triggered)
8197 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
8199 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
8201 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
8203 saxdb_write_string(ctx, KEY_REASON, bData->reason);
8204 saxdb_end_record(ctx);
8206 saxdb_end_record(ctx);
8210 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
8212 saxdb_start_record(ctx, name, 0);
8213 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
8214 saxdb_write_string(ctx, KEY_REASON, susp->reason);
8216 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
8218 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
8220 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
8222 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
8223 saxdb_end_record(ctx);
8227 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
8231 enum levelOption lvlOpt;
8232 enum charOption chOpt;
8235 saxdb_start_record(ctx, channel->channel->name, 1);
8237 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
8238 saxdb_write_int(ctx, KEY_MAX, channel->max);
8239 saxdb_write_int(ctx, KEY_MAX_TIME, channel->max_time);
8241 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
8242 if(channel->registrar)
8243 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
8244 if(channel->greeting)
8245 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
8246 if(channel->user_greeting)
8247 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
8248 if(channel->topic_mask)
8249 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
8250 if(channel->suspended)
8251 chanserv_write_suspended(ctx, "suspended", channel->suspended);
8253 saxdb_write_int(ctx, KEY_EXPIRE, channel->expiry);
8256 saxdb_write_string(ctx, KEY_VOTE, channel->vote);
8257 if(channel->vote_start)
8258 saxdb_write_int(ctx, KEY_VOTE_START, channel->vote_start);
8259 if (dict_size(channel->vote_options)) {
8260 saxdb_start_record(ctx, KEY_VOTE_OPTIONS, 1);
8261 for (it = dict_first(channel->vote_options); it; it = iter_next(it)) {
8262 struct vote_option *vOpt = iter_data(it);
8264 sprintf(str,"%i",vOpt->option_id);
8265 saxdb_start_record(ctx, str, 0);
8267 saxdb_write_int(ctx, KEY_VOTE_OPTION_VOTED, vOpt->voted);
8269 saxdb_write_string(ctx, KEY_VOTE_OPTION_NAME, vOpt->name);
8270 saxdb_end_record(ctx);
8272 saxdb_end_record(ctx);
8276 saxdb_start_record(ctx, KEY_OPTIONS, 0);
8277 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
8278 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8279 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
8280 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8282 buf[0] = channel->chOpts[chOpt];
8284 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
8286 saxdb_end_record(ctx);
8288 if(channel->modes.modes_set || channel->modes.modes_clear)
8290 mod_chanmode_format(&channel->modes, buf);
8291 saxdb_write_string(ctx, KEY_MODES, buf);
8294 high_present = chanserv_write_users(ctx, channel->users);
8295 chanserv_write_bans(ctx, channel->bans);
8297 if(dict_size(channel->notes))
8301 saxdb_start_record(ctx, KEY_NOTES, 1);
8302 for(it = dict_first(channel->notes); it; it = iter_next(it))
8304 struct note *note = iter_data(it);
8305 saxdb_start_record(ctx, iter_key(it), 0);
8306 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
8307 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
8308 saxdb_end_record(ctx);
8310 saxdb_end_record(ctx);
8313 if(channel->ownerTransfer)
8314 saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
8315 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
8316 saxdb_end_record(ctx);
8320 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
8324 saxdb_start_record(ctx, ntype->name, 0);
8325 switch(ntype->set_access_type)
8327 case NOTE_SET_CHANNEL_ACCESS:
8328 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
8330 case NOTE_SET_CHANNEL_SETTER:
8331 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
8333 case NOTE_SET_PRIVILEGED: default:
8334 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
8337 switch(ntype->visible_type)
8339 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
8340 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
8341 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
8343 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
8344 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
8345 saxdb_end_record(ctx);
8349 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
8351 struct do_not_register *dnr;
8352 dict_iterator_t it, next;
8354 for(it = dict_first(dnrs); it; it = next)
8356 next = iter_next(it);
8357 dnr = iter_data(it);
8358 if(dnr->expires && dnr->expires <= now)
8360 dict_remove(dnrs, iter_key(it));
8363 saxdb_start_record(ctx, dnr->chan_name, 0);
8365 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
8367 saxdb_write_int(ctx, KEY_EXPIRES, dnr->expires);
8368 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
8369 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
8370 saxdb_end_record(ctx);
8375 chanserv_saxdb_write(struct saxdb_context *ctx)
8378 struct chanData *channel;
8381 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
8382 for(it = dict_first(note_types); it; it = iter_next(it))
8383 chanserv_write_note_type(ctx, iter_data(it));
8384 saxdb_end_record(ctx);
8387 saxdb_start_record(ctx, KEY_DNR, 1);
8388 write_dnrs_helper(ctx, handle_dnrs);
8389 write_dnrs_helper(ctx, plain_dnrs);
8390 write_dnrs_helper(ctx, mask_dnrs);
8391 saxdb_end_record(ctx);
8394 saxdb_start_record(ctx, KEY_CHANNELS, 1);
8395 for(channel = channelList; channel; channel = channel->next)
8396 chanserv_write_channel(ctx, channel);
8397 saxdb_end_record(ctx);
8403 chanserv_db_cleanup(void) {
8405 unreg_part_func(handle_part);
8407 unregister_channel(channelList, "terminating.");
8408 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
8409 UnlockChannel(chanserv_conf.support_channels.list[ii]);
8410 free(chanserv_conf.support_channels.list);
8411 dict_delete(handle_dnrs);
8412 dict_delete(plain_dnrs);
8413 dict_delete(mask_dnrs);
8414 dict_delete(note_types);
8415 free_string_list(chanserv_conf.eightball);
8416 free_string_list(chanserv_conf.old_ban_names);
8417 free_string_list(chanserv_conf.set_shows);
8418 free(set_shows_list.list);
8419 free(uset_shows_list.list);
8422 struct userData *helper = helperList;
8423 helperList = helperList->next;
8428 #if defined(GCC_VARMACROS)
8429 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ARGS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ARGS)
8430 #elif defined(C99_VARMACROS)
8431 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, __VA_ARGS__)
8433 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
8434 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
8437 init_chanserv(const char *nick)
8439 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
8440 conf_register_reload(chanserv_conf_read);
8444 reg_server_link_func(handle_server_link);
8445 reg_new_channel_func(handle_new_channel);
8446 reg_join_func(handle_join);
8447 reg_part_func(handle_part);
8448 reg_kick_func(handle_kick);
8449 reg_topic_func(handle_topic);
8450 reg_mode_change_func(handle_mode);
8451 reg_nick_change_func(handle_nick_change);
8452 reg_auth_func(handle_auth);
8455 reg_handle_rename_func(handle_rename);
8456 reg_unreg_func(handle_unreg);
8458 handle_dnrs = dict_new();
8459 dict_set_free_data(handle_dnrs, free);
8460 plain_dnrs = dict_new();
8461 dict_set_free_data(plain_dnrs, free);
8462 mask_dnrs = dict_new();
8463 dict_set_free_data(mask_dnrs, free);
8465 reg_svccmd_unbind_func(handle_svccmd_unbind);
8466 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
8467 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
8468 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
8469 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
8470 DEFINE_COMMAND(dnrsearch, 3, 0, "template", "noregister", NULL);
8471 modcmd_register(chanserv_module, "dnrsearch print", NULL, 0, 0, NULL);
8472 modcmd_register(chanserv_module, "dnrsearch remove", NULL, 0, 0, NULL);
8473 modcmd_register(chanserv_module, "dnrsearch count", NULL, 0, 0, NULL);
8474 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
8475 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
8476 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
8477 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
8478 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
8480 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
8481 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
8483 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8484 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8485 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8486 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8487 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
8489 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
8490 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
8491 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
8492 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8493 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8495 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8496 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
8497 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8498 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
8500 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
8501 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
8502 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8503 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8504 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
8505 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8506 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8507 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8509 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8510 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8511 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8512 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
8513 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
8514 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
8515 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
8516 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
8517 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8518 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
8519 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
8520 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
8521 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8522 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8524 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
8525 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8526 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8527 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8528 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
8530 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
8531 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
8533 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
8534 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8535 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8536 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8537 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8538 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8539 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8540 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8541 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8542 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8543 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8545 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
8546 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
8548 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
8549 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
8550 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
8551 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
8553 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
8554 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
8555 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
8556 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
8557 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
8559 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8560 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8561 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8562 DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8563 DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8564 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8566 DEFINE_COMMAND(addvote, 1, MODCMD_REQUIRE_AUTHED, NULL);
8567 DEFINE_COMMAND(delvote, 1, MODCMD_REQUIRE_AUTHED, NULL);
8568 DEFINE_COMMAND(addoption, 1, MODCMD_REQUIRE_AUTHED, NULL);
8569 DEFINE_COMMAND(deloption, 1, MODCMD_REQUIRE_AUTHED, NULL);
8570 DEFINE_COMMAND(vote, 1, MODCMD_REQUIRE_AUTHED, NULL);
8571 DEFINE_COMMAND(startvote, 1, MODCMD_REQUIRE_AUTHED, NULL);
8572 DEFINE_COMMAND(endvote, 1, MODCMD_REQUIRE_AUTHED, NULL);
8573 DEFINE_COMMAND(voteresults, 1, MODCMD_REQUIRE_AUTHED, NULL);
8575 /* Channel options */
8576 DEFINE_CHANNEL_OPTION(defaulttopic);
8577 DEFINE_CHANNEL_OPTION(topicmask);
8578 DEFINE_CHANNEL_OPTION(greeting);
8579 DEFINE_CHANNEL_OPTION(usergreeting);
8580 DEFINE_CHANNEL_OPTION(modes);
8581 DEFINE_CHANNEL_OPTION(enfops);
8582 DEFINE_CHANNEL_OPTION(giveops);
8583 DEFINE_CHANNEL_OPTION(protect);
8584 DEFINE_CHANNEL_OPTION(enfmodes);
8585 DEFINE_CHANNEL_OPTION(enftopic);
8586 DEFINE_CHANNEL_OPTION(pubcmd);
8587 DEFINE_CHANNEL_OPTION(givevoice);
8588 DEFINE_CHANNEL_OPTION(userinfo);
8589 DEFINE_CHANNEL_OPTION(dynlimit);
8590 DEFINE_CHANNEL_OPTION(topicsnarf);
8591 DEFINE_CHANNEL_OPTION(vote);
8592 DEFINE_CHANNEL_OPTION(nodelete);
8593 DEFINE_CHANNEL_OPTION(toys);
8594 DEFINE_CHANNEL_OPTION(setters);
8595 DEFINE_CHANNEL_OPTION(topicrefresh);
8596 DEFINE_CHANNEL_OPTION(ctcpusers);
8597 DEFINE_CHANNEL_OPTION(ctcpreaction);
8598 DEFINE_CHANNEL_OPTION(inviteme);
8599 DEFINE_CHANNEL_OPTION(unreviewed);
8600 modcmd_register(chanserv_module, "set expire", chan_opt_expire, 1, 0, "flags", "+helping", NULL);
8601 modcmd_register(chanserv_module, "set unreviewed on", NULL, 0, 0, "flags", "+helping", NULL);
8602 modcmd_register(chanserv_module, "set unreviewed off", NULL, 0, 0, "flags", "+oper", NULL);
8604 DEFINE_CHANNEL_OPTION(offchannel);
8605 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
8607 /* Alias set topic to set defaulttopic for compatibility. */
8608 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
8611 DEFINE_USER_OPTION(noautoop);
8612 DEFINE_USER_OPTION(autoinvite);
8613 DEFINE_USER_OPTION(info);
8615 /* Alias uset autovoice to uset autoop. */
8616 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
8618 note_types = dict_new();
8619 dict_set_free_data(note_types, chanserv_deref_note_type);
8622 const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
8623 chanserv = AddLocalUser(nick, nick, NULL, "Channel Services", modes);
8624 service_register(chanserv)->trigger = '!';
8625 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
8627 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
8629 if(chanserv_conf.channel_expire_frequency)
8630 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
8632 if(chanserv_conf.dnr_expire_frequency)
8633 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
8635 if(chanserv_conf.refresh_period)
8637 unsigned long next_refresh;
8638 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
8639 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
8642 reg_exit_func(chanserv_db_cleanup);
8643 message_register_table(msgtab);