1 /* chanserv.c - Channel service bot
2 * Copyright 2000-2007 srvx Development Team
4 * This file is part of srvx.
6 * srvx is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with srvx; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
25 #include "opserv.h" /* for opserv_bad_channel() */
26 #include "nickserv.h" /* for oper_outranks() */
31 #define CHANSERV_CONF_NAME "services/chanserv"
33 /* ChanServ options */
34 #define KEY_SUPPORT_CHANNEL "support_channel"
35 #define KEY_SUPPORT_CHANNEL_MODES "support_channel_modes"
36 #define KEY_DB_BACKUP_FREQ "db_backup_freq"
37 #define KEY_INFO_DELAY "info_delay"
38 #define KEY_MAX_GREETLEN "max_greetlen"
39 #define KEY_ADJUST_THRESHOLD "adjust_threshold"
40 #define KEY_ADJUST_DELAY "adjust_delay"
41 #define KEY_CHAN_EXPIRE_FREQ "chan_expire_freq"
42 #define KEY_CHAN_EXPIRE_DELAY "chan_expire_delay"
43 #define KEY_DNR_EXPIRE_FREQ "dnr_expire_freq"
44 #define KEY_MAX_CHAN_USERS "max_chan_users"
45 #define KEY_MAX_CHAN_BANS "max_chan_bans"
46 #define KEY_NICK "nick"
47 #define KEY_OLD_CHANSERV_NAME "old_chanserv_name"
48 #define KEY_8BALL_RESPONSES "8ball"
49 #define KEY_OLD_BAN_NAMES "old_ban_names"
50 #define KEY_REFRESH_PERIOD "refresh_period"
51 #define KEY_CTCP_SHORT_BAN_DURATION "ctcp_short_ban_duration"
52 #define KEY_CTCP_LONG_BAN_DURATION "ctcp_long_ban_duration"
53 #define KEY_MAX_OWNED "max_owned"
54 #define KEY_IRC_OPERATOR_EPITHET "irc_operator_epithet"
55 #define KEY_NETWORK_HELPER_EPITHET "network_helper_epithet"
56 #define KEY_SUPPORT_HELPER_EPITHET "support_helper_epithet"
57 #define KEY_NODELETE_LEVEL "nodelete_level"
58 #define KEY_MAX_USERINFO_LENGTH "max_userinfo_length"
59 #define KEY_GIVEOWNERSHIP_PERIOD "giveownership_timeout"
60 #define KEY_INVITED_INTERVAL "invite_timeout"
62 /* ChanServ database */
63 #define KEY_CHANNELS "channels"
64 #define KEY_NOTE_TYPES "note_types"
66 /* Note type parameters */
67 #define KEY_NOTE_OPSERV_ACCESS "opserv_access"
68 #define KEY_NOTE_CHANNEL_ACCESS "channel_access"
69 #define KEY_NOTE_SETTER_ACCESS "setter_access"
70 #define KEY_NOTE_VISIBILITY "visibility"
71 #define KEY_NOTE_VIS_PRIVILEGED "privileged"
72 #define KEY_NOTE_VIS_CHANNEL_USERS "channel_users"
73 #define KEY_NOTE_VIS_ALL "all"
74 #define KEY_NOTE_MAX_LENGTH "max_length"
75 #define KEY_NOTE_SETTER "setter"
76 #define KEY_NOTE_NOTE "note"
78 /* Do-not-register channels */
80 #define KEY_DNR_SET "set"
81 #define KEY_DNR_SETTER "setter"
82 #define KEY_DNR_REASON "reason"
85 #define KEY_REGISTERED "registered"
86 #define KEY_REGISTRAR "registrar"
87 #define KEY_SUSPENDED "suspended"
88 #define KEY_PREVIOUS "previous"
89 #define KEY_SUSPENDER "suspender"
90 #define KEY_ISSUED "issued"
91 #define KEY_REVOKED "revoked"
92 #define KEY_SUSPEND_EXPIRES "suspend_expires"
93 #define KEY_SUSPEND_REASON "suspend_reason"
94 #define KEY_VISITED "visited"
95 #define KEY_TOPIC "topic"
96 #define KEY_GREETING "greeting"
97 #define KEY_USER_GREETING "user_greeting"
98 #define KEY_MODES "modes"
99 #define KEY_FLAGS "flags"
100 #define KEY_OPTIONS "options"
101 #define KEY_USERS "users"
102 #define KEY_BANS "bans"
103 #define KEY_MAX "max"
104 #define KEY_MAX_TIME "max_time"
105 #define KEY_NOTES "notes"
106 #define KEY_TOPIC_MASK "topic_mask"
107 #define KEY_OWNER_TRANSFER "owner_transfer"
108 #define KEY_EXPIRE "expire"
111 #define KEY_LEVEL "level"
112 #define KEY_INFO "info"
113 #define KEY_SEEN "seen"
116 #define KEY_VOTE "vote"
117 #define KEY_VOTE_START "votestart"
118 #define KEY_VOTE_OPTIONS "voptions"
119 #define KEY_VOTE_OPTION_NAME "voptionname"
120 #define KEY_VOTE_VOTED "vvoted"
121 #define KEY_VOTE_VOTEDFOR "vvotefor"
122 #define KEY_VOTE_OPTION_ID "voptionid"
123 #define KEY_VOTE_OPTION_VOTED "voptionvoted"
126 #define KEY_OWNER "owner"
127 #define KEY_REASON "reason"
128 #define KEY_SET "set"
129 #define KEY_DURATION "duration"
130 #define KEY_EXPIRES "expires"
131 #define KEY_TRIGGERED "triggered"
133 #define CHANNEL_DEFAULT_FLAGS (CHANNEL_OFFCHANNEL | CHANNEL_UNREVIEWED)
134 #define CHANNEL_PRESERVED_FLAGS (CHANNEL_UNREVIEWED)
135 #define CHANNEL_DEFAULT_OPTIONS "lmoooanpcnat"
137 /* Administrative messages */
138 static const struct message_entry msgtab[] = {
139 { "CSMSG_CHANNELS_EXPIRED", "%i channels expired." },
141 /* Channel registration */
142 { "CSMSG_REG_SUCCESS", "You now have ownership of $b%s$b." },
143 { "CSMSG_PROXY_SUCCESS", "%s now has ownership of $b%s$b." },
144 { "CSMSG_ALREADY_REGGED", "$b%s$b is registered to someone else." },
145 { "CSMSG_MUST_BE_OPPED", "You must be a channel operator in $b%s$b to register it." },
146 { "CSMSG_PROXY_FORBIDDEN", "You may not register a channel for someone else." },
147 { "CSMSG_OWN_TOO_MANY", "%s already owns enough channels (at least %d); use FORCE to override." },
149 /* Do-not-register channels */
150 { "CSMSG_NOT_DNR", "$b%s$b is not a valid channel name or *account." },
151 { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
152 { "CSMSG_DNR_INFO", "$b%s$b is do-not-register (by $b%s$b): %s" },
153 { "CSMSG_DNR_INFO_SET", "$b%s$b is do-not-register (set %s by $b%s$b): %s" },
154 { "CSMSG_DNR_INFO_SET_EXPIRES", "$b%s$b is do-not-register (set %s by $b%s$b; expires %s): %s" },
155 { "CSMSG_MORE_DNRS", "%d more do-not-register entries skipped." },
156 { "CSMSG_DNR_CHANNEL", "Only network staff may register $b%s$b." },
157 { "CSMSG_DNR_CHANNEL_MOVE", "Only network staff may move $b%s$b." },
158 { "CSMSG_DNR_ACCOUNT", "Only network staff may register channels to $b%s$b." },
159 { "CSMSG_NOREGISTER_CHANNEL", "$b%s$b has been added to the do-not-register list." },
160 { "CSMSG_NO_SUCH_DNR", "$b%s$b is not in the do-not-register list." },
161 { "CSMSG_DNR_REMOVED", "$b%s$b has been removed from the do-not-register list." },
162 { "CSMSG_DNR_BAD_ACTION", "$b%s$b is not a recognized do-not-register action." },
163 { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
165 /* Channel unregistration */
166 { "CSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
167 { "CSMSG_UNREG_NODELETE", "$b%s$b is protected from unregistration." },
168 { "CSMSG_CHAN_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended (%s)." },
169 { "CSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must use 'unregister %s'." },
172 { "CSMSG_MOVE_SUCCESS", "Channel registration has been moved to $b%s$b." },
173 { "CSMSG_MOVE_NODELETE", "$b%s$b is protected from unregistration, and cannot be moved." },
175 /* Channel merging */
176 { "CSMSG_MERGE_SUCCESS", "Channel successfully merged into $b%s$b." },
177 { "CSMSG_MERGE_SELF", "Merging cannot be performed if the source and target channels are the same." },
178 { "CSMSG_MERGE_NODELETE", "You may not merge a channel that is marked NoDelete." },
179 { "CSMSG_MERGE_SUSPENDED", "Merging cannot be performed if the source or target channel is suspended." },
180 { "CSMSG_MERGE_NOT_OWNER", "You must be the owner of the target channel (or a helper) to merge into the channel." },
182 /* Handle unregistration */
183 { "CSMSG_HANDLE_UNREGISTERED", "As a result of your account unregistration, you have been deleted from all of your channels' userlists." },
186 { "CSMSG_NOT_USER", "You lack access to $b%s$b." },
187 { "CSMSG_NO_CHAN_USER", "%s lacks access to $b%s$b." },
188 { "CSMSG_NO_ACCESS", "You lack sufficient access to use this command." },
189 { "CSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$C$b." },
190 { "CSMSG_MAXIMUM_BANS", "This channel has reached the ban count limit of $b%d$b." },
191 { "CSMSG_MAXIMUM_USERS", "This channel has reached the user count limit of $b%d$b." },
192 { "CSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel, and cannot be registered." },
193 { "CSMSG_GODMODE_UP", "You may not use $b%s$b to op yourself unless you are on the user list. Use the $bop$b command instead." },
194 { "CSMSG_ALREADY_OPPED", "You are already opped in $b%s$b." },
195 { "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." },
196 { "CSMSG_ALREADY_DOWN", "You are not opped or voiced in $b%s$b." },
197 { "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." },
198 { "CSMSG_OUT_OF_CHANNEL", "For some reason I don't seem to be in $b%s$b." },
199 { "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." },
201 /* Removing yourself from a channel. */
202 { "CSMSG_NO_OWNER_DELETEME", "You cannot delete your owner access in $b%s$b." },
203 { "CSMSG_CONFIRM_DELETEME", "To really remove yourself, you must use 'deleteme %s'." },
204 { "CSMSG_DELETED_YOU", "Your $b%d$b access has been deleted from $b%s$b." },
206 /* User management */
207 { "CSMSG_ADDED_USER", "Added %s to the %s user list with access %d." },
208 { "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." },
209 { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be greater than maximum (%d)." },
210 { "CSMSG_DELETED_USERS", "Deleted accounts matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list." },
211 { "CSMSG_TRIMMED_USERS", "Trimmed $b%d users$b with access from %d to %d from the %s user list who were inactive for at least %s." },
212 { "CSMSG_INCORRECT_ACCESS", "%s has access $b%d$b, not %s." },
213 { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with access %d)." },
214 { "CSMSG_CANNOT_TRIM", "You must include a minimum inactivity duration of at least 60 seconds to trim." },
216 { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." },
217 { "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." },
218 { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
219 { "CSMSG_TRANSFER_WAIT", "You must wait %s before you can give ownership of $b%s$b to someone else." },
220 { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
221 { "CSMSG_CONFIRM_GIVEOWNERSHIP", "To really give ownership to $b%1$s$b, you must use 'giveownership *%1$s %2$s'." },
222 { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
225 { "CSMSG_BAN_ADDED", "Permanently banned $b%s$b from %s." },
226 { "CSMSG_TIMED_BAN_ADDED", "Banned $b%s$b from %s for %s." },
227 { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
228 { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
229 { "CSMSG_REASON_CHANGE", "Reason for ban $b%s$b changed." },
230 { "CSMSG_BAN_EXTENDED", "Extended ban for $b%s$b expires in %s." },
231 { "CSMSG_BAN_REMOVED", "Matching ban(s) for $b%s$b removed." },
232 { "CSMSG_TRIMMED_BANS", "Trimmed $b%d bans$b from the %s ban list that were inactive for at least %s." },
233 { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
234 { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
235 { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
236 { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
237 { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
238 { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
239 { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban found for $b%s$b." },
240 { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
242 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
244 /* Channel management */
245 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
246 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
247 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
249 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
250 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
251 { "CSMSG_TOPICMASK_CONFLICT1", "I do not know how to make that topic work with the current topic mask in $b%s$b, which is: %s" },
252 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic is at most %d characters and matches the topic mask pattern." },
253 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
254 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
255 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
257 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
258 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
259 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
260 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
261 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
262 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
263 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you must use 'set defaults %s'." },
264 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
265 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
266 { "CSMSG_BAD_GIVEVOICE", "You cannot change GiveVoice to above GiveOps (%d)." },
267 { "CSMSG_BAD_GIVEOPS", "You cannot change GiveOps to below GiveVoice (%d)." },
268 { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
269 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
270 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
271 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
272 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
273 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
274 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
275 { "CSMSG_SET_MODES", "$bModes $b %s" },
276 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
277 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s" },
278 { "CSMSG_SET_OFFCHANNEL", "$bOffChannel $b %s" },
279 { "CSMSG_SET_USERINFO", "$bUserInfo $b %d" },
280 { "CSMSG_SET_GIVE_VOICE", "$bGiveVoice $b %d" },
281 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" },
282 { "CSMSG_SET_VOTE", "$bVote $b %d" },
283 { "CSMSG_SET_INVITEME", "$bInviteMe $b %d" },
284 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d" },
285 { "CSMSG_SET_GIVE_OPS", "$bGiveOps $b %d" },
286 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d" },
287 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d" },
288 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d" },
289 { "CSMSG_SET_SETTERS", "$bSetters $b %d" },
290 { "CSMSG_SET_CTCPUSERS", "$bCTCPUsers $b %d" },
291 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
292 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
293 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
294 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
295 { "CSMSG_SET_UNREVIEWED", "$bUnreviewed $b %s" },
296 { "CSMSG_SET_EXPIRE", "$bExpire $b %s" },
297 { "CSMSG_SET_EXPIRE_OFF", "$bExpire $b off" },
298 { "CSMSG_USET_NOAUTOOP", "$bNoAutoOp $b %s" },
299 { "CSMSG_USET_NOAUTOVOICE", "$bNoAutoVoice $b %s" },
300 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
301 { "CSMSG_USET_INFO", "$bInfo $b %s" },
303 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
304 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
305 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
306 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
307 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
308 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
309 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
310 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
311 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
312 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
313 { "CSMSG_PROTECT_NONE", "No users will be protected." },
314 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
315 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
316 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
317 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
318 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
319 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
320 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
321 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
322 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
323 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
324 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
325 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
327 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
328 { "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" },
329 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s." },
330 { "CSMSG_ALREADY_PRESENT", "%s is already in $b%s$b." },
331 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
332 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s for $S to invite you." },
333 { "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
334 { "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
336 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
337 { "CSMSG_NO_BANS", "No channel bans found on $b%s$b." },
338 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
340 /* Channel userlist */
341 { "CSMSG_ACCESS_ALL_HEADER", "%s users from level %d to %d:" },
342 { "CSMSG_ACCESS_SEARCH_HEADER", "%s users from level %d to %d matching %s:" },
343 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
344 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%d$b in %s." },
346 /* Channel note list */
347 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
348 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
349 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
350 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
351 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
352 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
353 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
354 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
355 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
356 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
357 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
358 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
359 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
360 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
361 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
363 /* Channel [un]suspension */
364 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
365 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
366 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
367 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
368 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
369 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
370 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
372 /* Access information */
373 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
374 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
375 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
376 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
377 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
378 { "CSMSG_USER_HAS_ACCESS", "%s has access $b%d$b in %s." },
379 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
380 { "CSMSG_HELPER_HAS_ACCESS", "%s has access $b%d$b in %s and has $bsecurity override$b enabled." },
381 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
382 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
383 { "CSMSG_OPERATOR_TITLE", "IRC operator" },
384 { "CSMSG_UC_H_TITLE", "network helper" },
385 { "CSMSG_LC_H_TITLE", "support helper" },
386 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
387 { "CSMSG_MYACCESS_COUNT", "%s has access in $b%d$b channels." },
388 { "CSMSG_MYACCESS_COUNT_1", "%s has access in $b%d$b channel." },
390 /* Seen information */
391 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
392 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
393 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
394 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
396 /* Names information */
397 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
398 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
400 /* Channel information */
401 { "CSMSG_CHANNEL_INFO", "$b%s$b Information:" },
402 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
403 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
404 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
405 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
406 { "CSMSG_CHANNEL_MAX_TIME", "$bRecord Visitors: $b%i (%s ago.)" },
407 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
408 { "CSMSG_CHANNEL_BANS", "$bBan Count: $b%i" },
409 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
410 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
411 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
412 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
413 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
414 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
415 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
416 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
417 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
418 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
419 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
420 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
421 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
422 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
424 { "CSMSG_PEEK_INFO", "$b%s$b Status:" },
425 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
426 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
427 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d (%d ops, %d voices, %d regulars)" },
428 { "CSMSG_PEEK_OPS", "$bOps:$b" },
429 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
431 /* Network information */
432 { "CSMSG_NETWORK_INFO", "Network Information:" },
433 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
434 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
435 { "CSMSG_NETWORK_BANS", "$bTotal Ban Count: $b%i" },
436 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
437 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
438 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
439 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
440 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
443 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
444 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
445 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
447 /* Channel searches */
448 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
449 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
450 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
451 { "CSMSG_CHANNEL_SEARCH_RESULTS", "The following channels were found:" },
453 /* Channel configuration */
454 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
455 { "CSMSG_INVALID_CFLAG", "$b%s$b is not a recognized channel flag." },
456 { "CSMSG_CHANNEL_OPTIONS", "Channel Options:" },
457 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
460 { "CSMSG_USER_OPTIONS", "User Options:" },
461 { "CSMSG_USER_PROTECTED_2", "That user is protected." },
464 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
465 { "CSMSG_PING_RESPONSE", "Pong!" },
466 { "CSMSG_WUT_RESPONSE", "wut" },
467 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
468 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
469 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
470 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
471 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
472 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
473 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
476 { "CSMSG_ADDVOTE_DONE", "Vote added. Use $baddoption$b to add at least 2 vote options and then $bstartvote$b to start the voting." },
477 { "CSMSG_ADDVOTE_FULL", "There is already a vote in this channel. Use $bdelvote$b to delete it." },
478 { "CSMSG_DELVOTE_DONE", "Vote deleted." },
479 { "CSMSG_NO_VOTE", "There is no vote in this channel." },
480 { "CSMSG_ADDOPTION_DONE", "Vote option added with id %i (%i - %i)." },
481 { "CSMSG_DELOPTION_DONE", "Vote option deleted." },
482 { "CSMSG_DELOPTION_NONE", "Vote option does not exist." },
483 { "CSMSG_VOTE_NEED_OPTIONS", "There must be at least 2 options in a vote." },
484 { "CSMSG_VOTE_NOT_STARTED", "The vote is not started. Use $bstartvote$b to allow voting." },
485 { "CSMSG_VOTE_QUESTION", "Question: %s" },
486 { "CSMSG_VOTE_OPTION", "$b%i$b: %s ($b%i$b votes)" },
487 { "CSMSG_VOTERES_QUESTION", "Question: %s" },
488 { "CSMSG_VOTERES_OPTION", "
\ 2%i
\ 2: %s (
\ 2%i
\ 2 votes)" },
489 { "CSMSG_STARTVOTE_TOP", "
\ 2%s
\ 2 has started a voting:" },
490 { "CSMSG_STARTVOTE_QUESTION", "
\ 2Question:
\ 2 %s" },
491 { "CSMSG_STARTVOTE_OPTION", "
\ 2%i:
\ 2 %s" },
492 { "CSMSG_STARTVOTE_ACCESS", "All channel users with at least
\ 2%i
\ 2 access can vote." },
493 { "CSMSG_STARTVOTE_HOWTO", "To vote for an option, use
\ 2vote ID
\ 2. To see the available options and the current votes, use
\ 2vote
\ 2 without parameters." },
494 { "CSMSG_STARTVOTE_RUNNING", "The vote is already running." },
495 { "CSMSG_VOTE_VOTED", "You have already voted." },
496 { "CSMSG_VOTE_INVALID", "Vote option does not exist." },
497 { "CSMSG_VOTE_DONE", "You voted for $b%s$b." },
498 { "CSMSG_ENDVOTE_DONE", "The vote has been finished." },
499 { "CSMSG_ENDVOTE_STOPPED", "The vote is not running." },
502 { "CSMSG_EVENT_SEARCH_RESULTS", "The following channel events were found:" },
506 /* eject_user and unban_user flags */
507 #define ACTION_KICK 0x0001
508 #define ACTION_BAN 0x0002
509 #define ACTION_ADD_BAN 0x0004
510 #define ACTION_ADD_TIMED_BAN 0x0008
511 #define ACTION_UNBAN 0x0010
512 #define ACTION_DEL_BAN 0x0020
514 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
515 #define MODELEN 40 + KEYLEN
519 #define CSFUNC_ARGS user, channel, argc, argv, cmd
521 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
522 #define CHANSERV_SYNTAX() svccmd_send_help(user, chanserv, cmd)
523 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
524 reply("MSG_MISSING_PARAMS", argv[0]); \
528 DECLARE_LIST(dnrList, struct do_not_register *);
529 DEFINE_LIST(dnrList, struct do_not_register *)
531 static int eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action);
533 struct userNode *chanserv;
536 static dict_t plain_dnrs, mask_dnrs, handle_dnrs;
537 static struct log_type *CS_LOG;
541 struct channelList support_channels;
542 struct mod_chanmode default_modes;
544 unsigned long db_backup_frequency;
545 unsigned long channel_expire_frequency;
546 unsigned long dnr_expire_frequency;
548 unsigned long invited_timeout;
550 unsigned long info_delay;
551 unsigned long adjust_delay;
552 unsigned long channel_expire_delay;
553 unsigned int nodelete_level;
555 unsigned int adjust_threshold;
556 int join_flood_threshold;
558 unsigned int greeting_length;
559 unsigned int refresh_period;
560 unsigned int giveownership_period;
562 unsigned int max_owned;
563 unsigned int max_chan_users;
564 unsigned int max_chan_bans;
565 unsigned int max_userinfo_length;
567 struct string_list *set_shows;
568 struct string_list *eightball;
569 struct string_list *old_ban_names;
571 const char *ctcp_short_ban_duration;
572 const char *ctcp_long_ban_duration;
574 const char *irc_operator_epithet;
575 const char *network_helper_epithet;
576 const char *support_helper_epithet;
581 struct userNode *user;
582 struct userNode *bot;
583 struct chanNode *channel;
585 unsigned short lowest;
586 unsigned short highest;
587 struct userData **users;
588 struct helpfile_table table;
593 struct userNode *user;
594 struct chanNode *chan;
597 enum note_access_type
599 NOTE_SET_CHANNEL_ACCESS,
600 NOTE_SET_CHANNEL_SETTER,
604 enum note_visible_type
607 NOTE_VIS_CHANNEL_USERS,
613 enum note_access_type set_access_type;
615 unsigned int min_opserv;
616 unsigned short min_ulevel;
618 enum note_visible_type visible_type;
619 unsigned int max_length;
626 struct note_type *type;
627 char setter[NICKSERV_HANDLE_LEN+1];
631 static unsigned int registered_channels;
632 static unsigned int banCount;
634 static const struct {
637 unsigned short level;
640 { "peon", "Peon", UL_PEON, '+' },
641 { "op", "Op", UL_OP, '@' },
642 { "master", "Master", UL_MASTER, '%' },
643 { "coowner", "Coowner", UL_COOWNER, '*' },
644 { "owner", "Owner", UL_OWNER, '!' },
645 { "helper", "BUG:", UL_HELPER, 'X' }
648 static const struct {
651 unsigned short default_value;
652 unsigned int old_idx;
653 unsigned int old_flag;
654 unsigned short flag_value;
656 { "CSMSG_SET_GIVE_VOICE", "givevoice", 100, ~0, CHANNEL_VOICE_ALL, 0 },
657 { "CSMSG_SET_GIVE_OPS", "giveops", 200, 2, 0, 0 },
658 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
659 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
660 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
661 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
662 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
663 { "CSMSG_SET_CTCPUSERS", "ctcpusers", 0, 9, 0, 0 },
664 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES, 1 },
665 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE, 200 },
666 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF, 1 },
667 { "CSMSG_SET_VOTE", "vote", 100, ~0, 0, 0 }
670 struct charOptionValues {
673 } protectValues[] = {
674 { 'a', "CSMSG_PROTECT_ALL" },
675 { 'e', "CSMSG_PROTECT_EQUAL" },
676 { 'l', "CSMSG_PROTECT_LOWER" },
677 { 'n', "CSMSG_PROTECT_NONE" }
679 { 'd', "CSMSG_TOYS_DISABLED" },
680 { 'n', "CSMSG_TOYS_PRIVATE" },
681 { 'p', "CSMSG_TOYS_PUBLIC" }
682 }, topicRefreshValues[] = {
683 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
684 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
685 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
686 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
687 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
688 }, ctcpReactionValues[] = {
689 { 'k', "CSMSG_CTCPREACTION_KICK" },
690 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
691 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
692 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
695 static const struct {
699 unsigned int old_idx;
701 struct charOptionValues *values;
703 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues), protectValues },
704 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues), toysValues },
705 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues), topicRefreshValues },
706 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 't', 10, ArrayLength(ctcpReactionValues), ctcpReactionValues }
709 struct userData *helperList;
710 struct chanData *channelList;
711 static struct module *chanserv_module;
712 static unsigned int userCount;
714 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
715 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
716 static void unregister_channel(struct chanData *channel, const char *reason);
719 user_level_from_name(const char *name, unsigned short clamp_level)
721 unsigned int level = 0, ii;
723 level = strtoul(name, NULL, 10);
724 else for(ii = 0; (ii < ArrayLength(accessLevels)) && !level; ++ii)
725 if(!irccasecmp(name, accessLevels[ii].name))
726 level = accessLevels[ii].level;
727 if(level > clamp_level)
733 parse_level_range(unsigned short *minl, unsigned short *maxl, const char *arg)
736 *minl = strtoul(arg, &sep, 10);
744 *maxl = strtoul(sep+1, &sep, 10);
752 _GetChannelUser(struct chanData *channel, struct handle_info *handle, int override, int allow_suspended)
754 struct userData *uData, **head;
756 if(!channel || !handle)
759 if(override && HANDLE_FLAGGED(handle, HELPING)
760 && ((handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel)))
762 for(uData = helperList;
763 uData && uData->handle != handle;
764 uData = uData->next);
768 uData = calloc(1, sizeof(struct userData));
769 uData->handle = handle;
771 uData->access = UL_HELPER;
777 uData->next = helperList;
779 helperList->prev = uData;
787 for(uData = channel->users; uData; uData = uData->next)
788 if((uData->handle == handle) && (allow_suspended || !IsUserSuspended(uData)))
791 head = &(channel->users);
794 if(uData && (uData != *head))
796 /* Shuffle the user to the head of whatever list he was in. */
798 uData->next->prev = uData->prev;
800 uData->prev->next = uData->next;
806 (**head).prev = uData;
813 /* Returns non-zero if user has at least the minimum access.
814 * exempt_owner is set when handling !set, so the owner can set things
817 int check_user_level(struct chanNode *channel, struct userNode *user, enum levelOption opt, int allow_override, int exempt_owner)
819 struct userData *uData;
820 struct chanData *cData = channel->channel_info;
821 unsigned short minimum = cData->lvlOpts[opt];
824 uData = _GetChannelUser(cData, user->handle_info, allow_override, 0);
827 if(minimum <= uData->access)
829 if((minimum > UL_OWNER) && (uData->access == UL_OWNER) && exempt_owner)
834 /* Scan for other users authenticated to the same handle
835 still in the channel. If so, keep them listed as present.
837 user is optional, if not null, it skips checking that userNode
838 (for the handle_part function) */
840 scan_user_presence(struct userData *uData, struct userNode *user)
844 if(IsSuspended(uData->channel)
845 || IsUserSuspended(uData)
846 || !(mn = find_handle_in_channel(uData->channel->channel, uData->handle, user)))
858 chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, const char *text, UNUSED_ARG(struct userNode *bot), UNUSED_ARG(unsigned int is_notice))
860 unsigned int eflags, argc;
862 static char *bad_ctcp_reason = "CTCPs to this channel are forbidden.";
864 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
865 if(!channel->channel_info
866 || IsSuspended(channel->channel_info)
868 || !ircncasecmp(text, "ACTION ", 7))
870 /* Figure out the minimum level needed to CTCP the channel */
871 if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
873 /* We need to enforce against them; do so. */
875 argv[0] = (char*)text;
876 argv[1] = user->nick;
878 if(GetUserMode(channel, user))
879 eflags |= ACTION_KICK;
880 switch(channel->channel_info->chOpts[chCTCPReaction]) {
881 default: case 'k': /* just do the kick */ break;
883 eflags |= ACTION_BAN;
886 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
887 argv[argc++] = (char*)chanserv_conf.ctcp_short_ban_duration;
890 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
891 argv[argc++] = (char*)chanserv_conf.ctcp_long_ban_duration;
894 argv[argc++] = bad_ctcp_reason;
895 eject_user(chanserv, channel, argc, argv, NULL, eflags);
899 chanserv_create_note_type(const char *name)
901 struct note_type *ntype = calloc(1, sizeof(*ntype) + strlen(name));
902 strcpy(ntype->name, name);
904 dict_insert(note_types, ntype->name, ntype);
909 free_vote_options(void *data)
911 struct vote_option *vOpt = data;
913 free(vOpt->option_str);
918 chanserv_deref_note_type(void *data)
920 struct note_type *ntype = data;
922 if(--ntype->refs > 0)
928 chanserv_flush_note_type(struct note_type *ntype)
930 struct chanData *cData;
931 for(cData = channelList; cData; cData = cData->next)
932 dict_remove(cData->notes, ntype->name);
936 chanserv_truncate_notes(struct note_type *ntype)
938 struct chanData *cData;
940 unsigned int size = sizeof(*note) + ntype->max_length;
942 for(cData = channelList; cData; cData = cData->next) {
943 note = dict_find(cData->notes, ntype->name, NULL);
946 if(strlen(note->note) <= ntype->max_length)
948 dict_remove2(cData->notes, ntype->name, 1);
949 note = realloc(note, size);
950 note->note[ntype->max_length] = 0;
951 dict_insert(cData->notes, ntype->name, note);
955 static int note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user);
958 chanserv_add_channel_note(struct chanData *channel, struct note_type *type, const char *setter, const char *text)
961 unsigned int len = strlen(text);
963 if(len > type->max_length) len = type->max_length;
964 note = calloc(1, sizeof(*note) + len);
966 strncpy(note->setter, setter, sizeof(note->setter)-1);
967 memcpy(note->note, text, len);
969 dict_insert(channel->notes, type->name, note);
975 chanserv_free_note(void *data)
977 struct note *note = data;
979 chanserv_deref_note_type(note->type);
980 assert(note->type->refs > 0); /* must use delnote to remove the type */
984 static MODCMD_FUNC(cmd_createnote) {
985 struct note_type *ntype;
986 unsigned int arg = 1, existed = 0, max_length;
988 if((ntype = dict_find(note_types, argv[1], NULL)))
991 ntype = chanserv_create_note_type(argv[arg]);
992 if(!irccasecmp(argv[++arg], "privileged"))
995 ntype->set_access_type = NOTE_SET_PRIVILEGED;
996 ntype->set_access.min_opserv = strtoul(argv[arg], NULL, 0);
998 else if(!irccasecmp(argv[arg], "channel"))
1000 unsigned short ulvl = user_level_from_name(argv[++arg], UL_OWNER);
1003 reply("CSMSG_INVALID_ACCESS", argv[arg]);
1006 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
1007 ntype->set_access.min_ulevel = ulvl;
1009 else if(!irccasecmp(argv[arg], "setter"))
1011 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
1015 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
1019 if(!irccasecmp(argv[++arg], "privileged"))
1020 ntype->visible_type = NOTE_VIS_PRIVILEGED;
1021 else if(!irccasecmp(argv[arg], "channel_users"))
1022 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
1023 else if(!irccasecmp(argv[arg], "all"))
1024 ntype->visible_type = NOTE_VIS_ALL;
1026 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
1030 if((arg+1) >= argc) {
1031 reply("MSG_MISSING_PARAMS", argv[0]);
1034 max_length = strtoul(argv[++arg], NULL, 0);
1035 if(max_length < 20 || max_length > 450)
1037 reply("CSMSG_BAD_MAX_LENGTH", argv[arg]);
1040 if(existed && (max_length < ntype->max_length))
1042 ntype->max_length = max_length;
1043 chanserv_truncate_notes(ntype);
1045 ntype->max_length = max_length;
1048 reply("CSMSG_NOTE_MODIFIED", ntype->name);
1050 reply("CSMSG_NOTE_CREATED", ntype->name);
1055 dict_remove(note_types, ntype->name);
1059 static MODCMD_FUNC(cmd_removenote) {
1060 struct note_type *ntype;
1063 ntype = dict_find(note_types, argv[1], NULL);
1064 force = (argc > 2) && !irccasecmp(argv[2], "force");
1067 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
1074 reply("CSMSG_NOTE_TYPE_USED", ntype->name);
1077 chanserv_flush_note_type(ntype);
1079 dict_remove(note_types, argv[1]);
1080 reply("CSMSG_NOTE_DELETED", argv[1]);
1085 chanserv_expire_channel(void *data)
1087 struct chanData *channel = data;
1088 char reason[MAXLEN];
1089 sprintf(reason, "channel expired.");
1090 channel->expiry = 0;
1091 spamserv_cs_unregister(NULL, channel->channel, expire, NULL);
1092 unregister_channel(channel, reason);
1095 static MODCMD_FUNC(chan_opt_expire)
1097 struct chanData *cData = channel->channel_info;
1098 unsigned long value = cData->expiry;
1102 if((!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
1104 reply("MSG_SETTING_PRIVILEGED", argv[0]);
1107 unsigned long expiry,duration;
1109 /* The two directions can have different ACLs. */
1110 if(!strcmp(argv[1], "0"))
1112 else if((duration = ParseInterval(argv[1])))
1113 expiry = now + duration;
1116 reply("MSG_INVALID_DURATION", argv[1]);
1120 if (expiry != value)
1124 timeq_del(value, chanserv_expire_channel, cData, 0);
1127 cData->expiry = value;
1130 timeq_add(expiry, chanserv_expire_channel, cData);
1135 if(cData->expiry > now) {
1136 char expirestr[INTERVALLEN];
1137 reply("CSMSG_SET_EXPIRE", intervalString(expirestr, cData->expiry - now, user->handle_info));
1139 reply("CSMSG_SET_EXPIRE_OFF");
1144 mode_lock_violated(const struct mod_chanmode *orig, const struct mod_chanmode *change)
1148 if(orig->modes_set & change->modes_clear)
1150 if(orig->modes_clear & change->modes_set)
1152 if((orig->modes_set & MODE_KEY) && (change->modes_set & MODE_KEY)
1153 && strcmp(orig->new_key, change->new_key))
1155 if((orig->modes_set & MODE_LIMIT) && (change->modes_set & MODE_LIMIT)
1156 && (orig->new_limit != change->new_limit))
1161 static char max_length_text[MAXLEN+1][16];
1163 static struct helpfile_expansion
1164 chanserv_expand_variable(const char *variable)
1166 struct helpfile_expansion exp;
1168 if(!irccasecmp(variable, "notes"))
1171 exp.type = HF_TABLE;
1172 exp.value.table.length = 1;
1173 exp.value.table.width = 3;
1174 exp.value.table.flags = 0;
1175 exp.value.table.contents = calloc(dict_size(note_types)+1, sizeof(char**));
1176 exp.value.table.contents[0] = calloc(exp.value.table.width, sizeof(char*));
1177 exp.value.table.contents[0][0] = "Note Type";
1178 exp.value.table.contents[0][1] = "Visibility";
1179 exp.value.table.contents[0][2] = "Max Length";
1180 for(it=dict_first(note_types); it; it=iter_next(it))
1182 struct note_type *ntype = iter_data(it);
1185 if(!note_type_visible_to_user(NULL, ntype, message_dest)) continue;
1186 row = exp.value.table.length++;
1187 exp.value.table.contents[row] = calloc(exp.value.table.width, sizeof(char*));
1188 exp.value.table.contents[row][0] = ntype->name;
1189 exp.value.table.contents[row][1] = (ntype->visible_type == NOTE_VIS_ALL) ? "all" :
1190 (ntype->visible_type == NOTE_VIS_CHANNEL_USERS) ? "chan users" :
1192 if(!max_length_text[ntype->max_length][0])
1193 snprintf(max_length_text[ntype->max_length], sizeof(max_length_text[ntype->max_length]), "%u", ntype->max_length);
1194 exp.value.table.contents[row][2] = max_length_text[ntype->max_length];
1199 exp.type = HF_STRING;
1200 exp.value.str = NULL;
1204 static struct chanData*
1205 register_channel(struct chanNode *cNode, char *registrar)
1207 struct chanData *channel;
1208 enum levelOption lvlOpt;
1209 enum charOption chOpt;
1211 channel = calloc(1, sizeof(struct chanData));
1213 channel->notes = dict_new();
1214 dict_set_free_data(channel->notes, chanserv_free_note);
1216 channel->registrar = strdup(registrar);
1217 channel->registered = now;
1218 channel->visited = now;
1219 channel->limitAdjusted = now;
1220 channel->ownerTransfer = now;
1221 channel->flags = CHANNEL_DEFAULT_FLAGS;
1222 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
1223 channel->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
1224 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
1225 channel->chOpts[chOpt] = charOptions[chOpt].default_value;
1227 channel->prev = NULL;
1228 channel->next = channelList;
1231 channelList->prev = channel;
1232 channelList = channel;
1233 registered_channels++;
1235 channel->channel = cNode;
1237 cNode->channel_info = channel;
1239 channel->vote = NULL;
1244 static struct userData*
1245 add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned short access_level, unsigned long seen, const char *info)
1247 struct userData *ud;
1249 if(access_level > UL_OWNER)
1252 ud = calloc(1, sizeof(*ud));
1253 ud->channel = channel;
1254 ud->handle = handle;
1256 ud->access = access_level;
1257 ud->info = info ? strdup(info) : NULL;
1260 ud->next = channel->users;
1262 channel->users->prev = ud;
1263 channel->users = ud;
1265 channel->userCount++;
1269 ud->u_next = ud->handle->channels;
1271 ud->u_next->u_prev = ud;
1272 ud->handle->channels = ud;
1278 del_channel_user(struct userData *user, int do_gc)
1280 struct chanData *channel = user->channel;
1282 channel->userCount--;
1286 user->prev->next = user->next;
1288 channel->users = user->next;
1290 user->next->prev = user->prev;
1293 user->u_prev->u_next = user->u_next;
1295 user->handle->channels = user->u_next;
1297 user->u_next->u_prev = user->u_prev;
1301 if(do_gc && !channel->users && !IsProtected(channel)) {
1302 spamserv_cs_unregister(NULL, channel->channel, lost_all_users, NULL);
1303 unregister_channel(channel, "lost all users.");
1307 static void expire_ban(void *data);
1310 add_channel_ban(struct chanData *channel, const char *mask, char *owner, unsigned long set, unsigned long triggered, unsigned long expires, char *reason)
1313 unsigned int ii, l1, l2;
1318 bd = malloc(sizeof(struct banData));
1320 bd->channel = channel;
1322 bd->triggered = triggered;
1323 bd->expires = expires;
1325 for(ii = 0; ii < chanserv_conf.old_ban_names->used; ++ii)
1327 extern const char *hidden_host_suffix;
1328 const char *old_name = chanserv_conf.old_ban_names->list[ii];
1332 l2 = strlen(old_name);
1335 if(irccasecmp(mask + l1 - l2, old_name))
1337 new_mask = alloca(MAXLEN);
1338 sprintf(new_mask, "%.*s%s", (int)(l1-l2), mask, hidden_host_suffix);
1341 safestrncpy(bd->mask, mask, sizeof(bd->mask));
1343 safestrncpy(bd->owner, owner, sizeof(bd->owner));
1344 bd->reason = strdup(reason);
1347 timeq_add(expires, expire_ban, bd);
1350 bd->next = channel->bans;
1352 channel->bans->prev = bd;
1354 channel->banCount++;
1361 del_channel_ban(struct banData *ban)
1363 ban->channel->banCount--;
1367 ban->prev->next = ban->next;
1369 ban->channel->bans = ban->next;
1372 ban->next->prev = ban->prev;
1375 timeq_del(0, expire_ban, ban, TIMEQ_IGNORE_WHEN);
1384 expire_ban(void *data)
1386 struct banData *bd = data;
1387 if(!IsSuspended(bd->channel))
1389 struct banList bans;
1390 struct mod_chanmode change;
1392 bans = bd->channel->channel->banlist;
1393 mod_chanmode_init(&change);
1394 for(ii=0; ii<bans.used; ii++)
1396 if(!strcmp(bans.list[ii]->ban, bd->mask))
1399 change.args[0].mode = MODE_REMOVE|MODE_BAN;
1400 change.args[0].u.hostmask = bd->mask;
1401 mod_chanmode_announce(chanserv, bd->channel->channel, &change);
1407 del_channel_ban(bd);
1410 static void chanserv_expire_suspension(void *data);
1413 unregister_channel(struct chanData *channel, const char *reason)
1415 struct mod_chanmode change;
1416 char msgbuf[MAXLEN];
1418 /* After channel unregistration, the following must be cleaned
1420 - Channel information.
1423 - Channel suspension data.
1424 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1430 timeq_del(0, NULL, channel, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
1434 mod_chanmode_init(&change);
1435 change.modes_clear |= MODE_REGISTERED;
1436 mod_chanmode_announce(chanserv, channel->channel, &change);
1439 while(channel->users)
1440 del_channel_user(channel->users, 0);
1442 while(channel->bans)
1443 del_channel_ban(channel->bans);
1445 free(channel->topic);
1446 free(channel->registrar);
1447 free(channel->greeting);
1448 free(channel->user_greeting);
1449 free(channel->topic_mask);
1452 channel->prev->next = channel->next;
1454 channelList = channel->next;
1457 channel->next->prev = channel->prev;
1459 if(channel->suspended)
1461 struct chanNode *cNode = channel->channel;
1462 struct suspended *suspended, *next_suspended;
1464 for(suspended = channel->suspended; suspended; suspended = next_suspended)
1466 next_suspended = suspended->previous;
1467 free(suspended->suspender);
1468 free(suspended->reason);
1469 if(suspended->expires)
1470 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
1475 cNode->channel_info = NULL;
1478 timeq_del(channel->expiry, chanserv_expire_channel, channel, 0);
1479 channel->channel->channel_info = NULL;
1481 dict_delete(channel->notes);
1482 sprintf(msgbuf, "%s %s", channel->channel->name, reason);
1483 if(!IsSuspended(channel))
1484 DelChannelUser(chanserv, channel->channel, msgbuf, 0);
1485 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, msgbuf);
1486 UnlockChannel(channel->channel);
1488 registered_channels--;
1492 expire_channels(UNUSED_ARG(void *data))
1494 struct chanData *channel, *next;
1495 struct userData *user;
1496 char delay[INTERVALLEN], reason[INTERVALLEN + 64];
1498 intervalString(delay, chanserv_conf.channel_expire_delay, NULL);
1499 sprintf(reason, "Channel registration automatically expired after %s of disuse.", delay);
1501 for(channel = channelList; channel; channel = next)
1503 next = channel->next;
1505 /* See if the channel can be expired. */
1506 if(((now - channel->visited) <= chanserv_conf.channel_expire_delay)
1507 || IsProtected(channel))
1510 /* Make sure there are no high-ranking users still in the channel. */
1511 for(user=channel->users; user; user=user->next)
1512 if(user->present && (user->access >= UL_PRESENT) && !HANDLE_FLAGGED(user->handle, BOT))
1517 /* Unregister the channel */
1518 log_module(CS_LOG, LOG_INFO, "(%s) Channel registration expired.", channel->channel->name);
1519 spamserv_cs_unregister(NULL, channel->channel, expire, NULL);
1520 unregister_channel(channel, "registration expired.");
1523 if(chanserv_conf.channel_expire_frequency)
1524 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
1528 expire_dnrs(UNUSED_ARG(void *data))
1530 dict_iterator_t it, next;
1531 struct do_not_register *dnr;
1533 for(it = dict_first(handle_dnrs); it; it = next)
1535 dnr = iter_data(it);
1536 next = iter_next(it);
1537 if(dnr->expires && dnr->expires <= now)
1538 dict_remove(handle_dnrs, dnr->chan_name + 1);
1540 for(it = dict_first(plain_dnrs); it; it = next)
1542 dnr = iter_data(it);
1543 next = iter_next(it);
1544 if(dnr->expires && dnr->expires <= now)
1545 dict_remove(plain_dnrs, dnr->chan_name + 1);
1547 for(it = dict_first(mask_dnrs); it; it = next)
1549 dnr = iter_data(it);
1550 next = iter_next(it);
1551 if(dnr->expires && dnr->expires <= now)
1552 dict_remove(mask_dnrs, dnr->chan_name + 1);
1555 if(chanserv_conf.dnr_expire_frequency)
1556 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
1560 protect_user(const struct userNode *victim, const struct userNode *aggressor, struct chanData *channel)
1562 char protect = channel->chOpts[chProtect];
1563 struct userData *cs_victim, *cs_aggressor;
1565 /* Don't protect if no one is to be protected, someone is attacking
1566 himself, or if the aggressor is an IRC Operator. */
1567 if(protect == 'n' || victim == aggressor || IsOper(aggressor))
1570 /* Don't protect if the victim isn't authenticated (because they
1571 can't be a channel user), unless we are to protect non-users
1573 cs_victim = GetChannelAccess(channel, victim->handle_info);
1574 if(protect != 'a' && !cs_victim)
1577 /* Protect if the aggressor isn't a user because at this point,
1578 the aggressor can only be less than or equal to the victim. */
1579 cs_aggressor = GetChannelAccess(channel, aggressor->handle_info);
1583 /* If the aggressor was a user, then the victim can't be helped. */
1590 if(cs_victim->access > cs_aggressor->access)
1595 if(cs_victim->access >= cs_aggressor->access)
1604 validate_op(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1606 struct chanData *cData = channel->channel_info;
1607 struct userData *cs_victim;
1609 if((!(cs_victim = GetChannelUser(cData, victim->handle_info))
1610 || (cs_victim->access < cData->lvlOpts[lvlGiveOps]))
1611 && !check_user_level(channel, user, lvlEnfOps, 0, 0))
1613 send_message(user, chanserv, "CSMSG_OPBY_LOCKED");
1621 validate_deop(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1623 if(IsService(victim))
1625 send_message(user, chanserv, "MSG_SERVICE_IMMUNE", victim->nick);
1629 if(protect_user(victim, user, channel->channel_info))
1631 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
1638 static struct do_not_register *
1639 chanserv_add_dnr(const char *chan_name, const char *setter, unsigned long expires, const char *reason)
1641 struct do_not_register *dnr = calloc(1, sizeof(*dnr)+strlen(reason));
1642 safestrncpy(dnr->chan_name, chan_name, sizeof(dnr->chan_name));
1643 safestrncpy(dnr->setter, setter, sizeof(dnr->setter));
1644 strcpy(dnr->reason, reason);
1646 dnr->expires = expires;
1647 if(dnr->chan_name[0] == '*')
1648 dict_insert(handle_dnrs, dnr->chan_name+1, dnr);
1649 else if(strpbrk(dnr->chan_name, "*?"))
1650 dict_insert(mask_dnrs, dnr->chan_name, dnr);
1652 dict_insert(plain_dnrs, dnr->chan_name, dnr);
1656 static struct dnrList
1657 chanserv_find_dnrs(const char *chan_name, const char *handle, unsigned int max)
1659 struct dnrList list;
1660 dict_iterator_t it, next;
1661 struct do_not_register *dnr;
1663 dnrList_init(&list);
1665 if(handle && (dnr = dict_find(handle_dnrs, handle, NULL)))
1667 if(dnr->expires && dnr->expires <= now)
1668 dict_remove(handle_dnrs, handle);
1669 else if(list.used < max)
1670 dnrList_append(&list, dnr);
1673 if(chan_name && (dnr = dict_find(plain_dnrs, chan_name, NULL)))
1675 if(dnr->expires && dnr->expires <= now)
1676 dict_remove(plain_dnrs, chan_name);
1677 else if(list.used < max)
1678 dnrList_append(&list, dnr);
1683 for(it = dict_first(mask_dnrs); it && list.used < max; it = next)
1685 next = iter_next(it);
1686 if(!match_ircglob(chan_name, iter_key(it)))
1688 dnr = iter_data(it);
1689 if(dnr->expires && dnr->expires <= now)
1690 dict_remove(mask_dnrs, iter_key(it));
1692 dnrList_append(&list, dnr);
1699 static int dnr_print_func(struct do_not_register *dnr, void *extra)
1701 struct userNode *user;
1702 char buf1[INTERVALLEN];
1703 char buf2[INTERVALLEN];
1710 strftime(buf1, sizeof(buf1), "%d %b %Y", localtime(&feh));
1715 strftime(buf2, sizeof(buf2), "%d %b %Y", localtime(&feh));
1716 send_message(user, chanserv, "CSMSG_DNR_INFO_SET_EXPIRES", dnr->chan_name, buf1, dnr->setter, buf2, dnr->reason);
1720 send_message(user, chanserv, "CSMSG_DNR_INFO_SET", dnr->chan_name, buf1, dnr->setter, dnr->reason);
1723 send_message(user, chanserv, "CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1728 chanserv_show_dnrs(struct userNode *user, struct svccmd *cmd, const char *chan_name, const char *handle)
1730 struct dnrList list;
1733 list = chanserv_find_dnrs(chan_name, handle, UINT_MAX);
1734 for(ii = 0; (ii < list.used) && (ii < 10); ++ii)
1735 dnr_print_func(list.list[ii], user);
1737 reply("CSMSG_MORE_DNRS", list.used - ii);
1742 struct do_not_register *
1743 chanserv_is_dnr(const char *chan_name, struct handle_info *handle)
1745 struct dnrList list;
1746 struct do_not_register *dnr;
1748 list = chanserv_find_dnrs(chan_name, handle ? handle->handle : NULL, 1);
1749 dnr = list.used ? list.list[0] : NULL;
1754 static unsigned int send_dnrs(struct userNode *user, dict_t dict)
1756 struct do_not_register *dnr;
1757 dict_iterator_t it, next;
1758 unsigned int matches = 0;
1760 for(it = dict_first(dict); it; it = next)
1762 dnr = iter_data(it);
1763 next = iter_next(it);
1764 if(dnr->expires && dnr->expires <= now)
1766 dict_remove(dict, iter_key(it));
1769 dnr_print_func(dnr, user);
1776 static CHANSERV_FUNC(cmd_noregister)
1780 unsigned long expiry, duration;
1781 unsigned int matches;
1785 reply("CSMSG_DNR_SEARCH_RESULTS");
1786 matches = send_dnrs(user, handle_dnrs);
1787 matches += send_dnrs(user, plain_dnrs);
1788 matches += send_dnrs(user, mask_dnrs);
1790 reply("MSG_MATCH_COUNT", matches);
1792 reply("MSG_NO_MATCHES");
1798 if(!IsChannelName(target) && (*target != '*'))
1800 reply("CSMSG_NOT_DNR", target);
1808 reply("MSG_INVALID_DURATION", argv[2]);
1812 if(!strcmp(argv[2], "0"))
1814 else if((duration = ParseInterval(argv[2])))
1815 expiry = now + duration;
1818 reply("MSG_INVALID_DURATION", argv[2]);
1822 reason = unsplit_string(argv + 3, argc - 3, NULL);
1823 if((*target == '*') && !get_handle_info(target + 1))
1825 reply("MSG_HANDLE_UNKNOWN", target + 1);
1828 chanserv_add_dnr(target, user->handle_info->handle, expiry, reason);
1829 reply("CSMSG_NOREGISTER_CHANNEL", target);
1833 reply("CSMSG_DNR_SEARCH_RESULTS");
1835 matches = chanserv_show_dnrs(user, cmd, NULL, target + 1);
1837 matches = chanserv_show_dnrs(user, cmd, target, NULL);
1839 reply("MSG_NO_MATCHES");
1843 static CHANSERV_FUNC(cmd_allowregister)
1845 const char *chan_name = argv[1];
1847 if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
1848 || dict_remove(plain_dnrs, chan_name)
1849 || dict_remove(mask_dnrs, chan_name))
1851 reply("CSMSG_DNR_REMOVED", chan_name);
1854 reply("CSMSG_NO_SUCH_DNR", chan_name);
1859 struct userNode *source;
1863 unsigned long min_set, max_set;
1864 unsigned long min_expires, max_expires;
1869 dnr_search_matches(const struct do_not_register *dnr, const struct dnr_search *search)
1871 return !((dnr->set < search->min_set)
1872 || (dnr->set > search->max_set)
1873 || (dnr->expires < search->min_expires)
1874 || (search->max_expires
1875 && ((dnr->expires == 0)
1876 || (dnr->expires > search->max_expires)))
1877 || (search->chan_mask
1878 && !match_ircglob(dnr->chan_name, search->chan_mask))
1879 || (search->setter_mask
1880 && !match_ircglob(dnr->setter, search->setter_mask))
1881 || (search->reason_mask
1882 && !match_ircglob(dnr->reason, search->reason_mask)));
1885 static struct dnr_search *
1886 dnr_search_create(struct userNode *user, struct svccmd *cmd, unsigned int argc, char *argv[])
1888 struct dnr_search *discrim;
1891 discrim = calloc(1, sizeof(*discrim));
1892 discrim->source = user;
1893 discrim->chan_mask = NULL;
1894 discrim->setter_mask = NULL;
1895 discrim->reason_mask = NULL;
1896 discrim->max_set = INT_MAX;
1897 discrim->limit = 50;
1899 for(ii=0; ii<argc; ++ii)
1903 reply("MSG_MISSING_PARAMS", argv[ii]);
1906 else if(0 == irccasecmp(argv[ii], "channel"))
1908 discrim->chan_mask = argv[++ii];
1910 else if(0 == irccasecmp(argv[ii], "setter"))
1912 discrim->setter_mask = argv[++ii];
1914 else if(0 == irccasecmp(argv[ii], "reason"))
1916 discrim->reason_mask = argv[++ii];
1918 else if(0 == irccasecmp(argv[ii], "limit"))
1920 discrim->limit = strtoul(argv[++ii], NULL, 0);
1922 else if(0 == irccasecmp(argv[ii], "set"))
1924 const char *cmp = argv[++ii];
1927 discrim->min_set = now - ParseInterval(cmp + 2);
1929 discrim->min_set = now - (ParseInterval(cmp + 1) - 1);
1930 } else if(cmp[0] == '=') {
1931 discrim->min_set = discrim->max_set = now - ParseInterval(cmp + 1);
1932 } else if(cmp[0] == '>') {
1934 discrim->max_set = now - ParseInterval(cmp + 2);
1936 discrim->max_set = now - (ParseInterval(cmp + 1) - 1);
1938 discrim->max_set = now - (ParseInterval(cmp) - 1);
1941 else if(0 == irccasecmp(argv[ii], "expires"))
1943 const char *cmp = argv[++ii];
1946 discrim->max_expires = now + ParseInterval(cmp + 2);
1948 discrim->max_expires = now + (ParseInterval(cmp + 1) - 1);
1949 } else if(cmp[0] == '=') {
1950 discrim->min_expires = discrim->max_expires = now + ParseInterval(cmp + 1);
1951 } else if(cmp[0] == '>') {
1953 discrim->min_expires = now + ParseInterval(cmp + 2);
1955 discrim->min_expires = now + (ParseInterval(cmp + 1) - 1);
1957 discrim->min_expires = now + (ParseInterval(cmp) - 1);
1962 reply("MSG_INVALID_CRITERIA", argv[ii]);
1973 typedef int (*dnr_search_func)(struct do_not_register *match, void *extra);
1976 dnr_search(struct dnr_search *discrim, dnr_search_func dsf, void *data)
1978 struct do_not_register *dnr;
1979 dict_iterator_t next;
1984 /* Initialize local variables. */
1987 if(discrim->chan_mask)
1989 int shift = (discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*') ? 2 : 0;
1990 if('\0' == discrim->chan_mask[shift + strcspn(discrim->chan_mask+shift, "*?")])
1994 if(target_fixed && discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*')
1996 /* Check against account-based DNRs. */
1997 dnr = dict_find(handle_dnrs, discrim->chan_mask + 2, NULL);
1998 if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
2001 else if(target_fixed)
2003 /* Check against channel-based DNRs. */
2004 dnr = dict_find(plain_dnrs, discrim->chan_mask, NULL);
2005 if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
2010 /* Exhaustively search account DNRs. */
2011 for(it = dict_first(handle_dnrs); it; it = next)
2013 next = iter_next(it);
2014 dnr = iter_data(it);
2015 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
2019 /* Do the same for channel DNRs. */
2020 for(it = dict_first(plain_dnrs); it; it = next)
2022 next = iter_next(it);
2023 dnr = iter_data(it);
2024 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
2028 /* Do the same for wildcarded channel DNRs. */
2029 for(it = dict_first(mask_dnrs); it; it = next)
2031 next = iter_next(it);
2032 dnr = iter_data(it);
2033 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
2041 dnr_remove_func(struct do_not_register *match, void *extra)
2043 struct userNode *user;
2046 chan_name = alloca(strlen(match->chan_name) + 1);
2047 strcpy(chan_name, match->chan_name);
2049 if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
2050 || dict_remove(plain_dnrs, chan_name)
2051 || dict_remove(mask_dnrs, chan_name))
2053 send_message(user, chanserv, "CSMSG_DNR_REMOVED", chan_name);
2059 dnr_count_func(struct do_not_register *match, void *extra)
2061 return 0; (void)match; (void)extra;
2064 static MODCMD_FUNC(cmd_dnrsearch)
2066 struct dnr_search *discrim;
2067 dnr_search_func action;
2068 struct svccmd *subcmd;
2069 unsigned int matches;
2072 sprintf(buf, "dnrsearch %s", argv[1]);
2073 subcmd = dict_find(cmd->parent->commands, buf, NULL);
2076 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
2079 if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
2081 if(!irccasecmp(argv[1], "print"))
2082 action = dnr_print_func;
2083 else if(!irccasecmp(argv[1], "remove"))
2084 action = dnr_remove_func;
2085 else if(!irccasecmp(argv[1], "count"))
2086 action = dnr_count_func;
2089 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
2093 discrim = dnr_search_create(user, cmd, argc-2, argv+2);
2097 if(action == dnr_print_func)
2098 reply("CSMSG_DNR_SEARCH_RESULTS");
2099 matches = dnr_search(discrim, action, user);
2101 reply("MSG_MATCH_COUNT", matches);
2103 reply("MSG_NO_MATCHES");
2109 chanserv_get_owned_count(struct handle_info *hi)
2111 struct userData *cList;
2114 for(owned=0, cList=hi->channels; cList; cList=cList->u_next)
2115 if(cList->access == UL_OWNER)
2120 static CHANSERV_FUNC(cmd_register)
2122 struct handle_info *handle;
2123 struct chanData *cData;
2124 struct modeNode *mn;
2125 char reason[MAXLEN];
2127 unsigned int new_channel, force=0;
2128 struct do_not_register *dnr;
2132 if(channel->channel_info)
2134 reply("CSMSG_ALREADY_REGGED", channel->name);
2138 if(channel->bad_channel)
2140 reply("CSMSG_ILLEGAL_CHANNEL", channel->name);
2145 && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
2147 reply("CSMSG_MUST_BE_OPPED", channel->name);
2152 chan_name = channel->name;
2156 if((argc < 2) || !IsChannelName(argv[1]))
2158 reply("MSG_NOT_CHANNEL_NAME");
2162 if(opserv_bad_channel(argv[1]))
2164 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2169 chan_name = argv[1];
2172 if(argc >= (new_channel+2))
2174 if(!IsHelping(user))
2176 reply("CSMSG_PROXY_FORBIDDEN");
2180 if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
2182 force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
2183 dnr = chanserv_is_dnr(chan_name, handle);
2187 handle = user->handle_info;
2188 dnr = chanserv_is_dnr(chan_name, handle);
2192 if(!IsHelping(user))
2193 reply("CSMSG_DNR_CHANNEL", chan_name);
2195 chanserv_show_dnrs(user, cmd, chan_name, handle->handle);
2199 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2201 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
2206 channel = AddChannel(argv[1], now, NULL, NULL);
2208 cData = register_channel(channel, user->handle_info->handle);
2209 scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL), NULL);
2210 cData->modes = chanserv_conf.default_modes;
2212 cData->modes.modes_set |= MODE_REGISTERED;
2213 if (IsOffChannel(cData))
2215 mod_chanmode_announce(chanserv, channel, &cData->modes);
2219 struct mod_chanmode *change = mod_chanmode_dup(&cData->modes, 1);
2220 change->args[change->argc].mode = MODE_CHANOP;
2221 change->args[change->argc].u.member = AddChannelUser(chanserv, channel);
2223 mod_chanmode_announce(chanserv, channel, change);
2224 mod_chanmode_free(change);
2227 /* Initialize the channel's max user record. */
2228 cData->max = channel->members.used;
2229 cData->max_time = 0;
2231 if(handle != user->handle_info)
2232 reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
2234 reply("CSMSG_REG_SUCCESS", channel->name);
2236 sprintf(reason, "%s registered to %s by %s.", channel->name, handle->handle, user->handle_info->handle);
2237 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2242 make_confirmation_string(struct userData *uData)
2244 static char strbuf[16];
2249 for(src = uData->handle->handle; *src; )
2250 accum = accum * 31 + toupper(*src++);
2252 for(src = uData->channel->channel->name; *src; )
2253 accum = accum * 31 + toupper(*src++);
2254 sprintf(strbuf, "%08x", accum);
2258 static CHANSERV_FUNC(cmd_unregister)
2261 char reason[MAXLEN];
2262 struct chanData *cData;
2263 struct userData *uData;
2265 cData = channel->channel_info;
2268 reply("CSMSG_NOT_REGISTERED", channel->name);
2272 uData = GetChannelUser(cData, user->handle_info);
2273 if(!uData || (uData->access < UL_OWNER))
2275 reply("CSMSG_NO_ACCESS");
2279 if(IsProtected(cData))
2281 reply("CSMSG_UNREG_NODELETE", channel->name);
2285 if(!IsHelping(user))
2287 const char *confirm_string;
2288 if(IsSuspended(cData))
2290 reply("CSMSG_CHAN_SUSPENDED", channel->name, cData->suspended->reason);
2293 confirm_string = make_confirmation_string(uData);
2294 if((argc < 2) || strcmp(argv[1], confirm_string))
2296 reply("CSMSG_CONFIRM_UNREG", confirm_string);
2301 sprintf(reason, "unregistered by %s.", user->handle_info->handle);
2302 name = strdup(channel->name);
2303 unregister_channel(cData, reason);
2304 spamserv_cs_unregister(user, channel, manually, "unregistered");
2305 reply("CSMSG_UNREG_SUCCESS", name);
2311 ss_cs_join_channel(struct chanNode *channel, int spamserv_join)
2313 extern struct userNode *spamserv;
2314 struct mod_chanmode *change;
2316 if(spamserv && spamserv_join && get_chanInfo(channel->name))
2318 change = mod_chanmode_alloc(2);
2320 change->args[0].mode = MODE_CHANOP;
2321 change->args[0].u.member = AddChannelUser(chanserv, channel);
2322 change->args[1].mode = MODE_CHANOP;
2323 change->args[1].u.member = AddChannelUser(spamserv, channel);
2327 change = mod_chanmode_alloc(1);
2329 change->args[0].mode = MODE_CHANOP;
2330 change->args[0].u.member = AddChannelUser(chanserv, channel);
2333 mod_chanmode_announce(chanserv, channel, change);
2334 mod_chanmode_free(change);
2337 static CHANSERV_FUNC(cmd_move)
2339 struct mod_chanmode change;
2340 struct chanNode *target;
2341 struct modeNode *mn;
2342 struct userData *uData;
2343 char reason[MAXLEN];
2344 struct do_not_register *dnr;
2345 int chanserv_join = 0, spamserv_join;
2349 if(IsProtected(channel->channel_info))
2351 reply("CSMSG_MOVE_NODELETE", channel->name);
2355 if(!IsChannelName(argv[1]))
2357 reply("MSG_NOT_CHANNEL_NAME");
2361 if(opserv_bad_channel(argv[1]))
2363 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2367 if(!IsHelping(user) || (argc < 3) || irccasecmp(argv[2], "force"))
2369 for(uData = channel->channel_info->users; uData; uData = uData->next)
2371 if((uData->access == UL_OWNER) && (dnr = chanserv_is_dnr(argv[1], uData->handle)))
2373 if(!IsHelping(user))
2374 reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]);
2376 chanserv_show_dnrs(user, cmd, argv[1], uData->handle->handle);
2382 mod_chanmode_init(&change);
2383 if(!(target = GetChannel(argv[1])))
2385 target = AddChannel(argv[1], now, NULL, NULL);
2386 if(!IsSuspended(channel->channel_info))
2389 else if(target->channel_info)
2391 reply("CSMSG_ALREADY_REGGED", target->name);
2394 else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
2395 && !IsHelping(user))
2397 reply("CSMSG_MUST_BE_OPPED", target->name);
2400 else if(!IsSuspended(channel->channel_info))
2405 /* Clear MODE_REGISTERED from old channel, add it to new. */
2407 change.modes_clear = MODE_REGISTERED;
2408 mod_chanmode_announce(chanserv, channel, &change);
2409 change.modes_clear = 0;
2410 change.modes_set = MODE_REGISTERED;
2411 mod_chanmode_announce(chanserv, target, &change);
2414 /* Move the channel_info to the target channel; it
2415 shouldn't be necessary to clear timeq callbacks
2416 for the old channel. */
2417 target->channel_info = channel->channel_info;
2418 target->channel_info->channel = target;
2419 channel->channel_info = NULL;
2421 /* Check whether users are present in the new channel. */
2422 for(uData = target->channel_info->users; uData; uData = uData->next)
2423 scan_user_presence(uData, NULL);
2425 spamserv_join = spamserv_cs_move_merge(user, channel, target, 1);
2428 ss_cs_join_channel(target, spamserv_join);
2430 sprintf(reason, "%s moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
2431 if(!IsSuspended(target->channel_info))
2433 char reason2[MAXLEN];
2434 sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
2435 DelChannelUser(chanserv, channel, reason2, 0);
2437 UnlockChannel(channel);
2438 LockChannel(target);
2439 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2440 reply("CSMSG_MOVE_SUCCESS", target->name);
2445 merge_users(struct chanData *source, struct chanData *target)
2447 struct userData *suData, *tuData, *next;
2453 /* Insert the source's users into the scratch area. */
2454 for(suData = source->users; suData; suData = suData->next)
2455 dict_insert(merge, suData->handle->handle, suData);
2457 /* Iterate through the target's users, looking for
2458 users common to both channels. The lower access is
2459 removed from either the scratch area or target user
2461 for(tuData = target->users; tuData; tuData = next)
2463 struct userData *choice;
2465 next = tuData->next;
2467 /* If a source user exists with the same handle as a target
2468 channel's user, resolve the conflict by removing one. */
2469 suData = dict_find(merge, tuData->handle->handle, NULL);
2473 /* Pick the data we want to keep. */
2474 /* If the access is the same, use the later seen time. */
2475 if(suData->access == tuData->access)
2476 choice = (suData->seen > tuData->seen) ? suData : tuData;
2477 else /* Otherwise, keep the higher access level. */
2478 choice = (suData->access > tuData->access) ? suData : tuData;
2479 /* Use the later seen time. */
2480 if(suData->seen < tuData->seen)
2481 suData->seen = tuData->seen;
2483 tuData->seen = suData->seen;
2485 /* Remove the user that wasn't picked. */
2486 if(choice == tuData)
2488 dict_remove(merge, suData->handle->handle);
2489 del_channel_user(suData, 0);
2492 del_channel_user(tuData, 0);
2495 /* Move the remaining users to the target channel. */
2496 for(it = dict_first(merge); it; it = iter_next(it))
2498 suData = iter_data(it);
2500 /* Insert the user into the target channel's linked list. */
2501 suData->prev = NULL;
2502 suData->next = target->users;
2503 suData->channel = target;
2506 target->users->prev = suData;
2507 target->users = suData;
2509 /* Update the user counts for the target channel; the
2510 source counts are left alone. */
2511 target->userCount++;
2513 /* Check whether the user is in the target channel. */
2514 scan_user_presence(suData, NULL);
2517 /* Possible to assert (source->users == NULL) here. */
2518 source->users = NULL;
2523 merge_bans(struct chanData *source, struct chanData *target)
2525 struct banData *sbData, *tbData, *sNext, *tNext, *tFront;
2527 /* Hold on to the original head of the target ban list
2528 to avoid comparing source bans with source bans. */
2529 tFront = target->bans;
2531 /* Perform a totally expensive O(n*m) merge, ick. */
2532 for(sbData = source->bans; sbData; sbData = sNext)
2534 /* Flag to track whether the ban's been moved
2535 to the destination yet. */
2538 /* Possible to assert (sbData->prev == NULL) here. */
2539 sNext = sbData->next;
2541 for(tbData = tFront; tbData; tbData = tNext)
2543 tNext = tbData->next;
2545 /* Perform two comparisons between each source
2546 and target ban, conflicts are resolved by
2547 keeping the broader ban and copying the later
2548 expiration and triggered time. */
2549 if(match_ircglobs(tbData->mask, sbData->mask))
2551 /* There is a broader ban in the target channel that
2552 overrides one in the source channel; remove the
2553 source ban and break. */
2554 if(sbData->expires > tbData->expires)
2555 tbData->expires = sbData->expires;
2556 if(sbData->triggered > tbData->triggered)
2557 tbData->triggered = sbData->triggered;
2558 del_channel_ban(sbData);
2561 else if(match_ircglobs(sbData->mask, tbData->mask))
2563 /* There is a broader ban in the source channel that
2564 overrides one in the target channel; remove the
2565 target ban, fall through and move the source over. */
2566 if(tbData->expires > sbData->expires)
2567 sbData->expires = tbData->expires;
2568 if(tbData->triggered > sbData->triggered)
2569 sbData->triggered = tbData->triggered;
2570 if(tbData == tFront)
2572 del_channel_ban(tbData);
2575 /* Source bans can override multiple target bans, so
2576 we allow a source to run through this loop multiple
2577 times, but we can only move it once. */
2582 /* Remove the source ban from the source ban list. */
2584 sbData->next->prev = sbData->prev;
2586 /* Modify the source ban's associated channel. */
2587 sbData->channel = target;
2589 /* Insert the ban into the target channel's linked list. */
2590 sbData->prev = NULL;
2591 sbData->next = target->bans;
2594 target->bans->prev = sbData;
2595 target->bans = sbData;
2597 /* Update the user counts for the target channel. */
2602 /* Possible to assert (source->bans == NULL) here. */
2603 source->bans = NULL;
2607 merge_data(struct chanData *source, struct chanData *target)
2609 /* Use more recent visited and owner-transfer time; use older
2610 * registered time. Bitwise or may_opchan. Use higher max.
2611 * Do not touch last_refresh, ban count or user counts.
2613 if(source->visited > target->visited)
2614 target->visited = source->visited;
2615 if(source->registered < target->registered)
2616 target->registered = source->registered;
2617 if(source->ownerTransfer > target->ownerTransfer)
2618 target->ownerTransfer = source->ownerTransfer;
2619 if(source->may_opchan)
2620 target->may_opchan = 1;
2621 if(source->max > target->max) {
2622 target->max = source->max;
2623 target->max_time = source->max_time;
2628 merge_channel(struct chanData *source, struct chanData *target)
2630 merge_users(source, target);
2631 merge_bans(source, target);
2632 merge_data(source, target);
2635 static CHANSERV_FUNC(cmd_merge)
2637 struct userData *target_user;
2638 struct chanNode *target;
2639 char reason[MAXLEN];
2643 /* Make sure the target channel exists and is registered to the user
2644 performing the command. */
2645 if(!(target = GetChannel(argv[1])))
2647 reply("MSG_INVALID_CHANNEL");
2651 if(!target->channel_info)
2653 reply("CSMSG_NOT_REGISTERED", target->name);
2657 if(IsProtected(channel->channel_info))
2659 reply("CSMSG_MERGE_NODELETE");
2663 if(IsSuspended(target->channel_info))
2665 reply("CSMSG_MERGE_SUSPENDED");
2669 if(channel == target)
2671 reply("CSMSG_MERGE_SELF");
2675 target_user = GetChannelUser(target->channel_info, user->handle_info);
2676 if(!target_user || (target_user->access < UL_OWNER))
2678 reply("CSMSG_MERGE_NOT_OWNER");
2682 /* Merge the channel structures and associated data. */
2683 merge_channel(channel->channel_info, target->channel_info);
2684 spamserv_cs_move_merge(user, channel, target, 0);
2685 sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
2686 unregister_channel(channel->channel_info, reason);
2687 reply("CSMSG_MERGE_SUCCESS", target->name);
2691 static CHANSERV_FUNC(cmd_opchan)
2693 struct mod_chanmode change;
2694 if(!IsHelping(user) && !channel->channel_info->may_opchan)
2696 reply("CSMSG_ALREADY_OPCHANNED", channel->name);
2699 channel->channel_info->may_opchan = 0;
2700 mod_chanmode_init(&change);
2702 change.args[0].mode = MODE_CHANOP;
2703 change.args[0].u.member = GetUserMode(channel, chanserv);
2704 if(!change.args[0].u.member)
2706 reply("CSMSG_OUT_OF_CHANNEL", channel->name);
2709 mod_chanmode_announce(chanserv, channel, &change);
2710 reply("CSMSG_OPCHAN_DONE", channel->name);
2714 static CHANSERV_FUNC(cmd_adduser)
2716 struct userData *actee;
2717 struct userData *actor, *real_actor;
2718 struct handle_info *handle;
2719 unsigned short access_level, override = 0;
2723 if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
2725 reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
2729 access_level = user_level_from_name(argv[2], UL_OWNER);
2732 reply("CSMSG_INVALID_ACCESS", argv[2]);
2736 actor = GetChannelUser(channel->channel_info, user->handle_info);
2737 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2739 if(actor->access <= access_level)
2741 reply("CSMSG_NO_BUMP_ACCESS");
2745 /* Trying to add someone with equal/more access? */
2746 if (!real_actor || real_actor->access <= access_level)
2747 override = CMD_LOG_OVERRIDE;
2749 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2752 if((actee = GetTrueChannelAccess(channel->channel_info, handle)))
2754 reply("CSMSG_USER_EXISTS", handle->handle, channel->name, actee->access);
2758 actee = add_channel_user(channel->channel_info, handle, access_level, 0, NULL);
2759 scan_user_presence(actee, NULL);
2760 reply("CSMSG_ADDED_USER", handle->handle, channel->name, access_level);
2761 return 1 | override;
2764 static CHANSERV_FUNC(cmd_clvl)
2766 struct handle_info *handle;
2767 struct userData *victim;
2768 struct userData *actor, *real_actor;
2769 unsigned short new_access, override = 0;
2770 int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
2774 actor = GetChannelUser(channel->channel_info, user->handle_info);
2775 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2777 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2780 if(handle == user->handle_info && !privileged)
2782 reply("CSMSG_NO_SELF_CLVL");
2786 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2788 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2792 if(actor->access <= victim->access && !privileged)
2794 reply("MSG_USER_OUTRANKED", handle->handle);
2798 new_access = user_level_from_name(argv[2], UL_OWNER);
2802 reply("CSMSG_INVALID_ACCESS", argv[2]);
2806 if(new_access >= actor->access && !privileged)
2808 reply("CSMSG_NO_BUMP_ACCESS");
2812 /* Trying to clvl a equal/higher user? */
2813 if(!real_actor || (real_actor->access <= victim->access && handle != user->handle_info))
2814 override = CMD_LOG_OVERRIDE;
2815 /* Trying to clvl someone to equal/higher access? */
2816 if(!real_actor || new_access >= real_actor->access)
2817 override = CMD_LOG_OVERRIDE;
2818 /* Helpers clvling themselves get caught by the "clvl someone to equal/higher access" check.
2819 * If they lower their own access it's not a big problem.
2822 victim->access = new_access;
2823 reply("CSMSG_CHANGED_ACCESS", handle->handle, new_access, channel->name);
2824 return 1 | override;
2827 static CHANSERV_FUNC(cmd_deluser)
2829 struct handle_info *handle;
2830 struct userData *victim;
2831 struct userData *actor, *real_actor;
2832 unsigned short access_level, override = 0;
2837 actor = GetChannelUser(channel->channel_info, user->handle_info);
2838 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2840 if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
2843 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2845 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2851 access_level = user_level_from_name(argv[1], UL_OWNER);
2854 reply("CSMSG_INVALID_ACCESS", argv[1]);
2857 if(access_level != victim->access)
2859 reply("CSMSG_INCORRECT_ACCESS", handle->handle, victim->access, argv[1]);
2865 access_level = victim->access;
2868 if((actor->access <= victim->access) && !IsHelping(user))
2870 reply("MSG_USER_OUTRANKED", victim->handle->handle);
2874 /* If people delete themselves it is an override, but they
2875 * could've used deleteme so we don't log it as an override
2877 if(!real_actor || (real_actor->access <= victim->access && real_actor != victim))
2878 override = CMD_LOG_OVERRIDE;
2880 chan_name = strdup(channel->name);
2881 del_channel_user(victim, 1);
2882 reply("CSMSG_DELETED_USER", handle->handle, access_level, chan_name);
2884 return 1 | override;
2888 cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
2890 struct userData *actor, *real_actor, *uData, *next;
2891 unsigned int override = 0;
2893 actor = GetChannelUser(channel->channel_info, user->handle_info);
2894 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2896 if(min_access > max_access)
2898 reply("CSMSG_BAD_RANGE", min_access, max_access);
2902 if(actor->access <= max_access)
2904 reply("CSMSG_NO_ACCESS");
2908 if(!real_actor || real_actor->access <= max_access)
2909 override = CMD_LOG_OVERRIDE;
2911 for(uData = channel->channel_info->users; uData; uData = next)
2915 if((uData->access >= min_access)
2916 && (uData->access <= max_access)
2917 && match_ircglob(uData->handle->handle, mask))
2918 del_channel_user(uData, 1);
2921 reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
2922 return 1 | override;
2925 static CHANSERV_FUNC(cmd_mdelowner)
2927 return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
2930 static CHANSERV_FUNC(cmd_mdelcoowner)
2932 return cmd_mdel_user(user, channel, UL_COOWNER, UL_COOWNER, argv[1], cmd);
2935 static CHANSERV_FUNC(cmd_mdelmaster)
2937 return cmd_mdel_user(user, channel, UL_MASTER, UL_MASTER, argv[1], cmd);
2940 static CHANSERV_FUNC(cmd_mdelop)
2942 return cmd_mdel_user(user, channel, UL_OP, UL_OP, argv[1], cmd);
2945 static CHANSERV_FUNC(cmd_mdelpeon)
2947 return cmd_mdel_user(user, channel, UL_PEON, UL_PEON, argv[1], cmd);
2951 cmd_trim_bans(struct userNode *user, struct chanNode *channel, unsigned long duration)
2953 struct banData *bData, *next;
2954 char interval[INTERVALLEN];
2956 unsigned long limit;
2959 limit = now - duration;
2960 for(bData = channel->channel_info->bans; bData; bData = next)
2964 if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
2967 del_channel_ban(bData);
2971 intervalString(interval, duration, user->handle_info);
2972 send_message(user, chanserv, "CSMSG_TRIMMED_BANS", count, channel->name, interval);
2977 cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration, int vacation)
2979 struct userData *actor, *uData, *next;
2980 char interval[INTERVALLEN];
2982 unsigned long limit;
2984 actor = GetChannelAccess(channel->channel_info, user->handle_info);
2985 if(min_access > max_access)
2987 send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
2991 if(!actor || actor->access <= max_access)
2993 send_message(user, chanserv, "CSMSG_NO_ACCESS");
2998 limit = now - duration;
2999 for(uData = channel->channel_info->users; uData; uData = next)
3003 if((uData->seen > limit)
3005 || (HANDLE_FLAGGED(uData->handle, FROZEN) && !vacation))
3008 if(((uData->access >= min_access) && (uData->access <= max_access))
3009 || (!max_access && (uData->access < actor->access)))
3011 del_channel_user(uData, 1);
3019 max_access = (actor->access > UL_OWNER) ? UL_OWNER : (actor->access - 1);
3021 send_message(user, chanserv, "CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration, user->handle_info));
3025 static CHANSERV_FUNC(cmd_trim)
3027 unsigned long duration;
3028 unsigned short min_level, max_level;
3033 vacation = argc > 3 && !strcmp(argv[3], "vacation");
3034 duration = ParseInterval(argv[2]);
3037 reply("CSMSG_CANNOT_TRIM");
3041 if(!irccasecmp(argv[1], "bans"))
3043 cmd_trim_bans(user, channel, duration);
3046 else if(!irccasecmp(argv[1], "users"))
3048 cmd_trim_users(user, channel, 0, 0, duration, vacation);
3051 else if(parse_level_range(&min_level, &max_level, argv[1]))
3053 cmd_trim_users(user, channel, min_level, max_level, duration, vacation);
3056 else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
3058 cmd_trim_users(user, channel, min_level, min_level, duration, vacation);
3063 reply("CSMSG_INVALID_TRIM", argv[1]);
3068 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
3069 to the user. cmd_all takes advantage of this. */
3070 static CHANSERV_FUNC(cmd_up)
3072 struct mod_chanmode change;
3073 struct userData *uData;
3076 mod_chanmode_init(&change);
3078 change.args[0].u.member = GetUserMode(channel, user);
3079 if(!change.args[0].u.member)
3082 reply("MSG_CHANNEL_ABSENT", channel->name);
3086 uData = GetChannelAccess(channel->channel_info, user->handle_info);
3090 reply("CSMSG_GODMODE_UP", argv[0]);
3093 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveOps])
3095 change.args[0].mode = MODE_CHANOP;
3096 errmsg = "CSMSG_ALREADY_OPPED";
3098 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveVoice])
3100 change.args[0].mode = MODE_VOICE;
3101 errmsg = "CSMSG_ALREADY_VOICED";
3106 reply("CSMSG_NO_ACCESS");
3109 change.args[0].mode &= ~change.args[0].u.member->modes;
3110 if(!change.args[0].mode)
3113 reply(errmsg, channel->name);
3116 modcmd_chanmode_announce(&change);
3120 static CHANSERV_FUNC(cmd_down)
3122 struct mod_chanmode change;
3124 mod_chanmode_init(&change);
3126 change.args[0].u.member = GetUserMode(channel, user);
3127 if(!change.args[0].u.member)
3130 reply("MSG_CHANNEL_ABSENT", channel->name);
3134 if(!change.args[0].u.member->modes)
3137 reply("CSMSG_ALREADY_DOWN", channel->name);
3141 change.args[0].mode = MODE_REMOVE | change.args[0].u.member->modes;
3142 modcmd_chanmode_announce(&change);
3146 static int cmd_all(struct userNode *user, UNUSED_ARG(struct chanNode *channel), UNUSED_ARG(unsigned int argc), UNUSED_ARG(char *argv[]), struct svccmd *cmd, modcmd_func_t mcmd)
3148 struct userData *cList;
3150 for(cList = user->handle_info->channels; cList; cList = cList->u_next)
3152 if(IsSuspended(cList->channel)
3153 || IsUserSuspended(cList)
3154 || !GetUserMode(cList->channel->channel, user))
3157 mcmd(user, cList->channel->channel, 0, NULL, cmd);
3163 static CHANSERV_FUNC(cmd_upall)
3165 return cmd_all(CSFUNC_ARGS, cmd_up);
3168 static CHANSERV_FUNC(cmd_downall)
3170 return cmd_all(CSFUNC_ARGS, cmd_down);
3173 typedef int validate_func_t(struct userNode *user, struct chanNode *channel, struct userNode *victim);
3174 typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
3177 modify_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, validate_func_t validate, chan_mode_t mode, char *action)
3179 unsigned int ii, valid;
3180 struct userNode *victim;
3181 struct mod_chanmode *change;
3183 change = mod_chanmode_alloc(argc - 1);
3185 for(ii=valid=0; ++ii < argc; )
3187 if(!(victim = GetUserH(argv[ii])))
3189 change->args[valid].mode = mode;
3190 change->args[valid].u.member = GetUserMode(channel, victim);
3191 if(!change->args[valid].u.member)
3193 if(validate && !validate(user, channel, victim))
3198 change->argc = valid;
3199 if(valid < (argc-1))
3200 reply("CSMSG_PROCESS_FAILED");
3203 modcmd_chanmode_announce(change);
3204 reply(action, channel->name);
3206 mod_chanmode_free(change);
3210 static CHANSERV_FUNC(cmd_op)
3212 return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
3215 static CHANSERV_FUNC(cmd_deop)
3217 return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
3220 static CHANSERV_FUNC(cmd_voice)
3222 return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
3225 static CHANSERV_FUNC(cmd_devoice)
3227 return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
3231 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, unsigned int *victimCount, struct modeNode **victims)
3237 for(ii=0; ii<channel->members.used; ii++)
3239 struct modeNode *mn = channel->members.list[ii];
3241 if(IsService(mn->user))
3244 if(!user_matches_glob(mn->user, ban, MATCH_USENICK | MATCH_VISIBLE))
3247 if(protect_user(mn->user, user, channel->channel_info))
3251 victims[(*victimCount)++] = mn;
3257 eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3259 struct userNode *victim;
3260 struct modeNode **victims;
3261 unsigned int offset, n, victimCount, duration = 0;
3262 char *reason = "Bye.", *ban, *name;
3263 char interval[INTERVALLEN];
3265 offset = (action & ACTION_ADD_TIMED_BAN) ? 3 : 2;
3266 REQUIRE_PARAMS(offset);
3269 reason = unsplit_string(argv + offset, argc - offset, NULL);
3270 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
3272 /* Truncate the reason to a length of TOPICLEN, as
3273 the ircd does; however, leave room for an ellipsis
3274 and the kicker's nick. */
3275 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
3279 if((victim = GetUserH(argv[1])))
3281 victims = alloca(sizeof(victims[0]));
3282 victims[0] = GetUserMode(channel, victim);
3283 /* XXX: The comparison with ACTION_KICK is just because all
3284 * other actions can work on users outside the channel, and we
3285 * want to allow those (e.g. unbans) in that case. If we add
3286 * some other ejection action for in-channel users, change
3288 victimCount = victims[0] ? 1 : 0;
3290 if(IsService(victim))
3292 reply("MSG_SERVICE_IMMUNE", victim->nick);
3296 if((action == ACTION_KICK) && !victimCount)
3298 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
3302 if(protect_user(victim, user, channel->channel_info))
3304 reply("CSMSG_USER_PROTECTED", victim->nick);
3308 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
3309 name = victim->nick;
3311 else if(!is_ircmask(argv[1]) && (*argv[1] == '*'))
3313 struct handle_info *hi;
3314 extern const char *titlehost_suffix;
3315 char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
3316 const char *accountname = argv[1] + 1;
3318 if(!(hi = get_handle_info(accountname)))
3320 reply("MSG_HANDLE_UNKNOWN", accountname);
3324 snprintf(banmask, sizeof(banmask), "*!*@%s.*.%s", hi->handle, titlehost_suffix);
3325 victims = alloca(sizeof(victims[0]) * channel->members.used);
3327 if(bad_channel_ban(channel, user, banmask, &victimCount, victims))
3329 reply("CSMSG_MASK_PROTECTED", banmask);
3333 if((action == ACTION_KICK) && (victimCount == 0))
3335 reply("CSMSG_NO_MATCHING_USERS", channel->name, banmask);
3339 name = ban = strdup(banmask);
3343 if(!is_ircmask(argv[1]))
3345 reply("MSG_NICK_UNKNOWN", argv[1]);
3349 victims = alloca(sizeof(victims[0]) * channel->members.used);
3351 if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
3353 reply("CSMSG_MASK_PROTECTED", argv[1]);
3357 if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
3359 reply("CSMSG_LAME_MASK", argv[1]);
3363 if((action == ACTION_KICK) && (victimCount == 0))
3365 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
3369 name = ban = strdup(argv[1]);
3372 /* Truncate the ban in place if necessary; we must ensure
3373 that 'ban' is a valid ban mask before sanitizing it. */
3374 sanitize_ircmask(ban);
3376 if(action & ACTION_ADD_BAN)
3378 struct banData *bData, *next;
3380 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans)
3382 reply("CSMSG_MAXIMUM_BANS", chanserv_conf.max_chan_bans);
3387 if(action & ACTION_ADD_TIMED_BAN)
3389 duration = ParseInterval(argv[2]);
3393 reply("CSMSG_DURATION_TOO_LOW");
3397 else if(duration > (86400 * 365 * 2))
3399 reply("CSMSG_DURATION_TOO_HIGH");
3405 for(bData = channel->channel_info->bans; bData; bData = next)
3407 if(match_ircglobs(bData->mask, ban))
3409 int exact = !irccasecmp(bData->mask, ban);
3411 /* The ban is redundant; there is already a ban
3412 with the same effect in place. */
3416 free(bData->reason);
3417 bData->reason = strdup(reason);
3418 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
3420 reply("CSMSG_REASON_CHANGE", ban);
3424 if(exact && bData->expires)
3428 /* If the ban matches an existing one exactly,
3429 extend the expiration time if the provided
3430 duration is longer. */
3431 if(duration && (now + duration > bData->expires))
3433 bData->expires = now + duration;
3444 /* Delete the expiration timeq entry and
3445 requeue if necessary. */
3446 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
3449 timeq_add(bData->expires, expire_ban, bData);
3453 /* automated kickban */
3456 reply("CSMSG_BAN_EXTENDED", ban, intervalString(interval, duration, user->handle_info));
3458 reply("CSMSG_BAN_ADDED", name, channel->name);
3464 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3471 if(match_ircglobs(ban, bData->mask))
3473 /* The ban we are adding makes previously existing
3474 bans redundant; silently remove them. */
3475 del_channel_ban(bData);
3479 bData = add_channel_ban(channel->channel_info, ban, (user->handle_info ? user->handle_info->handle : user->nick), now, (victimCount ? now : 0), (duration ? now + duration : 0), reason);
3481 name = ban = strdup(bData->mask);
3485 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
3487 extern const char *hidden_host_suffix;
3488 const char *old_name = chanserv_conf.old_ban_names->list[n];
3490 unsigned int l1, l2;
3493 l2 = strlen(old_name);
3496 if(irccasecmp(ban + l1 - l2, old_name))
3498 new_mask = malloc(MAXLEN);
3499 sprintf(new_mask, "%.*s%s", (int)(l1-l2), ban, hidden_host_suffix);
3501 name = ban = new_mask;
3506 if(action & ACTION_BAN)
3508 unsigned int exists;
3509 struct mod_chanmode *change;
3511 if(channel->banlist.used >= MAXBANS)
3514 reply("CSMSG_BANLIST_FULL", channel->name);
3519 exists = ChannelBanExists(channel, ban);
3520 change = mod_chanmode_alloc(victimCount + 1);
3521 for(n = 0; n < victimCount; ++n)
3523 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_VOICE;
3524 change->args[n].u.member = victims[n];
3528 change->args[n].mode = MODE_BAN;
3529 change->args[n++].u.hostmask = ban;
3533 modcmd_chanmode_announce(change);
3535 mod_chanmode_announce(chanserv, channel, change);
3536 mod_chanmode_free(change);
3538 if(exists && (action == ACTION_BAN))
3541 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3547 if(action & ACTION_KICK)
3549 char kick_reason[MAXLEN];
3550 sprintf(kick_reason, "(%s) %s", user->nick, reason);
3552 for(n = 0; n < victimCount; n++)
3553 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
3558 /* No response, since it was automated. */
3560 else if(action & ACTION_ADD_BAN)
3563 reply("CSMSG_TIMED_BAN_ADDED", name, channel->name, intervalString(interval, duration, user->handle_info));
3565 reply("CSMSG_BAN_ADDED", name, channel->name);
3567 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
3568 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
3569 else if(action & ACTION_BAN)
3570 reply("CSMSG_BAN_DONE", name, channel->name);
3571 else if(action & ACTION_KICK && victimCount)
3572 reply("CSMSG_KICK_DONE", name, channel->name);
3578 static CHANSERV_FUNC(cmd_kickban)
3580 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
3583 static CHANSERV_FUNC(cmd_kick)
3585 return eject_user(CSFUNC_ARGS, ACTION_KICK);
3588 static CHANSERV_FUNC(cmd_ban)
3590 return eject_user(CSFUNC_ARGS, ACTION_BAN);
3593 static CHANSERV_FUNC(cmd_addban)
3595 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN);
3598 static CHANSERV_FUNC(cmd_addtimedban)
3600 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN);
3603 static struct mod_chanmode *
3604 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
3606 struct mod_chanmode *change;
3607 unsigned char *match;
3608 unsigned int ii, count;
3610 match = alloca(bans->used);
3613 for(ii = count = 0; ii < bans->used; ++ii)
3615 match[ii] = user_matches_glob(actee, bans->list[ii]->ban,
3616 MATCH_USENICK | MATCH_VISIBLE);
3623 for(ii = count = 0; ii < bans->used; ++ii)
3625 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
3632 change = mod_chanmode_alloc(count);
3633 for(ii = count = 0; ii < bans->used; ++ii)
3637 change->args[count].mode = MODE_REMOVE | MODE_BAN;
3638 change->args[count++].u.hostmask = strdup(bans->list[ii]->ban);
3640 assert(count == change->argc);
3645 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3647 struct userNode *actee;
3653 /* may want to allow a comma delimited list of users... */
3654 if(!(actee = GetUserH(argv[1])))
3656 if(!is_ircmask(argv[1]) && *argv[1] == '*')
3658 char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
3659 const char *accountname = argv[1] + 1;
3661 snprintf(banmask, sizeof(banmask), "*!*@%s.*", accountname);
3662 mask = strdup(banmask);
3664 else if(!is_ircmask(argv[1]))
3666 reply("MSG_NICK_UNKNOWN", argv[1]);
3671 mask = strdup(argv[1]);
3675 /* We don't sanitize the mask here because ircu
3677 if(action & ACTION_UNBAN)
3679 struct mod_chanmode *change;
3680 change = find_matching_bans(&channel->banlist, actee, mask);
3685 modcmd_chanmode_announce(change);
3686 for(ii = 0; ii < change->argc; ++ii)
3687 free((char*)change->args[ii].u.hostmask);
3688 mod_chanmode_free(change);
3693 if(action & ACTION_DEL_BAN)
3695 struct banData *ban, *next;
3697 ban = channel->channel_info->bans;
3701 for( ; ban && !user_matches_glob(actee, ban->mask,
3702 MATCH_USENICK | MATCH_VISIBLE);
3705 for( ; ban && !match_ircglobs(mask, ban->mask);
3710 del_channel_ban(ban);
3717 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3719 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3725 static CHANSERV_FUNC(cmd_unban)
3727 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3730 static CHANSERV_FUNC(cmd_delban)
3732 /* it doesn't necessarily have to remove the channel ban - may want
3733 to make that an option. */
3734 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_BAN);
3737 static CHANSERV_FUNC(cmd_unbanme)
3739 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3740 long flags = ACTION_UNBAN;
3742 /* remove permanent bans if the user has the proper access. */
3743 if(uData->access >= UL_MASTER)
3744 flags |= ACTION_DEL_BAN;
3746 argv[1] = user->nick;
3747 return unban_user(user, channel, 2, argv, cmd, flags);
3750 static CHANSERV_FUNC(cmd_unbanall)
3752 struct mod_chanmode *change;
3755 if(!channel->banlist.used)
3757 reply("CSMSG_NO_BANS", channel->name);
3761 change = mod_chanmode_alloc(channel->banlist.used);
3762 for(ii=0; ii<channel->banlist.used; ii++)
3764 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3765 change->args[ii].u.hostmask = strdup(channel->banlist.list[ii]->ban);
3767 modcmd_chanmode_announce(change);
3768 for(ii = 0; ii < change->argc; ++ii)
3769 free((char*)change->args[ii].u.hostmask);
3770 mod_chanmode_free(change);
3771 reply("CSMSG_BANS_REMOVED", channel->name);
3775 static CHANSERV_FUNC(cmd_open)
3777 struct mod_chanmode *change;
3780 change = find_matching_bans(&channel->banlist, user, NULL);
3782 change = mod_chanmode_alloc(0);
3783 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3784 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3785 && channel->channel_info->modes.modes_set)
3786 change->modes_clear &= ~channel->channel_info->modes.modes_set;
3787 modcmd_chanmode_announce(change);
3788 reply("CSMSG_CHANNEL_OPENED", channel->name);
3789 for(ii = 0; ii < change->argc; ++ii)
3790 free((char*)change->args[ii].u.hostmask);
3791 mod_chanmode_free(change);
3795 static CHANSERV_FUNC(cmd_myaccess)
3797 static struct string_buffer sbuf;
3798 struct handle_info *target_handle;
3799 struct userData *uData;
3803 target_handle = user->handle_info;
3804 else if(!IsStaff(user))
3806 reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
3809 else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3812 if(!oper_outranks(user, target_handle))
3815 if(!target_handle->channels)
3817 reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
3821 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3822 for(uData = target_handle->channels; uData; uData = uData->u_next)
3824 struct chanData *cData = uData->channel;
3827 if(uData->access > UL_OWNER)
3829 if(IsProtected(cData)
3830 && (target_handle != user->handle_info)
3831 && !GetTrueChannelAccess(cData, user->handle_info))
3834 string_buffer_append_printf(&sbuf, "[%s (%d", cData->channel->name, uData->access);
3835 if(uData->flags != USER_AUTO_OP)
3836 string_buffer_append(&sbuf, ',');
3837 if(IsUserSuspended(uData))
3838 string_buffer_append(&sbuf, 's');
3839 if(IsUserAutoOp(uData))
3841 if(uData->access >= cData->lvlOpts[lvlGiveOps])
3842 string_buffer_append(&sbuf, 'o');
3843 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
3844 string_buffer_append(&sbuf, 'v');
3846 if(IsUserAutoInvite(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe]))
3847 string_buffer_append(&sbuf, 'i');
3849 string_buffer_append_printf(&sbuf, ")] %s", uData->info);
3851 string_buffer_append_string(&sbuf, ")]");
3852 string_buffer_append(&sbuf, '\0');
3853 send_message_type(4, user, cmd->parent->bot, "%s", sbuf.list);
3857 reply("CSMSG_MYACCESS_COUNT_1", target_handle->handle, ccount);
3859 reply("CSMSG_MYACCESS_COUNT", target_handle->handle, ccount);
3865 static CHANSERV_FUNC(cmd_access)
3867 struct userNode *target;
3868 struct handle_info *target_handle;
3869 struct userData *uData;
3871 char prefix[MAXLEN];
3876 target_handle = target->handle_info;
3878 else if((target = GetUserH(argv[1])))
3880 target_handle = target->handle_info;
3882 else if(argv[1][0] == '*')
3884 if(!(target_handle = get_handle_info(argv[1]+1)))
3886 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3892 reply("MSG_NICK_UNKNOWN", argv[1]);
3896 assert(target || target_handle);
3898 if(target == chanserv)
3900 reply("CSMSG_IS_CHANSERV");
3908 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
3913 reply("MSG_USER_AUTHENTICATE", target->nick);
3916 reply("MSG_AUTHENTICATE");
3922 const char *epithet = NULL, *type = NULL;
3925 epithet = chanserv_conf.irc_operator_epithet;
3926 type = user_find_message(user, "CSMSG_OPERATOR_TITLE");
3928 else if(IsNetworkHelper(target))
3930 epithet = chanserv_conf.network_helper_epithet;
3931 type = user_find_message(user, "CSMSG_UC_H_TITLE");
3933 else if(IsSupportHelper(target))
3935 epithet = chanserv_conf.support_helper_epithet;
3936 type = user_find_message(user, "CSMSG_LC_H_TITLE");
3940 if(target_handle->epithet)
3941 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
3943 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
3945 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
3949 sprintf(prefix, "%s", target_handle->handle);
3952 if(!channel->channel_info)
3954 reply("CSMSG_NOT_REGISTERED", channel->name);
3958 helping = HANDLE_FLAGGED(target_handle, HELPING)
3959 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
3960 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
3962 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
3963 /* To prevent possible information leaks, only show infolines
3964 * if the requestor is in the channel or it's their own
3966 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
3968 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
3970 /* Likewise, only say it's suspended if the user has active
3971 * access in that channel or it's their own entry. */
3972 if(IsUserSuspended(uData)
3973 && (GetChannelUser(channel->channel_info, user->handle_info)
3974 || (user->handle_info == uData->handle)))
3976 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
3981 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
3988 zoot_list(struct listData *list)
3990 struct userData *uData;
3991 unsigned int start, curr, highest, lowest;
3992 struct helpfile_table tmp_table;
3993 const char **temp, *msg;
3995 if(list->table.length == 1)
3998 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
4000 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
4001 msg = user_find_message(list->user, "MSG_NONE");
4002 send_message_type(4, list->user, list->bot, " %s", msg);
4004 tmp_table.width = list->table.width;
4005 tmp_table.flags = list->table.flags;
4006 list->table.contents[0][0] = " ";
4007 highest = list->highest;
4008 if(list->lowest != 0)
4009 lowest = list->lowest;
4010 else if(highest < 100)
4013 lowest = highest - 100;
4014 for(start = curr = 1; curr < list->table.length; )
4016 uData = list->users[curr-1];
4017 list->table.contents[curr++][0] = " ";
4018 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
4021 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, lowest, highest, list->search);
4023 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, lowest, highest);
4024 temp = list->table.contents[--start];
4025 list->table.contents[start] = list->table.contents[0];
4026 tmp_table.contents = list->table.contents + start;
4027 tmp_table.length = curr - start;
4028 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
4029 list->table.contents[start] = temp;
4031 highest = lowest - 1;
4032 lowest = (highest < 100) ? 0 : (highest - 99);
4038 def_list(struct listData *list)
4042 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
4044 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
4045 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4046 if(list->table.length == 1)
4048 msg = user_find_message(list->user, "MSG_NONE");
4049 send_message_type(4, list->user, list->bot, " %s", msg);
4054 userData_access_comp(const void *arg_a, const void *arg_b)
4056 const struct userData *a = *(struct userData**)arg_a;
4057 const struct userData *b = *(struct userData**)arg_b;
4059 if(a->access != b->access)
4060 res = b->access - a->access;
4062 res = irccasecmp(a->handle->handle, b->handle->handle);
4067 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
4069 void (*send_list)(struct listData *);
4070 struct userData *uData;
4071 struct listData lData;
4072 unsigned int matches;
4076 lData.bot = cmd->parent->bot;
4077 lData.channel = channel;
4078 lData.lowest = lowest;
4079 lData.highest = highest;
4080 lData.search = (argc > 1) ? argv[1] : NULL;
4081 send_list = def_list;
4082 (void)zoot_list; /* since it doesn't show user levels */
4084 if(user->handle_info)
4086 switch(user->handle_info->userlist_style)
4088 case HI_STYLE_DEF: send_list = def_list; break;
4089 case HI_STYLE_ZOOT: send_list = def_list; break;
4093 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
4095 for(uData = channel->channel_info->users; uData; uData = uData->next)
4097 if((uData->access < lowest)
4098 || (uData->access > highest)
4099 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
4101 lData.users[matches++] = uData;
4103 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
4105 lData.table.length = matches+1;
4106 lData.table.width = 4;
4107 lData.table.flags = TABLE_NO_FREE;
4108 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
4109 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
4110 lData.table.contents[0] = ary;
4113 ary[2] = "Last Seen";
4115 for(matches = 1; matches < lData.table.length; ++matches)
4117 char seen[INTERVALLEN];
4119 uData = lData.users[matches-1];
4120 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
4121 lData.table.contents[matches] = ary;
4122 ary[0] = strtab(uData->access);
4123 ary[1] = uData->handle->handle;
4126 else if(!uData->seen)
4129 ary[2] = intervalString(seen, now - uData->seen, user->handle_info);
4130 ary[2] = strdup(ary[2]);
4131 if(IsUserSuspended(uData))
4132 ary[3] = "Suspended";
4133 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
4134 ary[3] = "Vacation";
4135 else if(HANDLE_FLAGGED(uData->handle, BOT))
4141 for(matches = 1; matches < lData.table.length; ++matches)
4143 free((char*)lData.table.contents[matches][2]);
4144 free(lData.table.contents[matches]);
4146 free(lData.table.contents[0]);
4147 free(lData.table.contents);
4151 static CHANSERV_FUNC(cmd_users)
4153 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
4156 static CHANSERV_FUNC(cmd_wlist)
4158 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
4161 static CHANSERV_FUNC(cmd_clist)
4163 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
4166 static CHANSERV_FUNC(cmd_mlist)
4168 return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
4171 static CHANSERV_FUNC(cmd_olist)
4173 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
4176 static CHANSERV_FUNC(cmd_plist)
4178 return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
4181 static CHANSERV_FUNC(cmd_bans)
4183 struct userNode *search_u = NULL;
4184 struct helpfile_table tbl;
4185 unsigned int matches = 0, timed = 0, search_wilds = 0, ii;
4186 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
4187 const char *msg_never, *triggered, *expires;
4188 struct banData *ban, **bans;
4192 else if(strchr(search = argv[1], '!'))
4195 search_wilds = search[strcspn(search, "?*")];
4197 else if(!(search_u = GetUserH(search)))
4198 reply("MSG_NICK_UNKNOWN", search);
4200 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
4202 for(ban = channel->channel_info->bans; ban; ban = ban->next)
4206 if(!user_matches_glob(search_u, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
4211 if(search_wilds ? !match_ircglobs(search, ban->mask) : !match_ircglob(search, ban->mask))
4214 bans[matches++] = ban;
4219 tbl.length = matches + 1;
4220 tbl.width = 4 + timed;
4222 tbl.flags = TABLE_NO_FREE;
4223 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
4224 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4225 tbl.contents[0][0] = "Mask";
4226 tbl.contents[0][1] = "Set By";
4227 tbl.contents[0][2] = "Triggered";
4230 tbl.contents[0][3] = "Expires";
4231 tbl.contents[0][4] = "Reason";
4234 tbl.contents[0][3] = "Reason";
4237 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4239 free(tbl.contents[0]);
4244 msg_never = user_find_message(user, "MSG_NEVER");
4245 for(ii = 0; ii < matches; )
4251 else if(ban->expires)
4252 expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
4254 expires = msg_never;
4257 triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
4259 triggered = msg_never;
4261 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4262 tbl.contents[ii][0] = ban->mask;
4263 tbl.contents[ii][1] = ban->owner;
4264 tbl.contents[ii][2] = strdup(triggered);
4267 tbl.contents[ii][3] = strdup(expires);
4268 tbl.contents[ii][4] = ban->reason;
4271 tbl.contents[ii][3] = ban->reason;
4273 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4274 reply("MSG_MATCH_COUNT", matches);
4275 for(ii = 1; ii < tbl.length; ++ii)
4277 free((char*)tbl.contents[ii][2]);
4279 free((char*)tbl.contents[ii][3]);
4280 free(tbl.contents[ii]);
4282 free(tbl.contents[0]);
4288 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
4290 struct chanData *cData = channel->channel_info;
4291 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
4293 if(cData->topic_mask)
4294 return !match_ircglob(new_topic, cData->topic_mask);
4295 else if(cData->topic)
4296 return irccasecmp(new_topic, cData->topic);
4301 static CHANSERV_FUNC(cmd_topic)
4303 struct chanData *cData;
4306 cData = channel->channel_info;
4311 SetChannelTopic(channel, chanserv, cData->topic, 1);
4312 reply("CSMSG_TOPIC_SET", cData->topic);
4316 reply("CSMSG_NO_TOPIC", channel->name);
4320 topic = unsplit_string(argv + 1, argc - 1, NULL);
4321 /* If they say "!topic *", use an empty topic. */
4322 if((topic[0] == '*') && (topic[1] == 0))
4324 if(bad_topic(channel, user, topic))
4326 char *topic_mask = cData->topic_mask;
4329 char new_topic[TOPICLEN+1], tchar;
4330 int pos=0, starpos=-1, dpos=0, len;
4332 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
4339 len = strlen(topic);
4340 if((dpos + len) > TOPICLEN)
4341 len = TOPICLEN + 1 - dpos;
4342 memcpy(new_topic+dpos, topic, len);
4346 case '\\': tchar = topic_mask[pos++]; /* and fall through */
4347 default: new_topic[dpos++] = tchar; break;
4350 if((dpos > TOPICLEN) || tchar)
4353 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
4354 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
4357 new_topic[dpos] = 0;
4358 SetChannelTopic(channel, chanserv, new_topic, 1);
4360 reply("CSMSG_TOPIC_LOCKED", channel->name);
4365 SetChannelTopic(channel, chanserv, topic, 1);
4367 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
4369 /* Grab the topic and save it as the default topic. */
4371 cData->topic = strdup(channel->topic);
4377 static CHANSERV_FUNC(cmd_mode)
4379 struct userData *uData;
4380 struct mod_chanmode *change;
4386 change = &channel->channel_info->modes;
4387 if(change->modes_set || change->modes_clear) {
4388 modcmd_chanmode_announce(change);
4389 reply("CSMSG_DEFAULTED_MODES", channel->name);
4391 reply("CSMSG_NO_MODES", channel->name);
4395 uData = GetChannelUser(channel->channel_info, user->handle_info);
4397 base_oplevel = MAXOPLEVEL;
4398 else if (uData->access >= UL_OWNER)
4401 base_oplevel = 1 + UL_OWNER - uData->access;
4402 change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_IGN_REGISTERED|MCP_NO_APASS, base_oplevel);
4405 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
4409 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
4410 && mode_lock_violated(&channel->channel_info->modes, change))
4413 mod_chanmode_format(&channel->channel_info->modes, modes);
4414 reply("CSMSG_MODE_LOCKED", modes, channel->name);
4418 modcmd_chanmode_announce(change);
4419 mod_chanmode_format(change, fmt);
4420 mod_chanmode_free(change);
4421 reply("CSMSG_MODES_SET", fmt);
4426 chanserv_del_invite_mark(void *data)
4428 struct ChanUser *chanuser = data;
4429 struct chanNode *channel = chanuser->chan;
4431 if(!channel) return;
4432 for(i = 0; i < channel->invited.used; i++)
4434 if(channel->invited.list[i] == chanuser->user) {
4435 userList_remove(&channel->invited, chanuser->user);
4441 static CHANSERV_FUNC(cmd_invite)
4443 struct userData *uData;
4444 struct userNode *invite;
4445 struct ChanUser *chanuser;
4448 uData = GetChannelUser(channel->channel_info, user->handle_info);
4452 if(!(invite = GetUserH(argv[1])))
4454 reply("MSG_NICK_UNKNOWN", argv[1]);
4461 if(GetUserMode(channel, invite))
4463 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
4467 for(i = 0; i < channel->invited.used; i++)
4469 if(channel->invited.list[i] == invite) {
4470 reply("CSMSG_ALREADY_INVITED", invite->nick, channel->name);
4479 char *reason = unsplit_string(argv + 2, argc - 2, NULL);
4480 send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
4483 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
4485 irc_invite(chanserv, invite, channel);
4487 reply("CSMSG_INVITED_USER", argv[1], channel->name);
4489 userList_append(&channel->invited, invite);
4490 chanuser = calloc(1, sizeof(*chanuser));
4491 chanuser->user=invite;
4492 chanuser->chan=channel;
4493 timeq_add(now + chanserv_conf.invited_timeout, chanserv_del_invite_mark, chanuser);
4498 static CHANSERV_FUNC(cmd_inviteme)
4500 if(GetUserMode(channel, user))
4502 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
4505 if(channel->channel_info
4506 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
4508 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
4511 irc_invite(cmd->parent->bot, user, channel);
4515 static CHANSERV_FUNC(cmd_invitemeall)
4517 struct handle_info *target = user->handle_info;
4518 struct userData *uData;
4520 if(!target->channels)
4522 reply("CSMSG_SQUAT_ACCESS", target->handle);
4526 for(uData = target->channels; uData; uData = uData->u_next)
4528 struct chanData *cData = uData->channel;
4529 if(uData->access >= cData->lvlOpts[lvlInviteMe])
4531 irc_invite(cmd->parent->bot, user, cData->channel);
4538 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
4541 char buf1[INTERVALLEN], buf2[INTERVALLEN];
4543 /* We display things based on two dimensions:
4544 * - Issue time: present or absent
4545 * - Expiration: revoked, expired, expires in future, or indefinite expiration
4546 * (in order of precedence, so something both expired and revoked
4547 * only counts as revoked)
4549 combo = (suspended->issued ? 4 : 0)
4550 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
4552 case 0: /* no issue time, indefinite expiration */
4553 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
4555 case 1: /* no issue time, expires in future */
4556 intervalString(buf1, suspended->expires-now, user->handle_info);
4557 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
4559 case 2: /* no issue time, expired */
4560 intervalString(buf1, now-suspended->expires, user->handle_info);
4561 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
4563 case 3: /* no issue time, revoked */
4564 intervalString(buf1, now-suspended->revoked, user->handle_info);
4565 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
4567 case 4: /* issue time set, indefinite expiration */
4568 intervalString(buf1, now-suspended->issued, user->handle_info);
4569 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
4571 case 5: /* issue time set, expires in future */
4572 intervalString(buf1, now-suspended->issued, user->handle_info);
4573 intervalString(buf2, suspended->expires-now, user->handle_info);
4574 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
4576 case 6: /* issue time set, expired */
4577 intervalString(buf1, now-suspended->issued, user->handle_info);
4578 intervalString(buf2, now-suspended->expires, user->handle_info);
4579 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
4581 case 7: /* issue time set, revoked */
4582 intervalString(buf1, now-suspended->issued, user->handle_info);
4583 intervalString(buf2, now-suspended->revoked, user->handle_info);
4584 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
4587 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
4592 static CHANSERV_FUNC(cmd_info)
4594 char modes[MAXLEN], buffer[INTERVALLEN];
4595 struct userData *uData, *owner;
4596 struct chanData *cData;
4597 struct do_not_register *dnr;
4602 cData = channel->channel_info;
4603 reply("CSMSG_CHANNEL_INFO", channel->name);
4605 uData = GetChannelUser(cData, user->handle_info);
4606 if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
4608 mod_chanmode_format(&cData->modes, modes);
4609 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
4610 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
4613 for(it = dict_first(cData->notes); it; it = iter_next(it))
4617 note = iter_data(it);
4618 if(!note_type_visible_to_user(cData, note->type, user))
4621 padding = PADLEN - 1 - strlen(iter_key(it));
4622 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
4625 if(cData->max_time) {
4626 reply("CSMSG_CHANNEL_MAX_TIME", cData->max, intervalString(buffer, now - cData->max_time, user->handle_info));
4628 reply("CSMSG_CHANNEL_MAX", cData->max);
4630 for(owner = cData->users; owner; owner = owner->next)
4631 if(owner->access == UL_OWNER)
4632 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
4633 reply("CSMSG_CHANNEL_USERS", cData->userCount);
4634 reply("CSMSG_CHANNEL_BANS", cData->banCount);
4635 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
4637 privileged = IsStaff(user);
4639 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
4640 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
4641 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
4643 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
4644 chanserv_show_dnrs(user, cmd, channel->name, NULL);
4646 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
4648 struct suspended *suspended;
4649 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
4650 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
4651 show_suspension_info(cmd, user, suspended);
4653 else if(IsSuspended(cData))
4655 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
4656 show_suspension_info(cmd, user, cData->suspended);
4661 static CHANSERV_FUNC(cmd_netinfo)
4663 extern unsigned long boot_time;
4664 extern unsigned long burst_length;
4665 char interval[INTERVALLEN];
4667 reply("CSMSG_NETWORK_INFO");
4668 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
4669 reply("CSMSG_NETWORK_USERS", dict_size(clients));
4670 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
4671 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
4672 reply("CSMSG_NETWORK_BANS", banCount);
4673 reply("CSMSG_NETWORK_CHANUSERS", userCount);
4674 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
4675 reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
4680 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
4682 struct helpfile_table table;
4684 struct userNode *user;
4689 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4690 table.contents = alloca(list->used*sizeof(*table.contents));
4691 for(nn=0; nn<list->used; nn++)
4693 user = list->list[nn];
4694 if(user->modes & skip_flags)
4698 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
4701 nick = alloca(strlen(user->nick)+3);
4702 sprintf(nick, "(%s)", user->nick);
4706 table.contents[table.length][0] = nick;
4709 table_send(chanserv, to->nick, 0, NULL, table);
4712 static CHANSERV_FUNC(cmd_ircops)
4714 reply("CSMSG_STAFF_OPERS");
4715 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
4719 static CHANSERV_FUNC(cmd_helpers)
4721 reply("CSMSG_STAFF_HELPERS");
4722 send_staff_list(user, &curr_helpers, FLAGS_OPER);
4726 static CHANSERV_FUNC(cmd_staff)
4728 reply("CSMSG_NETWORK_STAFF");
4729 cmd_ircops(CSFUNC_ARGS);
4730 cmd_helpers(CSFUNC_ARGS);
4734 static CHANSERV_FUNC(cmd_peek)
4736 struct modeNode *mn;
4737 char modes[MODELEN];
4739 struct helpfile_table table;
4740 int opcount = 0, voicecount = 0, srvcount = 0;
4742 irc_make_chanmode(channel, modes);
4744 reply("CSMSG_PEEK_INFO", channel->name);
4745 reply("CSMSG_PEEK_TOPIC", channel->topic);
4746 reply("CSMSG_PEEK_MODES", modes);
4750 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4751 table.contents = alloca(channel->members.used*sizeof(*table.contents));
4752 for(n = 0; n < channel->members.used; n++)
4754 mn = channel->members.list[n];
4755 if(IsLocal(mn->user))
4757 else if(mn->modes & MODE_CHANOP)
4759 else if(mn->modes & MODE_VOICE)
4762 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
4764 table.contents[table.length] = alloca(sizeof(**table.contents));
4765 table.contents[table.length][0] = mn->user->nick;
4769 reply("CSMSG_PEEK_USERS", channel->members.used, opcount, voicecount,
4770 (channel->members.used - opcount - voicecount - srvcount));
4774 reply("CSMSG_PEEK_OPS");
4775 table_send(chanserv, user->nick, 0, NULL, table);
4778 reply("CSMSG_PEEK_NO_OPS");
4782 static MODCMD_FUNC(cmd_wipeinfo)
4784 struct handle_info *victim;
4785 struct userData *ud, *actor, *real_actor;
4786 unsigned int override = 0;
4789 actor = GetChannelUser(channel->channel_info, user->handle_info);
4790 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
4791 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4793 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4795 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4798 if((ud->access >= actor->access) && (ud != actor))
4800 reply("MSG_USER_OUTRANKED", victim->handle);
4803 if((ud != real_actor) && (!real_actor || (ud->access >= real_actor->access)))
4804 override = CMD_LOG_OVERRIDE;
4808 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4809 return 1 | override;
4812 static CHANSERV_FUNC(cmd_resync)
4814 struct mod_chanmode *changes;
4815 struct chanData *cData = channel->channel_info;
4816 unsigned int ii, used;
4818 changes = mod_chanmode_alloc(channel->members.used * 2);
4819 for(ii = used = 0; ii < channel->members.used; ++ii)
4821 struct modeNode *mn = channel->members.list[ii];
4822 struct userData *uData;
4824 if(IsService(mn->user))
4827 uData = GetChannelAccess(cData, mn->user->handle_info);
4828 if(!cData->lvlOpts[lvlGiveOps]
4829 || (uData && uData->access >= cData->lvlOpts[lvlGiveOps]))
4831 if(!(mn->modes & MODE_CHANOP))
4833 if(!uData || IsUserAutoOp(uData))
4835 changes->args[used].mode = MODE_CHANOP;
4836 changes->args[used++].u.member = mn;
4837 if(!(mn->modes & MODE_VOICE))
4839 changes->args[used].mode = MODE_VOICE;
4840 changes->args[used++].u.member = mn;
4845 else if(!cData->lvlOpts[lvlGiveVoice]
4846 || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice]))
4848 if(mn->modes & MODE_CHANOP)
4850 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4851 changes->args[used++].u.member = mn;
4853 if(!(mn->modes & MODE_VOICE) && (!uData || IsUserAutoOp(uData)))
4855 changes->args[used].mode = MODE_VOICE;
4856 changes->args[used++].u.member = mn;
4863 changes->args[used].mode = MODE_REMOVE | mn->modes;
4864 changes->args[used++].u.member = mn;
4868 changes->argc = used;
4869 modcmd_chanmode_announce(changes);
4870 mod_chanmode_free(changes);
4871 reply("CSMSG_RESYNCED_USERS", channel->name);
4875 static CHANSERV_FUNC(cmd_seen)
4877 struct userData *uData;
4878 struct handle_info *handle;
4879 char seen[INTERVALLEN];
4883 if(!irccasecmp(argv[1], chanserv->nick))
4885 reply("CSMSG_IS_CHANSERV");
4889 if(!(handle = get_handle_info(argv[1])))
4891 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4895 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4897 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4902 reply("CSMSG_USER_PRESENT", handle->handle);
4903 else if(uData->seen)
4904 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
4906 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4908 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4909 reply("CSMSG_USER_VACATION", handle->handle);
4914 static MODCMD_FUNC(cmd_names)
4916 struct userNode *targ;
4917 struct userData *targData;
4918 unsigned int ii, pos;
4921 for(ii=pos=0; ii<channel->members.used; ++ii)
4923 targ = channel->members.list[ii]->user;
4924 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4927 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
4930 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4934 if(IsUserSuspended(targData))
4936 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4939 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4940 reply("CSMSG_END_NAMES", channel->name);
4945 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
4947 switch(ntype->visible_type)
4949 case NOTE_VIS_ALL: return 1;
4950 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
4951 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
4956 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
4958 struct userData *uData;
4960 switch(ntype->set_access_type)
4962 case NOTE_SET_CHANNEL_ACCESS:
4963 if(!user->handle_info)
4965 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
4967 return uData->access >= ntype->set_access.min_ulevel;
4968 case NOTE_SET_CHANNEL_SETTER:
4969 return check_user_level(channel, user, lvlSetters, 1, 0);
4970 case NOTE_SET_PRIVILEGED: default:
4971 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
4975 static CHANSERV_FUNC(cmd_note)
4977 struct chanData *cData;
4979 struct note_type *ntype;
4981 cData = channel->channel_info;
4984 reply("CSMSG_NOT_REGISTERED", channel->name);
4988 /* If no arguments, show all visible notes for the channel. */
4994 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
4996 note = iter_data(it);
4997 if(!note_type_visible_to_user(cData, note->type, user))
5000 reply("CSMSG_NOTELIST_HEADER", channel->name);
5001 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
5004 reply("CSMSG_NOTELIST_END", channel->name);
5006 reply("CSMSG_NOTELIST_EMPTY", channel->name);
5008 /* If one argument, show the named note. */
5011 if((note = dict_find(cData->notes, argv[1], NULL))
5012 && note_type_visible_to_user(cData, note->type, user))
5014 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
5016 else if((ntype = dict_find(note_types, argv[1], NULL))
5017 && note_type_visible_to_user(NULL, ntype, user))
5019 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
5024 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
5028 /* Assume they're trying to set a note. */
5032 ntype = dict_find(note_types, argv[1], NULL);
5035 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
5038 else if(note_type_settable_by_user(channel, ntype, user))
5040 note_text = unsplit_string(argv+2, argc-2, NULL);
5041 if((note = dict_find(cData->notes, argv[1], NULL)))
5042 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
5043 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
5044 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
5046 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
5048 /* The note is viewable to staff only, so return 0
5049 to keep the invocation from getting logged (or
5050 regular users can see it in !events). */
5056 reply("CSMSG_NO_ACCESS");
5063 static CHANSERV_FUNC(cmd_delnote)
5068 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
5069 || !note_type_settable_by_user(channel, note->type, user))
5071 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
5074 dict_remove(channel->channel_info->notes, note->type->name);
5075 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
5079 static CHANSERV_FUNC(cmd_events)
5081 struct logSearch discrim;
5082 struct logReport report;
5083 unsigned int matches, limit;
5085 limit = (argc > 1) ? atoi(argv[1]) : 10;
5086 if(limit < 1 || limit > 200)
5089 memset(&discrim, 0, sizeof(discrim));
5090 discrim.masks.bot = chanserv;
5091 discrim.masks.channel_name = channel->name;
5093 discrim.masks.command = argv[2];
5094 discrim.limit = limit;
5095 discrim.max_time = INT_MAX;
5096 discrim.severities = 1 << LOG_COMMAND;
5097 report.reporter = chanserv;
5099 reply("CSMSG_EVENT_SEARCH_RESULTS");
5100 matches = log_entry_search(&discrim, log_report_entry, &report);
5102 reply("MSG_MATCH_COUNT", matches);
5104 reply("MSG_NO_MATCHES");
5108 static CHANSERV_FUNC(cmd_say)
5114 msg = unsplit_string(argv + 1, argc - 1, NULL);
5115 send_channel_message(channel, cmd->parent->bot, "%s", msg);
5117 else if(*argv[1] == '*' && argv[1][1] != '\0')
5119 struct handle_info *hi;
5120 struct userNode *authed;
5123 msg = unsplit_string(argv + 2, argc - 2, NULL);
5125 if (!(hi = get_handle_info(argv[1] + 1)))
5127 reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
5131 for (authed = hi->users; authed; authed = authed->next_authed)
5132 send_target_message(5, authed->nick, cmd->parent->bot, "%s", msg);
5134 else if(GetUserH(argv[1]))
5137 msg = unsplit_string(argv + 2, argc - 2, NULL);
5138 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
5142 reply("MSG_NOT_TARGET_NAME");
5148 static CHANSERV_FUNC(cmd_emote)
5154 /* CTCP is so annoying. */
5155 msg = unsplit_string(argv + 1, argc - 1, NULL);
5156 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
5158 else if(*argv[1] == '*' && argv[1][1] != '\0')
5160 struct handle_info *hi;
5161 struct userNode *authed;
5164 msg = unsplit_string(argv + 2, argc - 2, NULL);
5166 if (!(hi = get_handle_info(argv[1] + 1)))
5168 reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
5172 for (authed = hi->users; authed; authed = authed->next_authed)
5173 send_target_message(5, authed->nick, cmd->parent->bot, "\001ACTION %s\001", msg);
5175 else if(GetUserH(argv[1]))
5177 msg = unsplit_string(argv + 2, argc - 2, NULL);
5178 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
5182 reply("MSG_NOT_TARGET_NAME");
5188 struct channelList *
5189 chanserv_support_channels(void)
5191 return &chanserv_conf.support_channels;
5194 static CHANSERV_FUNC(cmd_expire)
5196 int channel_count = registered_channels;
5197 expire_channels(NULL);
5198 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
5203 chanserv_expire_suspension(void *data)
5205 struct suspended *suspended = data;
5206 struct chanNode *channel;
5209 /* Update the channel registration data structure. */
5210 if(!suspended->expires || (now < suspended->expires))
5211 suspended->revoked = now;
5212 channel = suspended->cData->channel;
5213 suspended->cData->channel = channel;
5214 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
5216 /* If appropriate, re-join ChanServ to the channel. */
5217 if(!IsOffChannel(suspended->cData))
5219 spamserv_cs_suspend(channel, 0, 0, NULL);
5220 ss_cs_join_channel(channel, 1);
5223 /* Mark everyone currently in the channel as present. */
5224 for(ii = 0; ii < channel->members.used; ++ii)
5226 struct userData *uData = GetChannelAccess(suspended->cData, channel->members.list[ii]->user->handle_info);
5235 static CHANSERV_FUNC(cmd_csuspend)
5237 struct suspended *suspended;
5238 char reason[MAXLEN];
5239 unsigned long expiry, duration;
5240 struct userData *uData;
5244 if(IsProtected(channel->channel_info))
5246 reply("CSMSG_SUSPEND_NODELETE", channel->name);
5250 if(argv[1][0] == '!')
5252 else if(IsSuspended(channel->channel_info))
5254 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
5255 show_suspension_info(cmd, user, channel->channel_info->suspended);
5259 if(!strcmp(argv[1], "0"))
5261 else if((duration = ParseInterval(argv[1])))
5262 expiry = now + duration;
5265 reply("MSG_INVALID_DURATION", argv[1]);
5269 unsplit_string(argv + 2, argc - 2, reason);
5271 suspended = calloc(1, sizeof(*suspended));
5272 suspended->revoked = 0;
5273 suspended->issued = now;
5274 suspended->suspender = strdup(user->handle_info->handle);
5275 suspended->expires = expiry;
5276 suspended->reason = strdup(reason);
5277 suspended->cData = channel->channel_info;
5278 suspended->previous = suspended->cData->suspended;
5279 suspended->cData->suspended = suspended;
5281 if(suspended->expires)
5282 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
5284 if(IsSuspended(channel->channel_info))
5286 suspended->previous->revoked = now;
5287 if(suspended->previous->expires)
5288 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
5289 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
5290 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5294 /* Mark all users in channel as absent. */
5295 for(uData = channel->channel_info->users; uData; uData = uData->next)
5304 /* Mark the channel as suspended, then part. */
5305 channel->channel_info->flags |= CHANNEL_SUSPENDED;
5306 spamserv_cs_suspend(channel, expiry, 1, suspended->reason);
5307 DelChannelUser(chanserv, channel, suspended->reason, 0);
5308 reply("CSMSG_SUSPENDED", channel->name);
5309 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
5310 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5315 static CHANSERV_FUNC(cmd_cunsuspend)
5317 struct suspended *suspended;
5318 char message[MAXLEN];
5320 if(!IsSuspended(channel->channel_info))
5322 reply("CSMSG_NOT_SUSPENDED", channel->name);
5326 suspended = channel->channel_info->suspended;
5328 /* Expire the suspension and join ChanServ to the channel. */
5329 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
5330 chanserv_expire_suspension(suspended);
5331 reply("CSMSG_UNSUSPENDED", channel->name);
5332 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
5333 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
5337 typedef struct chanservSearch
5342 unsigned long unvisited;
5343 unsigned long registered;
5345 unsigned long flags;
5349 typedef void (*channel_search_func)(struct chanData *channel, void *data);
5352 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
5357 search = malloc(sizeof(struct chanservSearch));
5358 memset(search, 0, sizeof(*search));
5361 for(i = 0; i < argc; i++)
5363 /* Assume all criteria require arguments. */
5366 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
5370 if(!irccasecmp(argv[i], "name"))
5371 search->name = argv[++i];
5372 else if(!irccasecmp(argv[i], "registrar"))
5373 search->registrar = argv[++i];
5374 else if(!irccasecmp(argv[i], "unvisited"))
5375 search->unvisited = ParseInterval(argv[++i]);
5376 else if(!irccasecmp(argv[i], "registered"))
5377 search->registered = ParseInterval(argv[++i]);
5378 else if(!irccasecmp(argv[i], "flags"))
5381 if(!irccasecmp(argv[i], "nodelete"))
5382 search->flags |= CHANNEL_NODELETE;
5383 else if(!irccasecmp(argv[i], "suspended"))
5384 search->flags |= CHANNEL_SUSPENDED;
5385 else if(!irccasecmp(argv[i], "unreviewed"))
5386 search->flags |= CHANNEL_UNREVIEWED;
5389 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
5393 else if(!irccasecmp(argv[i], "limit"))
5394 search->limit = strtoul(argv[++i], NULL, 10);
5397 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
5402 if(search->name && !strcmp(search->name, "*"))
5404 if(search->registrar && !strcmp(search->registrar, "*"))
5405 search->registrar = 0;
5414 chanserv_channel_match(struct chanData *channel, search_t search)
5416 const char *name = channel->channel->name;
5417 if((search->name && !match_ircglob(name, search->name)) ||
5418 (search->registrar && !channel->registrar) ||
5419 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
5420 (search->unvisited && (now - channel->visited) < search->unvisited) ||
5421 (search->registered && (now - channel->registered) > search->registered) ||
5422 (search->flags && ((search->flags & channel->flags) != search->flags)))
5429 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
5431 struct chanData *channel;
5432 unsigned int matches = 0;
5434 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
5436 if(!chanserv_channel_match(channel, search))
5446 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
5451 search_print(struct chanData *channel, void *data)
5453 send_message_type(4, data, chanserv, "%s", channel->channel->name);
5456 static CHANSERV_FUNC(cmd_search)
5459 unsigned int matches;
5460 channel_search_func action;
5464 if(!irccasecmp(argv[1], "count"))
5465 action = search_count;
5466 else if(!irccasecmp(argv[1], "print"))
5467 action = search_print;
5470 reply("CSMSG_ACTION_INVALID", argv[1]);
5474 search = chanserv_search_create(user, argc - 2, argv + 2);
5478 if(action == search_count)
5479 search->limit = INT_MAX;
5481 if(action == search_print)
5482 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
5484 matches = chanserv_channel_search(search, action, user);
5487 reply("MSG_MATCH_COUNT", matches);
5489 reply("MSG_NO_MATCHES");
5495 static CHANSERV_FUNC(cmd_unvisited)
5497 struct chanData *cData;
5498 unsigned long interval = chanserv_conf.channel_expire_delay;
5499 char buffer[INTERVALLEN];
5500 unsigned int limit = 25, matches = 0;
5504 interval = ParseInterval(argv[1]);
5506 limit = atoi(argv[2]);
5509 intervalString(buffer, interval, user->handle_info);
5510 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
5512 for(cData = channelList; cData && matches < limit; cData = cData->next)
5514 if((now - cData->visited) < interval)
5517 intervalString(buffer, now - cData->visited, user->handle_info);
5518 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
5525 static MODCMD_FUNC(chan_opt_defaulttopic)
5531 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5533 reply("CSMSG_TOPIC_LOCKED", channel->name);
5537 topic = unsplit_string(argv+1, argc-1, NULL);
5539 free(channel->channel_info->topic);
5540 if(topic[0] == '*' && topic[1] == 0)
5542 topic = channel->channel_info->topic = NULL;
5546 topic = channel->channel_info->topic = strdup(topic);
5547 if(channel->channel_info->topic_mask
5548 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
5549 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5551 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
5554 if(channel->channel_info->topic)
5555 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
5557 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
5561 static MODCMD_FUNC(chan_opt_topicmask)
5565 struct chanData *cData = channel->channel_info;
5568 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5570 reply("CSMSG_TOPIC_LOCKED", channel->name);
5574 mask = unsplit_string(argv+1, argc-1, NULL);
5576 if(cData->topic_mask)
5577 free(cData->topic_mask);
5578 if(mask[0] == '*' && mask[1] == 0)
5580 cData->topic_mask = 0;
5584 cData->topic_mask = strdup(mask);
5586 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
5587 else if(!match_ircglob(cData->topic, cData->topic_mask))
5588 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5592 if(channel->channel_info->topic_mask)
5593 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
5595 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
5599 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
5603 char *greeting = unsplit_string(argv+1, argc-1, NULL);
5607 if(greeting[0] == '*' && greeting[1] == 0)
5611 unsigned int length = strlen(greeting);
5612 if(length > chanserv_conf.greeting_length)
5614 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
5617 *data = strdup(greeting);
5626 reply(name, user_find_message(user, "MSG_NONE"));
5630 static MODCMD_FUNC(chan_opt_greeting)
5632 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
5635 static MODCMD_FUNC(chan_opt_usergreeting)
5637 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
5640 static MODCMD_FUNC(chan_opt_modes)
5642 struct mod_chanmode *new_modes;
5647 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
5649 reply("CSMSG_NO_ACCESS");
5652 if(argv[1][0] == '*' && argv[1][1] == 0)
5654 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
5656 else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_IGN_REGISTERED|MCP_NO_APASS, 0)))
5658 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5661 else if(new_modes->argc > 1)
5663 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5664 mod_chanmode_free(new_modes);
5669 channel->channel_info->modes = *new_modes;
5670 modcmd_chanmode_announce(new_modes);
5671 mod_chanmode_free(new_modes);
5675 mod_chanmode_format(&channel->channel_info->modes, modes);
5677 reply("CSMSG_SET_MODES", modes);
5679 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
5683 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5685 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5687 struct chanData *cData = channel->channel_info;
5692 /* Set flag according to value. */
5693 if(enabled_string(argv[1]))
5695 cData->flags |= mask;
5698 else if(disabled_string(argv[1]))
5700 cData->flags &= ~mask;
5705 reply("MSG_INVALID_BINARY", argv[1]);
5711 /* Find current option value. */
5712 value = (cData->flags & mask) ? 1 : 0;
5716 reply(name, user_find_message(user, "MSG_ON"));
5718 reply(name, user_find_message(user, "MSG_OFF"));
5722 static MODCMD_FUNC(chan_opt_nodelete)
5724 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
5726 reply("MSG_SETTING_PRIVILEGED", argv[0]);
5730 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
5733 static MODCMD_FUNC(chan_opt_dynlimit)
5735 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
5738 static MODCMD_FUNC(chan_opt_offchannel)
5740 struct chanData *cData = channel->channel_info;
5745 /* Set flag according to value. */
5746 if(enabled_string(argv[1]))
5748 if(!IsOffChannel(cData))
5749 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
5750 cData->flags |= CHANNEL_OFFCHANNEL;
5753 else if(disabled_string(argv[1]))
5755 if(IsOffChannel(cData))
5757 struct mod_chanmode change;
5758 mod_chanmode_init(&change);
5760 change.args[0].mode = MODE_CHANOP;
5761 change.args[0].u.member = AddChannelUser(chanserv, channel);
5762 mod_chanmode_announce(chanserv, channel, &change);
5764 cData->flags &= ~CHANNEL_OFFCHANNEL;
5769 reply("MSG_INVALID_BINARY", argv[1]);
5775 /* Find current option value. */
5776 value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
5780 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
5782 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
5786 static MODCMD_FUNC(chan_opt_unreviewed)
5788 struct chanData *cData = channel->channel_info;
5789 int value = (cData->flags & CHANNEL_UNREVIEWED) ? 1 : 0;
5795 /* The two directions can have different ACLs. */
5796 if(enabled_string(argv[1]))
5798 else if(disabled_string(argv[1]))
5802 reply("MSG_INVALID_BINARY", argv[1]);
5806 if (new_value != value)
5808 struct svccmd *subcmd;
5809 char subcmd_name[32];
5811 snprintf(subcmd_name, sizeof(subcmd_name), "%s %s", argv[0], (new_value ? "on" : "off"));
5812 subcmd = dict_find(cmd->parent->commands, subcmd_name, NULL);
5815 reply("MSG_COMMAND_DISABLED", subcmd_name);
5818 else if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
5822 cData->flags |= CHANNEL_UNREVIEWED;
5825 free(cData->registrar);
5826 cData->registrar = strdup(user->handle_info->handle);
5827 cData->flags &= ~CHANNEL_UNREVIEWED;
5834 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_ON"));
5836 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_OFF"));
5840 static MODCMD_FUNC(chan_opt_defaults)
5842 struct userData *uData;
5843 struct chanData *cData;
5844 const char *confirm;
5845 enum levelOption lvlOpt;
5846 enum charOption chOpt;
5848 cData = channel->channel_info;
5849 uData = GetChannelUser(cData, user->handle_info);
5850 if(!uData || (uData->access < UL_OWNER))
5852 reply("CSMSG_OWNER_DEFAULTS", channel->name);
5855 confirm = make_confirmation_string(uData);
5856 if((argc < 2) || strcmp(argv[1], confirm))
5858 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
5861 cData->flags = (CHANNEL_DEFAULT_FLAGS & ~CHANNEL_PRESERVED_FLAGS)
5862 | (cData->flags & CHANNEL_PRESERVED_FLAGS);
5863 cData->modes = chanserv_conf.default_modes;
5864 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
5865 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
5866 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
5867 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
5868 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
5873 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5875 struct chanData *cData = channel->channel_info;
5876 struct userData *uData;
5877 unsigned short value;
5881 if(!check_user_level(channel, user, option, 1, 1))
5883 reply("CSMSG_CANNOT_SET");
5886 value = user_level_from_name(argv[1], UL_OWNER+1);
5887 if(!value && strcmp(argv[1], "0"))
5889 reply("CSMSG_INVALID_ACCESS", argv[1]);
5892 uData = GetChannelUser(cData, user->handle_info);
5893 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
5895 reply("CSMSG_BAD_SETLEVEL");
5901 if(value > cData->lvlOpts[lvlGiveOps])
5903 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
5908 if(value < cData->lvlOpts[lvlGiveVoice])
5910 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
5915 /* This test only applies to owners, since non-owners
5916 * trying to set an option to above their level get caught
5917 * by the CSMSG_BAD_SETLEVEL test above.
5919 if(value > uData->access)
5921 reply("CSMSG_BAD_SETTERS");
5928 cData->lvlOpts[option] = value;
5930 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
5934 static MODCMD_FUNC(chan_opt_enfops)
5936 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
5939 static MODCMD_FUNC(chan_opt_giveops)
5941 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
5944 static MODCMD_FUNC(chan_opt_enfmodes)
5946 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
5949 static MODCMD_FUNC(chan_opt_enftopic)
5951 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
5954 static MODCMD_FUNC(chan_opt_pubcmd)
5956 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
5959 static MODCMD_FUNC(chan_opt_setters)
5961 return channel_level_option(lvlSetters, CSFUNC_ARGS);
5964 static MODCMD_FUNC(chan_opt_ctcpusers)
5966 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
5969 static MODCMD_FUNC(chan_opt_userinfo)
5971 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
5974 static MODCMD_FUNC(chan_opt_givevoice)
5976 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
5979 static MODCMD_FUNC(chan_opt_topicsnarf)
5981 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
5984 static MODCMD_FUNC(chan_opt_vote)
5986 return channel_level_option(lvlVote, CSFUNC_ARGS);
5989 static MODCMD_FUNC(chan_opt_inviteme)
5991 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
5995 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5997 struct chanData *cData = channel->channel_info;
5998 int count = charOptions[option].count, idx;
6002 idx = atoi(argv[1]);
6004 if(!isdigit(argv[1][0]) || (idx < 0) || (idx >= count))
6006 reply("CSMSG_INVALID_NUMERIC", idx);
6007 /* Show possible values. */
6008 for(idx = 0; idx < count; idx++)
6009 reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
6013 cData->chOpts[option] = charOptions[option].values[idx].value;
6017 /* Find current option value. */
6020 (idx < count) && (cData->chOpts[option] != charOptions[option].values[idx].value);
6024 /* Somehow, the option value is corrupt; reset it to the default. */
6025 cData->chOpts[option] = charOptions[option].default_value;
6030 reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
6034 static MODCMD_FUNC(chan_opt_protect)
6036 return channel_multiple_option(chProtect, CSFUNC_ARGS);
6039 static MODCMD_FUNC(chan_opt_toys)
6041 return channel_multiple_option(chToys, CSFUNC_ARGS);
6044 static MODCMD_FUNC(chan_opt_ctcpreaction)
6046 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
6049 static MODCMD_FUNC(chan_opt_topicrefresh)
6051 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
6054 static struct svccmd_list set_shows_list;
6057 handle_svccmd_unbind(struct svccmd *target) {
6059 for(ii=0; ii<set_shows_list.used; ++ii)
6060 if(target == set_shows_list.list[ii])
6061 set_shows_list.used = 0;
6064 static CHANSERV_FUNC(cmd_set)
6066 struct svccmd *subcmd;
6070 /* Check if we need to (re-)initialize set_shows_list. */
6071 if(!set_shows_list.used)
6073 if(!set_shows_list.size)
6075 set_shows_list.size = chanserv_conf.set_shows->used;
6076 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
6078 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
6080 const char *name = chanserv_conf.set_shows->list[ii];
6081 sprintf(buf, "%s %s", argv[0], name);
6082 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6085 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
6088 svccmd_list_append(&set_shows_list, subcmd);
6094 reply("CSMSG_CHANNEL_OPTIONS");
6095 for(ii = 0; ii < set_shows_list.used; ii++)
6097 subcmd = set_shows_list.list[ii];
6098 subcmd->command->func(user, channel, 1, argv+1, subcmd);
6103 sprintf(buf, "%s %s", argv[0], argv[1]);
6104 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6107 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
6110 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
6112 reply("CSMSG_NO_ACCESS");
6118 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
6122 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
6124 struct userData *uData;
6126 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6129 reply("CSMSG_NOT_USER", channel->name);
6135 /* Just show current option value. */
6137 else if(enabled_string(argv[1]))
6139 uData->flags |= mask;
6141 else if(disabled_string(argv[1]))
6143 uData->flags &= ~mask;
6147 reply("MSG_INVALID_BINARY", argv[1]);
6151 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
6155 static MODCMD_FUNC(user_opt_noautoop)
6157 struct userData *uData;
6159 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6162 reply("CSMSG_NOT_USER", channel->name);
6165 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
6166 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
6168 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
6171 static MODCMD_FUNC(user_opt_autoinvite)
6173 if((argc > 1) && !check_user_level(channel, user, lvlInviteMe, 1, 0))
6175 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
6177 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
6180 static MODCMD_FUNC(user_opt_info)
6182 struct userData *uData;
6185 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6189 /* If they got past the command restrictions (which require access)
6190 * but fail this test, we have some fool with security override on.
6192 reply("CSMSG_NOT_USER", channel->name);
6199 infoline = unsplit_string(argv + 1, argc - 1, NULL);
6200 if(strlen(infoline) > chanserv_conf.max_userinfo_length)
6202 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
6205 bp = strcspn(infoline, "\001");
6208 reply("CSMSG_BAD_INFOLINE", infoline[bp]);
6213 if(infoline[0] == '*' && infoline[1] == 0)
6216 uData->info = strdup(infoline);
6219 reply("CSMSG_USET_INFO", uData->info);
6221 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
6225 struct svccmd_list uset_shows_list;
6227 static CHANSERV_FUNC(cmd_uset)
6229 struct svccmd *subcmd;
6233 /* Check if we need to (re-)initialize uset_shows_list. */
6234 if(!uset_shows_list.used)
6238 "NoAutoOp", "AutoInvite", "Info"
6241 if(!uset_shows_list.size)
6243 uset_shows_list.size = ArrayLength(options);
6244 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
6246 for(ii = 0; ii < ArrayLength(options); ii++)
6248 const char *name = options[ii];
6249 sprintf(buf, "%s %s", argv[0], name);
6250 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6253 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
6256 svccmd_list_append(&uset_shows_list, subcmd);
6262 /* Do this so options are presented in a consistent order. */
6263 reply("CSMSG_USER_OPTIONS");
6264 for(ii = 0; ii < uset_shows_list.used; ii++)
6265 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
6269 sprintf(buf, "%s %s", argv[0], argv[1]);
6270 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6273 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
6277 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
6280 static CHANSERV_FUNC(cmd_giveownership)
6282 struct handle_info *new_owner_hi;
6283 struct userData *new_owner;
6284 struct userData *curr_user;
6285 struct userData *invoker;
6286 struct chanData *cData = channel->channel_info;
6287 struct do_not_register *dnr;
6288 const char *confirm;
6290 unsigned short co_access;
6291 char reason[MAXLEN];
6294 curr_user = GetChannelAccess(cData, user->handle_info);
6295 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
6296 if(!curr_user || (curr_user->access != UL_OWNER))
6298 struct userData *owner = NULL;
6299 for(curr_user = channel->channel_info->users;
6301 curr_user = curr_user->next)
6303 if(curr_user->access != UL_OWNER)
6307 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
6314 else if(!force && (now < cData->ownerTransfer + chanserv_conf.giveownership_period))
6316 char delay[INTERVALLEN];
6317 intervalString(delay, cData->ownerTransfer + chanserv_conf.giveownership_period - now, user->handle_info);
6318 reply("CSMSG_TRANSFER_WAIT", delay, channel->name);
6321 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
6323 if(new_owner_hi == user->handle_info)
6325 reply("CSMSG_NO_TRANSFER_SELF");
6328 new_owner = GetChannelAccess(cData, new_owner_hi);
6333 new_owner = add_channel_user(cData, new_owner_hi, UL_OWNER - 1, 0, NULL);
6337 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
6341 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
6343 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
6346 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
6347 if(!IsHelping(user))
6348 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
6350 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi->handle);
6353 invoker = GetChannelUser(cData, user->handle_info);
6354 if(invoker->access <= UL_OWNER)
6356 confirm = make_confirmation_string(curr_user);
6357 if((argc < 3) || strcmp(argv[2], confirm))
6359 reply("CSMSG_CONFIRM_GIVEOWNERSHIP", new_owner_hi->handle, confirm);
6363 if(new_owner->access >= UL_COOWNER)
6364 co_access = new_owner->access;
6366 co_access = UL_COOWNER;
6367 new_owner->access = UL_OWNER;
6369 curr_user->access = co_access;
6370 cData->ownerTransfer = now;
6371 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
6372 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
6373 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
6377 static CHANSERV_FUNC(cmd_suspend)
6379 struct handle_info *hi;
6380 struct userData *actor, *real_actor, *target;
6381 unsigned int override = 0;
6384 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6385 actor = GetChannelUser(channel->channel_info, user->handle_info);
6386 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
6387 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6389 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6392 if(target->access >= actor->access)
6394 reply("MSG_USER_OUTRANKED", hi->handle);
6397 if(target->flags & USER_SUSPENDED)
6399 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
6404 target->present = 0;
6407 if(!real_actor || target->access >= real_actor->access)
6408 override = CMD_LOG_OVERRIDE;
6409 target->flags |= USER_SUSPENDED;
6410 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
6411 return 1 | override;
6414 static CHANSERV_FUNC(cmd_unsuspend)
6416 struct handle_info *hi;
6417 struct userData *actor, *real_actor, *target;
6418 unsigned int override = 0;
6421 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6422 actor = GetChannelUser(channel->channel_info, user->handle_info);
6423 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
6424 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6426 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6429 if(target->access >= actor->access)
6431 reply("MSG_USER_OUTRANKED", hi->handle);
6434 if(!(target->flags & USER_SUSPENDED))
6436 reply("CSMSG_NOT_SUSPENDED", hi->handle);
6439 if(!real_actor || target->access >= real_actor->access)
6440 override = CMD_LOG_OVERRIDE;
6441 target->flags &= ~USER_SUSPENDED;
6442 scan_user_presence(target, NULL);
6443 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
6444 return 1 | override;
6447 static MODCMD_FUNC(cmd_deleteme)
6449 struct handle_info *hi;
6450 struct userData *target;
6451 const char *confirm_string;
6452 unsigned short access_level;
6455 hi = user->handle_info;
6456 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6458 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6461 if(target->access == UL_OWNER)
6463 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
6466 confirm_string = make_confirmation_string(target);
6467 if((argc < 2) || strcmp(argv[1], confirm_string))
6469 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
6472 access_level = target->access;
6473 channel_name = strdup(channel->name);
6474 del_channel_user(target, 1);
6475 reply("CSMSG_DELETED_YOU", access_level, channel_name);
6480 static CHANSERV_FUNC(cmd_addvote)
6482 struct chanData *cData = channel->channel_info;
6483 struct userData *uData, *target;
6484 struct handle_info *hi;
6485 if (!cData) return 0;
6487 hi = user->handle_info;
6488 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6490 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6493 if(target->access < 300) {
6494 reply("CSMSG_NO_ACCESS");
6498 reply("CSMSG_ADDVOTE_FULL");
6502 msg = unsplit_string(argv + 1, argc - 1, NULL);
6503 cData->vote = strdup(msg);
6504 cData->vote_start=0;
6505 dict_delete(cData->vote_options);
6506 cData->vote_options = dict_new();
6507 dict_set_free_data(cData->vote_options, free_vote_options);
6508 for(uData = channel->channel_info->users; uData; uData = uData->next)
6513 reply("CSMSG_ADDVOTE_DONE");
6517 static CHANSERV_FUNC(cmd_delvote)
6519 struct chanData *cData = channel->channel_info;
6520 struct userData *target;
6521 struct handle_info *hi;
6522 if (!cData) return 0;
6523 hi = user->handle_info;
6524 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6526 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6529 if(target->access < 300) {
6530 reply("CSMSG_NO_ACCESS");
6534 reply("CSMSG_NO_VOTE");
6539 reply("CSMSG_DELVOTE_DONE");
6543 static CHANSERV_FUNC(cmd_addoption)
6545 struct chanData *cData = channel->channel_info;
6546 struct userData *target;
6547 struct handle_info *hi;
6548 if (!cData) return 0;
6550 hi = user->handle_info;
6551 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6553 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6556 if(target->access < 300) {
6557 reply("CSMSG_NO_ACCESS");
6561 reply("CSMSG_NO_VOTE");
6567 msg = unsplit_string(argv + 1, argc - 1, NULL);
6570 unsigned int lastid = 1;
6571 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6572 struct vote_option *cvOpt = iter_data(it);
6573 if(cvOpt->option_id > lastid)
6574 lastid = cvOpt->option_id;
6576 struct vote_option *vOpt;
6577 vOpt = calloc(1, sizeof(*vOpt));
6578 vOpt->name = strdup(msg);
6579 vOpt->option_id = (lastid + 1);
6581 sprintf(str,"%i",(lastid + 1));
6582 vOpt->option_str = strdup(str);
6584 dict_insert(cData->vote_options,vOpt->option_str,vOpt);
6586 reply("CSMSG_ADDOPTION_DONE",dict_size(cData->vote_options),lastid,(lastid + 1));
6590 static CHANSERV_FUNC(cmd_deloption)
6592 struct chanData *cData = channel->channel_info;
6593 struct userData *uData, *target;
6594 struct handle_info *hi;
6595 if (!cData) return 0;
6597 hi = user->handle_info;
6598 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6600 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6603 if(target->access < 300) {
6604 reply("CSMSG_NO_ACCESS");
6608 reply("CSMSG_NO_VOTE");
6611 if(cData->vote_start) {
6612 if(dict_size(cData->vote_options) < 3) {
6613 reply("CSMSG_VOTE_NEED_OPTIONS");
6618 int find_id = atoi(argv[1]);
6620 unsigned int found = 0;
6623 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6625 if (find_id == ii) {
6626 struct vote_option *vOpt = iter_data(it);
6627 found = vOpt->option_id;
6629 sprintf(str,"%i",vOpt->option_id);
6630 dict_remove(cData->vote_options, str);
6635 for(uData = channel->channel_info->users; uData; uData = uData->next) {
6636 if(uData->votefor == found) {
6641 reply("CSMSG_DELOPTION_DONE");
6644 reply("CSMSG_DELOPTION_NONE");
6649 static CHANSERV_FUNC(cmd_vote)
6651 struct chanData *cData = channel->channel_info;
6652 struct userData *target;
6653 struct handle_info *hi;
6654 unsigned int votedfor = 0;
6655 char *votedfor_str = NULL;
6657 if (!cData || !cData->vote) {
6658 reply("CSMSG_NO_VOTE");
6661 if(argc > 1 && cData->vote_start) {
6662 hi = user->handle_info;
6663 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6665 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6668 if(!check_user_level(channel, user, lvlVote, 1, 0)) {
6669 reply("CSMSG_NO_ACCESS");
6673 reply("CSMSG_VOTE_VOTED");
6676 int find_id = atoi(argv[1]);
6679 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6681 if (find_id == ii) {
6682 struct vote_option *vOpt = iter_data(it);
6685 target->votefor = vOpt->option_id;
6686 votedfor = vOpt->option_id;
6687 votedfor_str = vOpt->name;
6691 reply("CSMSG_VOTE_INVALID");
6695 if (!cData->vote_start) {
6696 reply("CSMSG_VOTE_NOT_STARTED");
6698 reply("CSMSG_VOTE_QUESTION",cData->vote);
6700 unsigned int voteid = 0;
6703 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6704 struct vote_option *vOpt = iter_data(it);
6706 reply("CSMSG_VOTE_OPTION",voteid,vOpt->name,vOpt->voted);
6708 if(argc > 1 && cData->vote_start && votedfor_str) {
6709 reply("CSMSG_VOTE_DONE",votedfor_str);
6714 static CHANSERV_FUNC(cmd_startvote)
6716 struct chanData *cData = channel->channel_info;
6717 struct userData *target;
6718 struct handle_info *hi;
6719 if (!cData) return 0;
6720 hi = user->handle_info;
6721 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6723 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6726 if(target->access < 300) {
6727 reply("CSMSG_NO_ACCESS");
6731 reply("CSMSG_NO_VOTE");
6734 if(cData->vote_start) {
6735 reply("CSMSG_STARTVOTE_RUNNING");
6738 if(dict_size(cData->vote_options) < 2) {
6739 reply("CSMSG_VOTE_NEED_OPTIONS");
6742 cData->vote_start = 1;
6743 char response[MAXLEN];
6744 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_TOP"), user->nick);
6745 irc_privmsg(cmd->parent->bot, channel->name, response);
6746 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_QUESTION"), cData->vote);
6747 irc_privmsg(cmd->parent->bot, channel->name, response);
6748 unsigned int voteid = 0;
6750 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6751 struct vote_option *vOpt = iter_data(it);
6753 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_OPTION"), voteid, vOpt->name);
6754 irc_privmsg(cmd->parent->bot, channel->name, response);
6756 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_ACCESS"), cData->lvlOpts[lvlVote]); //Todo
6757 irc_privmsg(cmd->parent->bot, channel->name, response);
6758 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_HOWTO")); //Todo
6759 irc_privmsg(cmd->parent->bot, channel->name, response);
6763 static CHANSERV_FUNC(cmd_endvote)
6765 struct chanData *cData = channel->channel_info;
6766 struct userData *target;
6767 struct handle_info *hi;
6768 if (!cData) return 0;
6769 hi = user->handle_info;
6770 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6772 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6775 if(target->access < 300) {
6776 reply("CSMSG_NO_ACCESS");
6780 reply("CSMSG_NO_VOTE");
6783 if(!cData->vote_start) {
6784 reply("CSMSG_ENDVOTE_STOPPED");
6787 cData->vote_start = 0;
6788 reply("CSMSG_ENDVOTE_DONE");
6792 static CHANSERV_FUNC(cmd_voteresults)
6794 struct chanData *cData = channel->channel_info;
6795 struct userData *target;
6796 struct handle_info *hi;
6797 if (!cData) return 0;
6799 reply("CSMSG_NO_VOTE");
6802 if (argc > 1 && !irccasecmp(argv[1], "*")) {
6803 hi = user->handle_info;
6804 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6806 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6809 if(target->access < 300) {
6810 reply("CSMSG_NO_ACCESS");
6813 char response[MAXLEN];
6814 sprintf(response, user_find_message(user, "CSMSG_VOTERES_QUESTION"), cData->vote);
6815 irc_privmsg(cmd->parent->bot, channel->name, response);
6816 unsigned int voteid = 0;
6818 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6819 struct vote_option *vOpt = iter_data(it);
6821 sprintf(response, user_find_message(user, "CSMSG_VOTERES_OPTION"), voteid, vOpt->name, vOpt->voted);
6822 irc_privmsg(cmd->parent->bot, channel->name, response);
6825 reply("CSMSG_VOTE_QUESTION",cData->vote);
6826 unsigned int voteid = 0;
6828 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6829 struct vote_option *vOpt = iter_data(it);
6831 reply("CSMSG_VOTE_OPTION",voteid,vOpt->name,vOpt->voted);
6838 chanserv_refresh_topics(UNUSED_ARG(void *data))
6840 unsigned int refresh_num = (now - self->link_time) / chanserv_conf.refresh_period;
6841 struct chanData *cData;
6844 for(cData = channelList; cData; cData = cData->next)
6846 if(IsSuspended(cData))
6848 opt = cData->chOpts[chTopicRefresh];
6851 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
6854 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
6855 cData->last_refresh = refresh_num;
6857 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
6860 static CHANSERV_FUNC(cmd_unf)
6864 char response[MAXLEN];
6865 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
6866 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6867 irc_privmsg(cmd->parent->bot, channel->name, response);
6870 reply("CSMSG_UNF_RESPONSE");
6874 static CHANSERV_FUNC(cmd_ping)
6878 char response[MAXLEN];
6879 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
6880 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6881 irc_privmsg(cmd->parent->bot, channel->name, response);
6884 reply("CSMSG_PING_RESPONSE");
6888 static CHANSERV_FUNC(cmd_wut)
6892 char response[MAXLEN];
6893 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
6894 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6895 irc_privmsg(cmd->parent->bot, channel->name, response);
6898 reply("CSMSG_WUT_RESPONSE");
6902 static CHANSERV_FUNC(cmd_8ball)
6904 unsigned int i, j, accum;
6909 for(i=1; i<argc; i++)
6910 for(j=0; argv[i][j]; j++)
6911 accum = (accum << 5) - accum + toupper(argv[i][j]);
6912 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
6915 char response[MAXLEN];
6916 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
6917 irc_privmsg(cmd->parent->bot, channel->name, response);
6920 send_message_type(4, user, cmd->parent->bot, "%s", resp);
6924 static CHANSERV_FUNC(cmd_d)
6926 unsigned long sides, count, modifier, ii, total;
6927 char response[MAXLEN], *sep;
6931 if((count = strtoul(argv[1], &sep, 10)) < 1)
6941 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
6942 && (sides = strtoul(sep+1, &sep, 10)) > 1)
6946 else if((sep[0] == '-') && isdigit(sep[1]))
6947 modifier = strtoul(sep, NULL, 10);
6948 else if((sep[0] == '+') && isdigit(sep[1]))
6949 modifier = strtoul(sep+1, NULL, 10);
6956 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
6961 reply("CSMSG_BAD_DICE_COUNT", count, 10);
6964 for(total = ii = 0; ii < count; ++ii)
6965 total += (rand() % sides) + 1;
6968 if((count > 1) || modifier)
6970 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
6971 sprintf(response, fmt, total, count, sides, modifier);
6975 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
6976 sprintf(response, fmt, total, sides);
6979 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
6981 send_message_type(4, user, cmd->parent->bot, "%s", response);
6985 static CHANSERV_FUNC(cmd_huggle)
6987 /* CTCP must be via PRIVMSG, never notice */
6989 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
6991 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
6996 chanserv_adjust_limit(void *data)
6998 struct mod_chanmode change;
6999 struct chanData *cData = data;
7000 struct chanNode *channel = cData->channel;
7003 if(IsSuspended(cData))
7006 cData->limitAdjusted = now;
7007 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
7008 if(cData->modes.modes_set & MODE_LIMIT)
7010 if(limit > cData->modes.new_limit)
7011 limit = cData->modes.new_limit;
7012 else if(limit == cData->modes.new_limit)
7016 mod_chanmode_init(&change);
7017 change.modes_set = MODE_LIMIT;
7018 change.new_limit = limit;
7019 mod_chanmode_announce(chanserv, channel, &change);
7023 handle_new_channel(struct chanNode *channel)
7025 struct chanData *cData;
7027 if(!(cData = channel->channel_info))
7030 if(cData->modes.modes_set || cData->modes.modes_clear)
7031 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
7033 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
7034 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
7037 /* Welcome to my worst nightmare. Warning: Read (or modify)
7038 the code below at your own risk. */
7040 handle_join(struct modeNode *mNode)
7042 struct mod_chanmode change;
7043 struct userNode *user = mNode->user;
7044 struct chanNode *channel = mNode->channel;
7045 struct chanData *cData;
7046 struct userData *uData = NULL;
7047 struct banData *bData;
7048 struct handle_info *handle;
7049 unsigned int modes = 0, info = 0;
7053 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
7056 cData = channel->channel_info;
7057 if(channel->members.used > cData->max) {
7058 cData->max = channel->members.used;
7059 cData->max_time = now;
7062 for(i = 0; i < channel->invited.used; i++)
7064 if(channel->invited.list[i] == user) {
7065 userList_remove(&channel->invited, user);
7069 /* Check for bans. If they're joining through a ban, one of two
7071 * 1: Join during a netburst, by riding the break. Kick them
7072 * unless they have ops or voice in the channel.
7073 * 2: They're allowed to join through the ban (an invite in
7074 * ircu2.10, or a +e on Hybrid, or something).
7075 * If they're not joining through a ban, and the banlist is not
7076 * full, see if they're on the banlist for the channel. If so,
7079 if(user->uplink->burst && !mNode->modes)
7082 for(ii = 0; ii < channel->banlist.used; ii++)
7084 if(user_matches_glob(user, channel->banlist.list[ii]->ban, MATCH_USENICK))
7086 /* Riding a netburst. Naughty. */
7087 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
7093 mod_chanmode_init(&change);
7095 if(channel->banlist.used < MAXBANS)
7097 /* Not joining through a ban. */
7098 for(bData = cData->bans;
7099 bData && !user_matches_glob(user, bData->mask, MATCH_USENICK);
7100 bData = bData->next);
7104 char kick_reason[MAXLEN];
7105 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
7107 bData->triggered = now;
7108 if(bData != cData->bans)
7110 /* Shuffle the ban to the head of the list. */
7112 bData->next->prev = bData->prev;
7114 bData->prev->next = bData->next;
7117 bData->next = cData->bans;
7120 cData->bans->prev = bData;
7121 cData->bans = bData;
7124 change.args[0].mode = MODE_BAN;
7125 change.args[0].u.hostmask = bData->mask;
7126 mod_chanmode_announce(chanserv, channel, &change);
7127 KickChannelUser(user, channel, chanserv, kick_reason);
7132 /* ChanServ will not modify the limits in join-flooded channels,
7133 or when there are enough slots left below the limit. */
7134 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
7135 && !channel->join_flooded
7136 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
7138 /* The user count has begun "bumping" into the channel limit,
7139 so set a timer to raise the limit a bit. Any previous
7140 timers are removed so three incoming users within the delay
7141 results in one limit change, not three. */
7143 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7144 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7147 if(channel->join_flooded)
7149 /* don't automatically give ops or voice during a join flood */
7151 else if(cData->lvlOpts[lvlGiveOps] == 0)
7152 modes |= MODE_CHANOP;
7153 else if(cData->lvlOpts[lvlGiveVoice] == 0)
7154 modes |= MODE_VOICE;
7156 greeting = cData->greeting;
7157 if(user->handle_info)
7159 handle = user->handle_info;
7161 if(IsHelper(user) && !IsHelping(user))
7164 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7166 if(channel == chanserv_conf.support_channels.list[ii])
7168 HANDLE_SET_FLAG(user->handle_info, HELPING);
7174 uData = GetTrueChannelAccess(cData, handle);
7175 if(uData && !IsUserSuspended(uData))
7177 /* Ops and above were handled by the above case. */
7178 if(IsUserAutoOp(uData))
7180 if(uData->access >= cData->lvlOpts[lvlGiveOps])
7181 modes |= MODE_CHANOP;
7182 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
7183 modes |= MODE_VOICE;
7185 if(uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
7186 cData->visited = now;
7187 if(cData->user_greeting)
7188 greeting = cData->user_greeting;
7190 && (uData->access >= cData->lvlOpts[lvlUserInfo])
7191 && ((now - uData->seen) >= chanserv_conf.info_delay)
7199 /* If user joining normally (not during burst), apply op or voice,
7200 * and send greeting/userinfo as appropriate.
7202 if(!user->uplink->burst)
7206 if(modes & MODE_CHANOP)
7207 modes &= ~MODE_VOICE;
7208 change.args[0].mode = modes;
7209 change.args[0].u.member = mNode;
7210 mod_chanmode_announce(chanserv, channel, &change);
7213 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
7214 if(uData && info && (modes || !(channel->modes & MODE_DELAYJOINS)))
7215 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
7221 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
7223 struct mod_chanmode change;
7224 struct userData *channel;
7225 unsigned int ii, jj;
7227 if(!user->handle_info)
7230 mod_chanmode_init(&change);
7232 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
7234 struct chanNode *cn;
7235 struct modeNode *mn;
7236 if(IsUserSuspended(channel)
7237 || IsSuspended(channel->channel)
7238 || !(cn = channel->channel->channel))
7241 mn = GetUserMode(cn, user);
7244 if(!IsUserSuspended(channel)
7245 && IsUserAutoInvite(channel)
7246 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
7248 && !user->uplink->burst)
7249 irc_invite(chanserv, user, cn);
7253 if(channel->access >= UL_PRESENT && !HANDLE_FLAGGED(channel->handle, BOT))
7254 channel->channel->visited = now;
7256 if(IsUserAutoOp(channel))
7258 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
7259 change.args[0].mode = MODE_CHANOP;
7260 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
7261 change.args[0].mode = MODE_VOICE;
7263 change.args[0].mode = 0;
7264 change.args[0].u.member = mn;
7265 if(change.args[0].mode)
7266 mod_chanmode_announce(chanserv, cn, &change);
7269 channel->seen = now;
7270 channel->present = 1;
7273 for(ii = 0; ii < user->channels.used; ++ii)
7275 struct chanNode *chan = user->channels.list[ii]->channel;
7276 struct banData *ban;
7278 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
7279 || !chan->channel_info
7280 || IsSuspended(chan->channel_info))
7282 for(jj = 0; jj < chan->banlist.used; ++jj)
7283 if(user_matches_glob(user, chan->banlist.list[jj]->ban, MATCH_USENICK))
7285 if(jj < chan->banlist.used)
7287 for(ban = chan->channel_info->bans; ban; ban = ban->next)
7289 char kick_reason[MAXLEN];
7290 if(!user_matches_glob(user, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
7292 change.args[0].mode = MODE_BAN;
7293 change.args[0].u.hostmask = ban->mask;
7294 mod_chanmode_announce(chanserv, chan, &change);
7295 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
7296 KickChannelUser(user, chan, chanserv, kick_reason);
7297 ban->triggered = now;
7302 if(IsSupportHelper(user))
7304 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7306 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
7308 HANDLE_SET_FLAG(user->handle_info, HELPING);
7316 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
7318 struct chanData *cData;
7319 struct userData *uData;
7321 cData = mn->channel->channel_info;
7322 if(!cData || IsSuspended(cData) || IsLocal(mn->user))
7325 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
7327 /* Allow for a bit of padding so that the limit doesn't
7328 track the user count exactly, which could get annoying. */
7329 if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
7331 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7332 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7336 if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
7338 scan_user_presence(uData, mn->user);
7340 if (uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
7341 cData->visited = now;
7344 if(IsHelping(mn->user) && IsSupportHelper(mn->user))
7347 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii) {
7348 struct chanNode *channel;
7349 struct userNode *exclude;
7350 /* When looking at the channel that is being /part'ed, we
7351 * have to skip over the client that is leaving. For
7352 * other channels, we must not do that.
7354 channel = chanserv_conf.support_channels.list[ii];
7355 exclude = (channel == mn->channel) ? mn->user : NULL;
7356 if(find_handle_in_channel(channel, mn->user->handle_info, exclude))
7359 if(ii == chanserv_conf.support_channels.used)
7360 HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
7365 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
7367 struct userData *uData;
7369 if(!channel->channel_info || !kicker || IsService(kicker)
7370 || (kicker == victim) || IsSuspended(channel->channel_info)
7371 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
7374 if(protect_user(victim, kicker, channel->channel_info))
7376 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED_2");
7377 KickChannelUser(kicker, channel, chanserv, reason);
7380 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
7385 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
7387 struct chanData *cData;
7389 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
7392 cData = channel->channel_info;
7393 if(bad_topic(channel, user, channel->topic))
7395 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
7396 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
7397 SetChannelTopic(channel, chanserv, old_topic, 1);
7398 else if(cData->topic)
7399 SetChannelTopic(channel, chanserv, cData->topic, 1);
7402 /* With topicsnarf, grab the topic and save it as the default topic. */
7403 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
7406 cData->topic = strdup(channel->topic);
7412 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
7414 struct mod_chanmode *bounce = NULL;
7415 unsigned int bnc, ii;
7418 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
7421 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
7422 && mode_lock_violated(&channel->channel_info->modes, change))
7424 char correct[MAXLEN];
7425 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
7426 mod_chanmode_format(&channel->channel_info->modes, correct);
7427 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
7429 for(ii = bnc = 0; ii < change->argc; ++ii)
7431 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
7433 const struct userNode *victim = change->args[ii].u.member->user;
7434 if(!protect_user(victim, user, channel->channel_info))
7437 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7440 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
7441 bounce->args[bnc].u.member = GetUserMode(channel, user);
7442 if(bounce->args[bnc].u.member)
7446 bounce->args[bnc].mode = MODE_CHANOP;
7447 bounce->args[bnc].u.member = change->args[ii].u.member;
7449 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
7451 else if(change->args[ii].mode & MODE_CHANOP)
7453 const struct userNode *victim = change->args[ii].u.member->user;
7454 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
7457 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7458 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
7459 bounce->args[bnc].u.member = change->args[ii].u.member;
7462 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
7464 const char *ban = change->args[ii].u.hostmask;
7465 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
7468 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7469 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
7470 bounce->args[bnc].u.hostmask = strdup(ban);
7472 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
7477 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
7478 mod_chanmode_announce(chanserv, channel, bounce);
7479 for(ii = 0; ii < change->argc; ++ii)
7480 if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
7481 free((char*)bounce->args[ii].u.hostmask);
7482 mod_chanmode_free(bounce);
7487 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
7489 struct chanNode *channel;
7490 struct banData *bData;
7491 struct mod_chanmode change;
7492 unsigned int ii, jj;
7493 char kick_reason[MAXLEN];
7495 mod_chanmode_init(&change);
7497 change.args[0].mode = MODE_BAN;
7498 for(ii = 0; ii < user->channels.used; ++ii)
7500 channel = user->channels.list[ii]->channel;
7501 /* Need not check for bans if they're opped or voiced. */
7502 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
7504 /* Need not check for bans unless channel registration is active. */
7505 if(!channel->channel_info || IsSuspended(channel->channel_info))
7507 /* Look for a matching ban already on the channel. */
7508 for(jj = 0; jj < channel->banlist.used; ++jj)
7509 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
7511 /* Need not act if we found one. */
7512 if(jj < channel->banlist.used)
7514 /* Look for a matching ban in this channel. */
7515 for(bData = channel->channel_info->bans; bData; bData = bData->next)
7517 if(!user_matches_glob(user, bData->mask, MATCH_USENICK | MATCH_VISIBLE))
7519 change.args[0].u.hostmask = bData->mask;
7520 mod_chanmode_announce(chanserv, channel, &change);
7521 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
7522 KickChannelUser(user, channel, chanserv, kick_reason);
7523 bData->triggered = now;
7524 break; /* we don't need to check any more bans in the channel */
7529 static void handle_rename(struct handle_info *handle, const char *old_handle)
7531 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
7535 dict_remove2(handle_dnrs, old_handle, 1);
7536 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
7537 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
7542 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
7544 struct userNode *h_user;
7546 if(handle->channels)
7548 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
7549 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
7551 while(handle->channels)
7552 del_channel_user(handle->channels, 1);
7557 handle_server_link(UNUSED_ARG(struct server *server))
7559 struct chanData *cData;
7561 for(cData = channelList; cData; cData = cData->next)
7563 if(!IsSuspended(cData))
7564 cData->may_opchan = 1;
7565 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
7566 && !cData->channel->join_flooded
7567 && ((cData->channel->limit - cData->channel->members.used)
7568 < chanserv_conf.adjust_threshold))
7570 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7571 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7577 chanserv_conf_read(void)
7581 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
7582 struct mod_chanmode *change;
7583 struct string_list *strlist;
7584 struct chanNode *chan;
7587 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
7589 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
7592 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7593 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7594 chanserv_conf.support_channels.used = 0;
7595 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
7597 for(ii = 0; ii < strlist->used; ++ii)
7599 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7602 chan = AddChannel(strlist->list[ii], now, str2, NULL);
7604 channelList_append(&chanserv_conf.support_channels, chan);
7607 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
7610 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7613 chan = AddChannel(str, now, str2, NULL);
7615 channelList_append(&chanserv_conf.support_channels, chan);
7617 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
7618 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
7619 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
7620 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
7621 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
7622 chanserv_conf.greeting_length = str ? atoi(str) : 200;
7623 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
7624 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
7625 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
7626 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
7627 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
7628 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
7629 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
7630 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
7631 str = database_get_data(conf_node, KEY_DNR_EXPIRE_FREQ, RECDB_QSTRING);
7632 chanserv_conf.dnr_expire_frequency = str ? ParseInterval(str) : 3600;
7633 str = database_get_data(conf_node, KEY_INVITED_INTERVAL, RECDB_QSTRING);
7634 chanserv_conf.invited_timeout = str ? ParseInterval(str) : 600*2;
7635 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
7636 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
7637 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
7638 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
7639 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
7640 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
7641 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
7642 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
7643 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
7645 NickChange(chanserv, str, 0);
7646 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
7647 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
7648 str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
7649 chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
7650 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
7651 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
7652 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
7653 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
7654 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
7655 chanserv_conf.max_owned = str ? atoi(str) : 5;
7656 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
7657 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
7658 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
7659 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
7660 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
7661 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
7662 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
7665 safestrncpy(mode_line, str, sizeof(mode_line));
7666 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
7667 if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE|MCP_NO_APASS, 0))
7668 && (change->argc < 2))
7670 chanserv_conf.default_modes = *change;
7671 mod_chanmode_free(change);
7673 free_string_list(chanserv_conf.set_shows);
7674 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
7676 strlist = string_list_copy(strlist);
7679 static const char *list[] = {
7680 /* free form text */
7681 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
7682 /* options based on user level */
7683 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
7684 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
7685 /* multiple choice options */
7686 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
7687 /* binary options */
7688 "DynLimit", "NoDelete", "expire", "Vote",
7692 strlist = alloc_string_list(ArrayLength(list)-1);
7693 for(ii=0; list[ii]; ii++)
7694 string_list_append(strlist, strdup(list[ii]));
7696 chanserv_conf.set_shows = strlist;
7697 /* We don't look things up now, in case the list refers to options
7698 * defined by modules initialized after this point. Just mark the
7699 * function list as invalid, so it will be initialized.
7701 set_shows_list.used = 0;
7702 free_string_list(chanserv_conf.eightball);
7703 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
7706 strlist = string_list_copy(strlist);
7710 strlist = alloc_string_list(4);
7711 string_list_append(strlist, strdup("Yes."));
7712 string_list_append(strlist, strdup("No."));
7713 string_list_append(strlist, strdup("Maybe so."));
7715 chanserv_conf.eightball = strlist;
7716 free_string_list(chanserv_conf.old_ban_names);
7717 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
7719 strlist = string_list_copy(strlist);
7721 strlist = alloc_string_list(2);
7722 chanserv_conf.old_ban_names = strlist;
7723 str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
7724 off_channel = str ? atoi(str) : 0;
7728 chanserv_note_type_read(const char *key, struct record_data *rd)
7731 struct note_type *ntype;
7734 if(!(obj = GET_RECORD_OBJECT(rd)))
7736 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
7739 if(!(ntype = chanserv_create_note_type(key)))
7741 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
7745 /* Figure out set access */
7746 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
7748 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7749 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
7751 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
7753 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
7754 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
7756 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
7758 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
7762 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
7763 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7764 ntype->set_access.min_opserv = 0;
7767 /* Figure out visibility */
7768 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
7769 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7770 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
7771 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7772 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
7773 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
7774 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
7775 ntype->visible_type = NOTE_VIS_ALL;
7777 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7779 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
7780 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
7784 vote_option_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7786 struct vote_option *vOpt;
7789 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7791 log_module(CS_LOG, LOG_ERROR, "Invalid vote option in %s.", chan->channel->name);
7795 vOpt = calloc(1, sizeof(*vOpt));
7796 vOpt->name = strdup(database_get_data(rd->d.object, KEY_VOTE_OPTION_NAME, RECDB_QSTRING));
7797 str = database_get_data(rd->d.object, KEY_VOTE_OPTION_VOTED, RECDB_QSTRING);
7798 vOpt->voted = str ? atoi(str) : 0;
7799 vOpt->option_id = str ? atoi(key) : 0;
7800 vOpt->option_str = strdup(key);
7801 dict_insert(chan->vote_options,vOpt->option_str,vOpt);
7805 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7807 struct handle_info *handle;
7808 struct userData *uData;
7809 char *seen, *inf, *flags, *voted, *votefor;
7810 unsigned long last_seen;
7811 unsigned short access_level;
7813 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7815 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
7819 access_level = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
7820 if(access_level > UL_OWNER)
7822 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
7826 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
7827 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
7828 last_seen = seen ? strtoul(seen, NULL, 0) : now;
7829 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
7830 voted = database_get_data(rd->d.object, KEY_VOTE_VOTED, RECDB_QSTRING);
7831 votefor = database_get_data(rd->d.object, KEY_VOTE_VOTEDFOR, RECDB_QSTRING);
7832 handle = get_handle_info(key);
7835 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
7839 uData = add_channel_user(chan, handle, access_level, last_seen, inf);
7840 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
7842 uData->voted = voted ? strtoul(voted, NULL, 0) : 0;
7843 uData->votefor = votefor ? strtoul(votefor, NULL, 0) : 0;
7851 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7853 struct banData *bData;
7854 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
7855 unsigned long set_time, triggered_time, expires_time;
7857 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7859 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
7863 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
7864 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
7865 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
7866 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
7867 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
7868 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
7869 if (!reason || !owner)
7872 set_time = set ? strtoul(set, NULL, 0) : now;
7873 triggered_time = triggered ? strtoul(triggered, NULL, 0) : 0;
7875 expires_time = strtoul(s_expires, NULL, 0);
7877 expires_time = set_time + atoi(s_duration);
7881 if(!reason || (expires_time && (expires_time < now)))
7884 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
7887 static struct suspended *
7888 chanserv_read_suspended(dict_t obj)
7890 struct suspended *suspended = calloc(1, sizeof(*suspended));
7894 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
7895 suspended->expires = str ? strtoul(str, NULL, 0) : 0;
7896 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
7897 suspended->revoked = str ? strtoul(str, NULL, 0) : 0;
7898 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
7899 suspended->issued = str ? strtoul(str, NULL, 0) : 0;
7900 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
7901 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
7902 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
7903 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
7908 chanserv_channel_read(const char *key, struct record_data *hir)
7910 struct suspended *suspended;
7911 struct mod_chanmode *modes;
7912 struct chanNode *cNode;
7913 struct chanData *cData;
7914 struct dict *channel, *obj;
7915 char *str, *argv[10];
7919 channel = hir->d.object;
7921 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
7924 cNode = AddChannel(key, now, NULL, NULL);
7927 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
7930 cData = register_channel(cNode, str);
7933 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
7937 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
7939 enum levelOption lvlOpt;
7940 enum charOption chOpt;
7942 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
7943 cData->flags = atoi(str);
7945 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7947 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
7949 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
7950 else if(levelOptions[lvlOpt].old_flag)
7952 if(cData->flags & levelOptions[lvlOpt].old_flag)
7953 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
7955 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
7959 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7961 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
7963 cData->chOpts[chOpt] = str[0];
7966 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
7968 enum levelOption lvlOpt;
7969 enum charOption chOpt;
7972 cData->flags = base64toint(str, 5);
7973 count = strlen(str += 5);
7974 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7977 if(levelOptions[lvlOpt].old_flag)
7979 if(cData->flags & levelOptions[lvlOpt].old_flag)
7980 lvl = levelOptions[lvlOpt].flag_value;
7982 lvl = levelOptions[lvlOpt].default_value;
7984 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
7986 case 'c': lvl = UL_COOWNER; break;
7987 case 'm': lvl = UL_MASTER; break;
7988 case 'n': lvl = UL_OWNER+1; break;
7989 case 'o': lvl = UL_OP; break;
7990 case 'p': lvl = UL_PEON; break;
7991 case 'w': lvl = UL_OWNER; break;
7992 default: lvl = 0; break;
7994 cData->lvlOpts[lvlOpt] = lvl;
7996 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7997 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
8000 if((str = database_get_data(hir->d.object, KEY_EXPIRE, RECDB_QSTRING)))
8002 cData->expiry = atoi(str);
8003 if(cData->expiry > 0) {
8004 if(cData->expiry > now) {
8005 timeq_add(cData->expiry, chanserv_expire_channel, cData);
8007 timeq_add(1, chanserv_expire_channel, cData);
8014 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
8016 suspended = chanserv_read_suspended(obj);
8017 cData->suspended = suspended;
8018 suspended->cData = cData;
8019 /* We could use suspended->expires and suspended->revoked to
8020 * set the CHANNEL_SUSPENDED flag, but we don't. */
8022 else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
8024 suspended = calloc(1, sizeof(*suspended));
8025 suspended->issued = 0;
8026 suspended->revoked = 0;
8027 suspended->suspender = strdup(str);
8028 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
8029 suspended->expires = str ? atoi(str) : 0;
8030 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
8031 suspended->reason = strdup(str ? str : "No reason");
8032 suspended->previous = NULL;
8033 cData->suspended = suspended;
8034 suspended->cData = cData;
8038 cData->flags &= ~CHANNEL_SUSPENDED;
8039 suspended = NULL; /* to squelch a warning */
8042 if(IsSuspended(cData)) {
8043 if(suspended->expires > now)
8044 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
8045 else if(suspended->expires)
8046 cData->flags &= ~CHANNEL_SUSPENDED;
8049 if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
8050 struct mod_chanmode change;
8051 mod_chanmode_init(&change);
8053 change.args[0].mode = MODE_CHANOP;
8054 change.args[0].u.member = AddChannelUser(chanserv, cNode);
8055 mod_chanmode_announce(chanserv, cNode, &change);
8058 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
8059 cData->registered = str ? strtoul(str, NULL, 0) : now;
8060 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
8061 cData->visited = str ? strtoul(str, NULL, 0) : now;
8062 str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
8063 cData->ownerTransfer = str ? strtoul(str, NULL, 0) : 0;
8064 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
8065 cData->max = str ? atoi(str) : 0;
8066 str = database_get_data(channel, KEY_MAX_TIME, RECDB_QSTRING);
8067 cData->max_time = str ? atoi(str) : 0;
8068 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
8069 cData->greeting = str ? strdup(str) : NULL;
8070 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
8071 cData->user_greeting = str ? strdup(str) : NULL;
8072 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
8073 cData->topic_mask = str ? strdup(str) : NULL;
8074 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
8075 cData->topic = str ? strdup(str) : NULL;
8077 str = database_get_data(channel, KEY_VOTE, RECDB_QSTRING);
8079 cData->vote = str ? strdup(str) : NULL;
8080 dict_delete(cData->vote_options);
8081 cData->vote_options = dict_new();
8082 dict_set_free_data(cData->vote_options, free_vote_options);
8083 str = database_get_data(channel, KEY_VOTE_START, RECDB_QSTRING);
8084 cData->vote_start = str ? atoi(str) : 0;
8085 obj = database_get_data(channel, KEY_VOTE_OPTIONS, RECDB_OBJECT);
8086 for(it = dict_first(obj); it; it = iter_next(it)) {
8087 vote_option_read_helper(iter_key(it), iter_data(it), cData);
8091 if(!IsSuspended(cData)
8092 && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
8093 && (argc = split_line(str, 0, ArrayLength(argv), argv))
8094 && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE|MCP_NO_APASS, 0))) {
8095 cData->modes = *modes;
8097 cData->modes.modes_set |= MODE_REGISTERED;
8098 if(cData->modes.argc > 1)
8099 cData->modes.argc = 1;
8100 mod_chanmode_announce(chanserv, cNode, &cData->modes);
8101 mod_chanmode_free(modes);
8104 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
8105 for(it = dict_first(obj); it; it = iter_next(it))
8106 user_read_helper(iter_key(it), iter_data(it), cData);
8108 if(!cData->users && !IsProtected(cData))
8110 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
8111 unregister_channel(cData, "has empty user list.");
8115 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
8116 for(it = dict_first(obj); it; it = iter_next(it))
8117 ban_read_helper(iter_key(it), iter_data(it), cData);
8119 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
8120 for(it = dict_first(obj); it; it = iter_next(it))
8122 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
8123 struct record_data *rd = iter_data(it);
8124 const char *note, *setter;
8126 if(rd->type != RECDB_OBJECT)
8128 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
8132 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
8134 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
8136 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
8140 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
8141 if(!setter) setter = "<unknown>";
8142 chanserv_add_channel_note(cData, ntype, setter, note);
8150 chanserv_dnr_read(const char *key, struct record_data *hir)
8152 const char *setter, *reason, *str;
8153 struct do_not_register *dnr;
8154 unsigned long expiry;
8156 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
8159 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
8162 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
8165 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
8168 str = database_get_data(hir->d.object, KEY_EXPIRES, RECDB_QSTRING);
8169 expiry = str ? strtoul(str, NULL, 0) : 0;
8170 if(expiry && expiry <= now)
8172 dnr = chanserv_add_dnr(key, setter, expiry, reason);
8175 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
8177 dnr->set = atoi(str);
8183 chanserv_saxdb_read(struct dict *database)
8185 struct dict *section;
8188 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
8189 for(it = dict_first(section); it; it = iter_next(it))
8190 chanserv_note_type_read(iter_key(it), iter_data(it));
8192 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
8193 for(it = dict_first(section); it; it = iter_next(it))
8194 chanserv_channel_read(iter_key(it), iter_data(it));
8196 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
8197 for(it = dict_first(section); it; it = iter_next(it))
8198 chanserv_dnr_read(iter_key(it), iter_data(it));
8204 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
8206 int high_present = 0;
8207 saxdb_start_record(ctx, KEY_USERS, 1);
8208 for(; uData; uData = uData->next)
8210 if((uData->access >= UL_PRESENT) && uData->present && !HANDLE_FLAGGED(uData->handle, BOT))
8212 saxdb_start_record(ctx, uData->handle->handle, 0);
8213 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
8214 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
8216 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
8217 if(uData->channel->vote && uData->voted)
8218 saxdb_write_int(ctx, KEY_VOTE_VOTED, uData->voted);
8219 if(uData->channel->vote && uData->votefor)
8220 saxdb_write_int(ctx, KEY_VOTE_VOTEDFOR, uData->votefor);
8222 saxdb_write_string(ctx, KEY_INFO, uData->info);
8223 saxdb_end_record(ctx);
8225 saxdb_end_record(ctx);
8226 return high_present;
8230 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
8234 saxdb_start_record(ctx, KEY_BANS, 1);
8235 for(; bData; bData = bData->next)
8237 saxdb_start_record(ctx, bData->mask, 0);
8238 saxdb_write_int(ctx, KEY_SET, bData->set);
8239 if(bData->triggered)
8240 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
8242 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
8244 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
8246 saxdb_write_string(ctx, KEY_REASON, bData->reason);
8247 saxdb_end_record(ctx);
8249 saxdb_end_record(ctx);
8253 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
8255 saxdb_start_record(ctx, name, 0);
8256 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
8257 saxdb_write_string(ctx, KEY_REASON, susp->reason);
8259 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
8261 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
8263 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
8265 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
8266 saxdb_end_record(ctx);
8270 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
8274 enum levelOption lvlOpt;
8275 enum charOption chOpt;
8278 saxdb_start_record(ctx, channel->channel->name, 1);
8280 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
8281 saxdb_write_int(ctx, KEY_MAX, channel->max);
8282 saxdb_write_int(ctx, KEY_MAX_TIME, channel->max_time);
8284 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
8285 if(channel->registrar)
8286 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
8287 if(channel->greeting)
8288 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
8289 if(channel->user_greeting)
8290 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
8291 if(channel->topic_mask)
8292 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
8293 if(channel->suspended)
8294 chanserv_write_suspended(ctx, "suspended", channel->suspended);
8296 saxdb_write_int(ctx, KEY_EXPIRE, channel->expiry);
8299 saxdb_write_string(ctx, KEY_VOTE, channel->vote);
8300 if(channel->vote_start)
8301 saxdb_write_int(ctx, KEY_VOTE_START, channel->vote_start);
8302 if (dict_size(channel->vote_options)) {
8303 saxdb_start_record(ctx, KEY_VOTE_OPTIONS, 1);
8304 for (it = dict_first(channel->vote_options); it; it = iter_next(it)) {
8305 struct vote_option *vOpt = iter_data(it);
8307 sprintf(str,"%i",vOpt->option_id);
8308 saxdb_start_record(ctx, str, 0);
8310 saxdb_write_int(ctx, KEY_VOTE_OPTION_VOTED, vOpt->voted);
8312 saxdb_write_string(ctx, KEY_VOTE_OPTION_NAME, vOpt->name);
8313 saxdb_end_record(ctx);
8315 saxdb_end_record(ctx);
8319 saxdb_start_record(ctx, KEY_OPTIONS, 0);
8320 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
8321 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8322 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
8323 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8325 buf[0] = channel->chOpts[chOpt];
8327 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
8329 saxdb_end_record(ctx);
8331 if(channel->modes.modes_set || channel->modes.modes_clear)
8333 mod_chanmode_format(&channel->modes, buf);
8334 saxdb_write_string(ctx, KEY_MODES, buf);
8337 high_present = chanserv_write_users(ctx, channel->users);
8338 chanserv_write_bans(ctx, channel->bans);
8340 if(dict_size(channel->notes))
8344 saxdb_start_record(ctx, KEY_NOTES, 1);
8345 for(it = dict_first(channel->notes); it; it = iter_next(it))
8347 struct note *note = iter_data(it);
8348 saxdb_start_record(ctx, iter_key(it), 0);
8349 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
8350 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
8351 saxdb_end_record(ctx);
8353 saxdb_end_record(ctx);
8356 if(channel->ownerTransfer)
8357 saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
8358 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
8359 saxdb_end_record(ctx);
8363 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
8367 saxdb_start_record(ctx, ntype->name, 0);
8368 switch(ntype->set_access_type)
8370 case NOTE_SET_CHANNEL_ACCESS:
8371 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
8373 case NOTE_SET_CHANNEL_SETTER:
8374 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
8376 case NOTE_SET_PRIVILEGED: default:
8377 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
8380 switch(ntype->visible_type)
8382 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
8383 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
8384 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
8386 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
8387 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
8388 saxdb_end_record(ctx);
8392 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
8394 struct do_not_register *dnr;
8395 dict_iterator_t it, next;
8397 for(it = dict_first(dnrs); it; it = next)
8399 next = iter_next(it);
8400 dnr = iter_data(it);
8401 if(dnr->expires && dnr->expires <= now)
8403 dict_remove(dnrs, iter_key(it));
8406 saxdb_start_record(ctx, dnr->chan_name, 0);
8408 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
8410 saxdb_write_int(ctx, KEY_EXPIRES, dnr->expires);
8411 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
8412 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
8413 saxdb_end_record(ctx);
8418 chanserv_saxdb_write(struct saxdb_context *ctx)
8421 struct chanData *channel;
8424 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
8425 for(it = dict_first(note_types); it; it = iter_next(it))
8426 chanserv_write_note_type(ctx, iter_data(it));
8427 saxdb_end_record(ctx);
8430 saxdb_start_record(ctx, KEY_DNR, 1);
8431 write_dnrs_helper(ctx, handle_dnrs);
8432 write_dnrs_helper(ctx, plain_dnrs);
8433 write_dnrs_helper(ctx, mask_dnrs);
8434 saxdb_end_record(ctx);
8437 saxdb_start_record(ctx, KEY_CHANNELS, 1);
8438 for(channel = channelList; channel; channel = channel->next)
8439 chanserv_write_channel(ctx, channel);
8440 saxdb_end_record(ctx);
8446 chanserv_db_cleanup(void) {
8448 unreg_part_func(handle_part);
8450 unregister_channel(channelList, "terminating.");
8451 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
8452 UnlockChannel(chanserv_conf.support_channels.list[ii]);
8453 free(chanserv_conf.support_channels.list);
8454 dict_delete(handle_dnrs);
8455 dict_delete(plain_dnrs);
8456 dict_delete(mask_dnrs);
8457 dict_delete(note_types);
8458 free_string_list(chanserv_conf.eightball);
8459 free_string_list(chanserv_conf.old_ban_names);
8460 free_string_list(chanserv_conf.set_shows);
8461 free(set_shows_list.list);
8462 free(uset_shows_list.list);
8465 struct userData *helper = helperList;
8466 helperList = helperList->next;
8471 #if defined(GCC_VARMACROS)
8472 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ARGS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ARGS)
8473 #elif defined(C99_VARMACROS)
8474 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, __VA_ARGS__)
8476 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
8477 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
8480 init_chanserv(const char *nick)
8482 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
8483 conf_register_reload(chanserv_conf_read);
8487 reg_server_link_func(handle_server_link);
8488 reg_new_channel_func(handle_new_channel);
8489 reg_join_func(handle_join);
8490 reg_part_func(handle_part);
8491 reg_kick_func(handle_kick);
8492 reg_topic_func(handle_topic);
8493 reg_mode_change_func(handle_mode);
8494 reg_nick_change_func(handle_nick_change);
8495 reg_auth_func(handle_auth);
8498 reg_handle_rename_func(handle_rename);
8499 reg_unreg_func(handle_unreg);
8501 handle_dnrs = dict_new();
8502 dict_set_free_data(handle_dnrs, free);
8503 plain_dnrs = dict_new();
8504 dict_set_free_data(plain_dnrs, free);
8505 mask_dnrs = dict_new();
8506 dict_set_free_data(mask_dnrs, free);
8508 reg_svccmd_unbind_func(handle_svccmd_unbind);
8509 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
8510 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
8511 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
8512 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
8513 DEFINE_COMMAND(dnrsearch, 3, 0, "template", "noregister", NULL);
8514 modcmd_register(chanserv_module, "dnrsearch print", NULL, 0, 0, NULL);
8515 modcmd_register(chanserv_module, "dnrsearch remove", NULL, 0, 0, NULL);
8516 modcmd_register(chanserv_module, "dnrsearch count", NULL, 0, 0, NULL);
8517 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
8518 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
8519 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
8520 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
8521 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
8523 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
8524 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
8526 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8527 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8528 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8529 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8530 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
8532 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
8533 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
8534 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
8535 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8536 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8538 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8539 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
8540 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8541 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
8543 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
8544 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
8545 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8546 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8547 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
8548 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8549 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8550 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8552 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8553 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8554 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8555 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
8556 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
8557 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
8558 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
8559 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
8560 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8561 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
8562 DEFINE_COMMAND(invitemeall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8563 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
8564 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
8565 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8566 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8568 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
8569 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8570 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8571 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8572 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
8574 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
8575 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
8577 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
8578 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8579 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8580 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8581 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8582 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8583 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8584 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8585 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8586 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8587 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8589 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
8590 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
8592 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
8593 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
8594 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
8595 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
8597 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
8598 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
8599 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
8600 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
8601 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
8603 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8604 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8605 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8606 DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8607 DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8608 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8610 DEFINE_COMMAND(addvote, 1, MODCMD_REQUIRE_AUTHED, NULL);
8611 DEFINE_COMMAND(delvote, 1, MODCMD_REQUIRE_AUTHED, NULL);
8612 DEFINE_COMMAND(addoption, 1, MODCMD_REQUIRE_AUTHED, NULL);
8613 DEFINE_COMMAND(deloption, 1, MODCMD_REQUIRE_AUTHED, NULL);
8614 DEFINE_COMMAND(vote, 1, MODCMD_REQUIRE_AUTHED, NULL);
8615 DEFINE_COMMAND(startvote, 1, MODCMD_REQUIRE_AUTHED, NULL);
8616 DEFINE_COMMAND(endvote, 1, MODCMD_REQUIRE_AUTHED, NULL);
8617 DEFINE_COMMAND(voteresults, 1, MODCMD_REQUIRE_AUTHED, NULL);
8619 /* Channel options */
8620 DEFINE_CHANNEL_OPTION(defaulttopic);
8621 DEFINE_CHANNEL_OPTION(topicmask);
8622 DEFINE_CHANNEL_OPTION(greeting);
8623 DEFINE_CHANNEL_OPTION(usergreeting);
8624 DEFINE_CHANNEL_OPTION(modes);
8625 DEFINE_CHANNEL_OPTION(enfops);
8626 DEFINE_CHANNEL_OPTION(giveops);
8627 DEFINE_CHANNEL_OPTION(protect);
8628 DEFINE_CHANNEL_OPTION(enfmodes);
8629 DEFINE_CHANNEL_OPTION(enftopic);
8630 DEFINE_CHANNEL_OPTION(pubcmd);
8631 DEFINE_CHANNEL_OPTION(givevoice);
8632 DEFINE_CHANNEL_OPTION(userinfo);
8633 DEFINE_CHANNEL_OPTION(dynlimit);
8634 DEFINE_CHANNEL_OPTION(topicsnarf);
8635 DEFINE_CHANNEL_OPTION(vote);
8636 DEFINE_CHANNEL_OPTION(nodelete);
8637 DEFINE_CHANNEL_OPTION(toys);
8638 DEFINE_CHANNEL_OPTION(setters);
8639 DEFINE_CHANNEL_OPTION(topicrefresh);
8640 DEFINE_CHANNEL_OPTION(ctcpusers);
8641 DEFINE_CHANNEL_OPTION(ctcpreaction);
8642 DEFINE_CHANNEL_OPTION(inviteme);
8643 DEFINE_CHANNEL_OPTION(unreviewed);
8644 modcmd_register(chanserv_module, "set expire", chan_opt_expire, 1, 0, "flags", "+helping", NULL);
8645 modcmd_register(chanserv_module, "set unreviewed on", NULL, 0, 0, "flags", "+helping", NULL);
8646 modcmd_register(chanserv_module, "set unreviewed off", NULL, 0, 0, "flags", "+oper", NULL);
8648 DEFINE_CHANNEL_OPTION(offchannel);
8649 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
8651 /* Alias set topic to set defaulttopic for compatibility. */
8652 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
8655 DEFINE_USER_OPTION(noautoop);
8656 DEFINE_USER_OPTION(autoinvite);
8657 DEFINE_USER_OPTION(info);
8659 /* Alias uset autovoice to uset autoop. */
8660 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
8662 note_types = dict_new();
8663 dict_set_free_data(note_types, chanserv_deref_note_type);
8666 const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
8667 chanserv = AddLocalUser(nick, nick, NULL, "Channel Services", modes);
8668 service_register(chanserv)->trigger = '!';
8669 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
8671 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
8673 if(chanserv_conf.channel_expire_frequency)
8674 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
8676 if(chanserv_conf.dnr_expire_frequency)
8677 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
8679 if(chanserv_conf.refresh_period)
8681 unsigned long next_refresh;
8682 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
8683 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
8686 reg_exit_func(chanserv_db_cleanup);
8687 message_register_table(msgtab);