1 /* chanserv.c - Channel service bot
2 * Copyright 2000-2007 srvx Development Team
4 * This file is part of srvx.
6 * srvx is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with srvx; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
25 #include "opserv.h" /* for opserv_bad_channel() */
26 #include "nickserv.h" /* for oper_outranks() */
31 #define CHANSERV_CONF_NAME "services/chanserv"
33 /* ChanServ options */
34 #define KEY_SUPPORT_CHANNEL "support_channel"
35 #define KEY_SUPPORT_CHANNEL_MODES "support_channel_modes"
36 #define KEY_DB_BACKUP_FREQ "db_backup_freq"
37 #define KEY_INFO_DELAY "info_delay"
38 #define KEY_MAX_GREETLEN "max_greetlen"
39 #define KEY_ADJUST_THRESHOLD "adjust_threshold"
40 #define KEY_ADJUST_DELAY "adjust_delay"
41 #define KEY_CHAN_EXPIRE_FREQ "chan_expire_freq"
42 #define KEY_CHAN_EXPIRE_DELAY "chan_expire_delay"
43 #define KEY_DNR_EXPIRE_FREQ "dnr_expire_freq"
44 #define KEY_MAX_CHAN_USERS "max_chan_users"
45 #define KEY_MAX_CHAN_BANS "max_chan_bans"
46 #define KEY_NICK "nick"
47 #define KEY_OLD_CHANSERV_NAME "old_chanserv_name"
48 #define KEY_8BALL_RESPONSES "8ball"
49 #define KEY_OLD_BAN_NAMES "old_ban_names"
50 #define KEY_REFRESH_PERIOD "refresh_period"
51 #define KEY_CTCP_SHORT_BAN_DURATION "ctcp_short_ban_duration"
52 #define KEY_CTCP_LONG_BAN_DURATION "ctcp_long_ban_duration"
53 #define KEY_MAX_OWNED "max_owned"
54 #define KEY_IRC_OPERATOR_EPITHET "irc_operator_epithet"
55 #define KEY_NETWORK_HELPER_EPITHET "network_helper_epithet"
56 #define KEY_SUPPORT_HELPER_EPITHET "support_helper_epithet"
57 #define KEY_NODELETE_LEVEL "nodelete_level"
58 #define KEY_MAX_USERINFO_LENGTH "max_userinfo_length"
59 #define KEY_GIVEOWNERSHIP_PERIOD "giveownership_timeout"
60 #define KEY_INVITED_INTERVAL "invite_timeout"
62 /* ChanServ database */
63 #define KEY_CHANNELS "channels"
64 #define KEY_NOTE_TYPES "note_types"
66 /* Note type parameters */
67 #define KEY_NOTE_OPSERV_ACCESS "opserv_access"
68 #define KEY_NOTE_CHANNEL_ACCESS "channel_access"
69 #define KEY_NOTE_SETTER_ACCESS "setter_access"
70 #define KEY_NOTE_VISIBILITY "visibility"
71 #define KEY_NOTE_VIS_PRIVILEGED "privileged"
72 #define KEY_NOTE_VIS_CHANNEL_USERS "channel_users"
73 #define KEY_NOTE_VIS_ALL "all"
74 #define KEY_NOTE_MAX_LENGTH "max_length"
75 #define KEY_NOTE_SETTER "setter"
76 #define KEY_NOTE_NOTE "note"
78 /* Do-not-register channels */
80 #define KEY_DNR_SET "set"
81 #define KEY_DNR_SETTER "setter"
82 #define KEY_DNR_REASON "reason"
85 #define KEY_REGISTERED "registered"
86 #define KEY_REGISTRAR "registrar"
87 #define KEY_SUSPENDED "suspended"
88 #define KEY_PREVIOUS "previous"
89 #define KEY_SUSPENDER "suspender"
90 #define KEY_ISSUED "issued"
91 #define KEY_REVOKED "revoked"
92 #define KEY_SUSPEND_EXPIRES "suspend_expires"
93 #define KEY_SUSPEND_REASON "suspend_reason"
94 #define KEY_VISITED "visited"
95 #define KEY_TOPIC "topic"
96 #define KEY_GREETING "greeting"
97 #define KEY_USER_GREETING "user_greeting"
98 #define KEY_MODES "modes"
99 #define KEY_FLAGS "flags"
100 #define KEY_OPTIONS "options"
101 #define KEY_USERS "users"
102 #define KEY_BANS "bans"
103 #define KEY_MAX "max"
104 #define KEY_MAX_TIME "max_time"
105 #define KEY_NOTES "notes"
106 #define KEY_TOPIC_MASK "topic_mask"
107 #define KEY_OWNER_TRANSFER "owner_transfer"
108 #define KEY_EXPIRE "expire"
111 #define KEY_LEVEL "level"
112 #define KEY_INFO "info"
113 #define KEY_SEEN "seen"
116 #define KEY_VOTE "vote"
117 #define KEY_VOTE_START "votestart"
118 #define KEY_VOTE_OPTIONS "voptions"
119 #define KEY_VOTE_OPTION_NAME "voptionname"
120 #define KEY_VOTE_VOTED "vvoted"
121 #define KEY_VOTE_VOTEDFOR "vvotefor"
122 #define KEY_VOTE_OPTION_ID "voptionid"
123 #define KEY_VOTE_OPTION_VOTED "voptionvoted"
126 #define KEY_OWNER "owner"
127 #define KEY_REASON "reason"
128 #define KEY_SET "set"
129 #define KEY_DURATION "duration"
130 #define KEY_EXPIRES "expires"
131 #define KEY_TRIGGERED "triggered"
133 #define CHANNEL_DEFAULT_FLAGS (CHANNEL_OFFCHANNEL | CHANNEL_UNREVIEWED)
134 #define CHANNEL_PRESERVED_FLAGS (CHANNEL_UNREVIEWED)
135 #define CHANNEL_DEFAULT_OPTIONS "lmoooanpcnat"
137 /* Administrative messages */
138 static const struct message_entry msgtab[] = {
139 { "CSMSG_CHANNELS_EXPIRED", "%i channels expired." },
141 /* Channel registration */
142 { "CSMSG_REG_SUCCESS", "You now have ownership of $b%s$b." },
143 { "CSMSG_PROXY_SUCCESS", "%s now has ownership of $b%s$b." },
144 { "CSMSG_ALREADY_REGGED", "$b%s$b is registered to someone else." },
145 { "CSMSG_MUST_BE_OPPED", "You must be a channel operator in $b%s$b to register it." },
146 { "CSMSG_PROXY_FORBIDDEN", "You may not register a channel for someone else." },
147 { "CSMSG_OWN_TOO_MANY", "%s already owns enough channels (at least %d); use FORCE to override." },
149 /* Do-not-register channels */
150 { "CSMSG_NOT_DNR", "$b%s$b is not a valid channel name or *account." },
151 { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
152 { "CSMSG_DNR_INFO", "$b%s$b is do-not-register (by $b%s$b): %s" },
153 { "CSMSG_DNR_INFO_SET", "$b%s$b is do-not-register (set %s by $b%s$b): %s" },
154 { "CSMSG_DNR_INFO_SET_EXPIRES", "$b%s$b is do-not-register (set %s by $b%s$b; expires %s): %s" },
155 { "CSMSG_MORE_DNRS", "%d more do-not-register entries skipped." },
156 { "CSMSG_DNR_CHANNEL", "Only network staff may register $b%s$b." },
157 { "CSMSG_DNR_CHANNEL_MOVE", "Only network staff may move $b%s$b." },
158 { "CSMSG_DNR_ACCOUNT", "Only network staff may register channels to $b%s$b." },
159 { "CSMSG_NOREGISTER_CHANNEL", "$b%s$b has been added to the do-not-register list." },
160 { "CSMSG_NO_SUCH_DNR", "$b%s$b is not in the do-not-register list." },
161 { "CSMSG_DNR_REMOVED", "$b%s$b has been removed from the do-not-register list." },
162 { "CSMSG_DNR_BAD_ACTION", "$b%s$b is not a recognized do-not-register action." },
163 { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
165 /* Channel unregistration */
166 { "CSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
167 { "CSMSG_UNREG_NODELETE", "$b%s$b is protected from unregistration." },
168 { "CSMSG_CHAN_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended (%s)." },
169 { "CSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must use 'unregister %s'." },
172 { "CSMSG_MOVE_SUCCESS", "Channel registration has been moved to $b%s$b." },
173 { "CSMSG_MOVE_NODELETE", "$b%s$b is protected from unregistration, and cannot be moved." },
175 /* Channel merging */
176 { "CSMSG_MERGE_SUCCESS", "Channel successfully merged into $b%s$b." },
177 { "CSMSG_MERGE_SELF", "Merging cannot be performed if the source and target channels are the same." },
178 { "CSMSG_MERGE_NODELETE", "You may not merge a channel that is marked NoDelete." },
179 { "CSMSG_MERGE_SUSPENDED", "Merging cannot be performed if the source or target channel is suspended." },
180 { "CSMSG_MERGE_NOT_OWNER", "You must be the owner of the target channel (or a helper) to merge into the channel." },
182 /* Handle unregistration */
183 { "CSMSG_HANDLE_UNREGISTERED", "As a result of your account unregistration, you have been deleted from all of your channels' userlists." },
186 { "CSMSG_NOT_USER", "You lack access to $b%s$b." },
187 { "CSMSG_NO_CHAN_USER", "%s lacks access to $b%s$b." },
188 { "CSMSG_NO_ACCESS", "You lack sufficient access to use this command." },
189 { "CSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$C$b." },
190 { "CSMSG_MAXIMUM_BANS", "This channel has reached the ban count limit of $b%d$b." },
191 { "CSMSG_MAXIMUM_USERS", "This channel has reached the user count limit of $b%d$b." },
192 { "CSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel, and cannot be registered." },
193 { "CSMSG_GODMODE_UP", "You may not use $b%s$b to op yourself unless you are on the user list. Use the $bop$b command instead." },
194 { "CSMSG_ALREADY_OPPED", "You are already opped in $b%s$b." },
195 { "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." },
196 { "CSMSG_ALREADY_DOWN", "You are not opped or voiced in $b%s$b." },
197 { "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." },
198 { "CSMSG_OUT_OF_CHANNEL", "For some reason I don't seem to be in $b%s$b." },
199 { "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." },
201 /* Removing yourself from a channel. */
202 { "CSMSG_NO_OWNER_DELETEME", "You cannot delete your owner access in $b%s$b." },
203 { "CSMSG_CONFIRM_DELETEME", "To really remove yourself, you must use 'deleteme %s'." },
204 { "CSMSG_DELETED_YOU", "Your $b%d$b access has been deleted from $b%s$b." },
206 /* User management */
207 { "CSMSG_ADDED_USER", "Added %s to the %s user list with access %d." },
208 { "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." },
209 { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be greater than maximum (%d)." },
210 { "CSMSG_DELETED_USERS", "Deleted accounts matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list." },
211 { "CSMSG_TRIMMED_USERS", "Trimmed $b%d users$b with access from %d to %d from the %s user list who were inactive for at least %s." },
212 { "CSMSG_INCORRECT_ACCESS", "%s has access $b%d$b, not %s." },
213 { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with access %d)." },
214 { "CSMSG_CANNOT_TRIM", "You must include a minimum inactivity duration of at least 60 seconds to trim." },
216 { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." },
217 { "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." },
218 { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
219 { "CSMSG_TRANSFER_WAIT", "You must wait %s before you can give ownership of $b%s$b to someone else." },
220 { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
221 { "CSMSG_CONFIRM_GIVEOWNERSHIP", "To really give ownership to $b%1$s$b, you must use 'giveownership *%1$s %2$s'." },
222 { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
225 { "CSMSG_BAN_ADDED", "Permanently banned $b%s$b from %s." },
226 { "CSMSG_TIMED_BAN_ADDED", "Banned $b%s$b from %s for %s." },
227 { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
228 { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
229 { "CSMSG_REASON_CHANGE", "Reason for ban $b%s$b changed." },
230 { "CSMSG_BAN_EXTENDED", "Extended ban for $b%s$b expires in %s." },
231 { "CSMSG_BAN_REMOVED", "Matching ban(s) for $b%s$b removed." },
232 { "CSMSG_TRIMMED_BANS", "Trimmed $b%d bans$b from the %s ban list that were inactive for at least %s." },
233 { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
234 { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
235 { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
236 { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
237 { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
238 { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
239 { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban found for $b%s$b." },
240 { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
242 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
244 /* Channel management */
245 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
246 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
247 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
249 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
250 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
251 { "CSMSG_TOPICMASK_CONFLICT1", "I do not know how to make that topic work with the current topic mask in $b%s$b, which is: %s" },
252 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic is at most %d characters and matches the topic mask pattern." },
253 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
254 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
255 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
257 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
258 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
259 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
260 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
261 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
262 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
263 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you must use 'set defaults %s'." },
264 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
265 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
266 { "CSMSG_BAD_GIVEVOICE", "You cannot change GiveVoice to above GiveOps (%d)." },
267 { "CSMSG_BAD_GIVEOPS", "You cannot change GiveOps to below GiveVoice (%d)." },
268 { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
269 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
270 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
271 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
272 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
273 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
274 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
275 { "CSMSG_SET_MODES", "$bModes $b %s" },
276 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
277 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s" },
278 { "CSMSG_SET_OFFCHANNEL", "$bOffChannel $b %s" },
279 { "CSMSG_SET_USERINFO", "$bUserInfo $b %d" },
280 { "CSMSG_SET_GIVE_VOICE", "$bGiveVoice $b %d" },
281 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" },
282 { "CSMSG_SET_VOTE", "$bVote $b %d" },
283 { "CSMSG_SET_INVITEME", "$bInviteMe $b %d" },
284 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d" },
285 { "CSMSG_SET_GIVE_OPS", "$bGiveOps $b %d" },
286 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d" },
287 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d" },
288 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d" },
289 { "CSMSG_SET_SETTERS", "$bSetters $b %d" },
290 { "CSMSG_SET_CTCPUSERS", "$bCTCPUsers $b %d" },
291 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
292 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
293 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
294 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
295 { "CSMSG_SET_UNREVIEWED", "$bUnreviewed $b %s" },
296 { "CSMSG_SET_EXPIRE", "$bExpire $b %s" },
297 { "CSMSG_SET_EXPIRE_OFF", "$bExpire $b off" },
298 { "CSMSG_USET_NOAUTOOP", "$bNoAutoOp $b %s" },
299 { "CSMSG_USET_NOAUTOVOICE", "$bNoAutoVoice $b %s" },
300 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
301 { "CSMSG_USET_INFO", "$bInfo $b %s" },
303 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
304 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
305 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
306 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
307 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
308 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
309 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
310 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
311 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
312 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
313 { "CSMSG_PROTECT_NONE", "No users will be protected." },
314 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
315 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
316 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
317 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
318 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
319 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
320 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
321 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
322 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
323 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
324 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
325 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
327 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
328 { "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" },
329 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s." },
330 { "CSMSG_ALREADY_PRESENT", "%s is already in $b%s$b." },
331 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
332 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s for $S to invite you." },
333 { "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
334 { "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
336 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
337 { "CSMSG_NO_BANS", "No channel bans found on $b%s$b." },
338 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
340 /* Channel userlist */
341 { "CSMSG_ACCESS_ALL_HEADER", "%s users from level %d to %d:" },
342 { "CSMSG_ACCESS_SEARCH_HEADER", "%s users from level %d to %d matching %s:" },
343 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
344 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%d$b in %s." },
346 /* Channel note list */
347 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
348 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
349 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
350 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
351 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
352 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
353 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
354 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
355 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
356 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
357 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
358 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
359 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
360 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
361 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
363 /* Channel [un]suspension */
364 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
365 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
366 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
367 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
368 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
369 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
370 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
372 /* Access information */
373 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
374 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
375 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
376 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
377 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
378 { "CSMSG_USER_HAS_ACCESS", "%s has access $b%d$b in %s." },
379 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
380 { "CSMSG_HELPER_HAS_ACCESS", "%s has access $b%d$b in %s and has $bsecurity override$b enabled." },
381 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
382 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
383 { "CSMSG_OPERATOR_TITLE", "IRC operator" },
384 { "CSMSG_UC_H_TITLE", "network helper" },
385 { "CSMSG_LC_H_TITLE", "support helper" },
386 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
387 { "CSMSG_MYACCESS_COUNT", "%s has access in $b%d$b channels." },
388 { "CSMSG_MYACCESS_COUNT_1", "%s has access in $b%d$b channel." },
390 /* Seen information */
391 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
392 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
393 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
394 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
396 /* Names information */
397 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
398 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
400 /* Channel information */
401 { "CSMSG_CHANNEL_INFO", "$b%s$b Information:" },
402 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
403 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
404 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
405 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
406 { "CSMSG_CHANNEL_MAX_TIME", "$bRecord Visitors: $b%i (%s ago.)" },
407 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
408 { "CSMSG_CHANNEL_BANS", "$bBan Count: $b%i" },
409 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
410 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
411 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
412 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
413 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
414 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
415 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
416 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
417 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
418 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
419 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
420 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
421 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
422 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
424 { "CSMSG_PEEK_INFO", "$b%s$b Status:" },
425 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
426 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
427 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d (%d ops, %d voices, %d regulars)" },
428 { "CSMSG_PEEK_OPS", "$bOps:$b" },
429 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
431 /* Network information */
432 { "CSMSG_NETWORK_INFO", "Network Information:" },
433 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
434 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
435 { "CSMSG_NETWORK_BANS", "$bTotal Ban Count: $b%i" },
436 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
437 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
438 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
439 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
440 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
443 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
444 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
445 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
447 /* Channel searches */
448 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
449 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
450 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
451 { "CSMSG_CHANNEL_SEARCH_RESULTS", "The following channels were found:" },
453 /* Channel configuration */
454 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
455 { "CSMSG_INVALID_CFLAG", "$b%s$b is not a recognized channel flag." },
456 { "CSMSG_CHANNEL_OPTIONS", "Channel Options:" },
457 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
460 { "CSMSG_USER_OPTIONS", "User Options:" },
461 { "CSMSG_USER_PROTECTED_2", "That user is protected." },
464 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
465 { "CSMSG_PING_RESPONSE", "Pong!" },
466 { "CSMSG_WUT_RESPONSE", "wut" },
467 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
468 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
469 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
470 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
471 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
472 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
473 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
476 { "CSMSG_ADDVOTE_DONE", "Vote added. Use $baddoption$b to add at least 2 vote options and then $bstartvote$b to start the voting." },
477 { "CSMSG_ADDVOTE_FULL", "There is already a vote in this channel. Use $bdelvote$b to delete it." },
478 { "CSMSG_DELVOTE_DONE", "Vote deleted." },
479 { "CSMSG_NO_VOTE", "There is no vote in this channel." },
480 { "CSMSG_ADDOPTION_DONE", "Vote option added with id %i (%i - %i)." },
481 { "CSMSG_DELOPTION_DONE", "Vote option deleted." },
482 { "CSMSG_DELOPTION_NONE", "Vote option does not exist." },
483 { "CSMSG_VOTE_NEED_OPTIONS", "There must be at least 2 options in a vote." },
484 { "CSMSG_VOTE_NOT_STARTED", "The vote is not started. Use $bstartvote$b to allow voting." },
485 { "CSMSG_VOTE_QUESTION", "Question: %s" },
486 { "CSMSG_VOTE_OPTION", "$b%i$b: %s ($b%i$b votes)" },
487 { "CSMSG_VOTERES_QUESTION", "Question: %s" },
488 { "CSMSG_VOTERES_OPTION", "
\ 2%i
\ 2: %s (
\ 2%i
\ 2 votes)" },
489 { "CSMSG_STARTVOTE_TOP", "
\ 2%s
\ 2 has started a voting:" },
490 { "CSMSG_STARTVOTE_QUESTION", "
\ 2Question:
\ 2 %s" },
491 { "CSMSG_STARTVOTE_OPTION", "
\ 2%i:
\ 2 %s" },
492 { "CSMSG_STARTVOTE_ACCESS", "All channel users with at least
\ 2%i
\ 2 access can vote." },
493 { "CSMSG_STARTVOTE_HOWTO", "To vote for an option, use
\ 2vote ID
\ 2. To see the available options and the current votes, use
\ 2vote
\ 2 without parameters." },
494 { "CSMSG_STARTVOTE_RUNNING", "The vote is already running." },
495 { "CSMSG_VOTE_VOTED", "You have already voted." },
496 { "CSMSG_VOTE_INVALID", "Vote option does not exist." },
497 { "CSMSG_VOTE_DONE", "You voted for $b%s$b." },
498 { "CSMSG_ENDVOTE_DONE", "The vote has been finished." },
499 { "CSMSG_ENDVOTE_STOPPED", "The vote is not running." },
502 { "CSMSG_EVENT_SEARCH_RESULTS", "The following channel events were found:" },
506 /* eject_user and unban_user flags */
507 #define ACTION_KICK 0x0001
508 #define ACTION_BAN 0x0002
509 #define ACTION_ADD_BAN 0x0004
510 #define ACTION_ADD_TIMED_BAN 0x0008
511 #define ACTION_UNBAN 0x0010
512 #define ACTION_DEL_BAN 0x0020
514 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
515 #define MODELEN 40 + KEYLEN
519 #define CSFUNC_ARGS user, channel, argc, argv, cmd
521 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
522 #define CHANSERV_SYNTAX() svccmd_send_help(user, chanserv, cmd)
523 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
524 reply("MSG_MISSING_PARAMS", argv[0]); \
528 DECLARE_LIST(dnrList, struct do_not_register *);
529 DEFINE_LIST(dnrList, struct do_not_register *)
531 static int eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action);
533 struct userNode *chanserv;
536 static dict_t plain_dnrs, mask_dnrs, handle_dnrs;
537 static struct log_type *CS_LOG;
541 struct channelList support_channels;
542 struct mod_chanmode default_modes;
544 unsigned long db_backup_frequency;
545 unsigned long channel_expire_frequency;
546 unsigned long dnr_expire_frequency;
548 unsigned long invited_timeout;
550 unsigned long info_delay;
551 unsigned long adjust_delay;
552 unsigned long channel_expire_delay;
553 unsigned int nodelete_level;
555 unsigned int adjust_threshold;
556 int join_flood_threshold;
558 unsigned int greeting_length;
559 unsigned int refresh_period;
560 unsigned int giveownership_period;
562 unsigned int max_owned;
563 unsigned int max_chan_users;
564 unsigned int max_chan_bans;
565 unsigned int max_userinfo_length;
567 struct string_list *set_shows;
568 struct string_list *eightball;
569 struct string_list *old_ban_names;
571 const char *ctcp_short_ban_duration;
572 const char *ctcp_long_ban_duration;
574 const char *irc_operator_epithet;
575 const char *network_helper_epithet;
576 const char *support_helper_epithet;
581 struct userNode *user;
582 struct userNode *bot;
583 struct chanNode *channel;
585 unsigned short lowest;
586 unsigned short highest;
587 struct userData **users;
588 struct helpfile_table table;
593 struct userNode *user;
594 struct chanNode *chan;
597 enum note_access_type
599 NOTE_SET_CHANNEL_ACCESS,
600 NOTE_SET_CHANNEL_SETTER,
604 enum note_visible_type
607 NOTE_VIS_CHANNEL_USERS,
613 enum note_access_type set_access_type;
615 unsigned int min_opserv;
616 unsigned short min_ulevel;
618 enum note_visible_type visible_type;
619 unsigned int max_length;
626 struct note_type *type;
627 char setter[NICKSERV_HANDLE_LEN+1];
631 static unsigned int registered_channels;
632 static unsigned int banCount;
634 static const struct {
637 unsigned short level;
640 { "peon", "Peon", UL_PEON, '+' },
641 { "op", "Op", UL_OP, '@' },
642 { "master", "Master", UL_MASTER, '%' },
643 { "coowner", "Coowner", UL_COOWNER, '*' },
644 { "owner", "Owner", UL_OWNER, '!' },
645 { "helper", "BUG:", UL_HELPER, 'X' }
648 static const struct {
651 unsigned short default_value;
652 unsigned int old_idx;
653 unsigned int old_flag;
654 unsigned short flag_value;
656 { "CSMSG_SET_GIVE_VOICE", "givevoice", 100, ~0, CHANNEL_VOICE_ALL, 0 },
657 { "CSMSG_SET_GIVE_OPS", "giveops", 200, 2, 0, 0 },
658 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
659 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
660 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
661 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
662 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
663 { "CSMSG_SET_CTCPUSERS", "ctcpusers", 0, 9, 0, 0 },
664 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES, 1 },
665 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE, 200 },
666 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF, 1 },
667 { "CSMSG_SET_VOTE", "vote", 100, ~0, 0, 0 }
670 struct charOptionValues {
673 } protectValues[] = {
674 { 'a', "CSMSG_PROTECT_ALL" },
675 { 'e', "CSMSG_PROTECT_EQUAL" },
676 { 'l', "CSMSG_PROTECT_LOWER" },
677 { 'n', "CSMSG_PROTECT_NONE" }
679 { 'd', "CSMSG_TOYS_DISABLED" },
680 { 'n', "CSMSG_TOYS_PRIVATE" },
681 { 'p', "CSMSG_TOYS_PUBLIC" }
682 }, topicRefreshValues[] = {
683 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
684 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
685 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
686 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
687 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
688 }, ctcpReactionValues[] = {
689 { 'k', "CSMSG_CTCPREACTION_KICK" },
690 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
691 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
692 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
695 static const struct {
699 unsigned int old_idx;
701 struct charOptionValues *values;
703 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues), protectValues },
704 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues), toysValues },
705 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues), topicRefreshValues },
706 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 't', 10, ArrayLength(ctcpReactionValues), ctcpReactionValues }
709 struct userData *helperList;
710 struct chanData *channelList;
711 static struct module *chanserv_module;
712 static unsigned int userCount;
714 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
715 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
716 static void unregister_channel(struct chanData *channel, const char *reason);
719 user_level_from_name(const char *name, unsigned short clamp_level)
721 unsigned int level = 0, ii;
723 level = strtoul(name, NULL, 10);
724 else for(ii = 0; (ii < ArrayLength(accessLevels)) && !level; ++ii)
725 if(!irccasecmp(name, accessLevels[ii].name))
726 level = accessLevels[ii].level;
727 if(level > clamp_level)
733 parse_level_range(unsigned short *minl, unsigned short *maxl, const char *arg)
736 *minl = strtoul(arg, &sep, 10);
744 *maxl = strtoul(sep+1, &sep, 10);
752 _GetChannelUser(struct chanData *channel, struct handle_info *handle, int override, int allow_suspended)
754 struct userData *uData, **head;
756 if(!channel || !handle)
759 if(override && HANDLE_FLAGGED(handle, HELPING)
760 && ((handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel)))
762 for(uData = helperList;
763 uData && uData->handle != handle;
764 uData = uData->next);
768 uData = calloc(1, sizeof(struct userData));
769 uData->handle = handle;
771 uData->access = UL_HELPER;
777 uData->next = helperList;
779 helperList->prev = uData;
787 for(uData = channel->users; uData; uData = uData->next)
788 if((uData->handle == handle) && (allow_suspended || !IsUserSuspended(uData)))
791 head = &(channel->users);
794 if(uData && (uData != *head))
796 /* Shuffle the user to the head of whatever list he was in. */
798 uData->next->prev = uData->prev;
800 uData->prev->next = uData->next;
806 (**head).prev = uData;
813 /* Returns non-zero if user has at least the minimum access.
814 * exempt_owner is set when handling !set, so the owner can set things
817 int check_user_level(struct chanNode *channel, struct userNode *user, enum levelOption opt, int allow_override, int exempt_owner)
819 struct userData *uData;
820 struct chanData *cData = channel->channel_info;
821 unsigned short minimum = cData->lvlOpts[opt];
824 uData = _GetChannelUser(cData, user->handle_info, allow_override, 0);
827 if(minimum <= uData->access)
829 if((minimum > UL_OWNER) && (uData->access == UL_OWNER) && exempt_owner)
834 /* Scan for other users authenticated to the same handle
835 still in the channel. If so, keep them listed as present.
837 user is optional, if not null, it skips checking that userNode
838 (for the handle_part function) */
840 scan_user_presence(struct userData *uData, struct userNode *user)
844 if(IsSuspended(uData->channel)
845 || IsUserSuspended(uData)
846 || !(mn = find_handle_in_channel(uData->channel->channel, uData->handle, user)))
858 chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, const char *text, UNUSED_ARG(struct userNode *bot), UNUSED_ARG(unsigned int is_notice))
860 unsigned int eflags, argc;
862 static char *bad_ctcp_reason = "CTCPs to this channel are forbidden.";
864 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
865 if(!channel->channel_info
866 || IsSuspended(channel->channel_info)
868 || !ircncasecmp(text, "ACTION ", 7))
870 /* Figure out the minimum level needed to CTCP the channel */
871 if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
873 /* We need to enforce against them; do so. */
875 argv[0] = (char*)text;
876 argv[1] = user->nick;
878 if(GetUserMode(channel, user))
879 eflags |= ACTION_KICK;
880 switch(channel->channel_info->chOpts[chCTCPReaction]) {
881 default: case 'k': /* just do the kick */ break;
883 eflags |= ACTION_BAN;
886 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
887 argv[argc++] = (char*)chanserv_conf.ctcp_short_ban_duration;
890 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
891 argv[argc++] = (char*)chanserv_conf.ctcp_long_ban_duration;
894 argv[argc++] = bad_ctcp_reason;
895 eject_user(chanserv, channel, argc, argv, NULL, eflags);
899 chanserv_create_note_type(const char *name)
901 struct note_type *ntype = calloc(1, sizeof(*ntype) + strlen(name));
902 strcpy(ntype->name, name);
904 dict_insert(note_types, ntype->name, ntype);
909 free_vote_options(void *data)
911 struct vote_option *vOpt = data;
913 free(vOpt->option_str);
918 chanserv_deref_note_type(void *data)
920 struct note_type *ntype = data;
922 if(--ntype->refs > 0)
928 chanserv_flush_note_type(struct note_type *ntype)
930 struct chanData *cData;
931 for(cData = channelList; cData; cData = cData->next)
932 dict_remove(cData->notes, ntype->name);
936 chanserv_truncate_notes(struct note_type *ntype)
938 struct chanData *cData;
940 unsigned int size = sizeof(*note) + ntype->max_length;
942 for(cData = channelList; cData; cData = cData->next) {
943 note = dict_find(cData->notes, ntype->name, NULL);
946 if(strlen(note->note) <= ntype->max_length)
948 dict_remove2(cData->notes, ntype->name, 1);
949 note = realloc(note, size);
950 note->note[ntype->max_length] = 0;
951 dict_insert(cData->notes, ntype->name, note);
955 static int note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user);
958 chanserv_add_channel_note(struct chanData *channel, struct note_type *type, const char *setter, const char *text)
961 unsigned int len = strlen(text);
963 if(len > type->max_length) len = type->max_length;
964 note = calloc(1, sizeof(*note) + len);
966 strncpy(note->setter, setter, sizeof(note->setter)-1);
967 memcpy(note->note, text, len);
969 dict_insert(channel->notes, type->name, note);
975 chanserv_free_note(void *data)
977 struct note *note = data;
979 chanserv_deref_note_type(note->type);
980 assert(note->type->refs > 0); /* must use delnote to remove the type */
984 static MODCMD_FUNC(cmd_createnote) {
985 struct note_type *ntype;
986 unsigned int arg = 1, existed = 0, max_length;
988 if((ntype = dict_find(note_types, argv[1], NULL)))
991 ntype = chanserv_create_note_type(argv[arg]);
992 if(!irccasecmp(argv[++arg], "privileged"))
995 ntype->set_access_type = NOTE_SET_PRIVILEGED;
996 ntype->set_access.min_opserv = strtoul(argv[arg], NULL, 0);
998 else if(!irccasecmp(argv[arg], "channel"))
1000 unsigned short ulvl = user_level_from_name(argv[++arg], UL_OWNER);
1003 reply("CSMSG_INVALID_ACCESS", argv[arg]);
1006 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
1007 ntype->set_access.min_ulevel = ulvl;
1009 else if(!irccasecmp(argv[arg], "setter"))
1011 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
1015 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
1019 if(!irccasecmp(argv[++arg], "privileged"))
1020 ntype->visible_type = NOTE_VIS_PRIVILEGED;
1021 else if(!irccasecmp(argv[arg], "channel_users"))
1022 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
1023 else if(!irccasecmp(argv[arg], "all"))
1024 ntype->visible_type = NOTE_VIS_ALL;
1026 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
1030 if((arg+1) >= argc) {
1031 reply("MSG_MISSING_PARAMS", argv[0]);
1034 max_length = strtoul(argv[++arg], NULL, 0);
1035 if(max_length < 20 || max_length > 450)
1037 reply("CSMSG_BAD_MAX_LENGTH", argv[arg]);
1040 if(existed && (max_length < ntype->max_length))
1042 ntype->max_length = max_length;
1043 chanserv_truncate_notes(ntype);
1045 ntype->max_length = max_length;
1048 reply("CSMSG_NOTE_MODIFIED", ntype->name);
1050 reply("CSMSG_NOTE_CREATED", ntype->name);
1055 dict_remove(note_types, ntype->name);
1059 static MODCMD_FUNC(cmd_removenote) {
1060 struct note_type *ntype;
1063 ntype = dict_find(note_types, argv[1], NULL);
1064 force = (argc > 2) && !irccasecmp(argv[2], "force");
1067 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
1074 reply("CSMSG_NOTE_TYPE_USED", ntype->name);
1077 chanserv_flush_note_type(ntype);
1079 dict_remove(note_types, argv[1]);
1080 reply("CSMSG_NOTE_DELETED", argv[1]);
1085 chanserv_expire_channel(void *data)
1087 struct chanData *channel = data;
1088 char reason[MAXLEN];
1089 sprintf(reason, "channel expired.");
1090 channel->expiry = 0;
1091 spamserv_cs_unregister(NULL, channel->channel, expire, NULL);
1092 unregister_channel(channel, reason);
1095 static MODCMD_FUNC(chan_opt_expire)
1097 struct chanData *cData = channel->channel_info;
1098 unsigned long value = cData->expiry;
1102 if((!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
1104 reply("MSG_SETTING_PRIVILEGED", argv[0]);
1107 unsigned long expiry,duration;
1109 /* The two directions can have different ACLs. */
1110 if(!strcmp(argv[1], "0"))
1112 else if((duration = ParseInterval(argv[1])))
1113 expiry = now + duration;
1116 reply("MSG_INVALID_DURATION", argv[1]);
1120 if (expiry != value)
1124 timeq_del(value, chanserv_expire_channel, cData, 0);
1127 cData->expiry = value;
1130 timeq_add(expiry, chanserv_expire_channel, cData);
1135 if(cData->expiry > now) {
1136 char expirestr[INTERVALLEN];
1137 reply("CSMSG_SET_EXPIRE", intervalString(expirestr, cData->expiry - now, user->handle_info));
1139 reply("CSMSG_SET_EXPIRE_OFF");
1144 mode_lock_violated(const struct mod_chanmode *orig, const struct mod_chanmode *change)
1148 if(orig->modes_set & change->modes_clear)
1150 if(orig->modes_clear & change->modes_set)
1152 if((orig->modes_set & MODE_KEY) && (change->modes_set & MODE_KEY)
1153 && strcmp(orig->new_key, change->new_key))
1155 if((orig->modes_set & MODE_LIMIT) && (change->modes_set & MODE_LIMIT)
1156 && (orig->new_limit != change->new_limit))
1161 static char max_length_text[MAXLEN+1][16];
1163 static struct helpfile_expansion
1164 chanserv_expand_variable(const char *variable)
1166 struct helpfile_expansion exp;
1168 if(!irccasecmp(variable, "notes"))
1171 exp.type = HF_TABLE;
1172 exp.value.table.length = 1;
1173 exp.value.table.width = 3;
1174 exp.value.table.flags = 0;
1175 exp.value.table.contents = calloc(dict_size(note_types)+1, sizeof(char**));
1176 exp.value.table.contents[0] = calloc(exp.value.table.width, sizeof(char*));
1177 exp.value.table.contents[0][0] = "Note Type";
1178 exp.value.table.contents[0][1] = "Visibility";
1179 exp.value.table.contents[0][2] = "Max Length";
1180 for(it=dict_first(note_types); it; it=iter_next(it))
1182 struct note_type *ntype = iter_data(it);
1185 if(!note_type_visible_to_user(NULL, ntype, message_dest)) continue;
1186 row = exp.value.table.length++;
1187 exp.value.table.contents[row] = calloc(exp.value.table.width, sizeof(char*));
1188 exp.value.table.contents[row][0] = ntype->name;
1189 exp.value.table.contents[row][1] = (ntype->visible_type == NOTE_VIS_ALL) ? "all" :
1190 (ntype->visible_type == NOTE_VIS_CHANNEL_USERS) ? "chan users" :
1192 if(!max_length_text[ntype->max_length][0])
1193 snprintf(max_length_text[ntype->max_length], sizeof(max_length_text[ntype->max_length]), "%u", ntype->max_length);
1194 exp.value.table.contents[row][2] = max_length_text[ntype->max_length];
1199 exp.type = HF_STRING;
1200 exp.value.str = NULL;
1204 static struct chanData*
1205 register_channel(struct chanNode *cNode, char *registrar)
1207 struct chanData *channel;
1208 enum levelOption lvlOpt;
1209 enum charOption chOpt;
1211 channel = calloc(1, sizeof(struct chanData));
1213 channel->notes = dict_new();
1214 dict_set_free_data(channel->notes, chanserv_free_note);
1216 channel->registrar = strdup(registrar);
1217 channel->registered = now;
1218 channel->visited = now;
1219 channel->limitAdjusted = now;
1220 channel->ownerTransfer = now;
1221 channel->flags = CHANNEL_DEFAULT_FLAGS;
1222 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
1223 channel->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
1224 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
1225 channel->chOpts[chOpt] = charOptions[chOpt].default_value;
1227 channel->prev = NULL;
1228 channel->next = channelList;
1231 channelList->prev = channel;
1232 channelList = channel;
1233 registered_channels++;
1235 channel->channel = cNode;
1237 cNode->channel_info = channel;
1239 channel->vote = NULL;
1244 static struct userData*
1245 add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned short access_level, unsigned long seen, const char *info)
1247 struct userData *ud;
1249 if(access_level > UL_OWNER)
1252 ud = calloc(1, sizeof(*ud));
1253 ud->channel = channel;
1254 ud->handle = handle;
1256 ud->access = access_level;
1257 ud->info = info ? strdup(info) : NULL;
1260 ud->next = channel->users;
1262 channel->users->prev = ud;
1263 channel->users = ud;
1265 channel->userCount++;
1269 ud->u_next = ud->handle->channels;
1271 ud->u_next->u_prev = ud;
1272 ud->handle->channels = ud;
1278 del_channel_user(struct userData *user, int do_gc)
1280 struct chanData *channel = user->channel;
1282 channel->userCount--;
1286 user->prev->next = user->next;
1288 channel->users = user->next;
1290 user->next->prev = user->prev;
1293 user->u_prev->u_next = user->u_next;
1295 user->handle->channels = user->u_next;
1297 user->u_next->u_prev = user->u_prev;
1301 if(do_gc && !channel->users && !IsProtected(channel)) {
1302 spamserv_cs_unregister(NULL, channel->channel, lost_all_users, NULL);
1303 unregister_channel(channel, "lost all users.");
1307 static void expire_ban(void *data);
1310 add_channel_ban(struct chanData *channel, const char *mask, char *owner, unsigned long set, unsigned long triggered, unsigned long expires, char *reason)
1313 unsigned int ii, l1, l2;
1318 bd = malloc(sizeof(struct banData));
1320 bd->channel = channel;
1322 bd->triggered = triggered;
1323 bd->expires = expires;
1325 for(ii = 0; ii < chanserv_conf.old_ban_names->used; ++ii)
1327 extern const char *hidden_host_suffix;
1328 const char *old_name = chanserv_conf.old_ban_names->list[ii];
1332 l2 = strlen(old_name);
1335 if(irccasecmp(mask + l1 - l2, old_name))
1337 new_mask = alloca(MAXLEN);
1338 sprintf(new_mask, "%.*s%s", (int)(l1-l2), mask, hidden_host_suffix);
1341 safestrncpy(bd->mask, mask, sizeof(bd->mask));
1343 safestrncpy(bd->owner, owner, sizeof(bd->owner));
1344 bd->reason = strdup(reason);
1347 timeq_add(expires, expire_ban, bd);
1350 bd->next = channel->bans;
1352 channel->bans->prev = bd;
1354 channel->banCount++;
1361 del_channel_ban(struct banData *ban)
1363 ban->channel->banCount--;
1367 ban->prev->next = ban->next;
1369 ban->channel->bans = ban->next;
1372 ban->next->prev = ban->prev;
1375 timeq_del(0, expire_ban, ban, TIMEQ_IGNORE_WHEN);
1384 expire_ban(void *data)
1386 struct banData *bd = data;
1387 if(!IsSuspended(bd->channel))
1389 struct banList bans;
1390 struct mod_chanmode change;
1392 bans = bd->channel->channel->banlist;
1393 mod_chanmode_init(&change);
1394 for(ii=0; ii<bans.used; ii++)
1396 if(!strcmp(bans.list[ii]->ban, bd->mask))
1399 change.args[0].mode = MODE_REMOVE|MODE_BAN;
1400 change.args[0].u.hostmask = bd->mask;
1401 mod_chanmode_announce(chanserv, bd->channel->channel, &change);
1407 del_channel_ban(bd);
1410 static void chanserv_expire_suspension(void *data);
1413 unregister_channel(struct chanData *channel, const char *reason)
1415 struct mod_chanmode change;
1416 char msgbuf[MAXLEN];
1418 /* After channel unregistration, the following must be cleaned
1420 - Channel information.
1423 - Channel suspension data.
1424 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1430 timeq_del(0, NULL, channel, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
1434 mod_chanmode_init(&change);
1435 change.modes_clear |= MODE_REGISTERED;
1436 mod_chanmode_announce(chanserv, channel->channel, &change);
1439 while(channel->users)
1440 del_channel_user(channel->users, 0);
1442 while(channel->bans)
1443 del_channel_ban(channel->bans);
1445 free(channel->topic);
1446 free(channel->registrar);
1447 free(channel->greeting);
1448 free(channel->user_greeting);
1449 free(channel->topic_mask);
1452 channel->prev->next = channel->next;
1454 channelList = channel->next;
1457 channel->next->prev = channel->prev;
1459 if(channel->suspended)
1461 struct chanNode *cNode = channel->channel;
1462 struct suspended *suspended, *next_suspended;
1464 for(suspended = channel->suspended; suspended; suspended = next_suspended)
1466 next_suspended = suspended->previous;
1467 free(suspended->suspender);
1468 free(suspended->reason);
1469 if(suspended->expires)
1470 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
1475 cNode->channel_info = NULL;
1478 timeq_del(channel->expiry, chanserv_expire_channel, channel, 0);
1479 channel->channel->channel_info = NULL;
1481 dict_delete(channel->notes);
1482 sprintf(msgbuf, "%s %s", channel->channel->name, reason);
1483 if(!IsSuspended(channel))
1484 DelChannelUser(chanserv, channel->channel, msgbuf, 0);
1485 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, msgbuf);
1486 UnlockChannel(channel->channel);
1488 registered_channels--;
1492 expire_channels(UNUSED_ARG(void *data))
1494 struct chanData *channel, *next;
1495 struct userData *user;
1496 char delay[INTERVALLEN], reason[INTERVALLEN + 64];
1498 intervalString(delay, chanserv_conf.channel_expire_delay, NULL);
1499 sprintf(reason, "Channel registration automatically expired after %s of disuse.", delay);
1501 for(channel = channelList; channel; channel = next)
1503 next = channel->next;
1505 /* See if the channel can be expired. */
1506 if(((now - channel->visited) <= chanserv_conf.channel_expire_delay)
1507 || IsProtected(channel))
1510 /* Make sure there are no high-ranking users still in the channel. */
1511 for(user=channel->users; user; user=user->next)
1512 if(user->present && (user->access >= UL_PRESENT) && !HANDLE_FLAGGED(user->handle, BOT))
1517 /* Unregister the channel */
1518 log_module(CS_LOG, LOG_INFO, "(%s) Channel registration expired.", channel->channel->name);
1519 spamserv_cs_unregister(NULL, channel->channel, expire, NULL);
1520 unregister_channel(channel, "registration expired.");
1523 if(chanserv_conf.channel_expire_frequency)
1524 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
1528 expire_dnrs(UNUSED_ARG(void *data))
1530 dict_iterator_t it, next;
1531 struct do_not_register *dnr;
1533 for(it = dict_first(handle_dnrs); it; it = next)
1535 dnr = iter_data(it);
1536 next = iter_next(it);
1537 if(dnr->expires && dnr->expires <= now)
1538 dict_remove(handle_dnrs, dnr->chan_name + 1);
1540 for(it = dict_first(plain_dnrs); it; it = next)
1542 dnr = iter_data(it);
1543 next = iter_next(it);
1544 if(dnr->expires && dnr->expires <= now)
1545 dict_remove(plain_dnrs, dnr->chan_name + 1);
1547 for(it = dict_first(mask_dnrs); it; it = next)
1549 dnr = iter_data(it);
1550 next = iter_next(it);
1551 if(dnr->expires && dnr->expires <= now)
1552 dict_remove(mask_dnrs, dnr->chan_name + 1);
1555 if(chanserv_conf.dnr_expire_frequency)
1556 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
1560 protect_user(const struct userNode *victim, const struct userNode *aggressor, struct chanData *channel)
1562 char protect = channel->chOpts[chProtect];
1563 struct userData *cs_victim, *cs_aggressor;
1565 /* Don't protect if no one is to be protected, someone is attacking
1566 himself, or if the aggressor is an IRC Operator. */
1567 if(protect == 'n' || victim == aggressor || IsOper(aggressor))
1570 /* Don't protect if the victim isn't authenticated (because they
1571 can't be a channel user), unless we are to protect non-users
1573 cs_victim = GetChannelAccess(channel, victim->handle_info);
1574 if(protect != 'a' && !cs_victim)
1577 /* Protect if the aggressor isn't a user because at this point,
1578 the aggressor can only be less than or equal to the victim. */
1579 cs_aggressor = GetChannelAccess(channel, aggressor->handle_info);
1583 /* If the aggressor was a user, then the victim can't be helped. */
1590 if(cs_victim->access > cs_aggressor->access)
1595 if(cs_victim->access >= cs_aggressor->access)
1604 validate_op(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1606 struct chanData *cData = channel->channel_info;
1607 struct userData *cs_victim;
1609 if((!(cs_victim = GetChannelUser(cData, victim->handle_info))
1610 || (cs_victim->access < cData->lvlOpts[lvlGiveOps]))
1611 && !check_user_level(channel, user, lvlEnfOps, 0, 0))
1613 send_message(user, chanserv, "CSMSG_OPBY_LOCKED");
1621 validate_deop(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1623 if(IsService(victim))
1625 send_message(user, chanserv, "MSG_SERVICE_IMMUNE", victim->nick);
1629 if(protect_user(victim, user, channel->channel_info))
1631 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
1638 static struct do_not_register *
1639 chanserv_add_dnr(const char *chan_name, const char *setter, unsigned long expires, const char *reason)
1641 struct do_not_register *dnr = calloc(1, sizeof(*dnr)+strlen(reason));
1642 safestrncpy(dnr->chan_name, chan_name, sizeof(dnr->chan_name));
1643 safestrncpy(dnr->setter, setter, sizeof(dnr->setter));
1644 strcpy(dnr->reason, reason);
1646 dnr->expires = expires;
1647 if(dnr->chan_name[0] == '*')
1648 dict_insert(handle_dnrs, dnr->chan_name+1, dnr);
1649 else if(strpbrk(dnr->chan_name, "*?"))
1650 dict_insert(mask_dnrs, dnr->chan_name, dnr);
1652 dict_insert(plain_dnrs, dnr->chan_name, dnr);
1656 static struct dnrList
1657 chanserv_find_dnrs(const char *chan_name, const char *handle, unsigned int max)
1659 struct dnrList list;
1660 dict_iterator_t it, next;
1661 struct do_not_register *dnr;
1663 dnrList_init(&list);
1665 if(handle && (dnr = dict_find(handle_dnrs, handle, NULL)))
1667 if(dnr->expires && dnr->expires <= now)
1668 dict_remove(handle_dnrs, handle);
1669 else if(list.used < max)
1670 dnrList_append(&list, dnr);
1673 if(chan_name && (dnr = dict_find(plain_dnrs, chan_name, NULL)))
1675 if(dnr->expires && dnr->expires <= now)
1676 dict_remove(plain_dnrs, chan_name);
1677 else if(list.used < max)
1678 dnrList_append(&list, dnr);
1683 for(it = dict_first(mask_dnrs); it && list.used < max; it = next)
1685 next = iter_next(it);
1686 if(!match_ircglob(chan_name, iter_key(it)))
1688 dnr = iter_data(it);
1689 if(dnr->expires && dnr->expires <= now)
1690 dict_remove(mask_dnrs, iter_key(it));
1692 dnrList_append(&list, dnr);
1699 static int dnr_print_func(struct do_not_register *dnr, void *extra)
1701 struct userNode *user;
1702 char buf1[INTERVALLEN];
1703 char buf2[INTERVALLEN];
1710 strftime(buf1, sizeof(buf1), "%d %b %Y", localtime(&feh));
1715 strftime(buf2, sizeof(buf2), "%d %b %Y", localtime(&feh));
1716 send_message(user, chanserv, "CSMSG_DNR_INFO_SET_EXPIRES", dnr->chan_name, buf1, dnr->setter, buf2, dnr->reason);
1720 send_message(user, chanserv, "CSMSG_DNR_INFO_SET", dnr->chan_name, buf1, dnr->setter, dnr->reason);
1723 send_message(user, chanserv, "CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1728 chanserv_show_dnrs(struct userNode *user, struct svccmd *cmd, const char *chan_name, const char *handle)
1730 struct dnrList list;
1733 list = chanserv_find_dnrs(chan_name, handle, UINT_MAX);
1734 for(ii = 0; (ii < list.used) && (ii < 10); ++ii)
1735 dnr_print_func(list.list[ii], user);
1737 reply("CSMSG_MORE_DNRS", list.used - ii);
1742 struct do_not_register *
1743 chanserv_is_dnr(const char *chan_name, struct handle_info *handle)
1745 struct dnrList list;
1746 struct do_not_register *dnr;
1748 list = chanserv_find_dnrs(chan_name, handle ? handle->handle : NULL, 1);
1749 dnr = list.used ? list.list[0] : NULL;
1754 static unsigned int send_dnrs(struct userNode *user, dict_t dict)
1756 struct do_not_register *dnr;
1757 dict_iterator_t it, next;
1758 unsigned int matches = 0;
1760 for(it = dict_first(dict); it; it = next)
1762 dnr = iter_data(it);
1763 next = iter_next(it);
1764 if(dnr->expires && dnr->expires <= now)
1766 dict_remove(dict, iter_key(it));
1769 dnr_print_func(dnr, user);
1776 static CHANSERV_FUNC(cmd_noregister)
1780 unsigned long expiry, duration;
1781 unsigned int matches;
1785 reply("CSMSG_DNR_SEARCH_RESULTS");
1786 matches = send_dnrs(user, handle_dnrs);
1787 matches += send_dnrs(user, plain_dnrs);
1788 matches += send_dnrs(user, mask_dnrs);
1790 reply("MSG_MATCH_COUNT", matches);
1792 reply("MSG_NO_MATCHES");
1798 if(!IsChannelName(target) && (*target != '*'))
1800 reply("CSMSG_NOT_DNR", target);
1808 reply("MSG_INVALID_DURATION", argv[2]);
1812 if(!strcmp(argv[2], "0"))
1814 else if((duration = ParseInterval(argv[2])))
1815 expiry = now + duration;
1818 reply("MSG_INVALID_DURATION", argv[2]);
1822 reason = unsplit_string(argv + 3, argc - 3, NULL);
1823 if((*target == '*') && !get_handle_info(target + 1))
1825 reply("MSG_HANDLE_UNKNOWN", target + 1);
1828 chanserv_add_dnr(target, user->handle_info->handle, expiry, reason);
1829 reply("CSMSG_NOREGISTER_CHANNEL", target);
1833 reply("CSMSG_DNR_SEARCH_RESULTS");
1835 matches = chanserv_show_dnrs(user, cmd, NULL, target + 1);
1837 matches = chanserv_show_dnrs(user, cmd, target, NULL);
1839 reply("MSG_NO_MATCHES");
1843 static CHANSERV_FUNC(cmd_allowregister)
1845 const char *chan_name = argv[1];
1847 if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
1848 || dict_remove(plain_dnrs, chan_name)
1849 || dict_remove(mask_dnrs, chan_name))
1851 reply("CSMSG_DNR_REMOVED", chan_name);
1854 reply("CSMSG_NO_SUCH_DNR", chan_name);
1859 struct userNode *source;
1863 unsigned long min_set, max_set;
1864 unsigned long min_expires, max_expires;
1869 dnr_search_matches(const struct do_not_register *dnr, const struct dnr_search *search)
1871 return !((dnr->set < search->min_set)
1872 || (dnr->set > search->max_set)
1873 || (dnr->expires < search->min_expires)
1874 || (search->max_expires
1875 && ((dnr->expires == 0)
1876 || (dnr->expires > search->max_expires)))
1877 || (search->chan_mask
1878 && !match_ircglob(dnr->chan_name, search->chan_mask))
1879 || (search->setter_mask
1880 && !match_ircglob(dnr->setter, search->setter_mask))
1881 || (search->reason_mask
1882 && !match_ircglob(dnr->reason, search->reason_mask)));
1885 static struct dnr_search *
1886 dnr_search_create(struct userNode *user, struct svccmd *cmd, unsigned int argc, char *argv[])
1888 struct dnr_search *discrim;
1891 discrim = calloc(1, sizeof(*discrim));
1892 discrim->source = user;
1893 discrim->chan_mask = NULL;
1894 discrim->setter_mask = NULL;
1895 discrim->reason_mask = NULL;
1896 discrim->max_set = INT_MAX;
1897 discrim->limit = 50;
1899 for(ii=0; ii<argc; ++ii)
1903 reply("MSG_MISSING_PARAMS", argv[ii]);
1906 else if(0 == irccasecmp(argv[ii], "channel"))
1908 discrim->chan_mask = argv[++ii];
1910 else if(0 == irccasecmp(argv[ii], "setter"))
1912 discrim->setter_mask = argv[++ii];
1914 else if(0 == irccasecmp(argv[ii], "reason"))
1916 discrim->reason_mask = argv[++ii];
1918 else if(0 == irccasecmp(argv[ii], "limit"))
1920 discrim->limit = strtoul(argv[++ii], NULL, 0);
1922 else if(0 == irccasecmp(argv[ii], "set"))
1924 const char *cmp = argv[++ii];
1927 discrim->min_set = now - ParseInterval(cmp + 2);
1929 discrim->min_set = now - (ParseInterval(cmp + 1) - 1);
1930 } else if(cmp[0] == '=') {
1931 discrim->min_set = discrim->max_set = now - ParseInterval(cmp + 1);
1932 } else if(cmp[0] == '>') {
1934 discrim->max_set = now - ParseInterval(cmp + 2);
1936 discrim->max_set = now - (ParseInterval(cmp + 1) - 1);
1938 discrim->max_set = now - (ParseInterval(cmp) - 1);
1941 else if(0 == irccasecmp(argv[ii], "expires"))
1943 const char *cmp = argv[++ii];
1946 discrim->max_expires = now + ParseInterval(cmp + 2);
1948 discrim->max_expires = now + (ParseInterval(cmp + 1) - 1);
1949 } else if(cmp[0] == '=') {
1950 discrim->min_expires = discrim->max_expires = now + ParseInterval(cmp + 1);
1951 } else if(cmp[0] == '>') {
1953 discrim->min_expires = now + ParseInterval(cmp + 2);
1955 discrim->min_expires = now + (ParseInterval(cmp + 1) - 1);
1957 discrim->min_expires = now + (ParseInterval(cmp) - 1);
1962 reply("MSG_INVALID_CRITERIA", argv[ii]);
1973 typedef int (*dnr_search_func)(struct do_not_register *match, void *extra);
1976 dnr_search(struct dnr_search *discrim, dnr_search_func dsf, void *data)
1978 struct do_not_register *dnr;
1979 dict_iterator_t next;
1984 /* Initialize local variables. */
1987 if(discrim->chan_mask)
1989 int shift = (discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*') ? 2 : 0;
1990 if('\0' == discrim->chan_mask[shift + strcspn(discrim->chan_mask+shift, "*?")])
1994 if(target_fixed && discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*')
1996 /* Check against account-based DNRs. */
1997 dnr = dict_find(handle_dnrs, discrim->chan_mask + 2, NULL);
1998 if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
2001 else if(target_fixed)
2003 /* Check against channel-based DNRs. */
2004 dnr = dict_find(plain_dnrs, discrim->chan_mask, NULL);
2005 if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
2010 /* Exhaustively search account DNRs. */
2011 for(it = dict_first(handle_dnrs); it; it = next)
2013 next = iter_next(it);
2014 dnr = iter_data(it);
2015 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
2019 /* Do the same for channel DNRs. */
2020 for(it = dict_first(plain_dnrs); it; it = next)
2022 next = iter_next(it);
2023 dnr = iter_data(it);
2024 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
2028 /* Do the same for wildcarded channel DNRs. */
2029 for(it = dict_first(mask_dnrs); it; it = next)
2031 next = iter_next(it);
2032 dnr = iter_data(it);
2033 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
2041 dnr_remove_func(struct do_not_register *match, void *extra)
2043 struct userNode *user;
2046 chan_name = alloca(strlen(match->chan_name) + 1);
2047 strcpy(chan_name, match->chan_name);
2049 if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
2050 || dict_remove(plain_dnrs, chan_name)
2051 || dict_remove(mask_dnrs, chan_name))
2053 send_message(user, chanserv, "CSMSG_DNR_REMOVED", chan_name);
2059 dnr_count_func(struct do_not_register *match, void *extra)
2061 return 0; (void)match; (void)extra;
2064 static MODCMD_FUNC(cmd_dnrsearch)
2066 struct dnr_search *discrim;
2067 dnr_search_func action;
2068 struct svccmd *subcmd;
2069 unsigned int matches;
2072 sprintf(buf, "dnrsearch %s", argv[1]);
2073 subcmd = dict_find(cmd->parent->commands, buf, NULL);
2076 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
2079 if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
2081 if(!irccasecmp(argv[1], "print"))
2082 action = dnr_print_func;
2083 else if(!irccasecmp(argv[1], "remove"))
2084 action = dnr_remove_func;
2085 else if(!irccasecmp(argv[1], "count"))
2086 action = dnr_count_func;
2089 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
2093 discrim = dnr_search_create(user, cmd, argc-2, argv+2);
2097 if(action == dnr_print_func)
2098 reply("CSMSG_DNR_SEARCH_RESULTS");
2099 matches = dnr_search(discrim, action, user);
2101 reply("MSG_MATCH_COUNT", matches);
2103 reply("MSG_NO_MATCHES");
2109 chanserv_get_owned_count(struct handle_info *hi)
2111 struct userData *cList;
2114 for(owned=0, cList=hi->channels; cList; cList=cList->u_next)
2115 if(cList->access == UL_OWNER)
2120 static CHANSERV_FUNC(cmd_register)
2122 struct handle_info *handle;
2123 struct chanData *cData;
2124 struct modeNode *mn;
2125 char reason[MAXLEN];
2127 unsigned int new_channel, force=0;
2128 struct do_not_register *dnr;
2132 if(channel->channel_info)
2134 reply("CSMSG_ALREADY_REGGED", channel->name);
2138 if(channel->bad_channel)
2140 reply("CSMSG_ILLEGAL_CHANNEL", channel->name);
2145 && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
2147 reply("CSMSG_MUST_BE_OPPED", channel->name);
2152 chan_name = channel->name;
2156 if((argc < 2) || !IsChannelName(argv[1]))
2158 reply("MSG_NOT_CHANNEL_NAME");
2162 if(opserv_bad_channel(argv[1]))
2164 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2169 chan_name = argv[1];
2172 if(argc >= (new_channel+2))
2174 if(!IsHelping(user))
2176 reply("CSMSG_PROXY_FORBIDDEN");
2180 if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
2182 force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
2183 dnr = chanserv_is_dnr(chan_name, handle);
2187 handle = user->handle_info;
2188 dnr = chanserv_is_dnr(chan_name, handle);
2192 if(!IsHelping(user))
2193 reply("CSMSG_DNR_CHANNEL", chan_name);
2195 chanserv_show_dnrs(user, cmd, chan_name, handle->handle);
2199 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2201 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
2206 channel = AddChannel(argv[1], now, NULL, NULL);
2208 cData = register_channel(channel, user->handle_info->handle);
2209 scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL), NULL);
2210 cData->modes = chanserv_conf.default_modes;
2212 cData->modes.modes_set |= MODE_REGISTERED;
2213 if (IsOffChannel(cData))
2215 mod_chanmode_announce(chanserv, channel, &cData->modes);
2219 struct mod_chanmode *change = mod_chanmode_dup(&cData->modes, 1);
2220 change->args[change->argc].mode = MODE_CHANOP;
2221 change->args[change->argc].u.member = AddChannelUser(chanserv, channel);
2223 mod_chanmode_announce(chanserv, channel, change);
2224 mod_chanmode_free(change);
2227 /* Initialize the channel's max user record. */
2228 cData->max = channel->members.used;
2229 cData->max_time = 0;
2231 if(handle != user->handle_info)
2232 reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
2234 reply("CSMSG_REG_SUCCESS", channel->name);
2236 sprintf(reason, "%s registered to %s by %s.", channel->name, handle->handle, user->handle_info->handle);
2237 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2242 make_confirmation_string(struct userData *uData)
2244 static char strbuf[16];
2249 for(src = uData->handle->handle; *src; )
2250 accum = accum * 31 + toupper(*src++);
2252 for(src = uData->channel->channel->name; *src; )
2253 accum = accum * 31 + toupper(*src++);
2254 sprintf(strbuf, "%08x", accum);
2258 static CHANSERV_FUNC(cmd_unregister)
2261 char reason[MAXLEN];
2262 struct chanData *cData;
2263 struct userData *uData;
2265 cData = channel->channel_info;
2268 reply("CSMSG_NOT_REGISTERED", channel->name);
2272 uData = GetChannelUser(cData, user->handle_info);
2273 if(!uData || (uData->access < UL_OWNER))
2275 reply("CSMSG_NO_ACCESS");
2279 if(IsProtected(cData))
2281 reply("CSMSG_UNREG_NODELETE", channel->name);
2285 if(!IsHelping(user))
2287 const char *confirm_string;
2288 if(IsSuspended(cData))
2290 reply("CSMSG_CHAN_SUSPENDED", channel->name, cData->suspended->reason);
2293 confirm_string = make_confirmation_string(uData);
2294 if((argc < 2) || strcmp(argv[1], confirm_string))
2296 reply("CSMSG_CONFIRM_UNREG", confirm_string);
2301 sprintf(reason, "unregistered by %s.", user->handle_info->handle);
2302 name = strdup(channel->name);
2303 unregister_channel(cData, reason);
2304 spamserv_cs_unregister(user, channel, manually, "unregistered");
2305 reply("CSMSG_UNREG_SUCCESS", name);
2311 ss_cs_join_channel(struct chanNode *channel, int spamserv_join)
2313 extern struct userNode *spamserv;
2314 struct mod_chanmode *change;
2316 if(spamserv && spamserv_join && get_chanInfo(channel->name))
2318 change = mod_chanmode_alloc(2);
2320 change->args[0].mode = MODE_CHANOP;
2321 change->args[0].u.member = AddChannelUser(chanserv, channel);
2322 change->args[1].mode = MODE_CHANOP;
2323 change->args[1].u.member = AddChannelUser(spamserv, channel);
2327 change = mod_chanmode_alloc(1);
2329 change->args[0].mode = MODE_CHANOP;
2330 change->args[0].u.member = AddChannelUser(chanserv, channel);
2333 mod_chanmode_announce(chanserv, channel, change);
2334 mod_chanmode_free(change);
2337 static CHANSERV_FUNC(cmd_move)
2339 struct mod_chanmode change;
2340 struct chanNode *target;
2341 struct modeNode *mn;
2342 struct userData *uData;
2343 char reason[MAXLEN];
2344 struct do_not_register *dnr;
2345 int chanserv_join = 0, spamserv_join;
2349 if(IsProtected(channel->channel_info))
2351 reply("CSMSG_MOVE_NODELETE", channel->name);
2355 if(!IsChannelName(argv[1]))
2357 reply("MSG_NOT_CHANNEL_NAME");
2361 if(opserv_bad_channel(argv[1]))
2363 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2367 if(!IsHelping(user) || (argc < 3) || irccasecmp(argv[2], "force"))
2369 for(uData = channel->channel_info->users; uData; uData = uData->next)
2371 if((uData->access == UL_OWNER) && (dnr = chanserv_is_dnr(argv[1], uData->handle)))
2373 if(!IsHelping(user))
2374 reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]);
2376 chanserv_show_dnrs(user, cmd, argv[1], uData->handle->handle);
2382 mod_chanmode_init(&change);
2383 if(!(target = GetChannel(argv[1])))
2385 target = AddChannel(argv[1], now, NULL, NULL);
2386 if(!IsSuspended(channel->channel_info))
2389 else if(target->channel_info)
2391 reply("CSMSG_ALREADY_REGGED", target->name);
2394 else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
2395 && !IsHelping(user))
2397 reply("CSMSG_MUST_BE_OPPED", target->name);
2400 else if(!IsSuspended(channel->channel_info))
2405 /* Clear MODE_REGISTERED from old channel, add it to new. */
2407 change.modes_clear = MODE_REGISTERED;
2408 mod_chanmode_announce(chanserv, channel, &change);
2409 change.modes_clear = 0;
2410 change.modes_set = MODE_REGISTERED;
2411 mod_chanmode_announce(chanserv, target, &change);
2414 /* Move the channel_info to the target channel; it
2415 shouldn't be necessary to clear timeq callbacks
2416 for the old channel. */
2417 target->channel_info = channel->channel_info;
2418 target->channel_info->channel = target;
2419 channel->channel_info = NULL;
2421 /* Check whether users are present in the new channel. */
2422 for(uData = target->channel_info->users; uData; uData = uData->next)
2423 scan_user_presence(uData, NULL);
2425 spamserv_join = spamserv_cs_move_merge(user, channel, target, 1);
2428 ss_cs_join_channel(target, spamserv_join);
2430 sprintf(reason, "%s moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
2431 if(!IsSuspended(target->channel_info))
2433 char reason2[MAXLEN];
2434 sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
2435 DelChannelUser(chanserv, channel, reason2, 0);
2437 UnlockChannel(channel);
2438 LockChannel(target);
2439 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2440 reply("CSMSG_MOVE_SUCCESS", target->name);
2445 merge_users(struct chanData *source, struct chanData *target)
2447 struct userData *suData, *tuData, *next;
2453 /* Insert the source's users into the scratch area. */
2454 for(suData = source->users; suData; suData = suData->next)
2455 dict_insert(merge, suData->handle->handle, suData);
2457 /* Iterate through the target's users, looking for
2458 users common to both channels. The lower access is
2459 removed from either the scratch area or target user
2461 for(tuData = target->users; tuData; tuData = next)
2463 struct userData *choice;
2465 next = tuData->next;
2467 /* If a source user exists with the same handle as a target
2468 channel's user, resolve the conflict by removing one. */
2469 suData = dict_find(merge, tuData->handle->handle, NULL);
2473 /* Pick the data we want to keep. */
2474 /* If the access is the same, use the later seen time. */
2475 if(suData->access == tuData->access)
2476 choice = (suData->seen > tuData->seen) ? suData : tuData;
2477 else /* Otherwise, keep the higher access level. */
2478 choice = (suData->access > tuData->access) ? suData : tuData;
2479 /* Use the later seen time. */
2480 if(suData->seen < tuData->seen)
2481 suData->seen = tuData->seen;
2483 tuData->seen = suData->seen;
2485 /* Remove the user that wasn't picked. */
2486 if(choice == tuData)
2488 dict_remove(merge, suData->handle->handle);
2489 del_channel_user(suData, 0);
2492 del_channel_user(tuData, 0);
2495 /* Move the remaining users to the target channel. */
2496 for(it = dict_first(merge); it; it = iter_next(it))
2498 suData = iter_data(it);
2500 /* Insert the user into the target channel's linked list. */
2501 suData->prev = NULL;
2502 suData->next = target->users;
2503 suData->channel = target;
2506 target->users->prev = suData;
2507 target->users = suData;
2509 /* Update the user counts for the target channel; the
2510 source counts are left alone. */
2511 target->userCount++;
2513 /* Check whether the user is in the target channel. */
2514 scan_user_presence(suData, NULL);
2517 /* Possible to assert (source->users == NULL) here. */
2518 source->users = NULL;
2523 merge_bans(struct chanData *source, struct chanData *target)
2525 struct banData *sbData, *tbData, *sNext, *tNext, *tFront;
2527 /* Hold on to the original head of the target ban list
2528 to avoid comparing source bans with source bans. */
2529 tFront = target->bans;
2531 /* Perform a totally expensive O(n*m) merge, ick. */
2532 for(sbData = source->bans; sbData; sbData = sNext)
2534 /* Flag to track whether the ban's been moved
2535 to the destination yet. */
2538 /* Possible to assert (sbData->prev == NULL) here. */
2539 sNext = sbData->next;
2541 for(tbData = tFront; tbData; tbData = tNext)
2543 tNext = tbData->next;
2545 /* Perform two comparisons between each source
2546 and target ban, conflicts are resolved by
2547 keeping the broader ban and copying the later
2548 expiration and triggered time. */
2549 if(match_ircglobs(tbData->mask, sbData->mask))
2551 /* There is a broader ban in the target channel that
2552 overrides one in the source channel; remove the
2553 source ban and break. */
2554 if(sbData->expires > tbData->expires)
2555 tbData->expires = sbData->expires;
2556 if(sbData->triggered > tbData->triggered)
2557 tbData->triggered = sbData->triggered;
2558 del_channel_ban(sbData);
2561 else if(match_ircglobs(sbData->mask, tbData->mask))
2563 /* There is a broader ban in the source channel that
2564 overrides one in the target channel; remove the
2565 target ban, fall through and move the source over. */
2566 if(tbData->expires > sbData->expires)
2567 sbData->expires = tbData->expires;
2568 if(tbData->triggered > sbData->triggered)
2569 sbData->triggered = tbData->triggered;
2570 if(tbData == tFront)
2572 del_channel_ban(tbData);
2575 /* Source bans can override multiple target bans, so
2576 we allow a source to run through this loop multiple
2577 times, but we can only move it once. */
2582 /* Remove the source ban from the source ban list. */
2584 sbData->next->prev = sbData->prev;
2586 /* Modify the source ban's associated channel. */
2587 sbData->channel = target;
2589 /* Insert the ban into the target channel's linked list. */
2590 sbData->prev = NULL;
2591 sbData->next = target->bans;
2594 target->bans->prev = sbData;
2595 target->bans = sbData;
2597 /* Update the user counts for the target channel. */
2602 /* Possible to assert (source->bans == NULL) here. */
2603 source->bans = NULL;
2607 merge_data(struct chanData *source, struct chanData *target)
2609 /* Use more recent visited and owner-transfer time; use older
2610 * registered time. Bitwise or may_opchan. Use higher max.
2611 * Do not touch last_refresh, ban count or user counts.
2613 if(source->visited > target->visited)
2614 target->visited = source->visited;
2615 if(source->registered < target->registered)
2616 target->registered = source->registered;
2617 if(source->ownerTransfer > target->ownerTransfer)
2618 target->ownerTransfer = source->ownerTransfer;
2619 if(source->may_opchan)
2620 target->may_opchan = 1;
2621 if(source->max > target->max) {
2622 target->max = source->max;
2623 target->max_time = source->max_time;
2628 merge_channel(struct chanData *source, struct chanData *target)
2630 merge_users(source, target);
2631 merge_bans(source, target);
2632 merge_data(source, target);
2635 static CHANSERV_FUNC(cmd_merge)
2637 struct userData *target_user;
2638 struct chanNode *target;
2639 char reason[MAXLEN];
2643 /* Make sure the target channel exists and is registered to the user
2644 performing the command. */
2645 if(!(target = GetChannel(argv[1])))
2647 reply("MSG_INVALID_CHANNEL");
2651 if(!target->channel_info)
2653 reply("CSMSG_NOT_REGISTERED", target->name);
2657 if(IsProtected(channel->channel_info))
2659 reply("CSMSG_MERGE_NODELETE");
2663 if(IsSuspended(target->channel_info))
2665 reply("CSMSG_MERGE_SUSPENDED");
2669 if(channel == target)
2671 reply("CSMSG_MERGE_SELF");
2675 target_user = GetChannelUser(target->channel_info, user->handle_info);
2676 if(!target_user || (target_user->access < UL_OWNER))
2678 reply("CSMSG_MERGE_NOT_OWNER");
2682 /* Merge the channel structures and associated data. */
2683 merge_channel(channel->channel_info, target->channel_info);
2684 spamserv_cs_move_merge(user, channel, target, 0);
2685 sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
2686 unregister_channel(channel->channel_info, reason);
2687 reply("CSMSG_MERGE_SUCCESS", target->name);
2691 static CHANSERV_FUNC(cmd_opchan)
2693 struct mod_chanmode change;
2694 if(!IsHelping(user) && !channel->channel_info->may_opchan)
2696 reply("CSMSG_ALREADY_OPCHANNED", channel->name);
2699 channel->channel_info->may_opchan = 0;
2700 mod_chanmode_init(&change);
2702 change.args[0].mode = MODE_CHANOP;
2703 change.args[0].u.member = GetUserMode(channel, chanserv);
2704 if(!change.args[0].u.member)
2706 reply("CSMSG_OUT_OF_CHANNEL", channel->name);
2709 mod_chanmode_announce(chanserv, channel, &change);
2710 reply("CSMSG_OPCHAN_DONE", channel->name);
2714 static CHANSERV_FUNC(cmd_adduser)
2716 struct userData *actee;
2717 struct userData *actor, *real_actor;
2718 struct handle_info *handle;
2719 unsigned short access_level, override = 0;
2723 if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
2725 reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
2729 access_level = user_level_from_name(argv[2], UL_OWNER);
2732 reply("CSMSG_INVALID_ACCESS", argv[2]);
2736 actor = GetChannelUser(channel->channel_info, user->handle_info);
2737 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2739 if(actor->access <= access_level)
2741 reply("CSMSG_NO_BUMP_ACCESS");
2745 /* Trying to add someone with equal/more access? */
2746 if (!real_actor || real_actor->access <= access_level)
2747 override = CMD_LOG_OVERRIDE;
2749 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2752 if((actee = GetTrueChannelAccess(channel->channel_info, handle)))
2754 reply("CSMSG_USER_EXISTS", handle->handle, channel->name, actee->access);
2758 actee = add_channel_user(channel->channel_info, handle, access_level, 0, NULL);
2759 scan_user_presence(actee, NULL);
2760 reply("CSMSG_ADDED_USER", handle->handle, channel->name, access_level);
2761 return 1 | override;
2764 static CHANSERV_FUNC(cmd_clvl)
2766 struct handle_info *handle;
2767 struct userData *victim;
2768 struct userData *actor, *real_actor;
2769 unsigned short new_access, override = 0;
2770 int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
2774 actor = GetChannelUser(channel->channel_info, user->handle_info);
2775 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2777 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2780 if(handle == user->handle_info && !privileged)
2782 reply("CSMSG_NO_SELF_CLVL");
2786 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2788 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2792 if(actor->access <= victim->access && !privileged)
2794 reply("MSG_USER_OUTRANKED", handle->handle);
2798 new_access = user_level_from_name(argv[2], UL_OWNER);
2802 reply("CSMSG_INVALID_ACCESS", argv[2]);
2806 if(new_access >= actor->access && !privileged)
2808 reply("CSMSG_NO_BUMP_ACCESS");
2812 /* Trying to clvl a equal/higher user? */
2813 if(!real_actor || (real_actor->access <= victim->access && handle != user->handle_info))
2814 override = CMD_LOG_OVERRIDE;
2815 /* Trying to clvl someone to equal/higher access? */
2816 if(!real_actor || new_access >= real_actor->access)
2817 override = CMD_LOG_OVERRIDE;
2818 /* Helpers clvling themselves get caught by the "clvl someone to equal/higher access" check.
2819 * If they lower their own access it's not a big problem.
2822 victim->access = new_access;
2823 reply("CSMSG_CHANGED_ACCESS", handle->handle, new_access, channel->name);
2824 return 1 | override;
2827 static CHANSERV_FUNC(cmd_deluser)
2829 struct handle_info *handle;
2830 struct userData *victim;
2831 struct userData *actor, *real_actor;
2832 unsigned short access_level, override = 0;
2837 actor = GetChannelUser(channel->channel_info, user->handle_info);
2838 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2840 if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
2843 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2845 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2851 access_level = user_level_from_name(argv[1], UL_OWNER);
2854 reply("CSMSG_INVALID_ACCESS", argv[1]);
2857 if(access_level != victim->access)
2859 reply("CSMSG_INCORRECT_ACCESS", handle->handle, victim->access, argv[1]);
2865 access_level = victim->access;
2868 if((actor->access <= victim->access) && !IsHelping(user))
2870 reply("MSG_USER_OUTRANKED", victim->handle->handle);
2874 /* If people delete themselves it is an override, but they
2875 * could've used deleteme so we don't log it as an override
2877 if(!real_actor || (real_actor->access <= victim->access && real_actor != victim))
2878 override = CMD_LOG_OVERRIDE;
2880 chan_name = strdup(channel->name);
2881 del_channel_user(victim, 1);
2882 reply("CSMSG_DELETED_USER", handle->handle, access_level, chan_name);
2884 return 1 | override;
2888 cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
2890 struct userData *actor, *real_actor, *uData, *next;
2891 unsigned int override = 0;
2893 actor = GetChannelUser(channel->channel_info, user->handle_info);
2894 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2896 if(min_access > max_access)
2898 reply("CSMSG_BAD_RANGE", min_access, max_access);
2902 if(actor->access <= max_access)
2904 reply("CSMSG_NO_ACCESS");
2908 if(!real_actor || real_actor->access <= max_access)
2909 override = CMD_LOG_OVERRIDE;
2911 for(uData = channel->channel_info->users; uData; uData = next)
2915 if((uData->access >= min_access)
2916 && (uData->access <= max_access)
2917 && match_ircglob(uData->handle->handle, mask))
2918 del_channel_user(uData, 1);
2921 reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
2922 return 1 | override;
2925 static CHANSERV_FUNC(cmd_mdelowner)
2927 return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
2930 static CHANSERV_FUNC(cmd_mdelcoowner)
2932 return cmd_mdel_user(user, channel, UL_COOWNER, UL_COOWNER, argv[1], cmd);
2935 static CHANSERV_FUNC(cmd_mdelmaster)
2937 return cmd_mdel_user(user, channel, UL_MASTER, UL_MASTER, argv[1], cmd);
2940 static CHANSERV_FUNC(cmd_mdelop)
2942 return cmd_mdel_user(user, channel, UL_OP, UL_OP, argv[1], cmd);
2945 static CHANSERV_FUNC(cmd_mdelpeon)
2947 return cmd_mdel_user(user, channel, UL_PEON, UL_PEON, argv[1], cmd);
2951 cmd_trim_bans(struct userNode *user, struct chanNode *channel, unsigned long duration)
2953 struct banData *bData, *next;
2954 char interval[INTERVALLEN];
2956 unsigned long limit;
2959 limit = now - duration;
2960 for(bData = channel->channel_info->bans; bData; bData = next)
2964 if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
2967 del_channel_ban(bData);
2971 intervalString(interval, duration, user->handle_info);
2972 send_message(user, chanserv, "CSMSG_TRIMMED_BANS", count, channel->name, interval);
2977 cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration, int vacation)
2979 struct userData *actor, *uData, *next;
2980 char interval[INTERVALLEN];
2982 unsigned long limit;
2984 actor = GetChannelAccess(channel->channel_info, user->handle_info);
2985 if(min_access > max_access)
2987 send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
2991 if(!actor || actor->access <= max_access)
2993 send_message(user, chanserv, "CSMSG_NO_ACCESS");
2998 limit = now - duration;
2999 for(uData = channel->channel_info->users; uData; uData = next)
3003 if((uData->seen > limit)
3005 || (HANDLE_FLAGGED(uData->handle, FROZEN) && !vacation))
3008 if(((uData->access >= min_access) && (uData->access <= max_access))
3009 || (!max_access && (uData->access < actor->access)))
3011 del_channel_user(uData, 1);
3019 max_access = (actor->access > UL_OWNER) ? UL_OWNER : (actor->access - 1);
3021 send_message(user, chanserv, "CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration, user->handle_info));
3025 static CHANSERV_FUNC(cmd_trim)
3027 unsigned long duration;
3028 unsigned short min_level, max_level;
3033 vacation = argc > 3 && !strcmp(argv[3], "vacation");
3034 duration = ParseInterval(argv[2]);
3037 reply("CSMSG_CANNOT_TRIM");
3041 if(!irccasecmp(argv[1], "bans"))
3043 cmd_trim_bans(user, channel, duration);
3046 else if(!irccasecmp(argv[1], "users"))
3048 cmd_trim_users(user, channel, 0, 0, duration, vacation);
3051 else if(parse_level_range(&min_level, &max_level, argv[1]))
3053 cmd_trim_users(user, channel, min_level, max_level, duration, vacation);
3056 else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
3058 cmd_trim_users(user, channel, min_level, min_level, duration, vacation);
3063 reply("CSMSG_INVALID_TRIM", argv[1]);
3068 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
3069 to the user. cmd_all takes advantage of this. */
3070 static CHANSERV_FUNC(cmd_up)
3072 struct mod_chanmode change;
3073 struct userData *uData;
3076 mod_chanmode_init(&change);
3078 change.args[0].u.member = GetUserMode(channel, user);
3079 if(!change.args[0].u.member)
3082 reply("MSG_CHANNEL_ABSENT", channel->name);
3086 uData = GetChannelAccess(channel->channel_info, user->handle_info);
3090 reply("CSMSG_GODMODE_UP", argv[0]);
3093 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveOps])
3095 change.args[0].mode = MODE_CHANOP;
3096 errmsg = "CSMSG_ALREADY_OPPED";
3098 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveVoice])
3100 change.args[0].mode = MODE_VOICE;
3101 errmsg = "CSMSG_ALREADY_VOICED";
3106 reply("CSMSG_NO_ACCESS");
3109 change.args[0].mode &= ~change.args[0].u.member->modes;
3110 if(!change.args[0].mode)
3113 reply(errmsg, channel->name);
3116 modcmd_chanmode_announce(&change);
3120 static CHANSERV_FUNC(cmd_down)
3122 struct mod_chanmode change;
3124 mod_chanmode_init(&change);
3126 change.args[0].u.member = GetUserMode(channel, user);
3127 if(!change.args[0].u.member)
3130 reply("MSG_CHANNEL_ABSENT", channel->name);
3134 if(!change.args[0].u.member->modes)
3137 reply("CSMSG_ALREADY_DOWN", channel->name);
3141 change.args[0].mode = MODE_REMOVE | change.args[0].u.member->modes;
3142 modcmd_chanmode_announce(&change);
3146 static int cmd_all(struct userNode *user, UNUSED_ARG(struct chanNode *channel), UNUSED_ARG(unsigned int argc), UNUSED_ARG(char *argv[]), struct svccmd *cmd, modcmd_func_t mcmd)
3148 struct userData *cList;
3150 for(cList = user->handle_info->channels; cList; cList = cList->u_next)
3152 if(IsSuspended(cList->channel)
3153 || IsUserSuspended(cList)
3154 || !GetUserMode(cList->channel->channel, user))
3157 mcmd(user, cList->channel->channel, 0, NULL, cmd);
3163 static CHANSERV_FUNC(cmd_upall)
3165 return cmd_all(CSFUNC_ARGS, cmd_up);
3168 static CHANSERV_FUNC(cmd_downall)
3170 return cmd_all(CSFUNC_ARGS, cmd_down);
3173 typedef int validate_func_t(struct userNode *user, struct chanNode *channel, struct userNode *victim);
3174 typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
3177 modify_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, validate_func_t validate, chan_mode_t mode, char *action)
3179 unsigned int ii, valid;
3180 struct userNode *victim;
3181 struct mod_chanmode *change;
3183 change = mod_chanmode_alloc(argc - 1);
3185 for(ii=valid=0; ++ii < argc; )
3187 if(!(victim = GetUserH(argv[ii])))
3189 change->args[valid].mode = mode;
3190 change->args[valid].u.member = GetUserMode(channel, victim);
3191 if(!change->args[valid].u.member)
3193 if(validate && !validate(user, channel, victim))
3198 change->argc = valid;
3199 if(valid < (argc-1))
3200 reply("CSMSG_PROCESS_FAILED");
3203 modcmd_chanmode_announce(change);
3204 reply(action, channel->name);
3206 mod_chanmode_free(change);
3210 static CHANSERV_FUNC(cmd_op)
3212 return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
3215 static CHANSERV_FUNC(cmd_deop)
3217 return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
3220 static CHANSERV_FUNC(cmd_voice)
3222 return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
3225 static CHANSERV_FUNC(cmd_devoice)
3227 return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
3231 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, unsigned int *victimCount, struct modeNode **victims)
3237 for(ii=0; ii<channel->members.used; ii++)
3239 struct modeNode *mn = channel->members.list[ii];
3241 if(IsService(mn->user))
3244 if(!user_matches_glob(mn->user, ban, MATCH_USENICK | MATCH_VISIBLE))
3247 if(protect_user(mn->user, user, channel->channel_info))
3251 victims[(*victimCount)++] = mn;
3257 eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3259 struct userNode *victim;
3260 struct modeNode **victims;
3261 unsigned int offset, n, victimCount, duration = 0;
3262 char *reason = "Bye.", *ban, *name;
3263 char interval[INTERVALLEN];
3265 offset = (action & ACTION_ADD_TIMED_BAN) ? 3 : 2;
3266 REQUIRE_PARAMS(offset);
3267 if(argc > offset && IsNetServ(user))
3269 if(*argv[offset] == '$') {
3270 struct userNode *hib;
3271 const char *accountnameb = argv[offset] + 1;
3272 if(!(hib = GetUserH(accountnameb)))
3274 reply("MSG_HANDLE_UNKNOWN", accountnameb);
3283 reason = unsplit_string(argv + offset, argc - offset, NULL);
3284 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
3286 /* Truncate the reason to a length of TOPICLEN, as
3287 the ircd does; however, leave room for an ellipsis
3288 and the kicker's nick. */
3289 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
3293 if((victim = GetUserH(argv[1])))
3295 victims = alloca(sizeof(victims[0]));
3296 victims[0] = GetUserMode(channel, victim);
3297 /* XXX: The comparison with ACTION_KICK is just because all
3298 * other actions can work on users outside the channel, and we
3299 * want to allow those (e.g. unbans) in that case. If we add
3300 * some other ejection action for in-channel users, change
3302 victimCount = victims[0] ? 1 : 0;
3304 if(IsService(victim))
3306 reply("MSG_SERVICE_IMMUNE", victim->nick);
3310 if((action == ACTION_KICK) && !victimCount)
3312 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
3316 if(protect_user(victim, user, channel->channel_info))
3318 reply("CSMSG_USER_PROTECTED", victim->nick);
3322 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
3323 name = victim->nick;
3325 else if(!is_ircmask(argv[1]) && (*argv[1] == '*'))
3327 struct handle_info *hi;
3328 extern const char *titlehost_suffix;
3329 char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
3330 const char *accountname = argv[1] + 1;
3332 if(!(hi = get_handle_info(accountname)))
3334 reply("MSG_HANDLE_UNKNOWN", accountname);
3338 snprintf(banmask, sizeof(banmask), "*!*@%s.*.%s", hi->handle, titlehost_suffix);
3339 victims = alloca(sizeof(victims[0]) * channel->members.used);
3341 if(bad_channel_ban(channel, user, banmask, &victimCount, victims))
3343 reply("CSMSG_MASK_PROTECTED", banmask);
3347 if((action == ACTION_KICK) && (victimCount == 0))
3349 reply("CSMSG_NO_MATCHING_USERS", channel->name, banmask);
3353 name = ban = strdup(banmask);
3357 if(!is_ircmask(argv[1]))
3359 reply("MSG_NICK_UNKNOWN", argv[1]);
3363 victims = alloca(sizeof(victims[0]) * channel->members.used);
3365 if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
3367 reply("CSMSG_MASK_PROTECTED", argv[1]);
3371 if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
3373 reply("CSMSG_LAME_MASK", argv[1]);
3377 if((action == ACTION_KICK) && (victimCount == 0))
3379 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
3383 name = ban = strdup(argv[1]);
3386 /* Truncate the ban in place if necessary; we must ensure
3387 that 'ban' is a valid ban mask before sanitizing it. */
3388 sanitize_ircmask(ban);
3390 if(action & ACTION_ADD_BAN)
3392 struct banData *bData, *next;
3394 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans)
3396 reply("CSMSG_MAXIMUM_BANS", chanserv_conf.max_chan_bans);
3401 if(action & ACTION_ADD_TIMED_BAN)
3403 duration = ParseInterval(argv[2]);
3407 reply("CSMSG_DURATION_TOO_LOW");
3411 else if(duration > (86400 * 365 * 2))
3413 reply("CSMSG_DURATION_TOO_HIGH");
3419 for(bData = channel->channel_info->bans; bData; bData = next)
3421 if(match_ircglobs(bData->mask, ban))
3423 int exact = !irccasecmp(bData->mask, ban);
3425 /* The ban is redundant; there is already a ban
3426 with the same effect in place. */
3430 free(bData->reason);
3431 bData->reason = strdup(reason);
3432 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
3434 reply("CSMSG_REASON_CHANGE", ban);
3438 if(exact && bData->expires)
3442 /* If the ban matches an existing one exactly,
3443 extend the expiration time if the provided
3444 duration is longer. */
3445 if(duration && (now + duration > bData->expires))
3447 bData->expires = now + duration;
3458 /* Delete the expiration timeq entry and
3459 requeue if necessary. */
3460 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
3463 timeq_add(bData->expires, expire_ban, bData);
3467 /* automated kickban */
3470 reply("CSMSG_BAN_EXTENDED", ban, intervalString(interval, duration, user->handle_info));
3472 reply("CSMSG_BAN_ADDED", name, channel->name);
3478 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3485 if(match_ircglobs(ban, bData->mask))
3487 /* The ban we are adding makes previously existing
3488 bans redundant; silently remove them. */
3489 del_channel_ban(bData);
3493 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);
3495 name = ban = strdup(bData->mask);
3499 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
3501 extern const char *hidden_host_suffix;
3502 const char *old_name = chanserv_conf.old_ban_names->list[n];
3504 unsigned int l1, l2;
3507 l2 = strlen(old_name);
3510 if(irccasecmp(ban + l1 - l2, old_name))
3512 new_mask = malloc(MAXLEN);
3513 sprintf(new_mask, "%.*s%s", (int)(l1-l2), ban, hidden_host_suffix);
3515 name = ban = new_mask;
3520 if(action & ACTION_BAN)
3522 unsigned int exists;
3523 struct mod_chanmode *change;
3525 if(channel->banlist.used >= MAXBANS)
3528 reply("CSMSG_BANLIST_FULL", channel->name);
3533 exists = ChannelBanExists(channel, ban);
3534 change = mod_chanmode_alloc(victimCount + 1);
3535 for(n = 0; n < victimCount; ++n)
3537 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_VOICE;
3538 change->args[n].u.member = victims[n];
3542 change->args[n].mode = MODE_BAN;
3543 change->args[n++].u.hostmask = ban;
3547 modcmd_chanmode_announce(change);
3549 mod_chanmode_announce(chanserv, channel, change);
3550 mod_chanmode_free(change);
3552 if(exists && (action == ACTION_BAN))
3555 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3561 if(action & ACTION_KICK)
3563 char kick_reason[MAXLEN];
3564 sprintf(kick_reason, "(%s) %s", user->nick, reason);
3566 for(n = 0; n < victimCount; n++)
3567 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
3572 /* No response, since it was automated. */
3574 else if(action & ACTION_ADD_BAN)
3577 reply("CSMSG_TIMED_BAN_ADDED", name, channel->name, intervalString(interval, duration, user->handle_info));
3579 reply("CSMSG_BAN_ADDED", name, channel->name);
3581 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
3582 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
3583 else if(action & ACTION_BAN)
3584 reply("CSMSG_BAN_DONE", name, channel->name);
3585 else if(action & ACTION_KICK && victimCount)
3586 reply("CSMSG_KICK_DONE", name, channel->name);
3592 static CHANSERV_FUNC(cmd_kickban)
3594 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
3597 static CHANSERV_FUNC(cmd_kick)
3599 return eject_user(CSFUNC_ARGS, ACTION_KICK);
3602 static CHANSERV_FUNC(cmd_ban)
3604 return eject_user(CSFUNC_ARGS, ACTION_BAN);
3607 static CHANSERV_FUNC(cmd_addban)
3609 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN);
3612 static CHANSERV_FUNC(cmd_addtimedban)
3614 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN);
3617 struct mod_chanmode *
3618 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
3620 struct mod_chanmode *change;
3621 unsigned char *match;
3622 unsigned int ii, count;
3624 match = alloca(bans->used);
3627 for(ii = count = 0; ii < bans->used; ++ii)
3629 match[ii] = user_matches_glob(actee, bans->list[ii]->ban,
3630 MATCH_USENICK | MATCH_VISIBLE);
3637 for(ii = count = 0; ii < bans->used; ++ii)
3639 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
3646 change = mod_chanmode_alloc(count);
3647 for(ii = count = 0; ii < bans->used; ++ii)
3651 change->args[count].mode = MODE_REMOVE | MODE_BAN;
3652 change->args[count++].u.hostmask = strdup(bans->list[ii]->ban);
3654 assert(count == change->argc);
3659 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3661 struct userNode *actee;
3667 /* may want to allow a comma delimited list of users... */
3668 if(!(actee = GetUserH(argv[1])))
3670 if(!is_ircmask(argv[1]) && *argv[1] == '*')
3672 char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
3673 const char *accountname = argv[1] + 1;
3675 snprintf(banmask, sizeof(banmask), "*!*@%s.*", accountname);
3676 mask = strdup(banmask);
3678 else if(!is_ircmask(argv[1]))
3680 reply("MSG_NICK_UNKNOWN", argv[1]);
3685 mask = strdup(argv[1]);
3689 /* We don't sanitize the mask here because ircu
3691 if(action & ACTION_UNBAN)
3693 struct mod_chanmode *change;
3694 change = find_matching_bans(&channel->banlist, actee, mask);
3699 modcmd_chanmode_announce(change);
3700 for(ii = 0; ii < change->argc; ++ii)
3701 free((char*)change->args[ii].u.hostmask);
3702 mod_chanmode_free(change);
3707 if(action & ACTION_DEL_BAN)
3709 struct banData *ban, *next;
3711 ban = channel->channel_info->bans;
3715 for( ; ban && !user_matches_glob(actee, ban->mask,
3716 MATCH_USENICK | MATCH_VISIBLE);
3719 for( ; ban && !match_ircglobs(mask, ban->mask);
3724 del_channel_ban(ban);
3731 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3733 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3739 static CHANSERV_FUNC(cmd_unban)
3741 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3744 static CHANSERV_FUNC(cmd_delban)
3746 /* it doesn't necessarily have to remove the channel ban - may want
3747 to make that an option. */
3748 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_BAN);
3751 static CHANSERV_FUNC(cmd_unbanme)
3753 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3754 long flags = ACTION_UNBAN;
3756 /* remove permanent bans if the user has the proper access. */
3757 if(uData->access >= UL_MASTER)
3758 flags |= ACTION_DEL_BAN;
3760 argv[1] = user->nick;
3761 return unban_user(user, channel, 2, argv, cmd, flags);
3764 static CHANSERV_FUNC(cmd_unbanall)
3766 struct mod_chanmode *change;
3769 if(!channel->banlist.used)
3771 reply("CSMSG_NO_BANS", channel->name);
3775 change = mod_chanmode_alloc(channel->banlist.used);
3776 for(ii=0; ii<channel->banlist.used; ii++)
3778 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3779 change->args[ii].u.hostmask = strdup(channel->banlist.list[ii]->ban);
3781 modcmd_chanmode_announce(change);
3782 for(ii = 0; ii < change->argc; ++ii)
3783 free((char*)change->args[ii].u.hostmask);
3784 mod_chanmode_free(change);
3785 reply("CSMSG_BANS_REMOVED", channel->name);
3789 static CHANSERV_FUNC(cmd_open)
3791 struct mod_chanmode *change;
3794 change = find_matching_bans(&channel->banlist, user, NULL);
3796 change = mod_chanmode_alloc(0);
3797 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3798 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3799 && channel->channel_info->modes.modes_set)
3800 change->modes_clear &= ~channel->channel_info->modes.modes_set;
3801 modcmd_chanmode_announce(change);
3802 reply("CSMSG_CHANNEL_OPENED", channel->name);
3803 for(ii = 0; ii < change->argc; ++ii)
3804 free((char*)change->args[ii].u.hostmask);
3805 mod_chanmode_free(change);
3809 static CHANSERV_FUNC(cmd_myaccess)
3811 static struct string_buffer sbuf;
3812 struct handle_info *target_handle;
3813 struct userData *uData;
3817 target_handle = user->handle_info;
3818 else if(!IsStaff(user))
3820 reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
3823 else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3826 if(!oper_outranks(user, target_handle))
3829 if(!target_handle->channels)
3831 reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
3835 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3836 for(uData = target_handle->channels; uData; uData = uData->u_next)
3838 struct chanData *cData = uData->channel;
3841 if(uData->access > UL_OWNER)
3843 if(IsProtected(cData)
3844 && (target_handle != user->handle_info)
3845 && !GetTrueChannelAccess(cData, user->handle_info))
3848 string_buffer_append_printf(&sbuf, "[%s (%d", cData->channel->name, uData->access);
3849 if(uData->flags != USER_AUTO_OP)
3850 string_buffer_append(&sbuf, ',');
3851 if(IsUserSuspended(uData))
3852 string_buffer_append(&sbuf, 's');
3853 if(IsUserAutoOp(uData))
3855 if(uData->access >= cData->lvlOpts[lvlGiveOps])
3856 string_buffer_append(&sbuf, 'o');
3857 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
3858 string_buffer_append(&sbuf, 'v');
3860 if(IsUserAutoInvite(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe]))
3861 string_buffer_append(&sbuf, 'i');
3863 string_buffer_append_printf(&sbuf, ")] %s", uData->info);
3865 string_buffer_append_string(&sbuf, ")]");
3866 string_buffer_append(&sbuf, '\0');
3867 send_message_type(4, user, cmd->parent->bot, "%s", sbuf.list);
3871 reply("CSMSG_MYACCESS_COUNT_1", target_handle->handle, ccount);
3873 reply("CSMSG_MYACCESS_COUNT", target_handle->handle, ccount);
3879 static CHANSERV_FUNC(cmd_access)
3881 struct userNode *target;
3882 struct handle_info *target_handle;
3883 struct userData *uData;
3885 char prefix[MAXLEN];
3890 target_handle = target->handle_info;
3892 else if((target = GetUserH(argv[1])))
3894 target_handle = target->handle_info;
3896 else if(argv[1][0] == '*')
3898 if(!(target_handle = get_handle_info(argv[1]+1)))
3900 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3906 reply("MSG_NICK_UNKNOWN", argv[1]);
3910 assert(target || target_handle);
3912 if(target == chanserv)
3914 reply("CSMSG_IS_CHANSERV");
3922 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
3927 reply("MSG_USER_AUTHENTICATE", target->nick);
3930 reply("MSG_AUTHENTICATE");
3936 const char *epithet = NULL, *type = NULL;
3939 epithet = chanserv_conf.irc_operator_epithet;
3940 type = user_find_message(user, "CSMSG_OPERATOR_TITLE");
3942 else if(IsNetworkHelper(target))
3944 epithet = chanserv_conf.network_helper_epithet;
3945 type = user_find_message(user, "CSMSG_UC_H_TITLE");
3947 else if(IsSupportHelper(target))
3949 epithet = chanserv_conf.support_helper_epithet;
3950 type = user_find_message(user, "CSMSG_LC_H_TITLE");
3954 if(target_handle->epithet)
3955 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
3957 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
3959 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
3963 sprintf(prefix, "%s", target_handle->handle);
3966 if(!channel->channel_info)
3968 reply("CSMSG_NOT_REGISTERED", channel->name);
3972 helping = HANDLE_FLAGGED(target_handle, HELPING)
3973 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
3974 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
3976 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
3977 /* To prevent possible information leaks, only show infolines
3978 * if the requestor is in the channel or it's their own
3980 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
3982 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
3984 /* Likewise, only say it's suspended if the user has active
3985 * access in that channel or it's their own entry. */
3986 if(IsUserSuspended(uData)
3987 && (GetChannelUser(channel->channel_info, user->handle_info)
3988 || (user->handle_info == uData->handle)))
3990 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
3995 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
4002 zoot_list(struct listData *list)
4004 struct userData *uData;
4005 unsigned int start, curr, highest, lowest;
4006 struct helpfile_table tmp_table;
4007 const char **temp, *msg;
4009 if(list->table.length == 1)
4012 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
4014 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
4015 msg = user_find_message(list->user, "MSG_NONE");
4016 send_message_type(4, list->user, list->bot, " %s", msg);
4018 tmp_table.width = list->table.width;
4019 tmp_table.flags = list->table.flags;
4020 list->table.contents[0][0] = " ";
4021 highest = list->highest;
4022 if(list->lowest != 0)
4023 lowest = list->lowest;
4024 else if(highest < 100)
4027 lowest = highest - 100;
4028 for(start = curr = 1; curr < list->table.length; )
4030 uData = list->users[curr-1];
4031 list->table.contents[curr++][0] = " ";
4032 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
4035 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, lowest, highest, list->search);
4037 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, lowest, highest);
4038 temp = list->table.contents[--start];
4039 list->table.contents[start] = list->table.contents[0];
4040 tmp_table.contents = list->table.contents + start;
4041 tmp_table.length = curr - start;
4042 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
4043 list->table.contents[start] = temp;
4045 highest = lowest - 1;
4046 lowest = (highest < 100) ? 0 : (highest - 99);
4052 def_list(struct listData *list)
4056 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
4058 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
4059 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4060 if(list->table.length == 1)
4062 msg = user_find_message(list->user, "MSG_NONE");
4063 send_message_type(4, list->user, list->bot, " %s", msg);
4068 userData_access_comp(const void *arg_a, const void *arg_b)
4070 const struct userData *a = *(struct userData**)arg_a;
4071 const struct userData *b = *(struct userData**)arg_b;
4073 if(a->access != b->access)
4074 res = b->access - a->access;
4076 res = irccasecmp(a->handle->handle, b->handle->handle);
4081 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
4083 void (*send_list)(struct listData *);
4084 struct userData *uData;
4085 struct listData lData;
4086 unsigned int matches;
4090 lData.bot = cmd->parent->bot;
4091 lData.channel = channel;
4092 lData.lowest = lowest;
4093 lData.highest = highest;
4094 lData.search = (argc > 1) ? argv[1] : NULL;
4095 send_list = def_list;
4096 (void)zoot_list; /* since it doesn't show user levels */
4098 if(user->handle_info)
4100 switch(user->handle_info->userlist_style)
4102 case HI_STYLE_DEF: send_list = def_list; break;
4103 case HI_STYLE_ZOOT: send_list = def_list; break;
4107 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
4109 for(uData = channel->channel_info->users; uData; uData = uData->next)
4111 if((uData->access < lowest)
4112 || (uData->access > highest)
4113 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
4115 lData.users[matches++] = uData;
4117 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
4119 lData.table.length = matches+1;
4120 lData.table.width = 4;
4121 lData.table.flags = TABLE_NO_FREE;
4122 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
4123 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
4124 lData.table.contents[0] = ary;
4127 ary[2] = "Last Seen";
4129 for(matches = 1; matches < lData.table.length; ++matches)
4131 char seen[INTERVALLEN];
4133 uData = lData.users[matches-1];
4134 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
4135 lData.table.contents[matches] = ary;
4136 ary[0] = strtab(uData->access);
4137 ary[1] = uData->handle->handle;
4140 else if(!uData->seen)
4143 ary[2] = intervalString(seen, now - uData->seen, user->handle_info);
4144 ary[2] = strdup(ary[2]);
4145 if(IsUserSuspended(uData))
4146 ary[3] = "Suspended";
4147 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
4148 ary[3] = "Vacation";
4149 else if(HANDLE_FLAGGED(uData->handle, BOT))
4155 for(matches = 1; matches < lData.table.length; ++matches)
4157 free((char*)lData.table.contents[matches][2]);
4158 free(lData.table.contents[matches]);
4160 free(lData.table.contents[0]);
4161 free(lData.table.contents);
4165 static CHANSERV_FUNC(cmd_users)
4167 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
4170 static CHANSERV_FUNC(cmd_wlist)
4172 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
4175 static CHANSERV_FUNC(cmd_clist)
4177 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
4180 static CHANSERV_FUNC(cmd_mlist)
4182 return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
4185 static CHANSERV_FUNC(cmd_olist)
4187 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
4190 static CHANSERV_FUNC(cmd_plist)
4192 return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
4195 static CHANSERV_FUNC(cmd_bans)
4197 struct userNode *search_u = NULL;
4198 struct helpfile_table tbl;
4199 unsigned int matches = 0, timed = 0, search_wilds = 0, ii;
4200 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
4201 const char *msg_never, *triggered, *expires;
4202 struct banData *ban, **bans;
4206 else if(strchr(search = argv[1], '!'))
4209 search_wilds = search[strcspn(search, "?*")];
4211 else if(!(search_u = GetUserH(search)))
4212 reply("MSG_NICK_UNKNOWN", search);
4214 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
4216 for(ban = channel->channel_info->bans; ban; ban = ban->next)
4220 if(!user_matches_glob(search_u, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
4225 if(search_wilds ? !match_ircglobs(search, ban->mask) : !match_ircglob(search, ban->mask))
4228 bans[matches++] = ban;
4233 tbl.length = matches + 1;
4234 tbl.width = 4 + timed;
4236 tbl.flags = TABLE_NO_FREE;
4237 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
4238 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4239 tbl.contents[0][0] = "Mask";
4240 tbl.contents[0][1] = "Set By";
4241 tbl.contents[0][2] = "Triggered";
4244 tbl.contents[0][3] = "Expires";
4245 tbl.contents[0][4] = "Reason";
4248 tbl.contents[0][3] = "Reason";
4251 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4253 free(tbl.contents[0]);
4258 msg_never = user_find_message(user, "MSG_NEVER");
4259 for(ii = 0; ii < matches; )
4265 else if(ban->expires)
4266 expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
4268 expires = msg_never;
4271 triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
4273 triggered = msg_never;
4275 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4276 tbl.contents[ii][0] = ban->mask;
4277 tbl.contents[ii][1] = ban->owner;
4278 tbl.contents[ii][2] = strdup(triggered);
4281 tbl.contents[ii][3] = strdup(expires);
4282 tbl.contents[ii][4] = ban->reason;
4285 tbl.contents[ii][3] = ban->reason;
4287 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4288 reply("MSG_MATCH_COUNT", matches);
4289 for(ii = 1; ii < tbl.length; ++ii)
4291 free((char*)tbl.contents[ii][2]);
4293 free((char*)tbl.contents[ii][3]);
4294 free(tbl.contents[ii]);
4296 free(tbl.contents[0]);
4302 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
4304 struct chanData *cData = channel->channel_info;
4305 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
4307 if(cData->topic_mask)
4308 return !match_ircglob(new_topic, cData->topic_mask);
4309 else if(cData->topic)
4310 return irccasecmp(new_topic, cData->topic);
4315 static CHANSERV_FUNC(cmd_topic)
4317 struct chanData *cData;
4320 cData = channel->channel_info;
4325 SetChannelTopic(channel, chanserv, cData->topic, 1);
4326 reply("CSMSG_TOPIC_SET", cData->topic);
4330 reply("CSMSG_NO_TOPIC", channel->name);
4334 topic = unsplit_string(argv + 1, argc - 1, NULL);
4335 /* If they say "!topic *", use an empty topic. */
4336 if((topic[0] == '*') && (topic[1] == 0))
4338 if(bad_topic(channel, user, topic))
4340 char *topic_mask = cData->topic_mask;
4343 char new_topic[TOPICLEN+1], tchar;
4344 int pos=0, starpos=-1, dpos=0, len;
4346 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
4353 len = strlen(topic);
4354 if((dpos + len) > TOPICLEN)
4355 len = TOPICLEN + 1 - dpos;
4356 memcpy(new_topic+dpos, topic, len);
4360 case '\\': tchar = topic_mask[pos++]; /* and fall through */
4361 default: new_topic[dpos++] = tchar; break;
4364 if((dpos > TOPICLEN) || tchar)
4367 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
4368 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
4371 new_topic[dpos] = 0;
4372 SetChannelTopic(channel, chanserv, new_topic, 1);
4374 reply("CSMSG_TOPIC_LOCKED", channel->name);
4379 SetChannelTopic(channel, chanserv, topic, 1);
4381 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
4383 /* Grab the topic and save it as the default topic. */
4385 cData->topic = strdup(channel->topic);
4391 static CHANSERV_FUNC(cmd_mode)
4393 struct userData *uData;
4394 struct mod_chanmode *change;
4400 change = &channel->channel_info->modes;
4401 if(change->modes_set || change->modes_clear) {
4402 modcmd_chanmode_announce(change);
4403 reply("CSMSG_DEFAULTED_MODES", channel->name);
4405 reply("CSMSG_NO_MODES", channel->name);
4409 uData = GetChannelUser(channel->channel_info, user->handle_info);
4411 base_oplevel = MAXOPLEVEL;
4412 else if (uData->access >= UL_OWNER)
4415 base_oplevel = 1 + UL_OWNER - uData->access;
4416 change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_IGN_REGISTERED|MCP_NO_APASS, base_oplevel);
4419 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
4423 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
4424 && mode_lock_violated(&channel->channel_info->modes, change))
4427 mod_chanmode_format(&channel->channel_info->modes, modes);
4428 reply("CSMSG_MODE_LOCKED", modes, channel->name);
4432 modcmd_chanmode_announce(change);
4433 mod_chanmode_format(change, fmt);
4434 mod_chanmode_free(change);
4435 reply("CSMSG_MODES_SET", fmt);
4440 chanserv_del_invite_mark(void *data)
4442 struct ChanUser *chanuser = data;
4443 struct chanNode *channel = chanuser->chan;
4445 if(!channel) return;
4446 for(i = 0; i < channel->invited.used; i++)
4448 if(channel->invited.list[i] == chanuser->user) {
4449 userList_remove(&channel->invited, chanuser->user);
4455 static CHANSERV_FUNC(cmd_invite)
4457 struct userData *uData;
4458 struct userNode *invite;
4459 struct ChanUser *chanuser;
4462 uData = GetChannelUser(channel->channel_info, user->handle_info);
4466 if(!(invite = GetUserH(argv[1])))
4468 reply("MSG_NICK_UNKNOWN", argv[1]);
4475 if(GetUserMode(channel, invite))
4477 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
4481 for(i = 0; i < channel->invited.used; i++)
4483 if(channel->invited.list[i] == invite) {
4484 reply("CSMSG_ALREADY_INVITED", invite->nick, channel->name);
4493 char *reason = unsplit_string(argv + 2, argc - 2, NULL);
4494 send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
4497 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
4499 irc_invite(chanserv, invite, channel);
4501 reply("CSMSG_INVITED_USER", argv[1], channel->name);
4503 userList_append(&channel->invited, invite);
4504 chanuser = calloc(1, sizeof(*chanuser));
4505 chanuser->user=invite;
4506 chanuser->chan=channel;
4507 timeq_add(now + chanserv_conf.invited_timeout, chanserv_del_invite_mark, chanuser);
4512 static CHANSERV_FUNC(cmd_inviteme)
4514 if(GetUserMode(channel, user))
4516 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
4519 if(channel->channel_info
4520 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
4522 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
4525 irc_invite(cmd->parent->bot, user, channel);
4529 static CHANSERV_FUNC(cmd_invitemeall)
4531 struct handle_info *target = user->handle_info;
4532 struct userData *uData;
4534 if(!target->channels)
4536 reply("CSMSG_SQUAT_ACCESS", target->handle);
4540 for(uData = target->channels; uData; uData = uData->u_next)
4542 struct chanData *cData = uData->channel;
4543 if(uData->access >= cData->lvlOpts[lvlInviteMe])
4545 irc_invite(cmd->parent->bot, user, cData->channel);
4552 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
4555 char buf1[INTERVALLEN], buf2[INTERVALLEN];
4557 /* We display things based on two dimensions:
4558 * - Issue time: present or absent
4559 * - Expiration: revoked, expired, expires in future, or indefinite expiration
4560 * (in order of precedence, so something both expired and revoked
4561 * only counts as revoked)
4563 combo = (suspended->issued ? 4 : 0)
4564 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
4566 case 0: /* no issue time, indefinite expiration */
4567 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
4569 case 1: /* no issue time, expires in future */
4570 intervalString(buf1, suspended->expires-now, user->handle_info);
4571 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
4573 case 2: /* no issue time, expired */
4574 intervalString(buf1, now-suspended->expires, user->handle_info);
4575 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
4577 case 3: /* no issue time, revoked */
4578 intervalString(buf1, now-suspended->revoked, user->handle_info);
4579 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
4581 case 4: /* issue time set, indefinite expiration */
4582 intervalString(buf1, now-suspended->issued, user->handle_info);
4583 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
4585 case 5: /* issue time set, expires in future */
4586 intervalString(buf1, now-suspended->issued, user->handle_info);
4587 intervalString(buf2, suspended->expires-now, user->handle_info);
4588 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
4590 case 6: /* issue time set, expired */
4591 intervalString(buf1, now-suspended->issued, user->handle_info);
4592 intervalString(buf2, now-suspended->expires, user->handle_info);
4593 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
4595 case 7: /* issue time set, revoked */
4596 intervalString(buf1, now-suspended->issued, user->handle_info);
4597 intervalString(buf2, now-suspended->revoked, user->handle_info);
4598 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
4601 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
4606 static CHANSERV_FUNC(cmd_info)
4608 char modes[MAXLEN], buffer[INTERVALLEN];
4609 struct userData *uData, *owner;
4610 struct chanData *cData;
4611 struct do_not_register *dnr;
4616 cData = channel->channel_info;
4617 reply("CSMSG_CHANNEL_INFO", channel->name);
4619 uData = GetChannelUser(cData, user->handle_info);
4620 if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
4622 mod_chanmode_format(&cData->modes, modes);
4623 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
4624 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
4627 for(it = dict_first(cData->notes); it; it = iter_next(it))
4631 note = iter_data(it);
4632 if(!note_type_visible_to_user(cData, note->type, user))
4635 padding = PADLEN - 1 - strlen(iter_key(it));
4636 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
4639 if(cData->max_time) {
4640 reply("CSMSG_CHANNEL_MAX_TIME", cData->max, intervalString(buffer, now - cData->max_time, user->handle_info));
4642 reply("CSMSG_CHANNEL_MAX", cData->max);
4644 for(owner = cData->users; owner; owner = owner->next)
4645 if(owner->access == UL_OWNER)
4646 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
4647 reply("CSMSG_CHANNEL_USERS", cData->userCount);
4648 reply("CSMSG_CHANNEL_BANS", cData->banCount);
4649 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
4651 privileged = IsStaff(user);
4653 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
4654 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
4655 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
4657 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
4658 chanserv_show_dnrs(user, cmd, channel->name, NULL);
4660 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
4662 struct suspended *suspended;
4663 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
4664 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
4665 show_suspension_info(cmd, user, suspended);
4667 else if(IsSuspended(cData))
4669 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
4670 show_suspension_info(cmd, user, cData->suspended);
4675 static CHANSERV_FUNC(cmd_netinfo)
4677 extern unsigned long boot_time;
4678 extern unsigned long burst_length;
4679 char interval[INTERVALLEN];
4681 reply("CSMSG_NETWORK_INFO");
4682 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
4683 reply("CSMSG_NETWORK_USERS", dict_size(clients));
4684 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
4685 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
4686 reply("CSMSG_NETWORK_BANS", banCount);
4687 reply("CSMSG_NETWORK_CHANUSERS", userCount);
4688 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
4689 reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
4694 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
4696 struct helpfile_table table;
4698 struct userNode *user;
4703 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4704 table.contents = alloca(list->used*sizeof(*table.contents));
4705 for(nn=0; nn<list->used; nn++)
4707 user = list->list[nn];
4708 if(user->modes & skip_flags)
4712 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
4715 nick = alloca(strlen(user->nick)+3);
4716 sprintf(nick, "(%s)", user->nick);
4720 table.contents[table.length][0] = nick;
4723 table_send(chanserv, to->nick, 0, NULL, table);
4726 static CHANSERV_FUNC(cmd_ircops)
4728 reply("CSMSG_STAFF_OPERS");
4729 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
4733 static CHANSERV_FUNC(cmd_helpers)
4735 reply("CSMSG_STAFF_HELPERS");
4736 send_staff_list(user, &curr_helpers, FLAGS_OPER);
4740 static CHANSERV_FUNC(cmd_staff)
4742 reply("CSMSG_NETWORK_STAFF");
4743 cmd_ircops(CSFUNC_ARGS);
4744 cmd_helpers(CSFUNC_ARGS);
4748 static CHANSERV_FUNC(cmd_peek)
4750 struct modeNode *mn;
4751 char modes[MODELEN];
4753 struct helpfile_table table;
4754 int opcount = 0, voicecount = 0, srvcount = 0;
4756 irc_make_chanmode(channel, modes);
4758 reply("CSMSG_PEEK_INFO", channel->name);
4759 reply("CSMSG_PEEK_TOPIC", channel->topic);
4760 reply("CSMSG_PEEK_MODES", modes);
4764 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4765 table.contents = alloca(channel->members.used*sizeof(*table.contents));
4766 for(n = 0; n < channel->members.used; n++)
4768 mn = channel->members.list[n];
4769 if(IsLocal(mn->user))
4771 else if(mn->modes & MODE_CHANOP)
4773 else if(mn->modes & MODE_VOICE)
4776 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
4778 table.contents[table.length] = alloca(sizeof(**table.contents));
4779 table.contents[table.length][0] = mn->user->nick;
4783 reply("CSMSG_PEEK_USERS", channel->members.used, opcount, voicecount,
4784 (channel->members.used - opcount - voicecount - srvcount));
4788 reply("CSMSG_PEEK_OPS");
4789 table_send(chanserv, user->nick, 0, NULL, table);
4792 reply("CSMSG_PEEK_NO_OPS");
4796 static MODCMD_FUNC(cmd_wipeinfo)
4798 struct handle_info *victim;
4799 struct userData *ud, *actor, *real_actor;
4800 unsigned int override = 0;
4803 actor = GetChannelUser(channel->channel_info, user->handle_info);
4804 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
4805 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4807 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4809 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4812 if((ud->access >= actor->access) && (ud != actor))
4814 reply("MSG_USER_OUTRANKED", victim->handle);
4817 if((ud != real_actor) && (!real_actor || (ud->access >= real_actor->access)))
4818 override = CMD_LOG_OVERRIDE;
4822 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4823 return 1 | override;
4826 static CHANSERV_FUNC(cmd_resync)
4828 struct mod_chanmode *changes;
4829 struct chanData *cData = channel->channel_info;
4830 unsigned int ii, used;
4832 changes = mod_chanmode_alloc(channel->members.used * 2);
4833 for(ii = used = 0; ii < channel->members.used; ++ii)
4835 struct modeNode *mn = channel->members.list[ii];
4836 struct userData *uData;
4838 if(IsService(mn->user))
4841 uData = GetChannelAccess(cData, mn->user->handle_info);
4842 if(!cData->lvlOpts[lvlGiveOps]
4843 || (uData && uData->access >= cData->lvlOpts[lvlGiveOps]))
4845 if(!(mn->modes & MODE_CHANOP))
4847 if(!uData || IsUserAutoOp(uData))
4849 changes->args[used].mode = MODE_CHANOP;
4850 changes->args[used++].u.member = mn;
4851 if(!(mn->modes & MODE_VOICE))
4853 changes->args[used].mode = MODE_VOICE;
4854 changes->args[used++].u.member = mn;
4859 else if(!cData->lvlOpts[lvlGiveVoice]
4860 || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice]))
4862 if(mn->modes & MODE_CHANOP)
4864 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4865 changes->args[used++].u.member = mn;
4867 if(!(mn->modes & MODE_VOICE) && (!uData || IsUserAutoOp(uData)))
4869 changes->args[used].mode = MODE_VOICE;
4870 changes->args[used++].u.member = mn;
4877 changes->args[used].mode = MODE_REMOVE | mn->modes;
4878 changes->args[used++].u.member = mn;
4882 changes->argc = used;
4883 modcmd_chanmode_announce(changes);
4884 mod_chanmode_free(changes);
4885 reply("CSMSG_RESYNCED_USERS", channel->name);
4889 static CHANSERV_FUNC(cmd_seen)
4891 struct userData *uData;
4892 struct handle_info *handle;
4893 char seen[INTERVALLEN];
4897 if(!irccasecmp(argv[1], chanserv->nick))
4899 reply("CSMSG_IS_CHANSERV");
4903 if(!(handle = get_handle_info(argv[1])))
4905 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4909 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4911 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4916 reply("CSMSG_USER_PRESENT", handle->handle);
4917 else if(uData->seen)
4918 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
4920 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4922 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4923 reply("CSMSG_USER_VACATION", handle->handle);
4928 static MODCMD_FUNC(cmd_names)
4930 struct userNode *targ;
4931 struct userData *targData;
4932 unsigned int ii, pos;
4935 for(ii=pos=0; ii<channel->members.used; ++ii)
4937 targ = channel->members.list[ii]->user;
4938 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4941 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
4944 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4948 if(IsUserSuspended(targData))
4950 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4953 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4954 reply("CSMSG_END_NAMES", channel->name);
4959 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
4961 switch(ntype->visible_type)
4963 case NOTE_VIS_ALL: return 1;
4964 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
4965 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
4970 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
4972 struct userData *uData;
4974 switch(ntype->set_access_type)
4976 case NOTE_SET_CHANNEL_ACCESS:
4977 if(!user->handle_info)
4979 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
4981 return uData->access >= ntype->set_access.min_ulevel;
4982 case NOTE_SET_CHANNEL_SETTER:
4983 return check_user_level(channel, user, lvlSetters, 1, 0);
4984 case NOTE_SET_PRIVILEGED: default:
4985 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
4989 static CHANSERV_FUNC(cmd_note)
4991 struct chanData *cData;
4993 struct note_type *ntype;
4995 cData = channel->channel_info;
4998 reply("CSMSG_NOT_REGISTERED", channel->name);
5002 /* If no arguments, show all visible notes for the channel. */
5008 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
5010 note = iter_data(it);
5011 if(!note_type_visible_to_user(cData, note->type, user))
5014 reply("CSMSG_NOTELIST_HEADER", channel->name);
5015 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
5018 reply("CSMSG_NOTELIST_END", channel->name);
5020 reply("CSMSG_NOTELIST_EMPTY", channel->name);
5022 /* If one argument, show the named note. */
5025 if((note = dict_find(cData->notes, argv[1], NULL))
5026 && note_type_visible_to_user(cData, note->type, user))
5028 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
5030 else if((ntype = dict_find(note_types, argv[1], NULL))
5031 && note_type_visible_to_user(NULL, ntype, user))
5033 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
5038 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
5042 /* Assume they're trying to set a note. */
5046 ntype = dict_find(note_types, argv[1], NULL);
5049 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
5052 else if(note_type_settable_by_user(channel, ntype, user))
5054 note_text = unsplit_string(argv+2, argc-2, NULL);
5055 if((note = dict_find(cData->notes, argv[1], NULL)))
5056 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
5057 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
5058 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
5060 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
5062 /* The note is viewable to staff only, so return 0
5063 to keep the invocation from getting logged (or
5064 regular users can see it in !events). */
5070 reply("CSMSG_NO_ACCESS");
5077 static CHANSERV_FUNC(cmd_delnote)
5082 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
5083 || !note_type_settable_by_user(channel, note->type, user))
5085 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
5088 dict_remove(channel->channel_info->notes, note->type->name);
5089 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
5093 static CHANSERV_FUNC(cmd_events)
5095 struct logSearch discrim;
5096 struct logReport report;
5097 unsigned int matches, limit;
5099 limit = (argc > 1) ? atoi(argv[1]) : 10;
5100 if(limit < 1 || limit > 200)
5103 memset(&discrim, 0, sizeof(discrim));
5104 discrim.masks.bot = chanserv;
5105 discrim.masks.channel_name = channel->name;
5107 discrim.masks.command = argv[2];
5108 discrim.limit = limit;
5109 discrim.max_time = INT_MAX;
5110 discrim.severities = 1 << LOG_COMMAND;
5111 report.reporter = chanserv;
5113 reply("CSMSG_EVENT_SEARCH_RESULTS");
5114 matches = log_entry_search(&discrim, log_report_entry, &report);
5116 reply("MSG_MATCH_COUNT", matches);
5118 reply("MSG_NO_MATCHES");
5122 static CHANSERV_FUNC(cmd_say)
5128 msg = unsplit_string(argv + 1, argc - 1, NULL);
5129 send_channel_message(channel, cmd->parent->bot, "%s", msg);
5131 else if(*argv[1] == '*' && argv[1][1] != '\0')
5133 struct handle_info *hi;
5134 struct userNode *authed;
5137 msg = unsplit_string(argv + 2, argc - 2, NULL);
5139 if (!(hi = get_handle_info(argv[1] + 1)))
5141 reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
5145 for (authed = hi->users; authed; authed = authed->next_authed)
5146 send_target_message(5, authed->nick, cmd->parent->bot, "%s", msg);
5148 else if(GetUserH(argv[1]))
5151 msg = unsplit_string(argv + 2, argc - 2, NULL);
5152 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
5156 reply("MSG_NOT_TARGET_NAME");
5162 static CHANSERV_FUNC(cmd_emote)
5168 /* CTCP is so annoying. */
5169 msg = unsplit_string(argv + 1, argc - 1, NULL);
5170 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
5172 else if(*argv[1] == '*' && argv[1][1] != '\0')
5174 struct handle_info *hi;
5175 struct userNode *authed;
5178 msg = unsplit_string(argv + 2, argc - 2, NULL);
5180 if (!(hi = get_handle_info(argv[1] + 1)))
5182 reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
5186 for (authed = hi->users; authed; authed = authed->next_authed)
5187 send_target_message(5, authed->nick, cmd->parent->bot, "\001ACTION %s\001", msg);
5189 else if(GetUserH(argv[1]))
5191 msg = unsplit_string(argv + 2, argc - 2, NULL);
5192 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
5196 reply("MSG_NOT_TARGET_NAME");
5202 struct channelList *
5203 chanserv_support_channels(void)
5205 return &chanserv_conf.support_channels;
5208 static CHANSERV_FUNC(cmd_expire)
5210 int channel_count = registered_channels;
5211 expire_channels(NULL);
5212 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
5217 chanserv_expire_suspension(void *data)
5219 struct suspended *suspended = data;
5220 struct chanNode *channel;
5223 /* Update the channel registration data structure. */
5224 if(!suspended->expires || (now < suspended->expires))
5225 suspended->revoked = now;
5226 channel = suspended->cData->channel;
5227 suspended->cData->channel = channel;
5228 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
5230 /* If appropriate, re-join ChanServ to the channel. */
5231 if(!IsOffChannel(suspended->cData))
5233 spamserv_cs_suspend(channel, 0, 0, NULL);
5234 ss_cs_join_channel(channel, 1);
5237 /* Mark everyone currently in the channel as present. */
5238 for(ii = 0; ii < channel->members.used; ++ii)
5240 struct userData *uData = GetChannelAccess(suspended->cData, channel->members.list[ii]->user->handle_info);
5249 static CHANSERV_FUNC(cmd_csuspend)
5251 struct suspended *suspended;
5252 char reason[MAXLEN];
5253 unsigned long expiry, duration;
5254 struct userData *uData;
5258 if(IsProtected(channel->channel_info))
5260 reply("CSMSG_SUSPEND_NODELETE", channel->name);
5264 if(argv[1][0] == '!')
5266 else if(IsSuspended(channel->channel_info))
5268 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
5269 show_suspension_info(cmd, user, channel->channel_info->suspended);
5273 if(!strcmp(argv[1], "0"))
5275 else if((duration = ParseInterval(argv[1])))
5276 expiry = now + duration;
5279 reply("MSG_INVALID_DURATION", argv[1]);
5283 unsplit_string(argv + 2, argc - 2, reason);
5285 suspended = calloc(1, sizeof(*suspended));
5286 suspended->revoked = 0;
5287 suspended->issued = now;
5288 suspended->suspender = strdup(user->handle_info->handle);
5289 suspended->expires = expiry;
5290 suspended->reason = strdup(reason);
5291 suspended->cData = channel->channel_info;
5292 suspended->previous = suspended->cData->suspended;
5293 suspended->cData->suspended = suspended;
5295 if(suspended->expires)
5296 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
5298 if(IsSuspended(channel->channel_info))
5300 suspended->previous->revoked = now;
5301 if(suspended->previous->expires)
5302 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
5303 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
5304 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5308 /* Mark all users in channel as absent. */
5309 for(uData = channel->channel_info->users; uData; uData = uData->next)
5318 /* Mark the channel as suspended, then part. */
5319 channel->channel_info->flags |= CHANNEL_SUSPENDED;
5320 spamserv_cs_suspend(channel, expiry, 1, suspended->reason);
5321 DelChannelUser(chanserv, channel, suspended->reason, 0);
5322 reply("CSMSG_SUSPENDED", channel->name);
5323 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
5324 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5329 static CHANSERV_FUNC(cmd_cunsuspend)
5331 struct suspended *suspended;
5332 char message[MAXLEN];
5334 if(!IsSuspended(channel->channel_info))
5336 reply("CSMSG_NOT_SUSPENDED", channel->name);
5340 suspended = channel->channel_info->suspended;
5342 /* Expire the suspension and join ChanServ to the channel. */
5343 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
5344 chanserv_expire_suspension(suspended);
5345 reply("CSMSG_UNSUSPENDED", channel->name);
5346 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
5347 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
5351 typedef struct chanservSearch
5356 unsigned long unvisited;
5357 unsigned long registered;
5359 unsigned long flags;
5363 typedef void (*channel_search_func)(struct chanData *channel, void *data);
5366 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
5371 search = malloc(sizeof(struct chanservSearch));
5372 memset(search, 0, sizeof(*search));
5375 for(i = 0; i < argc; i++)
5377 /* Assume all criteria require arguments. */
5380 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
5384 if(!irccasecmp(argv[i], "name"))
5385 search->name = argv[++i];
5386 else if(!irccasecmp(argv[i], "registrar"))
5387 search->registrar = argv[++i];
5388 else if(!irccasecmp(argv[i], "unvisited"))
5389 search->unvisited = ParseInterval(argv[++i]);
5390 else if(!irccasecmp(argv[i], "registered"))
5391 search->registered = ParseInterval(argv[++i]);
5392 else if(!irccasecmp(argv[i], "flags"))
5395 if(!irccasecmp(argv[i], "nodelete"))
5396 search->flags |= CHANNEL_NODELETE;
5397 else if(!irccasecmp(argv[i], "suspended"))
5398 search->flags |= CHANNEL_SUSPENDED;
5399 else if(!irccasecmp(argv[i], "unreviewed"))
5400 search->flags |= CHANNEL_UNREVIEWED;
5403 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
5407 else if(!irccasecmp(argv[i], "limit"))
5408 search->limit = strtoul(argv[++i], NULL, 10);
5411 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
5416 if(search->name && !strcmp(search->name, "*"))
5418 if(search->registrar && !strcmp(search->registrar, "*"))
5419 search->registrar = 0;
5428 chanserv_channel_match(struct chanData *channel, search_t search)
5430 const char *name = channel->channel->name;
5431 if((search->name && !match_ircglob(name, search->name)) ||
5432 (search->registrar && !channel->registrar) ||
5433 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
5434 (search->unvisited && (now - channel->visited) < search->unvisited) ||
5435 (search->registered && (now - channel->registered) > search->registered) ||
5436 (search->flags && ((search->flags & channel->flags) != search->flags)))
5443 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
5445 struct chanData *channel;
5446 unsigned int matches = 0;
5448 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
5450 if(!chanserv_channel_match(channel, search))
5460 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
5465 search_print(struct chanData *channel, void *data)
5467 send_message_type(4, data, chanserv, "%s", channel->channel->name);
5470 static CHANSERV_FUNC(cmd_search)
5473 unsigned int matches;
5474 channel_search_func action;
5478 if(!irccasecmp(argv[1], "count"))
5479 action = search_count;
5480 else if(!irccasecmp(argv[1], "print"))
5481 action = search_print;
5484 reply("CSMSG_ACTION_INVALID", argv[1]);
5488 search = chanserv_search_create(user, argc - 2, argv + 2);
5492 if(action == search_count)
5493 search->limit = INT_MAX;
5495 if(action == search_print)
5496 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
5498 matches = chanserv_channel_search(search, action, user);
5501 reply("MSG_MATCH_COUNT", matches);
5503 reply("MSG_NO_MATCHES");
5509 static CHANSERV_FUNC(cmd_unvisited)
5511 struct chanData *cData;
5512 unsigned long interval = chanserv_conf.channel_expire_delay;
5513 char buffer[INTERVALLEN];
5514 unsigned int limit = 25, matches = 0;
5518 interval = ParseInterval(argv[1]);
5520 limit = atoi(argv[2]);
5523 intervalString(buffer, interval, user->handle_info);
5524 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
5526 for(cData = channelList; cData && matches < limit; cData = cData->next)
5528 if((now - cData->visited) < interval)
5531 intervalString(buffer, now - cData->visited, user->handle_info);
5532 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
5539 static MODCMD_FUNC(chan_opt_defaulttopic)
5545 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5547 reply("CSMSG_TOPIC_LOCKED", channel->name);
5551 topic = unsplit_string(argv+1, argc-1, NULL);
5553 free(channel->channel_info->topic);
5554 if(topic[0] == '*' && topic[1] == 0)
5556 topic = channel->channel_info->topic = NULL;
5560 topic = channel->channel_info->topic = strdup(topic);
5561 if(channel->channel_info->topic_mask
5562 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
5563 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5565 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
5568 if(channel->channel_info->topic)
5569 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
5571 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
5575 static MODCMD_FUNC(chan_opt_topicmask)
5579 struct chanData *cData = channel->channel_info;
5582 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5584 reply("CSMSG_TOPIC_LOCKED", channel->name);
5588 mask = unsplit_string(argv+1, argc-1, NULL);
5590 if(cData->topic_mask)
5591 free(cData->topic_mask);
5592 if(mask[0] == '*' && mask[1] == 0)
5594 cData->topic_mask = 0;
5598 cData->topic_mask = strdup(mask);
5600 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
5601 else if(!match_ircglob(cData->topic, cData->topic_mask))
5602 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5606 if(channel->channel_info->topic_mask)
5607 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
5609 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
5613 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
5617 char *greeting = unsplit_string(argv+1, argc-1, NULL);
5621 if(greeting[0] == '*' && greeting[1] == 0)
5625 unsigned int length = strlen(greeting);
5626 if(length > chanserv_conf.greeting_length)
5628 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
5631 *data = strdup(greeting);
5640 reply(name, user_find_message(user, "MSG_NONE"));
5644 static MODCMD_FUNC(chan_opt_greeting)
5646 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
5649 static MODCMD_FUNC(chan_opt_usergreeting)
5651 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
5654 static MODCMD_FUNC(chan_opt_modes)
5656 struct mod_chanmode *new_modes;
5661 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
5663 reply("CSMSG_NO_ACCESS");
5666 if(argv[1][0] == '*' && argv[1][1] == 0)
5668 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
5670 else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_IGN_REGISTERED|MCP_NO_APASS, 0)))
5672 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5675 else if(new_modes->argc > 1)
5677 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5678 mod_chanmode_free(new_modes);
5683 channel->channel_info->modes = *new_modes;
5684 modcmd_chanmode_announce(new_modes);
5685 mod_chanmode_free(new_modes);
5689 mod_chanmode_format(&channel->channel_info->modes, modes);
5691 reply("CSMSG_SET_MODES", modes);
5693 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
5697 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5699 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5701 struct chanData *cData = channel->channel_info;
5706 /* Set flag according to value. */
5707 if(enabled_string(argv[1]))
5709 cData->flags |= mask;
5712 else if(disabled_string(argv[1]))
5714 cData->flags &= ~mask;
5719 reply("MSG_INVALID_BINARY", argv[1]);
5725 /* Find current option value. */
5726 value = (cData->flags & mask) ? 1 : 0;
5730 reply(name, user_find_message(user, "MSG_ON"));
5732 reply(name, user_find_message(user, "MSG_OFF"));
5736 static MODCMD_FUNC(chan_opt_nodelete)
5738 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
5740 reply("MSG_SETTING_PRIVILEGED", argv[0]);
5744 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
5747 static MODCMD_FUNC(chan_opt_dynlimit)
5749 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
5752 static MODCMD_FUNC(chan_opt_offchannel)
5754 struct chanData *cData = channel->channel_info;
5759 /* Set flag according to value. */
5760 if(enabled_string(argv[1]))
5762 if(!IsOffChannel(cData))
5763 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
5764 cData->flags |= CHANNEL_OFFCHANNEL;
5767 else if(disabled_string(argv[1]))
5769 if(IsOffChannel(cData))
5771 struct mod_chanmode change;
5772 mod_chanmode_init(&change);
5774 change.args[0].mode = MODE_CHANOP;
5775 change.args[0].u.member = AddChannelUser(chanserv, channel);
5776 mod_chanmode_announce(chanserv, channel, &change);
5778 cData->flags &= ~CHANNEL_OFFCHANNEL;
5783 reply("MSG_INVALID_BINARY", argv[1]);
5789 /* Find current option value. */
5790 value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
5794 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
5796 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
5800 static MODCMD_FUNC(chan_opt_unreviewed)
5802 struct chanData *cData = channel->channel_info;
5803 int value = (cData->flags & CHANNEL_UNREVIEWED) ? 1 : 0;
5809 /* The two directions can have different ACLs. */
5810 if(enabled_string(argv[1]))
5812 else if(disabled_string(argv[1]))
5816 reply("MSG_INVALID_BINARY", argv[1]);
5820 if (new_value != value)
5822 struct svccmd *subcmd;
5823 char subcmd_name[32];
5825 snprintf(subcmd_name, sizeof(subcmd_name), "%s %s", argv[0], (new_value ? "on" : "off"));
5826 subcmd = dict_find(cmd->parent->commands, subcmd_name, NULL);
5829 reply("MSG_COMMAND_DISABLED", subcmd_name);
5832 else if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
5836 cData->flags |= CHANNEL_UNREVIEWED;
5839 free(cData->registrar);
5840 cData->registrar = strdup(user->handle_info->handle);
5841 cData->flags &= ~CHANNEL_UNREVIEWED;
5848 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_ON"));
5850 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_OFF"));
5854 static MODCMD_FUNC(chan_opt_defaults)
5856 struct userData *uData;
5857 struct chanData *cData;
5858 const char *confirm;
5859 enum levelOption lvlOpt;
5860 enum charOption chOpt;
5862 cData = channel->channel_info;
5863 uData = GetChannelUser(cData, user->handle_info);
5864 if(!uData || (uData->access < UL_OWNER))
5866 reply("CSMSG_OWNER_DEFAULTS", channel->name);
5869 confirm = make_confirmation_string(uData);
5870 if((argc < 2) || strcmp(argv[1], confirm))
5872 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
5875 cData->flags = (CHANNEL_DEFAULT_FLAGS & ~CHANNEL_PRESERVED_FLAGS)
5876 | (cData->flags & CHANNEL_PRESERVED_FLAGS);
5877 cData->modes = chanserv_conf.default_modes;
5878 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
5879 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
5880 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
5881 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
5882 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
5887 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5889 struct chanData *cData = channel->channel_info;
5890 struct userData *uData;
5891 unsigned short value;
5895 if(!check_user_level(channel, user, option, 1, 1))
5897 reply("CSMSG_CANNOT_SET");
5900 value = user_level_from_name(argv[1], UL_OWNER+1);
5901 if(!value && strcmp(argv[1], "0"))
5903 reply("CSMSG_INVALID_ACCESS", argv[1]);
5906 uData = GetChannelUser(cData, user->handle_info);
5907 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
5909 reply("CSMSG_BAD_SETLEVEL");
5915 if(value > cData->lvlOpts[lvlGiveOps])
5917 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
5922 if(value < cData->lvlOpts[lvlGiveVoice])
5924 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
5929 /* This test only applies to owners, since non-owners
5930 * trying to set an option to above their level get caught
5931 * by the CSMSG_BAD_SETLEVEL test above.
5933 if(value > uData->access)
5935 reply("CSMSG_BAD_SETTERS");
5942 cData->lvlOpts[option] = value;
5944 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
5948 static MODCMD_FUNC(chan_opt_enfops)
5950 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
5953 static MODCMD_FUNC(chan_opt_giveops)
5955 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
5958 static MODCMD_FUNC(chan_opt_enfmodes)
5960 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
5963 static MODCMD_FUNC(chan_opt_enftopic)
5965 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
5968 static MODCMD_FUNC(chan_opt_pubcmd)
5970 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
5973 static MODCMD_FUNC(chan_opt_setters)
5975 return channel_level_option(lvlSetters, CSFUNC_ARGS);
5978 static MODCMD_FUNC(chan_opt_ctcpusers)
5980 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
5983 static MODCMD_FUNC(chan_opt_userinfo)
5985 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
5988 static MODCMD_FUNC(chan_opt_givevoice)
5990 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
5993 static MODCMD_FUNC(chan_opt_topicsnarf)
5995 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
5998 static MODCMD_FUNC(chan_opt_vote)
6000 return channel_level_option(lvlVote, CSFUNC_ARGS);
6003 static MODCMD_FUNC(chan_opt_inviteme)
6005 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
6009 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
6011 struct chanData *cData = channel->channel_info;
6012 int count = charOptions[option].count, idx;
6016 idx = atoi(argv[1]);
6018 if(!isdigit(argv[1][0]) || (idx < 0) || (idx >= count))
6020 reply("CSMSG_INVALID_NUMERIC", idx);
6021 /* Show possible values. */
6022 for(idx = 0; idx < count; idx++)
6023 reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
6027 cData->chOpts[option] = charOptions[option].values[idx].value;
6031 /* Find current option value. */
6034 (idx < count) && (cData->chOpts[option] != charOptions[option].values[idx].value);
6038 /* Somehow, the option value is corrupt; reset it to the default. */
6039 cData->chOpts[option] = charOptions[option].default_value;
6044 reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
6048 static MODCMD_FUNC(chan_opt_protect)
6050 return channel_multiple_option(chProtect, CSFUNC_ARGS);
6053 static MODCMD_FUNC(chan_opt_toys)
6055 return channel_multiple_option(chToys, CSFUNC_ARGS);
6058 static MODCMD_FUNC(chan_opt_ctcpreaction)
6060 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
6063 static MODCMD_FUNC(chan_opt_topicrefresh)
6065 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
6068 static struct svccmd_list set_shows_list;
6071 handle_svccmd_unbind(struct svccmd *target) {
6073 for(ii=0; ii<set_shows_list.used; ++ii)
6074 if(target == set_shows_list.list[ii])
6075 set_shows_list.used = 0;
6078 static CHANSERV_FUNC(cmd_set)
6080 struct svccmd *subcmd;
6084 /* Check if we need to (re-)initialize set_shows_list. */
6085 if(!set_shows_list.used)
6087 if(!set_shows_list.size)
6089 set_shows_list.size = chanserv_conf.set_shows->used;
6090 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
6092 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
6094 const char *name = chanserv_conf.set_shows->list[ii];
6095 sprintf(buf, "%s %s", argv[0], name);
6096 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6099 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
6102 svccmd_list_append(&set_shows_list, subcmd);
6108 reply("CSMSG_CHANNEL_OPTIONS");
6109 for(ii = 0; ii < set_shows_list.used; ii++)
6111 subcmd = set_shows_list.list[ii];
6112 subcmd->command->func(user, channel, 1, argv+1, subcmd);
6117 sprintf(buf, "%s %s", argv[0], argv[1]);
6118 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6121 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
6124 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
6126 reply("CSMSG_NO_ACCESS");
6132 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
6136 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
6138 struct userData *uData;
6140 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6143 reply("CSMSG_NOT_USER", channel->name);
6149 /* Just show current option value. */
6151 else if(enabled_string(argv[1]))
6153 uData->flags |= mask;
6155 else if(disabled_string(argv[1]))
6157 uData->flags &= ~mask;
6161 reply("MSG_INVALID_BINARY", argv[1]);
6165 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
6169 static MODCMD_FUNC(user_opt_noautoop)
6171 struct userData *uData;
6173 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6176 reply("CSMSG_NOT_USER", channel->name);
6179 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
6180 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
6182 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
6185 static MODCMD_FUNC(user_opt_autoinvite)
6187 if((argc > 1) && !check_user_level(channel, user, lvlInviteMe, 1, 0))
6189 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
6191 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
6194 static MODCMD_FUNC(user_opt_info)
6196 struct userData *uData;
6199 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6203 /* If they got past the command restrictions (which require access)
6204 * but fail this test, we have some fool with security override on.
6206 reply("CSMSG_NOT_USER", channel->name);
6213 infoline = unsplit_string(argv + 1, argc - 1, NULL);
6214 if(strlen(infoline) > chanserv_conf.max_userinfo_length)
6216 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
6219 bp = strcspn(infoline, "\001");
6222 reply("CSMSG_BAD_INFOLINE", infoline[bp]);
6227 if(infoline[0] == '*' && infoline[1] == 0)
6230 uData->info = strdup(infoline);
6233 reply("CSMSG_USET_INFO", uData->info);
6235 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
6239 struct svccmd_list uset_shows_list;
6241 static CHANSERV_FUNC(cmd_uset)
6243 struct svccmd *subcmd;
6247 /* Check if we need to (re-)initialize uset_shows_list. */
6248 if(!uset_shows_list.used)
6252 "NoAutoOp", "AutoInvite", "Info"
6255 if(!uset_shows_list.size)
6257 uset_shows_list.size = ArrayLength(options);
6258 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
6260 for(ii = 0; ii < ArrayLength(options); ii++)
6262 const char *name = options[ii];
6263 sprintf(buf, "%s %s", argv[0], name);
6264 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6267 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
6270 svccmd_list_append(&uset_shows_list, subcmd);
6276 /* Do this so options are presented in a consistent order. */
6277 reply("CSMSG_USER_OPTIONS");
6278 for(ii = 0; ii < uset_shows_list.used; ii++)
6279 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
6283 sprintf(buf, "%s %s", argv[0], argv[1]);
6284 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6287 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
6291 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
6294 static CHANSERV_FUNC(cmd_giveownership)
6296 struct handle_info *new_owner_hi;
6297 struct userData *new_owner;
6298 struct userData *curr_user;
6299 struct userData *invoker;
6300 struct chanData *cData = channel->channel_info;
6301 struct do_not_register *dnr;
6302 const char *confirm;
6304 unsigned short co_access;
6305 char reason[MAXLEN];
6308 curr_user = GetChannelAccess(cData, user->handle_info);
6309 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
6310 if(!curr_user || (curr_user->access != UL_OWNER))
6312 struct userData *owner = NULL;
6313 for(curr_user = channel->channel_info->users;
6315 curr_user = curr_user->next)
6317 if(curr_user->access != UL_OWNER)
6321 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
6328 else if(!force && (now < cData->ownerTransfer + chanserv_conf.giveownership_period))
6330 char delay[INTERVALLEN];
6331 intervalString(delay, cData->ownerTransfer + chanserv_conf.giveownership_period - now, user->handle_info);
6332 reply("CSMSG_TRANSFER_WAIT", delay, channel->name);
6335 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
6337 if(new_owner_hi == user->handle_info)
6339 reply("CSMSG_NO_TRANSFER_SELF");
6342 new_owner = GetChannelAccess(cData, new_owner_hi);
6347 new_owner = add_channel_user(cData, new_owner_hi, UL_OWNER - 1, 0, NULL);
6351 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
6355 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
6357 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
6360 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
6361 if(!IsHelping(user))
6362 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
6364 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi->handle);
6367 invoker = GetChannelUser(cData, user->handle_info);
6368 if(invoker->access <= UL_OWNER)
6370 confirm = make_confirmation_string(curr_user);
6371 if((argc < 3) || strcmp(argv[2], confirm))
6373 reply("CSMSG_CONFIRM_GIVEOWNERSHIP", new_owner_hi->handle, confirm);
6377 if(new_owner->access >= UL_COOWNER)
6378 co_access = new_owner->access;
6380 co_access = UL_COOWNER;
6381 new_owner->access = UL_OWNER;
6383 curr_user->access = co_access;
6384 cData->ownerTransfer = now;
6385 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
6386 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
6387 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
6391 static CHANSERV_FUNC(cmd_suspend)
6393 struct handle_info *hi;
6394 struct userData *actor, *real_actor, *target;
6395 unsigned int override = 0;
6398 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6399 actor = GetChannelUser(channel->channel_info, user->handle_info);
6400 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
6401 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6403 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6406 if(target->access >= actor->access)
6408 reply("MSG_USER_OUTRANKED", hi->handle);
6411 if(target->flags & USER_SUSPENDED)
6413 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
6418 target->present = 0;
6421 if(!real_actor || target->access >= real_actor->access)
6422 override = CMD_LOG_OVERRIDE;
6423 target->flags |= USER_SUSPENDED;
6424 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
6425 return 1 | override;
6428 static CHANSERV_FUNC(cmd_unsuspend)
6430 struct handle_info *hi;
6431 struct userData *actor, *real_actor, *target;
6432 unsigned int override = 0;
6435 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6436 actor = GetChannelUser(channel->channel_info, user->handle_info);
6437 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
6438 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6440 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6443 if(target->access >= actor->access)
6445 reply("MSG_USER_OUTRANKED", hi->handle);
6448 if(!(target->flags & USER_SUSPENDED))
6450 reply("CSMSG_NOT_SUSPENDED", hi->handle);
6453 if(!real_actor || target->access >= real_actor->access)
6454 override = CMD_LOG_OVERRIDE;
6455 target->flags &= ~USER_SUSPENDED;
6456 scan_user_presence(target, NULL);
6457 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
6458 return 1 | override;
6461 static MODCMD_FUNC(cmd_deleteme)
6463 struct handle_info *hi;
6464 struct userData *target;
6465 const char *confirm_string;
6466 unsigned short access_level;
6469 hi = user->handle_info;
6470 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6472 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6475 if(target->access == UL_OWNER)
6477 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
6480 confirm_string = make_confirmation_string(target);
6481 if((argc < 2) || strcmp(argv[1], confirm_string))
6483 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
6486 access_level = target->access;
6487 channel_name = strdup(channel->name);
6488 del_channel_user(target, 1);
6489 reply("CSMSG_DELETED_YOU", access_level, channel_name);
6494 static CHANSERV_FUNC(cmd_addvote)
6496 struct chanData *cData = channel->channel_info;
6497 struct userData *uData, *target;
6498 struct handle_info *hi;
6499 if (!cData) return 0;
6501 hi = user->handle_info;
6502 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6504 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6507 if(target->access < 300) {
6508 reply("CSMSG_NO_ACCESS");
6512 reply("CSMSG_ADDVOTE_FULL");
6516 msg = unsplit_string(argv + 1, argc - 1, NULL);
6517 cData->vote = strdup(msg);
6518 cData->vote_start=0;
6519 dict_delete(cData->vote_options);
6520 cData->vote_options = dict_new();
6521 dict_set_free_data(cData->vote_options, free_vote_options);
6522 for(uData = channel->channel_info->users; uData; uData = uData->next)
6527 reply("CSMSG_ADDVOTE_DONE");
6531 static CHANSERV_FUNC(cmd_delvote)
6533 struct chanData *cData = channel->channel_info;
6534 struct userData *target;
6535 struct handle_info *hi;
6536 if (!cData) return 0;
6537 hi = user->handle_info;
6538 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6540 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6543 if(target->access < 300) {
6544 reply("CSMSG_NO_ACCESS");
6548 reply("CSMSG_NO_VOTE");
6553 reply("CSMSG_DELVOTE_DONE");
6557 static CHANSERV_FUNC(cmd_addoption)
6559 struct chanData *cData = channel->channel_info;
6560 struct userData *target;
6561 struct handle_info *hi;
6562 if (!cData) return 0;
6564 hi = user->handle_info;
6565 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6567 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6570 if(target->access < 300) {
6571 reply("CSMSG_NO_ACCESS");
6575 reply("CSMSG_NO_VOTE");
6581 msg = unsplit_string(argv + 1, argc - 1, NULL);
6584 unsigned int lastid = 1;
6585 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6586 struct vote_option *cvOpt = iter_data(it);
6587 if(cvOpt->option_id > lastid)
6588 lastid = cvOpt->option_id;
6590 struct vote_option *vOpt;
6591 vOpt = calloc(1, sizeof(*vOpt));
6592 vOpt->name = strdup(msg);
6593 vOpt->option_id = (lastid + 1);
6595 sprintf(str,"%i",(lastid + 1));
6596 vOpt->option_str = strdup(str);
6598 dict_insert(cData->vote_options,vOpt->option_str,vOpt);
6600 reply("CSMSG_ADDOPTION_DONE",dict_size(cData->vote_options),lastid,(lastid + 1));
6604 static CHANSERV_FUNC(cmd_deloption)
6606 struct chanData *cData = channel->channel_info;
6607 struct userData *uData, *target;
6608 struct handle_info *hi;
6609 if (!cData) return 0;
6611 hi = user->handle_info;
6612 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6614 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6617 if(target->access < 300) {
6618 reply("CSMSG_NO_ACCESS");
6622 reply("CSMSG_NO_VOTE");
6625 if(cData->vote_start) {
6626 if(dict_size(cData->vote_options) < 3) {
6627 reply("CSMSG_VOTE_NEED_OPTIONS");
6632 int find_id = atoi(argv[1]);
6634 unsigned int found = 0;
6637 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6639 if (find_id == ii) {
6640 struct vote_option *vOpt = iter_data(it);
6641 found = vOpt->option_id;
6643 sprintf(str,"%i",vOpt->option_id);
6644 dict_remove(cData->vote_options, str);
6649 for(uData = channel->channel_info->users; uData; uData = uData->next) {
6650 if(uData->votefor == found) {
6655 reply("CSMSG_DELOPTION_DONE");
6658 reply("CSMSG_DELOPTION_NONE");
6663 static CHANSERV_FUNC(cmd_vote)
6665 struct chanData *cData = channel->channel_info;
6666 struct userData *target;
6667 struct handle_info *hi;
6668 unsigned int votedfor = 0;
6669 char *votedfor_str = NULL;
6671 if (!cData || !cData->vote) {
6672 reply("CSMSG_NO_VOTE");
6675 if(argc > 1 && cData->vote_start) {
6676 hi = user->handle_info;
6677 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6679 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6682 if(!check_user_level(channel, user, lvlVote, 1, 0)) {
6683 reply("CSMSG_NO_ACCESS");
6687 reply("CSMSG_VOTE_VOTED");
6690 int find_id = atoi(argv[1]);
6693 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6695 if (find_id == ii) {
6696 struct vote_option *vOpt = iter_data(it);
6699 target->votefor = vOpt->option_id;
6700 votedfor = vOpt->option_id;
6701 votedfor_str = vOpt->name;
6705 reply("CSMSG_VOTE_INVALID");
6709 if (!cData->vote_start) {
6710 reply("CSMSG_VOTE_NOT_STARTED");
6712 reply("CSMSG_VOTE_QUESTION",cData->vote);
6714 unsigned int voteid = 0;
6717 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6718 struct vote_option *vOpt = iter_data(it);
6720 reply("CSMSG_VOTE_OPTION",voteid,vOpt->name,vOpt->voted);
6722 if(argc > 1 && cData->vote_start && votedfor_str) {
6723 reply("CSMSG_VOTE_DONE",votedfor_str);
6728 static CHANSERV_FUNC(cmd_startvote)
6730 struct chanData *cData = channel->channel_info;
6731 struct userData *target;
6732 struct handle_info *hi;
6733 if (!cData) return 0;
6734 hi = user->handle_info;
6735 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6737 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6740 if(target->access < 300) {
6741 reply("CSMSG_NO_ACCESS");
6745 reply("CSMSG_NO_VOTE");
6748 if(cData->vote_start) {
6749 reply("CSMSG_STARTVOTE_RUNNING");
6752 if(dict_size(cData->vote_options) < 2) {
6753 reply("CSMSG_VOTE_NEED_OPTIONS");
6756 cData->vote_start = 1;
6757 char response[MAXLEN];
6758 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_TOP"), user->nick);
6759 irc_privmsg(cmd->parent->bot, channel->name, response);
6760 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_QUESTION"), cData->vote);
6761 irc_privmsg(cmd->parent->bot, channel->name, response);
6762 unsigned int voteid = 0;
6764 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6765 struct vote_option *vOpt = iter_data(it);
6767 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_OPTION"), voteid, vOpt->name);
6768 irc_privmsg(cmd->parent->bot, channel->name, response);
6770 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_ACCESS"), cData->lvlOpts[lvlVote]); //Todo
6771 irc_privmsg(cmd->parent->bot, channel->name, response);
6772 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_HOWTO")); //Todo
6773 irc_privmsg(cmd->parent->bot, channel->name, response);
6777 static CHANSERV_FUNC(cmd_endvote)
6779 struct chanData *cData = channel->channel_info;
6780 struct userData *target;
6781 struct handle_info *hi;
6782 if (!cData) return 0;
6783 hi = user->handle_info;
6784 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6786 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6789 if(target->access < 300) {
6790 reply("CSMSG_NO_ACCESS");
6794 reply("CSMSG_NO_VOTE");
6797 if(!cData->vote_start) {
6798 reply("CSMSG_ENDVOTE_STOPPED");
6801 cData->vote_start = 0;
6802 reply("CSMSG_ENDVOTE_DONE");
6806 static CHANSERV_FUNC(cmd_voteresults)
6808 struct chanData *cData = channel->channel_info;
6809 struct userData *target;
6810 struct handle_info *hi;
6811 if (!cData) return 0;
6813 reply("CSMSG_NO_VOTE");
6816 if (argc > 1 && !irccasecmp(argv[1], "*")) {
6817 hi = user->handle_info;
6818 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6820 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6823 if(target->access < 300) {
6824 reply("CSMSG_NO_ACCESS");
6827 char response[MAXLEN];
6828 sprintf(response, user_find_message(user, "CSMSG_VOTERES_QUESTION"), cData->vote);
6829 irc_privmsg(cmd->parent->bot, channel->name, response);
6830 unsigned int voteid = 0;
6832 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6833 struct vote_option *vOpt = iter_data(it);
6835 sprintf(response, user_find_message(user, "CSMSG_VOTERES_OPTION"), voteid, vOpt->name, vOpt->voted);
6836 irc_privmsg(cmd->parent->bot, channel->name, response);
6839 reply("CSMSG_VOTE_QUESTION",cData->vote);
6840 unsigned int voteid = 0;
6842 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6843 struct vote_option *vOpt = iter_data(it);
6845 reply("CSMSG_VOTE_OPTION",voteid,vOpt->name,vOpt->voted);
6852 chanserv_refresh_topics(UNUSED_ARG(void *data))
6854 unsigned int refresh_num = (now - self->link_time) / chanserv_conf.refresh_period;
6855 struct chanData *cData;
6858 for(cData = channelList; cData; cData = cData->next)
6860 if(IsSuspended(cData))
6862 opt = cData->chOpts[chTopicRefresh];
6865 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
6868 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
6869 cData->last_refresh = refresh_num;
6871 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
6874 static CHANSERV_FUNC(cmd_unf)
6878 char response[MAXLEN];
6879 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
6880 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6881 irc_privmsg(cmd->parent->bot, channel->name, response);
6884 reply("CSMSG_UNF_RESPONSE");
6888 static CHANSERV_FUNC(cmd_ping)
6892 char response[MAXLEN];
6893 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
6894 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6895 irc_privmsg(cmd->parent->bot, channel->name, response);
6898 reply("CSMSG_PING_RESPONSE");
6902 static CHANSERV_FUNC(cmd_wut)
6906 char response[MAXLEN];
6907 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
6908 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6909 irc_privmsg(cmd->parent->bot, channel->name, response);
6912 reply("CSMSG_WUT_RESPONSE");
6916 static CHANSERV_FUNC(cmd_8ball)
6918 unsigned int i, j, accum;
6923 for(i=1; i<argc; i++)
6924 for(j=0; argv[i][j]; j++)
6925 accum = (accum << 5) - accum + toupper(argv[i][j]);
6926 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
6929 char response[MAXLEN];
6930 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
6931 irc_privmsg(cmd->parent->bot, channel->name, response);
6934 send_message_type(4, user, cmd->parent->bot, "%s", resp);
6938 static CHANSERV_FUNC(cmd_d)
6940 unsigned long sides, count, modifier, ii, total;
6941 char response[MAXLEN], *sep;
6945 if((count = strtoul(argv[1], &sep, 10)) < 1)
6955 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
6956 && (sides = strtoul(sep+1, &sep, 10)) > 1)
6960 else if((sep[0] == '-') && isdigit(sep[1]))
6961 modifier = strtoul(sep, NULL, 10);
6962 else if((sep[0] == '+') && isdigit(sep[1]))
6963 modifier = strtoul(sep+1, NULL, 10);
6970 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
6975 reply("CSMSG_BAD_DICE_COUNT", count, 10);
6978 for(total = ii = 0; ii < count; ++ii)
6979 total += (rand() % sides) + 1;
6982 if((count > 1) || modifier)
6984 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
6985 sprintf(response, fmt, total, count, sides, modifier);
6989 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
6990 sprintf(response, fmt, total, sides);
6993 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
6995 send_message_type(4, user, cmd->parent->bot, "%s", response);
6999 static CHANSERV_FUNC(cmd_huggle)
7001 /* CTCP must be via PRIVMSG, never notice */
7003 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
7005 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
7010 chanserv_adjust_limit(void *data)
7012 struct mod_chanmode change;
7013 struct chanData *cData = data;
7014 struct chanNode *channel = cData->channel;
7017 if(IsSuspended(cData))
7020 cData->limitAdjusted = now;
7021 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
7022 if(cData->modes.modes_set & MODE_LIMIT)
7024 if(limit > cData->modes.new_limit)
7025 limit = cData->modes.new_limit;
7026 else if(limit == cData->modes.new_limit)
7030 mod_chanmode_init(&change);
7031 change.modes_set = MODE_LIMIT;
7032 change.new_limit = limit;
7033 mod_chanmode_announce(chanserv, channel, &change);
7037 handle_new_channel(struct chanNode *channel)
7039 struct chanData *cData;
7041 if(!(cData = channel->channel_info))
7044 if(cData->modes.modes_set || cData->modes.modes_clear)
7045 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
7047 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
7048 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
7051 /* Welcome to my worst nightmare. Warning: Read (or modify)
7052 the code below at your own risk. */
7054 handle_join(struct modeNode *mNode)
7056 struct mod_chanmode change;
7057 struct userNode *user = mNode->user;
7058 struct chanNode *channel = mNode->channel;
7059 struct chanData *cData;
7060 struct userData *uData = NULL;
7061 struct banData *bData;
7062 struct handle_info *handle;
7063 unsigned int modes = 0, info = 0;
7067 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
7070 cData = channel->channel_info;
7071 if(channel->members.used > cData->max) {
7072 cData->max = channel->members.used;
7073 cData->max_time = now;
7076 for(i = 0; i < channel->invited.used; i++)
7078 if(channel->invited.list[i] == user) {
7079 userList_remove(&channel->invited, user);
7083 /* Check for bans. If they're joining through a ban, one of two
7085 * 1: Join during a netburst, by riding the break. Kick them
7086 * unless they have ops or voice in the channel.
7087 * 2: They're allowed to join through the ban (an invite in
7088 * ircu2.10, or a +e on Hybrid, or something).
7089 * If they're not joining through a ban, and the banlist is not
7090 * full, see if they're on the banlist for the channel. If so,
7093 if(user->uplink->burst && !mNode->modes)
7096 for(ii = 0; ii < channel->banlist.used; ii++)
7098 if(user_matches_glob(user, channel->banlist.list[ii]->ban, MATCH_USENICK))
7100 /* Riding a netburst. Naughty. */
7101 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
7107 mod_chanmode_init(&change);
7109 if(channel->banlist.used < MAXBANS)
7111 /* Not joining through a ban. */
7112 for(bData = cData->bans;
7113 bData && !user_matches_glob(user, bData->mask, MATCH_USENICK);
7114 bData = bData->next);
7118 char kick_reason[MAXLEN];
7119 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
7121 bData->triggered = now;
7122 if(bData != cData->bans)
7124 /* Shuffle the ban to the head of the list. */
7126 bData->next->prev = bData->prev;
7128 bData->prev->next = bData->next;
7131 bData->next = cData->bans;
7134 cData->bans->prev = bData;
7135 cData->bans = bData;
7138 change.args[0].mode = MODE_BAN;
7139 change.args[0].u.hostmask = bData->mask;
7140 mod_chanmode_announce(chanserv, channel, &change);
7141 KickChannelUser(user, channel, chanserv, kick_reason);
7146 /* ChanServ will not modify the limits in join-flooded channels,
7147 or when there are enough slots left below the limit. */
7148 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
7149 && !channel->join_flooded
7150 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
7152 /* The user count has begun "bumping" into the channel limit,
7153 so set a timer to raise the limit a bit. Any previous
7154 timers are removed so three incoming users within the delay
7155 results in one limit change, not three. */
7157 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7158 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7161 if(channel->join_flooded)
7163 /* don't automatically give ops or voice during a join flood */
7165 else if(cData->lvlOpts[lvlGiveOps] == 0)
7166 modes |= MODE_CHANOP;
7167 else if(cData->lvlOpts[lvlGiveVoice] == 0)
7168 modes |= MODE_VOICE;
7170 greeting = cData->greeting;
7171 if(user->handle_info)
7173 handle = user->handle_info;
7175 if(IsHelper(user) && !IsHelping(user))
7178 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7180 if(channel == chanserv_conf.support_channels.list[ii])
7182 HANDLE_SET_FLAG(user->handle_info, HELPING);
7188 uData = GetTrueChannelAccess(cData, handle);
7189 if(uData && !IsUserSuspended(uData))
7191 /* Ops and above were handled by the above case. */
7192 if(IsUserAutoOp(uData))
7194 if(uData->access >= cData->lvlOpts[lvlGiveOps])
7195 modes |= MODE_CHANOP;
7196 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
7197 modes |= MODE_VOICE;
7199 if(uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
7200 cData->visited = now;
7201 if(cData->user_greeting)
7202 greeting = cData->user_greeting;
7204 && (uData->access >= cData->lvlOpts[lvlUserInfo])
7205 && ((now - uData->seen) >= chanserv_conf.info_delay)
7213 /* If user joining normally (not during burst), apply op or voice,
7214 * and send greeting/userinfo as appropriate.
7216 if(!user->uplink->burst)
7220 if(modes & MODE_CHANOP)
7221 modes &= ~MODE_VOICE;
7222 change.args[0].mode = modes;
7223 change.args[0].u.member = mNode;
7224 mod_chanmode_announce(chanserv, channel, &change);
7227 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
7228 if(uData && info && (modes || !(channel->modes & MODE_DELAYJOINS)))
7229 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
7235 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
7237 struct mod_chanmode change;
7238 struct userData *channel;
7239 unsigned int ii, jj;
7241 if(!user->handle_info)
7244 mod_chanmode_init(&change);
7246 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
7248 struct chanNode *cn;
7249 struct modeNode *mn;
7250 if(IsUserSuspended(channel)
7251 || IsSuspended(channel->channel)
7252 || !(cn = channel->channel->channel))
7255 mn = GetUserMode(cn, user);
7258 if(!IsUserSuspended(channel)
7259 && IsUserAutoInvite(channel)
7260 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
7262 && !user->uplink->burst)
7263 irc_invite(chanserv, user, cn);
7267 if(channel->access >= UL_PRESENT && !HANDLE_FLAGGED(channel->handle, BOT))
7268 channel->channel->visited = now;
7270 if(IsUserAutoOp(channel))
7272 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
7273 change.args[0].mode = MODE_CHANOP;
7274 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
7275 change.args[0].mode = MODE_VOICE;
7277 change.args[0].mode = 0;
7278 change.args[0].u.member = mn;
7279 if(change.args[0].mode)
7280 mod_chanmode_announce(chanserv, cn, &change);
7283 channel->seen = now;
7284 channel->present = 1;
7287 for(ii = 0; ii < user->channels.used; ++ii)
7289 struct chanNode *chan = user->channels.list[ii]->channel;
7290 struct banData *ban;
7292 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
7293 || !chan->channel_info
7294 || IsSuspended(chan->channel_info))
7296 for(jj = 0; jj < chan->banlist.used; ++jj)
7297 if(user_matches_glob(user, chan->banlist.list[jj]->ban, MATCH_USENICK))
7299 if(jj < chan->banlist.used)
7301 for(ban = chan->channel_info->bans; ban; ban = ban->next)
7303 char kick_reason[MAXLEN];
7304 if(!user_matches_glob(user, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
7306 change.args[0].mode = MODE_BAN;
7307 change.args[0].u.hostmask = ban->mask;
7308 mod_chanmode_announce(chanserv, chan, &change);
7309 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
7310 KickChannelUser(user, chan, chanserv, kick_reason);
7311 ban->triggered = now;
7316 if(IsSupportHelper(user))
7318 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7320 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
7322 HANDLE_SET_FLAG(user->handle_info, HELPING);
7330 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
7332 struct chanData *cData;
7333 struct userData *uData;
7335 cData = mn->channel->channel_info;
7336 if(!cData || IsSuspended(cData) || IsLocal(mn->user))
7339 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
7341 /* Allow for a bit of padding so that the limit doesn't
7342 track the user count exactly, which could get annoying. */
7343 if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
7345 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7346 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7350 if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
7352 scan_user_presence(uData, mn->user);
7354 if (uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
7355 cData->visited = now;
7358 if(IsHelping(mn->user) && IsSupportHelper(mn->user))
7361 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii) {
7362 struct chanNode *channel;
7363 struct userNode *exclude;
7364 /* When looking at the channel that is being /part'ed, we
7365 * have to skip over the client that is leaving. For
7366 * other channels, we must not do that.
7368 channel = chanserv_conf.support_channels.list[ii];
7369 exclude = (channel == mn->channel) ? mn->user : NULL;
7370 if(find_handle_in_channel(channel, mn->user->handle_info, exclude))
7373 if(ii == chanserv_conf.support_channels.used)
7374 HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
7379 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
7381 struct userData *uData;
7383 if(!channel->channel_info || !kicker || IsService(kicker)
7384 || (kicker == victim) || IsSuspended(channel->channel_info)
7385 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
7388 if(protect_user(victim, kicker, channel->channel_info))
7390 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED_2");
7391 KickChannelUser(kicker, channel, chanserv, reason);
7394 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
7399 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
7401 struct chanData *cData;
7403 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
7406 cData = channel->channel_info;
7407 if(bad_topic(channel, user, channel->topic))
7409 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
7410 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
7411 SetChannelTopic(channel, chanserv, old_topic, 1);
7412 else if(cData->topic)
7413 SetChannelTopic(channel, chanserv, cData->topic, 1);
7416 /* With topicsnarf, grab the topic and save it as the default topic. */
7417 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
7420 cData->topic = strdup(channel->topic);
7426 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
7428 struct mod_chanmode *bounce = NULL;
7429 unsigned int bnc, ii;
7432 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
7435 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
7436 && mode_lock_violated(&channel->channel_info->modes, change))
7438 char correct[MAXLEN];
7439 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
7440 mod_chanmode_format(&channel->channel_info->modes, correct);
7441 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
7443 for(ii = bnc = 0; ii < change->argc; ++ii)
7445 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
7447 const struct userNode *victim = change->args[ii].u.member->user;
7448 if(!protect_user(victim, user, channel->channel_info))
7451 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7454 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
7455 bounce->args[bnc].u.member = GetUserMode(channel, user);
7456 if(bounce->args[bnc].u.member)
7460 bounce->args[bnc].mode = MODE_CHANOP;
7461 bounce->args[bnc].u.member = change->args[ii].u.member;
7463 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
7465 else if(change->args[ii].mode & MODE_CHANOP)
7467 const struct userNode *victim = change->args[ii].u.member->user;
7468 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
7471 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7472 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
7473 bounce->args[bnc].u.member = change->args[ii].u.member;
7476 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
7478 const char *ban = change->args[ii].u.hostmask;
7479 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
7482 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7483 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
7484 bounce->args[bnc].u.hostmask = strdup(ban);
7486 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
7491 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
7492 mod_chanmode_announce(chanserv, channel, bounce);
7493 for(ii = 0; ii < change->argc; ++ii)
7494 if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
7495 free((char*)bounce->args[ii].u.hostmask);
7496 mod_chanmode_free(bounce);
7501 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
7503 struct chanNode *channel;
7504 struct banData *bData;
7505 struct mod_chanmode change;
7506 unsigned int ii, jj;
7507 char kick_reason[MAXLEN];
7509 mod_chanmode_init(&change);
7511 change.args[0].mode = MODE_BAN;
7512 for(ii = 0; ii < user->channels.used; ++ii)
7514 channel = user->channels.list[ii]->channel;
7515 /* Need not check for bans if they're opped or voiced. */
7516 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
7518 /* Need not check for bans unless channel registration is active. */
7519 if(!channel->channel_info || IsSuspended(channel->channel_info))
7521 /* Look for a matching ban already on the channel. */
7522 for(jj = 0; jj < channel->banlist.used; ++jj)
7523 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
7525 /* Need not act if we found one. */
7526 if(jj < channel->banlist.used)
7528 /* Look for a matching ban in this channel. */
7529 for(bData = channel->channel_info->bans; bData; bData = bData->next)
7531 if(!user_matches_glob(user, bData->mask, MATCH_USENICK | MATCH_VISIBLE))
7533 change.args[0].u.hostmask = bData->mask;
7534 mod_chanmode_announce(chanserv, channel, &change);
7535 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
7536 KickChannelUser(user, channel, chanserv, kick_reason);
7537 bData->triggered = now;
7538 break; /* we don't need to check any more bans in the channel */
7543 static void handle_rename(struct handle_info *handle, const char *old_handle)
7545 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
7549 dict_remove2(handle_dnrs, old_handle, 1);
7550 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
7551 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
7556 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
7558 struct userNode *h_user;
7560 if(handle->channels)
7562 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
7563 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
7565 while(handle->channels)
7566 del_channel_user(handle->channels, 1);
7571 handle_server_link(UNUSED_ARG(struct server *server))
7573 struct chanData *cData;
7575 for(cData = channelList; cData; cData = cData->next)
7577 if(!IsSuspended(cData))
7578 cData->may_opchan = 1;
7579 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
7580 && !cData->channel->join_flooded
7581 && ((cData->channel->limit - cData->channel->members.used)
7582 < chanserv_conf.adjust_threshold))
7584 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7585 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7591 chanserv_conf_read(void)
7595 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
7596 struct mod_chanmode *change;
7597 struct string_list *strlist;
7598 struct chanNode *chan;
7601 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
7603 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
7606 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7607 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7608 chanserv_conf.support_channels.used = 0;
7609 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
7611 for(ii = 0; ii < strlist->used; ++ii)
7613 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7616 chan = AddChannel(strlist->list[ii], now, str2, NULL);
7618 channelList_append(&chanserv_conf.support_channels, chan);
7621 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
7624 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7627 chan = AddChannel(str, now, str2, NULL);
7629 channelList_append(&chanserv_conf.support_channels, chan);
7631 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
7632 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
7633 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
7634 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
7635 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
7636 chanserv_conf.greeting_length = str ? atoi(str) : 200;
7637 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
7638 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
7639 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
7640 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
7641 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
7642 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
7643 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
7644 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
7645 str = database_get_data(conf_node, KEY_DNR_EXPIRE_FREQ, RECDB_QSTRING);
7646 chanserv_conf.dnr_expire_frequency = str ? ParseInterval(str) : 3600;
7647 str = database_get_data(conf_node, KEY_INVITED_INTERVAL, RECDB_QSTRING);
7648 chanserv_conf.invited_timeout = str ? ParseInterval(str) : 600*2;
7649 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
7650 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
7651 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
7652 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
7653 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
7654 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
7655 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
7656 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
7657 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
7659 NickChange(chanserv, str, 0);
7660 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
7661 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
7662 str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
7663 chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
7664 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
7665 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
7666 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
7667 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
7668 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
7669 chanserv_conf.max_owned = str ? atoi(str) : 5;
7670 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
7671 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
7672 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
7673 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
7674 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
7675 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
7676 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
7679 safestrncpy(mode_line, str, sizeof(mode_line));
7680 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
7681 if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE|MCP_NO_APASS, 0))
7682 && (change->argc < 2))
7684 chanserv_conf.default_modes = *change;
7685 mod_chanmode_free(change);
7687 free_string_list(chanserv_conf.set_shows);
7688 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
7690 strlist = string_list_copy(strlist);
7693 static const char *list[] = {
7694 /* free form text */
7695 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
7696 /* options based on user level */
7697 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
7698 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
7699 /* multiple choice options */
7700 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
7701 /* binary options */
7702 "DynLimit", "NoDelete", "expire", "Vote",
7706 strlist = alloc_string_list(ArrayLength(list)-1);
7707 for(ii=0; list[ii]; ii++)
7708 string_list_append(strlist, strdup(list[ii]));
7710 chanserv_conf.set_shows = strlist;
7711 /* We don't look things up now, in case the list refers to options
7712 * defined by modules initialized after this point. Just mark the
7713 * function list as invalid, so it will be initialized.
7715 set_shows_list.used = 0;
7716 free_string_list(chanserv_conf.eightball);
7717 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
7720 strlist = string_list_copy(strlist);
7724 strlist = alloc_string_list(4);
7725 string_list_append(strlist, strdup("Yes."));
7726 string_list_append(strlist, strdup("No."));
7727 string_list_append(strlist, strdup("Maybe so."));
7729 chanserv_conf.eightball = strlist;
7730 free_string_list(chanserv_conf.old_ban_names);
7731 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
7733 strlist = string_list_copy(strlist);
7735 strlist = alloc_string_list(2);
7736 chanserv_conf.old_ban_names = strlist;
7737 str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
7738 off_channel = str ? atoi(str) : 0;
7742 chanserv_note_type_read(const char *key, struct record_data *rd)
7745 struct note_type *ntype;
7748 if(!(obj = GET_RECORD_OBJECT(rd)))
7750 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
7753 if(!(ntype = chanserv_create_note_type(key)))
7755 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
7759 /* Figure out set access */
7760 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
7762 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7763 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
7765 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
7767 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
7768 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
7770 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
7772 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
7776 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
7777 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7778 ntype->set_access.min_opserv = 0;
7781 /* Figure out visibility */
7782 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
7783 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7784 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
7785 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7786 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
7787 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
7788 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
7789 ntype->visible_type = NOTE_VIS_ALL;
7791 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7793 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
7794 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
7798 vote_option_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7800 struct vote_option *vOpt;
7803 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7805 log_module(CS_LOG, LOG_ERROR, "Invalid vote option in %s.", chan->channel->name);
7809 vOpt = calloc(1, sizeof(*vOpt));
7810 vOpt->name = strdup(database_get_data(rd->d.object, KEY_VOTE_OPTION_NAME, RECDB_QSTRING));
7811 str = database_get_data(rd->d.object, KEY_VOTE_OPTION_VOTED, RECDB_QSTRING);
7812 vOpt->voted = str ? atoi(str) : 0;
7813 vOpt->option_id = str ? atoi(key) : 0;
7814 vOpt->option_str = strdup(key);
7815 dict_insert(chan->vote_options,vOpt->option_str,vOpt);
7819 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7821 struct handle_info *handle;
7822 struct userData *uData;
7823 char *seen, *inf, *flags, *voted, *votefor;
7824 unsigned long last_seen;
7825 unsigned short access_level;
7827 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7829 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
7833 access_level = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
7834 if(access_level > UL_OWNER)
7836 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
7840 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
7841 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
7842 last_seen = seen ? strtoul(seen, NULL, 0) : now;
7843 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
7844 voted = database_get_data(rd->d.object, KEY_VOTE_VOTED, RECDB_QSTRING);
7845 votefor = database_get_data(rd->d.object, KEY_VOTE_VOTEDFOR, RECDB_QSTRING);
7846 handle = get_handle_info(key);
7849 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
7853 uData = add_channel_user(chan, handle, access_level, last_seen, inf);
7854 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
7856 uData->voted = voted ? strtoul(voted, NULL, 0) : 0;
7857 uData->votefor = votefor ? strtoul(votefor, NULL, 0) : 0;
7865 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7867 struct banData *bData;
7868 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
7869 unsigned long set_time, triggered_time, expires_time;
7871 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7873 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
7877 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
7878 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
7879 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
7880 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
7881 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
7882 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
7883 if (!reason || !owner)
7886 set_time = set ? strtoul(set, NULL, 0) : now;
7887 triggered_time = triggered ? strtoul(triggered, NULL, 0) : 0;
7889 expires_time = strtoul(s_expires, NULL, 0);
7891 expires_time = set_time + atoi(s_duration);
7895 if(!reason || (expires_time && (expires_time < now)))
7898 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
7901 static struct suspended *
7902 chanserv_read_suspended(dict_t obj)
7904 struct suspended *suspended = calloc(1, sizeof(*suspended));
7908 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
7909 suspended->expires = str ? strtoul(str, NULL, 0) : 0;
7910 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
7911 suspended->revoked = str ? strtoul(str, NULL, 0) : 0;
7912 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
7913 suspended->issued = str ? strtoul(str, NULL, 0) : 0;
7914 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
7915 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
7916 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
7917 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
7922 chanserv_channel_read(const char *key, struct record_data *hir)
7924 struct suspended *suspended;
7925 struct mod_chanmode *modes;
7926 struct chanNode *cNode;
7927 struct chanData *cData;
7928 struct dict *channel, *obj;
7929 char *str, *argv[10];
7933 channel = hir->d.object;
7935 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
7938 cNode = AddChannel(key, now, NULL, NULL);
7941 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
7944 cData = register_channel(cNode, str);
7947 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
7951 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
7953 enum levelOption lvlOpt;
7954 enum charOption chOpt;
7956 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
7957 cData->flags = atoi(str);
7959 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7961 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
7963 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
7964 else if(levelOptions[lvlOpt].old_flag)
7966 if(cData->flags & levelOptions[lvlOpt].old_flag)
7967 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
7969 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
7973 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7975 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
7977 cData->chOpts[chOpt] = str[0];
7980 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
7982 enum levelOption lvlOpt;
7983 enum charOption chOpt;
7986 cData->flags = base64toint(str, 5);
7987 count = strlen(str += 5);
7988 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7991 if(levelOptions[lvlOpt].old_flag)
7993 if(cData->flags & levelOptions[lvlOpt].old_flag)
7994 lvl = levelOptions[lvlOpt].flag_value;
7996 lvl = levelOptions[lvlOpt].default_value;
7998 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
8000 case 'c': lvl = UL_COOWNER; break;
8001 case 'm': lvl = UL_MASTER; break;
8002 case 'n': lvl = UL_OWNER+1; break;
8003 case 'o': lvl = UL_OP; break;
8004 case 'p': lvl = UL_PEON; break;
8005 case 'w': lvl = UL_OWNER; break;
8006 default: lvl = 0; break;
8008 cData->lvlOpts[lvlOpt] = lvl;
8010 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8011 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
8014 if((str = database_get_data(hir->d.object, KEY_EXPIRE, RECDB_QSTRING)))
8016 cData->expiry = atoi(str);
8017 if(cData->expiry > 0) {
8018 if(cData->expiry > now) {
8019 timeq_add(cData->expiry, chanserv_expire_channel, cData);
8021 timeq_add(1, chanserv_expire_channel, cData);
8028 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
8030 suspended = chanserv_read_suspended(obj);
8031 cData->suspended = suspended;
8032 suspended->cData = cData;
8033 /* We could use suspended->expires and suspended->revoked to
8034 * set the CHANNEL_SUSPENDED flag, but we don't. */
8036 else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
8038 suspended = calloc(1, sizeof(*suspended));
8039 suspended->issued = 0;
8040 suspended->revoked = 0;
8041 suspended->suspender = strdup(str);
8042 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
8043 suspended->expires = str ? atoi(str) : 0;
8044 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
8045 suspended->reason = strdup(str ? str : "No reason");
8046 suspended->previous = NULL;
8047 cData->suspended = suspended;
8048 suspended->cData = cData;
8052 cData->flags &= ~CHANNEL_SUSPENDED;
8053 suspended = NULL; /* to squelch a warning */
8056 if(IsSuspended(cData)) {
8057 if(suspended->expires > now)
8058 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
8059 else if(suspended->expires)
8060 cData->flags &= ~CHANNEL_SUSPENDED;
8063 if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
8064 struct mod_chanmode change;
8065 mod_chanmode_init(&change);
8067 change.args[0].mode = MODE_CHANOP;
8068 change.args[0].u.member = AddChannelUser(chanserv, cNode);
8069 mod_chanmode_announce(chanserv, cNode, &change);
8072 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
8073 cData->registered = str ? strtoul(str, NULL, 0) : now;
8074 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
8075 cData->visited = str ? strtoul(str, NULL, 0) : now;
8076 str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
8077 cData->ownerTransfer = str ? strtoul(str, NULL, 0) : 0;
8078 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
8079 cData->max = str ? atoi(str) : 0;
8080 str = database_get_data(channel, KEY_MAX_TIME, RECDB_QSTRING);
8081 cData->max_time = str ? atoi(str) : 0;
8082 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
8083 cData->greeting = str ? strdup(str) : NULL;
8084 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
8085 cData->user_greeting = str ? strdup(str) : NULL;
8086 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
8087 cData->topic_mask = str ? strdup(str) : NULL;
8088 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
8089 cData->topic = str ? strdup(str) : NULL;
8091 str = database_get_data(channel, KEY_VOTE, RECDB_QSTRING);
8093 cData->vote = str ? strdup(str) : NULL;
8094 dict_delete(cData->vote_options);
8095 cData->vote_options = dict_new();
8096 dict_set_free_data(cData->vote_options, free_vote_options);
8097 str = database_get_data(channel, KEY_VOTE_START, RECDB_QSTRING);
8098 cData->vote_start = str ? atoi(str) : 0;
8099 obj = database_get_data(channel, KEY_VOTE_OPTIONS, RECDB_OBJECT);
8100 for(it = dict_first(obj); it; it = iter_next(it)) {
8101 vote_option_read_helper(iter_key(it), iter_data(it), cData);
8105 if(!IsSuspended(cData)
8106 && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
8107 && (argc = split_line(str, 0, ArrayLength(argv), argv))
8108 && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE|MCP_NO_APASS, 0))) {
8109 cData->modes = *modes;
8111 cData->modes.modes_set |= MODE_REGISTERED;
8112 if(cData->modes.argc > 1)
8113 cData->modes.argc = 1;
8114 mod_chanmode_announce(chanserv, cNode, &cData->modes);
8115 mod_chanmode_free(modes);
8118 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
8119 for(it = dict_first(obj); it; it = iter_next(it))
8120 user_read_helper(iter_key(it), iter_data(it), cData);
8122 if(!cData->users && !IsProtected(cData))
8124 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
8125 unregister_channel(cData, "has empty user list.");
8129 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
8130 for(it = dict_first(obj); it; it = iter_next(it))
8131 ban_read_helper(iter_key(it), iter_data(it), cData);
8133 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
8134 for(it = dict_first(obj); it; it = iter_next(it))
8136 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
8137 struct record_data *rd = iter_data(it);
8138 const char *note, *setter;
8140 if(rd->type != RECDB_OBJECT)
8142 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
8146 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
8148 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
8150 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
8154 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
8155 if(!setter) setter = "<unknown>";
8156 chanserv_add_channel_note(cData, ntype, setter, note);
8164 chanserv_dnr_read(const char *key, struct record_data *hir)
8166 const char *setter, *reason, *str;
8167 struct do_not_register *dnr;
8168 unsigned long expiry;
8170 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
8173 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
8176 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
8179 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
8182 str = database_get_data(hir->d.object, KEY_EXPIRES, RECDB_QSTRING);
8183 expiry = str ? strtoul(str, NULL, 0) : 0;
8184 if(expiry && expiry <= now)
8186 dnr = chanserv_add_dnr(key, setter, expiry, reason);
8189 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
8191 dnr->set = atoi(str);
8197 chanserv_saxdb_read(struct dict *database)
8199 struct dict *section;
8202 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
8203 for(it = dict_first(section); it; it = iter_next(it))
8204 chanserv_note_type_read(iter_key(it), iter_data(it));
8206 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
8207 for(it = dict_first(section); it; it = iter_next(it))
8208 chanserv_channel_read(iter_key(it), iter_data(it));
8210 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
8211 for(it = dict_first(section); it; it = iter_next(it))
8212 chanserv_dnr_read(iter_key(it), iter_data(it));
8218 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
8220 int high_present = 0;
8221 saxdb_start_record(ctx, KEY_USERS, 1);
8222 for(; uData; uData = uData->next)
8224 if((uData->access >= UL_PRESENT) && uData->present && !HANDLE_FLAGGED(uData->handle, BOT))
8226 saxdb_start_record(ctx, uData->handle->handle, 0);
8227 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
8228 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
8230 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
8231 if(uData->channel->vote && uData->voted)
8232 saxdb_write_int(ctx, KEY_VOTE_VOTED, uData->voted);
8233 if(uData->channel->vote && uData->votefor)
8234 saxdb_write_int(ctx, KEY_VOTE_VOTEDFOR, uData->votefor);
8236 saxdb_write_string(ctx, KEY_INFO, uData->info);
8237 saxdb_end_record(ctx);
8239 saxdb_end_record(ctx);
8240 return high_present;
8244 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
8248 saxdb_start_record(ctx, KEY_BANS, 1);
8249 for(; bData; bData = bData->next)
8251 saxdb_start_record(ctx, bData->mask, 0);
8252 saxdb_write_int(ctx, KEY_SET, bData->set);
8253 if(bData->triggered)
8254 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
8256 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
8258 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
8260 saxdb_write_string(ctx, KEY_REASON, bData->reason);
8261 saxdb_end_record(ctx);
8263 saxdb_end_record(ctx);
8267 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
8269 saxdb_start_record(ctx, name, 0);
8270 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
8271 saxdb_write_string(ctx, KEY_REASON, susp->reason);
8273 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
8275 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
8277 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
8279 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
8280 saxdb_end_record(ctx);
8284 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
8288 enum levelOption lvlOpt;
8289 enum charOption chOpt;
8292 saxdb_start_record(ctx, channel->channel->name, 1);
8294 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
8295 saxdb_write_int(ctx, KEY_MAX, channel->max);
8296 saxdb_write_int(ctx, KEY_MAX_TIME, channel->max_time);
8298 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
8299 if(channel->registrar)
8300 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
8301 if(channel->greeting)
8302 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
8303 if(channel->user_greeting)
8304 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
8305 if(channel->topic_mask)
8306 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
8307 if(channel->suspended)
8308 chanserv_write_suspended(ctx, "suspended", channel->suspended);
8310 saxdb_write_int(ctx, KEY_EXPIRE, channel->expiry);
8313 saxdb_write_string(ctx, KEY_VOTE, channel->vote);
8314 if(channel->vote_start)
8315 saxdb_write_int(ctx, KEY_VOTE_START, channel->vote_start);
8316 if (dict_size(channel->vote_options)) {
8317 saxdb_start_record(ctx, KEY_VOTE_OPTIONS, 1);
8318 for (it = dict_first(channel->vote_options); it; it = iter_next(it)) {
8319 struct vote_option *vOpt = iter_data(it);
8321 sprintf(str,"%i",vOpt->option_id);
8322 saxdb_start_record(ctx, str, 0);
8324 saxdb_write_int(ctx, KEY_VOTE_OPTION_VOTED, vOpt->voted);
8326 saxdb_write_string(ctx, KEY_VOTE_OPTION_NAME, vOpt->name);
8327 saxdb_end_record(ctx);
8329 saxdb_end_record(ctx);
8333 saxdb_start_record(ctx, KEY_OPTIONS, 0);
8334 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
8335 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8336 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
8337 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8339 buf[0] = channel->chOpts[chOpt];
8341 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
8343 saxdb_end_record(ctx);
8345 if(channel->modes.modes_set || channel->modes.modes_clear)
8347 mod_chanmode_format(&channel->modes, buf);
8348 saxdb_write_string(ctx, KEY_MODES, buf);
8351 high_present = chanserv_write_users(ctx, channel->users);
8352 chanserv_write_bans(ctx, channel->bans);
8354 if(dict_size(channel->notes))
8358 saxdb_start_record(ctx, KEY_NOTES, 1);
8359 for(it = dict_first(channel->notes); it; it = iter_next(it))
8361 struct note *note = iter_data(it);
8362 saxdb_start_record(ctx, iter_key(it), 0);
8363 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
8364 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
8365 saxdb_end_record(ctx);
8367 saxdb_end_record(ctx);
8370 if(channel->ownerTransfer)
8371 saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
8372 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
8373 saxdb_end_record(ctx);
8377 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
8381 saxdb_start_record(ctx, ntype->name, 0);
8382 switch(ntype->set_access_type)
8384 case NOTE_SET_CHANNEL_ACCESS:
8385 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
8387 case NOTE_SET_CHANNEL_SETTER:
8388 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
8390 case NOTE_SET_PRIVILEGED: default:
8391 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
8394 switch(ntype->visible_type)
8396 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
8397 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
8398 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
8400 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
8401 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
8402 saxdb_end_record(ctx);
8406 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
8408 struct do_not_register *dnr;
8409 dict_iterator_t it, next;
8411 for(it = dict_first(dnrs); it; it = next)
8413 next = iter_next(it);
8414 dnr = iter_data(it);
8415 if(dnr->expires && dnr->expires <= now)
8417 dict_remove(dnrs, iter_key(it));
8420 saxdb_start_record(ctx, dnr->chan_name, 0);
8422 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
8424 saxdb_write_int(ctx, KEY_EXPIRES, dnr->expires);
8425 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
8426 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
8427 saxdb_end_record(ctx);
8432 chanserv_saxdb_write(struct saxdb_context *ctx)
8435 struct chanData *channel;
8438 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
8439 for(it = dict_first(note_types); it; it = iter_next(it))
8440 chanserv_write_note_type(ctx, iter_data(it));
8441 saxdb_end_record(ctx);
8444 saxdb_start_record(ctx, KEY_DNR, 1);
8445 write_dnrs_helper(ctx, handle_dnrs);
8446 write_dnrs_helper(ctx, plain_dnrs);
8447 write_dnrs_helper(ctx, mask_dnrs);
8448 saxdb_end_record(ctx);
8451 saxdb_start_record(ctx, KEY_CHANNELS, 1);
8452 for(channel = channelList; channel; channel = channel->next)
8453 chanserv_write_channel(ctx, channel);
8454 saxdb_end_record(ctx);
8460 chanserv_db_cleanup(void) {
8462 unreg_part_func(handle_part);
8464 unregister_channel(channelList, "terminating.");
8465 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
8466 UnlockChannel(chanserv_conf.support_channels.list[ii]);
8467 free(chanserv_conf.support_channels.list);
8468 dict_delete(handle_dnrs);
8469 dict_delete(plain_dnrs);
8470 dict_delete(mask_dnrs);
8471 dict_delete(note_types);
8472 free_string_list(chanserv_conf.eightball);
8473 free_string_list(chanserv_conf.old_ban_names);
8474 free_string_list(chanserv_conf.set_shows);
8475 free(set_shows_list.list);
8476 free(uset_shows_list.list);
8479 struct userData *helper = helperList;
8480 helperList = helperList->next;
8485 #if defined(GCC_VARMACROS)
8486 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ARGS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ARGS)
8487 #elif defined(C99_VARMACROS)
8488 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, __VA_ARGS__)
8490 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
8491 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
8494 init_chanserv(const char *nick)
8496 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
8497 conf_register_reload(chanserv_conf_read);
8501 reg_server_link_func(handle_server_link);
8502 reg_new_channel_func(handle_new_channel);
8503 reg_join_func(handle_join);
8504 reg_part_func(handle_part);
8505 reg_kick_func(handle_kick);
8506 reg_topic_func(handle_topic);
8507 reg_mode_change_func(handle_mode);
8508 reg_nick_change_func(handle_nick_change);
8509 reg_auth_func(handle_auth);
8512 reg_handle_rename_func(handle_rename);
8513 reg_unreg_func(handle_unreg);
8515 handle_dnrs = dict_new();
8516 dict_set_free_data(handle_dnrs, free);
8517 plain_dnrs = dict_new();
8518 dict_set_free_data(plain_dnrs, free);
8519 mask_dnrs = dict_new();
8520 dict_set_free_data(mask_dnrs, free);
8522 reg_svccmd_unbind_func(handle_svccmd_unbind);
8523 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
8524 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
8525 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
8526 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
8527 DEFINE_COMMAND(dnrsearch, 3, 0, "template", "noregister", NULL);
8528 modcmd_register(chanserv_module, "dnrsearch print", NULL, 0, 0, NULL);
8529 modcmd_register(chanserv_module, "dnrsearch remove", NULL, 0, 0, NULL);
8530 modcmd_register(chanserv_module, "dnrsearch count", NULL, 0, 0, NULL);
8531 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
8532 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
8533 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
8534 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
8535 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
8537 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
8538 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
8540 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8541 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8542 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8543 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8544 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
8546 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
8547 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
8548 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
8549 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8550 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8552 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8553 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
8554 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8555 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
8557 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
8558 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
8559 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8560 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8561 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
8562 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8563 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8564 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8566 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8567 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8568 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8569 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
8570 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
8571 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
8572 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
8573 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
8574 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8575 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
8576 DEFINE_COMMAND(invitemeall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8577 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
8578 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
8579 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8580 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8582 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
8583 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8584 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8585 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8586 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
8588 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
8589 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
8591 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
8592 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8593 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8594 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8595 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8596 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8597 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8598 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8599 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8600 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8601 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8603 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
8604 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
8606 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
8607 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
8608 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
8609 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
8611 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
8612 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
8613 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
8614 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
8615 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
8617 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8618 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8619 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8620 DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8621 DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8622 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8624 DEFINE_COMMAND(addvote, 1, MODCMD_REQUIRE_AUTHED, NULL);
8625 DEFINE_COMMAND(delvote, 1, MODCMD_REQUIRE_AUTHED, NULL);
8626 DEFINE_COMMAND(addoption, 1, MODCMD_REQUIRE_AUTHED, NULL);
8627 DEFINE_COMMAND(deloption, 1, MODCMD_REQUIRE_AUTHED, NULL);
8628 DEFINE_COMMAND(vote, 1, MODCMD_REQUIRE_AUTHED, NULL);
8629 DEFINE_COMMAND(startvote, 1, MODCMD_REQUIRE_AUTHED, NULL);
8630 DEFINE_COMMAND(endvote, 1, MODCMD_REQUIRE_AUTHED, NULL);
8631 DEFINE_COMMAND(voteresults, 1, MODCMD_REQUIRE_AUTHED, NULL);
8633 /* Channel options */
8634 DEFINE_CHANNEL_OPTION(defaulttopic);
8635 DEFINE_CHANNEL_OPTION(topicmask);
8636 DEFINE_CHANNEL_OPTION(greeting);
8637 DEFINE_CHANNEL_OPTION(usergreeting);
8638 DEFINE_CHANNEL_OPTION(modes);
8639 DEFINE_CHANNEL_OPTION(enfops);
8640 DEFINE_CHANNEL_OPTION(giveops);
8641 DEFINE_CHANNEL_OPTION(protect);
8642 DEFINE_CHANNEL_OPTION(enfmodes);
8643 DEFINE_CHANNEL_OPTION(enftopic);
8644 DEFINE_CHANNEL_OPTION(pubcmd);
8645 DEFINE_CHANNEL_OPTION(givevoice);
8646 DEFINE_CHANNEL_OPTION(userinfo);
8647 DEFINE_CHANNEL_OPTION(dynlimit);
8648 DEFINE_CHANNEL_OPTION(topicsnarf);
8649 DEFINE_CHANNEL_OPTION(vote);
8650 DEFINE_CHANNEL_OPTION(nodelete);
8651 DEFINE_CHANNEL_OPTION(toys);
8652 DEFINE_CHANNEL_OPTION(setters);
8653 DEFINE_CHANNEL_OPTION(topicrefresh);
8654 DEFINE_CHANNEL_OPTION(ctcpusers);
8655 DEFINE_CHANNEL_OPTION(ctcpreaction);
8656 DEFINE_CHANNEL_OPTION(inviteme);
8657 DEFINE_CHANNEL_OPTION(unreviewed);
8658 modcmd_register(chanserv_module, "set expire", chan_opt_expire, 1, 0, "flags", "+helping", NULL);
8659 modcmd_register(chanserv_module, "set unreviewed on", NULL, 0, 0, "flags", "+helping", NULL);
8660 modcmd_register(chanserv_module, "set unreviewed off", NULL, 0, 0, "flags", "+oper", NULL);
8662 DEFINE_CHANNEL_OPTION(offchannel);
8663 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
8665 /* Alias set topic to set defaulttopic for compatibility. */
8666 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
8669 DEFINE_USER_OPTION(noautoop);
8670 DEFINE_USER_OPTION(autoinvite);
8671 DEFINE_USER_OPTION(info);
8673 /* Alias uset autovoice to uset autoop. */
8674 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
8676 note_types = dict_new();
8677 dict_set_free_data(note_types, chanserv_deref_note_type);
8680 const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
8681 chanserv = AddLocalUser(nick, nick, NULL, "Channel Services", modes);
8682 service_register(chanserv)->trigger = '!';
8683 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
8685 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
8687 if(chanserv_conf.channel_expire_frequency)
8688 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
8690 if(chanserv_conf.dnr_expire_frequency)
8691 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
8693 if(chanserv_conf.refresh_period)
8695 unsigned long next_refresh;
8696 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
8697 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
8700 reg_exit_func(chanserv_db_cleanup);
8701 message_register_table(msgtab);