1 /* chanserv.c - Channel service bot
2 * Copyright 2000-2007 srvx Development Team
4 * This file is part of srvx.
6 * srvx is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with srvx; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
25 #include "opserv.h" /* for opserv_bad_channel() */
26 #include "nickserv.h" /* for oper_outranks() */
31 #define CHANSERV_CONF_NAME "services/chanserv"
33 /* ChanServ options */
34 #define KEY_SUPPORT_CHANNEL "support_channel"
35 #define KEY_SUPPORT_CHANNEL_MODES "support_channel_modes"
36 #define KEY_DB_BACKUP_FREQ "db_backup_freq"
37 #define KEY_INFO_DELAY "info_delay"
38 #define KEY_MAX_GREETLEN "max_greetlen"
39 #define KEY_ADJUST_THRESHOLD "adjust_threshold"
40 #define KEY_ADJUST_DELAY "adjust_delay"
41 #define KEY_CHAN_EXPIRE_FREQ "chan_expire_freq"
42 #define KEY_CHAN_EXPIRE_DELAY "chan_expire_delay"
43 #define KEY_DNR_EXPIRE_FREQ "dnr_expire_freq"
44 #define KEY_MAX_CHAN_USERS "max_chan_users"
45 #define KEY_MAX_CHAN_BANS "max_chan_bans"
46 #define KEY_NICK "nick"
47 #define KEY_OLD_CHANSERV_NAME "old_chanserv_name"
48 #define KEY_8BALL_RESPONSES "8ball"
49 #define KEY_OLD_BAN_NAMES "old_ban_names"
50 #define KEY_REFRESH_PERIOD "refresh_period"
51 #define KEY_CTCP_SHORT_BAN_DURATION "ctcp_short_ban_duration"
52 #define KEY_CTCP_LONG_BAN_DURATION "ctcp_long_ban_duration"
53 #define KEY_MAX_OWNED "max_owned"
54 #define KEY_IRC_OPERATOR_EPITHET "irc_operator_epithet"
55 #define KEY_NETWORK_HELPER_EPITHET "network_helper_epithet"
56 #define KEY_SUPPORT_HELPER_EPITHET "support_helper_epithet"
57 #define KEY_NODELETE_LEVEL "nodelete_level"
58 #define KEY_MAX_USERINFO_LENGTH "max_userinfo_length"
59 #define KEY_GIVEOWNERSHIP_PERIOD "giveownership_timeout"
60 #define KEY_INVITED_INTERVAL "invite_timeout"
62 /* ChanServ database */
63 #define KEY_CHANNELS "channels"
64 #define KEY_NOTE_TYPES "note_types"
66 /* Note type parameters */
67 #define KEY_NOTE_OPSERV_ACCESS "opserv_access"
68 #define KEY_NOTE_CHANNEL_ACCESS "channel_access"
69 #define KEY_NOTE_SETTER_ACCESS "setter_access"
70 #define KEY_NOTE_VISIBILITY "visibility"
71 #define KEY_NOTE_VIS_PRIVILEGED "privileged"
72 #define KEY_NOTE_VIS_CHANNEL_USERS "channel_users"
73 #define KEY_NOTE_VIS_ALL "all"
74 #define KEY_NOTE_MAX_LENGTH "max_length"
75 #define KEY_NOTE_SETTER "setter"
76 #define KEY_NOTE_NOTE "note"
78 /* Do-not-register channels */
80 #define KEY_DNR_SET "set"
81 #define KEY_DNR_SETTER "setter"
82 #define KEY_DNR_REASON "reason"
85 #define KEY_REGISTERED "registered"
86 #define KEY_REGISTRAR "registrar"
87 #define KEY_SUSPENDED "suspended"
88 #define KEY_PREVIOUS "previous"
89 #define KEY_SUSPENDER "suspender"
90 #define KEY_ISSUED "issued"
91 #define KEY_REVOKED "revoked"
92 #define KEY_SUSPEND_EXPIRES "suspend_expires"
93 #define KEY_SUSPEND_REASON "suspend_reason"
94 #define KEY_VISITED "visited"
95 #define KEY_TOPIC "topic"
96 #define KEY_GREETING "greeting"
97 #define KEY_USER_GREETING "user_greeting"
98 #define KEY_MODES "modes"
99 #define KEY_FLAGS "flags"
100 #define KEY_OPTIONS "options"
101 #define KEY_USERS "users"
102 #define KEY_BANS "bans"
103 #define KEY_MAX "max"
104 #define KEY_MAX_TIME "max_time"
105 #define KEY_NOTES "notes"
106 #define KEY_TOPIC_MASK "topic_mask"
107 #define KEY_OWNER_TRANSFER "owner_transfer"
108 #define KEY_EXPIRE "expire"
111 #define KEY_LEVEL "level"
112 #define KEY_INFO "info"
113 #define KEY_SEEN "seen"
116 #define KEY_VOTE "vote"
117 #define KEY_VOTE_START "votestart"
118 #define KEY_VOTE_OPTIONS "voptions"
119 #define KEY_VOTE_OPTION_NAME "voptionname"
120 #define KEY_VOTE_VOTED "vvoted"
121 #define KEY_VOTE_VOTEDFOR "vvotefor"
122 #define KEY_VOTE_OPTION_ID "voptionid"
123 #define KEY_VOTE_OPTION_VOTED "voptionvoted"
126 #define KEY_OWNER "owner"
127 #define KEY_REASON "reason"
128 #define KEY_SET "set"
129 #define KEY_DURATION "duration"
130 #define KEY_EXPIRES "expires"
131 #define KEY_TRIGGERED "triggered"
133 #define CHANNEL_DEFAULT_FLAGS (CHANNEL_OFFCHANNEL | CHANNEL_UNREVIEWED)
134 #define CHANNEL_PRESERVED_FLAGS (CHANNEL_UNREVIEWED)
135 #define CHANNEL_DEFAULT_OPTIONS "lmoooanpcnat"
137 /* Administrative messages */
138 static const struct message_entry msgtab[] = {
139 { "CSMSG_CHANNELS_EXPIRED", "%i channels expired." },
141 /* Channel registration */
142 { "CSMSG_REG_SUCCESS", "You now have ownership of $b%s$b." },
143 { "CSMSG_PROXY_SUCCESS", "%s now has ownership of $b%s$b." },
144 { "CSMSG_ALREADY_REGGED", "$b%s$b is registered to someone else." },
145 { "CSMSG_MUST_BE_OPPED", "You must be a channel operator in $b%s$b to register it." },
146 { "CSMSG_PROXY_FORBIDDEN", "You may not register a channel for someone else." },
147 { "CSMSG_OWN_TOO_MANY", "%s already owns enough channels (at least %d); use FORCE to override." },
149 /* Do-not-register channels */
150 { "CSMSG_NOT_DNR", "$b%s$b is not a valid channel name or *account." },
151 { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
152 { "CSMSG_DNR_INFO", "$b%s$b is do-not-register (by $b%s$b): %s" },
153 { "CSMSG_DNR_INFO_SET", "$b%s$b is do-not-register (set %s by $b%s$b): %s" },
154 { "CSMSG_DNR_INFO_SET_EXPIRES", "$b%s$b is do-not-register (set %s by $b%s$b; expires %s): %s" },
155 { "CSMSG_MORE_DNRS", "%d more do-not-register entries skipped." },
156 { "CSMSG_DNR_CHANNEL", "Only network staff may register $b%s$b." },
157 { "CSMSG_DNR_CHANNEL_MOVE", "Only network staff may move $b%s$b." },
158 { "CSMSG_DNR_ACCOUNT", "Only network staff may register channels to $b%s$b." },
159 { "CSMSG_NOREGISTER_CHANNEL", "$b%s$b has been added to the do-not-register list." },
160 { "CSMSG_NO_SUCH_DNR", "$b%s$b is not in the do-not-register list." },
161 { "CSMSG_DNR_REMOVED", "$b%s$b has been removed from the do-not-register list." },
162 { "CSMSG_DNR_BAD_ACTION", "$b%s$b is not a recognized do-not-register action." },
163 { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
165 /* Channel unregistration */
166 { "CSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
167 { "CSMSG_UNREG_NODELETE", "$b%s$b is protected from unregistration." },
168 { "CSMSG_CHAN_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended (%s)." },
169 { "CSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must use 'unregister %s'." },
172 { "CSMSG_MOVE_SUCCESS", "Channel registration has been moved to $b%s$b." },
173 { "CSMSG_MOVE_NODELETE", "$b%s$b is protected from unregistration, and cannot be moved." },
175 /* Channel merging */
176 { "CSMSG_MERGE_SUCCESS", "Channel successfully merged into $b%s$b." },
177 { "CSMSG_MERGE_SELF", "Merging cannot be performed if the source and target channels are the same." },
178 { "CSMSG_MERGE_NODELETE", "You may not merge a channel that is marked NoDelete." },
179 { "CSMSG_MERGE_SUSPENDED", "Merging cannot be performed if the source or target channel is suspended." },
180 { "CSMSG_MERGE_NOT_OWNER", "You must be the owner of the target channel (or a helper) to merge into the channel." },
182 /* Handle unregistration */
183 { "CSMSG_HANDLE_UNREGISTERED", "As a result of your account unregistration, you have been deleted from all of your channels' userlists." },
186 { "CSMSG_NOT_USER", "You lack access to $b%s$b." },
187 { "CSMSG_NO_CHAN_USER", "%s lacks access to $b%s$b." },
188 { "CSMSG_NO_ACCESS", "You lack sufficient access to use this command." },
189 { "CSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$C$b." },
190 { "CSMSG_MAXIMUM_BANS", "This channel has reached the ban count limit of $b%d$b." },
191 { "CSMSG_MAXIMUM_USERS", "This channel has reached the user count limit of $b%d$b." },
192 { "CSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel, and cannot be registered." },
193 { "CSMSG_GODMODE_UP", "You may not use $b%s$b to op yourself unless you are on the user list. Use the $bop$b command instead." },
194 { "CSMSG_ALREADY_OPPED", "You are already opped in $b%s$b." },
195 { "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." },
196 { "CSMSG_ALREADY_DOWN", "You are not opped or voiced in $b%s$b." },
197 { "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." },
198 { "CSMSG_OUT_OF_CHANNEL", "For some reason I don't seem to be in $b%s$b." },
199 { "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." },
201 /* Removing yourself from a channel. */
202 { "CSMSG_NO_OWNER_DELETEME", "You cannot delete your owner access in $b%s$b." },
203 { "CSMSG_CONFIRM_DELETEME", "To really remove yourself, you must use 'deleteme %s'." },
204 { "CSMSG_DELETED_YOU", "Your $b%d$b access has been deleted from $b%s$b." },
206 /* User management */
207 { "CSMSG_ADDED_USER", "Added %s to the %s user list with access %d." },
208 { "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." },
209 { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be greater than maximum (%d)." },
210 { "CSMSG_DELETED_USERS", "Deleted accounts matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list." },
211 { "CSMSG_TRIMMED_USERS", "Trimmed $b%d users$b with access from %d to %d from the %s user list who were inactive for at least %s." },
212 { "CSMSG_INCORRECT_ACCESS", "%s has access $b%d$b, not %s." },
213 { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with access %d)." },
214 { "CSMSG_CANNOT_TRIM", "You must include a minimum inactivity duration of at least 60 seconds to trim." },
216 { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." },
217 { "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." },
218 { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
219 { "CSMSG_TRANSFER_WAIT", "You must wait %s before you can give ownership of $b%s$b to someone else." },
220 { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
221 { "CSMSG_CONFIRM_GIVEOWNERSHIP", "To really give ownership to $b%1$s$b, you must use 'giveownership *%1$s %2$s'." },
222 { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
225 { "CSMSG_BAN_ADDED", "Permanently banned $b%s$b from %s." },
226 { "CSMSG_TIMED_BAN_ADDED", "Banned $b%s$b from %s for %s." },
227 { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
228 { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
229 { "CSMSG_REASON_CHANGE", "Reason for ban $b%s$b changed." },
230 { "CSMSG_BAN_EXTENDED", "Extended ban for $b%s$b expires in %s." },
231 { "CSMSG_BAN_REMOVED", "Matching ban(s) for $b%s$b removed." },
232 { "CSMSG_TRIMMED_BANS", "Trimmed $b%d bans$b from the %s ban list that were inactive for at least %s." },
233 { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
234 { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
235 { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
236 { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
237 { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
238 { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
239 { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban found for $b%s$b." },
240 { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
242 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
244 /* Channel management */
245 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
246 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
247 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
249 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
250 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
251 { "CSMSG_TOPICMASK_CONFLICT1", "I do not know how to make that topic work with the current topic mask in $b%s$b, which is: %s" },
252 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic is at most %d characters and matches the topic mask pattern." },
253 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
254 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
255 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
257 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
258 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
259 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
260 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
261 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
262 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
263 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you must use 'set defaults %s'." },
264 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
265 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
266 { "CSMSG_BAD_GIVEVOICE", "You cannot change GiveVoice to above GiveOps (%d)." },
267 { "CSMSG_BAD_GIVEOPS", "You cannot change GiveOps to below GiveVoice (%d)." },
268 { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
269 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
270 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
271 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
272 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
273 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
274 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
275 { "CSMSG_SET_MODES", "$bModes $b %s" },
276 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
277 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s" },
278 { "CSMSG_SET_OFFCHANNEL", "$bOffChannel $b %s" },
279 { "CSMSG_SET_USERINFO", "$bUserInfo $b %d" },
280 { "CSMSG_SET_GIVE_VOICE", "$bGiveVoice $b %d" },
281 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" },
282 { "CSMSG_SET_VOTE", "$bVote $b %d" },
283 { "CSMSG_SET_INVITEME", "$bInviteMe $b %d" },
284 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d" },
285 { "CSMSG_SET_GIVE_OPS", "$bGiveOps $b %d" },
286 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d" },
287 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d" },
288 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d" },
289 { "CSMSG_SET_SETTERS", "$bSetters $b %d" },
290 { "CSMSG_SET_CTCPUSERS", "$bCTCPUsers $b %d" },
291 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
292 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
293 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
294 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
295 { "CSMSG_SET_UNREVIEWED", "$bUnreviewed $b %s" },
296 { "CSMSG_SET_EXPIRE", "$bExpire $b %s" },
297 { "CSMSG_SET_EXPIRE_OFF", "$bExpire $b off" },
298 { "CSMSG_USET_NOAUTOOP", "$bNoAutoOp $b %s" },
299 { "CSMSG_USET_NOAUTOVOICE", "$bNoAutoVoice $b %s" },
300 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
301 { "CSMSG_USET_INFO", "$bInfo $b %s" },
303 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
304 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
305 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
306 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
307 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
308 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
309 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
310 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
311 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
312 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
313 { "CSMSG_PROTECT_NONE", "No users will be protected." },
314 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
315 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
316 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
317 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
318 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
319 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
320 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
321 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
322 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
323 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
324 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
325 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
327 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
328 { "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" },
329 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s." },
330 { "CSMSG_ALREADY_PRESENT", "%s is already in $b%s$b." },
331 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
332 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s for $S to invite you." },
333 { "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
334 { "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
336 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
337 { "CSMSG_NO_BANS", "No channel bans found on $b%s$b." },
338 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
340 /* Channel userlist */
341 { "CSMSG_ACCESS_ALL_HEADER", "%s users from level %d to %d:" },
342 { "CSMSG_ACCESS_SEARCH_HEADER", "%s users from level %d to %d matching %s:" },
343 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
344 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%d$b in %s." },
346 /* Channel note list */
347 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
348 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
349 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
350 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
351 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
352 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
353 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
354 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
355 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
356 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
357 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
358 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
359 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
360 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
361 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
363 /* Channel [un]suspension */
364 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
365 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
366 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
367 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
368 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
369 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
370 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
372 /* Access information */
373 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
374 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
375 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
376 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
377 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
378 { "CSMSG_USER_HAS_ACCESS", "%s has access $b%d$b in %s." },
379 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
380 { "CSMSG_HELPER_HAS_ACCESS", "%s has access $b%d$b in %s and has $bsecurity override$b enabled." },
381 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
382 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
383 { "CSMSG_OPERATOR_TITLE", "IRC operator" },
384 { "CSMSG_UC_H_TITLE", "network helper" },
385 { "CSMSG_LC_H_TITLE", "support helper" },
386 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
387 { "CSMSG_MYACCESS_COUNT", "%s has access in $b%d$b channels." },
388 { "CSMSG_MYACCESS_COUNT_1", "%s has access in $b%d$b channel." },
390 /* Seen information */
391 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
392 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
393 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
394 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
396 /* Names information */
397 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
398 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
400 /* Channel information */
401 { "CSMSG_CHANNEL_INFO", "$b%s$b Information:" },
402 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
403 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
404 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
405 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
406 { "CSMSG_CHANNEL_MAX_TIME", "$bRecord Visitors: $b%i (%s ago.)" },
407 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
408 { "CSMSG_CHANNEL_BANS", "$bBan Count: $b%i" },
409 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
410 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
411 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
412 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
413 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
414 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
415 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
416 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
417 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
418 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
419 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
420 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
421 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
422 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
424 { "CSMSG_PEEK_INFO", "$b%s$b Status:" },
425 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
426 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
427 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d (%d ops, %d voices, %d regulars)" },
428 { "CSMSG_PEEK_OPS", "$bOps:$b" },
429 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
431 /* Network information */
432 { "CSMSG_NETWORK_INFO", "Network Information:" },
433 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
434 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
435 { "CSMSG_NETWORK_BANS", "$bTotal Ban Count: $b%i" },
436 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
437 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
438 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
439 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
440 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
443 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
444 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
445 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
447 /* Channel searches */
448 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
449 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
450 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
451 { "CSMSG_CHANNEL_SEARCH_RESULTS", "The following channels were found:" },
453 /* Channel configuration */
454 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
455 { "CSMSG_INVALID_CFLAG", "$b%s$b is not a recognized channel flag." },
456 { "CSMSG_CHANNEL_OPTIONS", "Channel Options:" },
457 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
460 { "CSMSG_USER_OPTIONS", "User Options:" },
461 { "CSMSG_USER_PROTECTED_2", "That user is protected." },
464 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
465 { "CSMSG_PING_RESPONSE", "Pong!" },
466 { "CSMSG_WUT_RESPONSE", "wut" },
467 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
468 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
469 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
470 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
471 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
472 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
473 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
476 { "CSMSG_ADDVOTE_DONE", "Vote added. Use $baddoption$b to add at least 2 vote options and then $bstartvote$b to start the voting." },
477 { "CSMSG_ADDVOTE_FULL", "There is already a vote in this channel. Use $bdelvote$b to delete it." },
478 { "CSMSG_DELVOTE_DONE", "Vote deleted." },
479 { "CSMSG_NO_VOTE", "There is no vote in this channel." },
480 { "CSMSG_ADDOPTION_DONE", "Vote option added with id %i (%i - %i)." },
481 { "CSMSG_DELOPTION_DONE", "Vote option deleted." },
482 { "CSMSG_DELOPTION_NONE", "Vote option does not exist." },
483 { "CSMSG_VOTE_NEED_OPTIONS", "There must be at least 2 options in a vote." },
484 { "CSMSG_VOTE_NOT_STARTED", "The vote is not started. Use $bstartvote$b to allow voting." },
485 { "CSMSG_VOTE_QUESTION", "Question: %s" },
486 { "CSMSG_VOTE_OPTION", "$b%i$b: %s ($b%i$b votes)" },
487 { "CSMSG_VOTERES_QUESTION", "Question: %s" },
488 { "CSMSG_VOTERES_OPTION", "
\ 2%i
\ 2: %s (
\ 2%i
\ 2 votes)" },
489 { "CSMSG_STARTVOTE_TOP", "
\ 2%s
\ 2 has started a voting:" },
490 { "CSMSG_STARTVOTE_QUESTION", "
\ 2Question:
\ 2 %s" },
491 { "CSMSG_STARTVOTE_OPTION", "
\ 2%i:
\ 2 %s" },
492 { "CSMSG_STARTVOTE_ACCESS", "All channel users with at least
\ 2%i
\ 2 access can vote." },
493 { "CSMSG_STARTVOTE_HOWTO", "To vote for an option, use
\ 2vote ID
\ 2. To see the available options and the current votes, use
\ 2vote
\ 2 without parameters." },
494 { "CSMSG_STARTVOTE_RUNNING", "The vote is already running." },
495 { "CSMSG_VOTE_VOTED", "You have already voted." },
496 { "CSMSG_VOTE_INVALID", "Vote option does not exist." },
497 { "CSMSG_VOTE_DONE", "You voted for $b%s$b." },
498 { "CSMSG_ENDVOTE_DONE", "The vote has been finished." },
499 { "CSMSG_ENDVOTE_STOPPED", "The vote is not running." },
502 { "CSMSG_EVENT_SEARCH_RESULTS", "The following channel events were found:" },
506 /* eject_user and unban_user flags */
507 #define ACTION_KICK 0x0001
508 #define ACTION_BAN 0x0002
509 #define ACTION_ADD_BAN 0x0004
510 #define ACTION_ADD_TIMED_BAN 0x0008
511 #define ACTION_UNBAN 0x0010
512 #define ACTION_DEL_BAN 0x0020
514 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
515 #define MODELEN 40 + KEYLEN
519 #define CSFUNC_ARGS user, channel, argc, argv, cmd
521 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
522 #define CHANSERV_SYNTAX() svccmd_send_help(user, chanserv, cmd)
523 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
524 reply("MSG_MISSING_PARAMS", argv[0]); \
528 DECLARE_LIST(dnrList, struct do_not_register *);
529 DEFINE_LIST(dnrList, struct do_not_register *)
531 static int eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action);
533 struct userNode *chanserv;
536 static dict_t plain_dnrs, mask_dnrs, handle_dnrs;
537 static struct log_type *CS_LOG;
541 struct channelList support_channels;
542 struct mod_chanmode default_modes;
544 unsigned long db_backup_frequency;
545 unsigned long channel_expire_frequency;
546 unsigned long dnr_expire_frequency;
548 unsigned long invited_timeout;
550 unsigned long info_delay;
551 unsigned long adjust_delay;
552 unsigned long channel_expire_delay;
553 unsigned int nodelete_level;
555 unsigned int adjust_threshold;
556 int join_flood_threshold;
558 unsigned int greeting_length;
559 unsigned int refresh_period;
560 unsigned int giveownership_period;
562 unsigned int max_owned;
563 unsigned int max_chan_users;
564 unsigned int max_chan_bans;
565 unsigned int max_userinfo_length;
567 struct string_list *set_shows;
568 struct string_list *eightball;
569 struct string_list *old_ban_names;
571 const char *ctcp_short_ban_duration;
572 const char *ctcp_long_ban_duration;
574 const char *irc_operator_epithet;
575 const char *network_helper_epithet;
576 const char *support_helper_epithet;
581 struct userNode *user;
582 struct userNode *bot;
583 struct chanNode *channel;
585 unsigned short lowest;
586 unsigned short highest;
587 struct userData **users;
588 struct helpfile_table table;
593 struct userNode *user;
594 struct chanNode *chan;
597 enum note_access_type
599 NOTE_SET_CHANNEL_ACCESS,
600 NOTE_SET_CHANNEL_SETTER,
604 enum note_visible_type
607 NOTE_VIS_CHANNEL_USERS,
613 enum note_access_type set_access_type;
615 unsigned int min_opserv;
616 unsigned short min_ulevel;
618 enum note_visible_type visible_type;
619 unsigned int max_length;
626 struct note_type *type;
627 char setter[NICKSERV_HANDLE_LEN+1];
631 static unsigned int registered_channels;
632 static unsigned int banCount;
634 static const struct {
637 unsigned short level;
640 { "peon", "Peon", UL_PEON, '+' },
641 { "op", "Op", UL_OP, '@' },
642 { "master", "Master", UL_MASTER, '%' },
643 { "coowner", "Coowner", UL_COOWNER, '*' },
644 { "owner", "Owner", UL_OWNER, '!' },
645 { "helper", "BUG:", UL_HELPER, 'X' }
648 static const struct {
651 unsigned short default_value;
652 unsigned int old_idx;
653 unsigned int old_flag;
654 unsigned short flag_value;
656 { "CSMSG_SET_GIVE_VOICE", "givevoice", 100, ~0, CHANNEL_VOICE_ALL, 0 },
657 { "CSMSG_SET_GIVE_OPS", "giveops", 200, 2, 0, 0 },
658 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
659 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
660 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
661 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
662 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
663 { "CSMSG_SET_CTCPUSERS", "ctcpusers", 0, 9, 0, 0 },
664 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES, 1 },
665 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE, 200 },
666 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF, 1 },
667 { "CSMSG_SET_VOTE", "vote", 100, ~0, 0, 0 }
670 struct charOptionValues {
673 } protectValues[] = {
674 { 'a', "CSMSG_PROTECT_ALL" },
675 { 'e', "CSMSG_PROTECT_EQUAL" },
676 { 'l', "CSMSG_PROTECT_LOWER" },
677 { 'n', "CSMSG_PROTECT_NONE" }
679 { 'd', "CSMSG_TOYS_DISABLED" },
680 { 'n', "CSMSG_TOYS_PRIVATE" },
681 { 'p', "CSMSG_TOYS_PUBLIC" }
682 }, topicRefreshValues[] = {
683 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
684 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
685 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
686 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
687 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
688 }, ctcpReactionValues[] = {
689 { 'k', "CSMSG_CTCPREACTION_KICK" },
690 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
691 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
692 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
695 static const struct {
699 unsigned int old_idx;
701 struct charOptionValues *values;
703 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues), protectValues },
704 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues), toysValues },
705 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues), topicRefreshValues },
706 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 't', 10, ArrayLength(ctcpReactionValues), ctcpReactionValues }
709 struct userData *helperList;
710 struct chanData *channelList;
711 static struct module *chanserv_module;
712 static unsigned int userCount;
714 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
715 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
716 static void unregister_channel(struct chanData *channel, const char *reason);
719 user_level_from_name(const char *name, unsigned short clamp_level)
721 unsigned int level = 0, ii;
723 level = strtoul(name, NULL, 10);
724 else for(ii = 0; (ii < ArrayLength(accessLevels)) && !level; ++ii)
725 if(!irccasecmp(name, accessLevels[ii].name))
726 level = accessLevels[ii].level;
727 if(level > clamp_level)
733 parse_level_range(unsigned short *minl, unsigned short *maxl, const char *arg)
736 *minl = strtoul(arg, &sep, 10);
744 *maxl = strtoul(sep+1, &sep, 10);
752 _GetChannelUser(struct chanData *channel, struct handle_info *handle, int override, int allow_suspended)
754 struct userData *uData, **head;
756 if(!channel || !handle)
759 if(override && HANDLE_FLAGGED(handle, HELPING)
760 && ((handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel)))
762 for(uData = helperList;
763 uData && uData->handle != handle;
764 uData = uData->next);
768 uData = calloc(1, sizeof(struct userData));
769 uData->handle = handle;
771 uData->access = UL_HELPER;
777 uData->next = helperList;
779 helperList->prev = uData;
787 for(uData = channel->users; uData; uData = uData->next)
788 if((uData->handle == handle) && (allow_suspended || !IsUserSuspended(uData)))
791 head = &(channel->users);
794 if(uData && (uData != *head))
796 /* Shuffle the user to the head of whatever list he was in. */
798 uData->next->prev = uData->prev;
800 uData->prev->next = uData->next;
806 (**head).prev = uData;
813 /* Returns non-zero if user has at least the minimum access.
814 * exempt_owner is set when handling !set, so the owner can set things
817 int check_user_level(struct chanNode *channel, struct userNode *user, enum levelOption opt, int allow_override, int exempt_owner)
819 struct userData *uData;
820 struct chanData *cData = channel->channel_info;
821 unsigned short minimum = cData->lvlOpts[opt];
824 uData = _GetChannelUser(cData, user->handle_info, allow_override, 0);
827 if(minimum <= uData->access)
829 if((minimum > UL_OWNER) && (uData->access == UL_OWNER) && exempt_owner)
834 /* Scan for other users authenticated to the same handle
835 still in the channel. If so, keep them listed as present.
837 user is optional, if not null, it skips checking that userNode
838 (for the handle_part function) */
840 scan_user_presence(struct userData *uData, struct userNode *user)
844 if(IsSuspended(uData->channel)
845 || IsUserSuspended(uData)
846 || !(mn = find_handle_in_channel(uData->channel->channel, uData->handle, user)))
858 chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, const char *text, UNUSED_ARG(struct userNode *bot), UNUSED_ARG(unsigned int is_notice))
860 unsigned int eflags, argc;
862 static char *bad_ctcp_reason = "CTCPs to this channel are forbidden.";
864 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
865 if(!channel->channel_info
866 || IsSuspended(channel->channel_info)
868 || !ircncasecmp(text, "ACTION ", 7))
870 /* Figure out the minimum level needed to CTCP the channel */
871 if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
873 /* We need to enforce against them; do so. */
875 argv[0] = (char*)text;
876 argv[1] = user->nick;
878 if(GetUserMode(channel, user))
879 eflags |= ACTION_KICK;
880 switch(channel->channel_info->chOpts[chCTCPReaction]) {
881 default: case 'k': /* just do the kick */ break;
883 eflags |= ACTION_BAN;
886 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
887 argv[argc++] = (char*)chanserv_conf.ctcp_short_ban_duration;
890 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
891 argv[argc++] = (char*)chanserv_conf.ctcp_long_ban_duration;
894 argv[argc++] = bad_ctcp_reason;
895 eject_user(chanserv, channel, argc, argv, NULL, eflags);
899 chanserv_create_note_type(const char *name)
901 struct note_type *ntype = calloc(1, sizeof(*ntype) + strlen(name));
902 strcpy(ntype->name, name);
904 dict_insert(note_types, ntype->name, ntype);
909 free_vote_options(void *data)
911 struct vote_option *vOpt = data;
913 free(vOpt->option_str);
918 chanserv_deref_note_type(void *data)
920 struct note_type *ntype = data;
922 if(--ntype->refs > 0)
928 chanserv_flush_note_type(struct note_type *ntype)
930 struct chanData *cData;
931 for(cData = channelList; cData; cData = cData->next)
932 dict_remove(cData->notes, ntype->name);
936 chanserv_truncate_notes(struct note_type *ntype)
938 struct chanData *cData;
940 unsigned int size = sizeof(*note) + ntype->max_length;
942 for(cData = channelList; cData; cData = cData->next) {
943 note = dict_find(cData->notes, ntype->name, NULL);
946 if(strlen(note->note) <= ntype->max_length)
948 dict_remove2(cData->notes, ntype->name, 1);
949 note = realloc(note, size);
950 note->note[ntype->max_length] = 0;
951 dict_insert(cData->notes, ntype->name, note);
955 static int note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user);
958 chanserv_add_channel_note(struct chanData *channel, struct note_type *type, const char *setter, const char *text)
961 unsigned int len = strlen(text);
963 if(len > type->max_length) len = type->max_length;
964 note = calloc(1, sizeof(*note) + len);
966 strncpy(note->setter, setter, sizeof(note->setter)-1);
967 memcpy(note->note, text, len);
969 dict_insert(channel->notes, type->name, note);
975 chanserv_free_note(void *data)
977 struct note *note = data;
979 chanserv_deref_note_type(note->type);
980 assert(note->type->refs > 0); /* must use delnote to remove the type */
984 static MODCMD_FUNC(cmd_createnote) {
985 struct note_type *ntype;
986 unsigned int arg = 1, existed = 0, max_length;
988 if((ntype = dict_find(note_types, argv[1], NULL)))
991 ntype = chanserv_create_note_type(argv[arg]);
992 if(!irccasecmp(argv[++arg], "privileged"))
995 ntype->set_access_type = NOTE_SET_PRIVILEGED;
996 ntype->set_access.min_opserv = strtoul(argv[arg], NULL, 0);
998 else if(!irccasecmp(argv[arg], "channel"))
1000 unsigned short ulvl = user_level_from_name(argv[++arg], UL_OWNER);
1003 reply("CSMSG_INVALID_ACCESS", argv[arg]);
1006 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
1007 ntype->set_access.min_ulevel = ulvl;
1009 else if(!irccasecmp(argv[arg], "setter"))
1011 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
1015 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
1019 if(!irccasecmp(argv[++arg], "privileged"))
1020 ntype->visible_type = NOTE_VIS_PRIVILEGED;
1021 else if(!irccasecmp(argv[arg], "channel_users"))
1022 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
1023 else if(!irccasecmp(argv[arg], "all"))
1024 ntype->visible_type = NOTE_VIS_ALL;
1026 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
1030 if((arg+1) >= argc) {
1031 reply("MSG_MISSING_PARAMS", argv[0]);
1034 max_length = strtoul(argv[++arg], NULL, 0);
1035 if(max_length < 20 || max_length > 450)
1037 reply("CSMSG_BAD_MAX_LENGTH", argv[arg]);
1040 if(existed && (max_length < ntype->max_length))
1042 ntype->max_length = max_length;
1043 chanserv_truncate_notes(ntype);
1045 ntype->max_length = max_length;
1048 reply("CSMSG_NOTE_MODIFIED", ntype->name);
1050 reply("CSMSG_NOTE_CREATED", ntype->name);
1055 dict_remove(note_types, ntype->name);
1059 static MODCMD_FUNC(cmd_removenote) {
1060 struct note_type *ntype;
1063 ntype = dict_find(note_types, argv[1], NULL);
1064 force = (argc > 2) && !irccasecmp(argv[2], "force");
1067 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
1074 reply("CSMSG_NOTE_TYPE_USED", ntype->name);
1077 chanserv_flush_note_type(ntype);
1079 dict_remove(note_types, argv[1]);
1080 reply("CSMSG_NOTE_DELETED", argv[1]);
1085 chanserv_expire_channel(void *data)
1087 struct chanData *channel = data;
1088 char reason[MAXLEN];
1089 sprintf(reason, "channel expired.");
1090 channel->expiry = 0;
1091 spamserv_cs_unregister(NULL, channel->channel, expire, NULL);
1092 unregister_channel(channel, reason);
1095 static MODCMD_FUNC(chan_opt_expire)
1097 struct chanData *cData = channel->channel_info;
1098 unsigned long value = cData->expiry;
1102 if((!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
1104 reply("MSG_SETTING_PRIVILEGED", argv[0]);
1107 unsigned long expiry,duration;
1109 /* The two directions can have different ACLs. */
1110 if(!strcmp(argv[1], "0"))
1112 else if((duration = ParseInterval(argv[1])))
1113 expiry = now + duration;
1116 reply("MSG_INVALID_DURATION", argv[1]);
1120 if (expiry != value)
1124 timeq_del(value, chanserv_expire_channel, cData, 0);
1127 cData->expiry = value;
1130 timeq_add(expiry, chanserv_expire_channel, cData);
1135 if(cData->expiry > now) {
1136 char expirestr[INTERVALLEN];
1137 reply("CSMSG_SET_EXPIRE", intervalString(expirestr, cData->expiry - now, user->handle_info));
1139 reply("CSMSG_SET_EXPIRE_OFF");
1144 mode_lock_violated(const struct mod_chanmode *orig, const struct mod_chanmode *change)
1148 if(orig->modes_set & change->modes_clear)
1150 if(orig->modes_clear & change->modes_set)
1152 if((orig->modes_set & MODE_KEY) && (change->modes_set & MODE_KEY)
1153 && strcmp(orig->new_key, change->new_key))
1155 if((orig->modes_set & MODE_LIMIT) && (change->modes_set & MODE_LIMIT)
1156 && (orig->new_limit != change->new_limit))
1161 static char max_length_text[MAXLEN+1][16];
1163 static struct helpfile_expansion
1164 chanserv_expand_variable(const char *variable)
1166 struct helpfile_expansion exp;
1168 if(!irccasecmp(variable, "notes"))
1171 exp.type = HF_TABLE;
1172 exp.value.table.length = 1;
1173 exp.value.table.width = 3;
1174 exp.value.table.flags = 0;
1175 exp.value.table.contents = calloc(dict_size(note_types)+1, sizeof(char**));
1176 exp.value.table.contents[0] = calloc(exp.value.table.width, sizeof(char*));
1177 exp.value.table.contents[0][0] = "Note Type";
1178 exp.value.table.contents[0][1] = "Visibility";
1179 exp.value.table.contents[0][2] = "Max Length";
1180 for(it=dict_first(note_types); it; it=iter_next(it))
1182 struct note_type *ntype = iter_data(it);
1185 if(!note_type_visible_to_user(NULL, ntype, message_dest)) continue;
1186 row = exp.value.table.length++;
1187 exp.value.table.contents[row] = calloc(exp.value.table.width, sizeof(char*));
1188 exp.value.table.contents[row][0] = ntype->name;
1189 exp.value.table.contents[row][1] = (ntype->visible_type == NOTE_VIS_ALL) ? "all" :
1190 (ntype->visible_type == NOTE_VIS_CHANNEL_USERS) ? "chan users" :
1192 if(!max_length_text[ntype->max_length][0])
1193 snprintf(max_length_text[ntype->max_length], sizeof(max_length_text[ntype->max_length]), "%u", ntype->max_length);
1194 exp.value.table.contents[row][2] = max_length_text[ntype->max_length];
1199 exp.type = HF_STRING;
1200 exp.value.str = NULL;
1204 static struct chanData*
1205 register_channel(struct chanNode *cNode, char *registrar)
1207 struct chanData *channel;
1208 enum levelOption lvlOpt;
1209 enum charOption chOpt;
1211 channel = calloc(1, sizeof(struct chanData));
1213 channel->notes = dict_new();
1214 dict_set_free_data(channel->notes, chanserv_free_note);
1216 channel->registrar = strdup(registrar);
1217 channel->registered = now;
1218 channel->visited = now;
1219 channel->limitAdjusted = now;
1220 channel->ownerTransfer = now;
1221 channel->flags = CHANNEL_DEFAULT_FLAGS;
1222 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
1223 channel->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
1224 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
1225 channel->chOpts[chOpt] = charOptions[chOpt].default_value;
1227 channel->prev = NULL;
1228 channel->next = channelList;
1231 channelList->prev = channel;
1232 channelList = channel;
1233 registered_channels++;
1235 channel->channel = cNode;
1237 cNode->channel_info = channel;
1239 channel->vote = NULL;
1244 static struct userData*
1245 add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned short access_level, unsigned long seen, const char *info)
1247 struct userData *ud;
1249 if(access_level > UL_OWNER)
1252 ud = calloc(1, sizeof(*ud));
1253 ud->channel = channel;
1254 ud->handle = handle;
1256 ud->access = access_level;
1257 ud->info = info ? strdup(info) : NULL;
1260 ud->next = channel->users;
1262 channel->users->prev = ud;
1263 channel->users = ud;
1265 channel->userCount++;
1269 ud->u_next = ud->handle->channels;
1271 ud->u_next->u_prev = ud;
1272 ud->handle->channels = ud;
1278 del_channel_user(struct userData *user, int do_gc)
1280 struct chanData *channel = user->channel;
1282 channel->userCount--;
1286 user->prev->next = user->next;
1288 channel->users = user->next;
1290 user->next->prev = user->prev;
1293 user->u_prev->u_next = user->u_next;
1295 user->handle->channels = user->u_next;
1297 user->u_next->u_prev = user->u_prev;
1301 if(do_gc && !channel->users && !IsProtected(channel)) {
1302 spamserv_cs_unregister(NULL, channel->channel, lost_all_users, NULL);
1303 unregister_channel(channel, "lost all users.");
1307 static void expire_ban(void *data);
1310 add_channel_ban(struct chanData *channel, const char *mask, char *owner, unsigned long set, unsigned long triggered, unsigned long expires, char *reason)
1313 unsigned int ii, l1, l2;
1318 bd = malloc(sizeof(struct banData));
1320 bd->channel = channel;
1322 bd->triggered = triggered;
1323 bd->expires = expires;
1325 for(ii = 0; ii < chanserv_conf.old_ban_names->used; ++ii)
1327 extern const char *hidden_host_suffix;
1328 const char *old_name = chanserv_conf.old_ban_names->list[ii];
1332 l2 = strlen(old_name);
1335 if(irccasecmp(mask + l1 - l2, old_name))
1337 new_mask = alloca(MAXLEN);
1338 sprintf(new_mask, "%.*s%s", (int)(l1-l2), mask, hidden_host_suffix);
1341 safestrncpy(bd->mask, mask, sizeof(bd->mask));
1343 safestrncpy(bd->owner, owner, sizeof(bd->owner));
1344 bd->reason = strdup(reason);
1347 timeq_add(expires, expire_ban, bd);
1350 bd->next = channel->bans;
1352 channel->bans->prev = bd;
1354 channel->banCount++;
1361 del_channel_ban(struct banData *ban)
1363 ban->channel->banCount--;
1367 ban->prev->next = ban->next;
1369 ban->channel->bans = ban->next;
1372 ban->next->prev = ban->prev;
1375 timeq_del(0, expire_ban, ban, TIMEQ_IGNORE_WHEN);
1384 expire_ban(void *data)
1386 struct banData *bd = data;
1387 if(!IsSuspended(bd->channel))
1389 struct banList bans;
1390 struct mod_chanmode change;
1392 bans = bd->channel->channel->banlist;
1393 mod_chanmode_init(&change);
1394 for(ii=0; ii<bans.used; ii++)
1396 if(!strcmp(bans.list[ii]->ban, bd->mask))
1399 change.args[0].mode = MODE_REMOVE|MODE_BAN;
1400 change.args[0].u.hostmask = bd->mask;
1401 mod_chanmode_announce(chanserv, bd->channel->channel, &change);
1407 del_channel_ban(bd);
1410 static void chanserv_expire_suspension(void *data);
1413 unregister_channel(struct chanData *channel, const char *reason)
1415 struct mod_chanmode change;
1416 char msgbuf[MAXLEN];
1418 /* After channel unregistration, the following must be cleaned
1420 - Channel information.
1423 - Channel suspension data.
1424 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1430 timeq_del(0, NULL, channel, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
1434 mod_chanmode_init(&change);
1435 change.modes_clear |= MODE_REGISTERED;
1436 mod_chanmode_announce(chanserv, channel->channel, &change);
1439 while(channel->users)
1440 del_channel_user(channel->users, 0);
1442 while(channel->bans)
1443 del_channel_ban(channel->bans);
1445 free(channel->topic);
1446 free(channel->registrar);
1447 free(channel->greeting);
1448 free(channel->user_greeting);
1449 free(channel->topic_mask);
1452 channel->prev->next = channel->next;
1454 channelList = channel->next;
1457 channel->next->prev = channel->prev;
1459 if(channel->suspended)
1461 struct chanNode *cNode = channel->channel;
1462 struct suspended *suspended, *next_suspended;
1464 for(suspended = channel->suspended; suspended; suspended = next_suspended)
1466 next_suspended = suspended->previous;
1467 free(suspended->suspender);
1468 free(suspended->reason);
1469 if(suspended->expires)
1470 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
1475 cNode->channel_info = NULL;
1478 timeq_del(channel->expiry, chanserv_expire_channel, channel, 0);
1479 channel->channel->channel_info = NULL;
1481 dict_delete(channel->notes);
1482 sprintf(msgbuf, "%s %s", channel->channel->name, reason);
1483 if(!IsSuspended(channel))
1484 DelChannelUser(chanserv, channel->channel, msgbuf, 0);
1485 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, msgbuf);
1486 UnlockChannel(channel->channel);
1488 registered_channels--;
1492 expire_channels(UNUSED_ARG(void *data))
1494 struct chanData *channel, *next;
1495 struct userData *user;
1496 char delay[INTERVALLEN], reason[INTERVALLEN + 64];
1498 intervalString(delay, chanserv_conf.channel_expire_delay, NULL);
1499 sprintf(reason, "Channel registration automatically expired after %s of disuse.", delay);
1501 for(channel = channelList; channel; channel = next)
1503 next = channel->next;
1505 /* See if the channel can be expired. */
1506 if(((now - channel->visited) <= chanserv_conf.channel_expire_delay)
1507 || IsProtected(channel))
1510 /* Make sure there are no high-ranking users still in the channel. */
1511 for(user=channel->users; user; user=user->next)
1512 if(user->present && (user->access >= UL_PRESENT) && !HANDLE_FLAGGED(user->handle, BOT))
1517 /* Unregister the channel */
1518 log_module(CS_LOG, LOG_INFO, "(%s) Channel registration expired.", channel->channel->name);
1519 spamserv_cs_unregister(NULL, channel->channel, expire, NULL);
1520 unregister_channel(channel, "registration expired.");
1523 if(chanserv_conf.channel_expire_frequency)
1524 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
1528 expire_dnrs(UNUSED_ARG(void *data))
1530 dict_iterator_t it, next;
1531 struct do_not_register *dnr;
1533 for(it = dict_first(handle_dnrs); it; it = next)
1535 dnr = iter_data(it);
1536 next = iter_next(it);
1537 if(dnr->expires && dnr->expires <= now)
1538 dict_remove(handle_dnrs, dnr->chan_name + 1);
1540 for(it = dict_first(plain_dnrs); it; it = next)
1542 dnr = iter_data(it);
1543 next = iter_next(it);
1544 if(dnr->expires && dnr->expires <= now)
1545 dict_remove(plain_dnrs, dnr->chan_name + 1);
1547 for(it = dict_first(mask_dnrs); it; it = next)
1549 dnr = iter_data(it);
1550 next = iter_next(it);
1551 if(dnr->expires && dnr->expires <= now)
1552 dict_remove(mask_dnrs, dnr->chan_name + 1);
1555 if(chanserv_conf.dnr_expire_frequency)
1556 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
1560 protect_user(const struct userNode *victim, const struct userNode *aggressor, struct chanData *channel)
1562 char protect = channel->chOpts[chProtect];
1563 struct userData *cs_victim, *cs_aggressor;
1565 /* Don't protect if no one is to be protected, someone is attacking
1566 himself, or if the aggressor is an IRC Operator. */
1567 if(protect == 'n' || victim == aggressor || IsOper(aggressor))
1570 /* Don't protect if the victim isn't authenticated (because they
1571 can't be a channel user), unless we are to protect non-users
1573 cs_victim = GetChannelAccess(channel, victim->handle_info);
1574 if(protect != 'a' && !cs_victim)
1577 /* Protect if the aggressor isn't a user because at this point,
1578 the aggressor can only be less than or equal to the victim. */
1579 cs_aggressor = GetChannelAccess(channel, aggressor->handle_info);
1583 /* If the aggressor was a user, then the victim can't be helped. */
1590 if(cs_victim->access > cs_aggressor->access)
1595 if(cs_victim->access >= cs_aggressor->access)
1604 validate_op(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1606 struct chanData *cData = channel->channel_info;
1607 struct userData *cs_victim;
1609 if((!(cs_victim = GetChannelUser(cData, victim->handle_info))
1610 || (cs_victim->access < cData->lvlOpts[lvlGiveOps]))
1611 && !check_user_level(channel, user, lvlEnfOps, 0, 0))
1613 send_message(user, chanserv, "CSMSG_OPBY_LOCKED");
1621 validate_deop(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1623 if(IsService(victim))
1625 send_message(user, chanserv, "MSG_SERVICE_IMMUNE", victim->nick);
1629 if(protect_user(victim, user, channel->channel_info))
1631 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
1638 static struct do_not_register *
1639 chanserv_add_dnr(const char *chan_name, const char *setter, unsigned long expires, const char *reason)
1641 struct do_not_register *dnr = calloc(1, sizeof(*dnr)+strlen(reason));
1642 safestrncpy(dnr->chan_name, chan_name, sizeof(dnr->chan_name));
1643 safestrncpy(dnr->setter, setter, sizeof(dnr->setter));
1644 strcpy(dnr->reason, reason);
1646 dnr->expires = expires;
1647 if(dnr->chan_name[0] == '*')
1648 dict_insert(handle_dnrs, dnr->chan_name+1, dnr);
1649 else if(strpbrk(dnr->chan_name, "*?"))
1650 dict_insert(mask_dnrs, dnr->chan_name, dnr);
1652 dict_insert(plain_dnrs, dnr->chan_name, dnr);
1656 static struct dnrList
1657 chanserv_find_dnrs(const char *chan_name, const char *handle, unsigned int max)
1659 struct dnrList list;
1660 dict_iterator_t it, next;
1661 struct do_not_register *dnr;
1663 dnrList_init(&list);
1665 if(handle && (dnr = dict_find(handle_dnrs, handle, NULL)))
1667 if(dnr->expires && dnr->expires <= now)
1668 dict_remove(handle_dnrs, handle);
1669 else if(list.used < max)
1670 dnrList_append(&list, dnr);
1673 if(chan_name && (dnr = dict_find(plain_dnrs, chan_name, NULL)))
1675 if(dnr->expires && dnr->expires <= now)
1676 dict_remove(plain_dnrs, chan_name);
1677 else if(list.used < max)
1678 dnrList_append(&list, dnr);
1683 for(it = dict_first(mask_dnrs); it && list.used < max; it = next)
1685 next = iter_next(it);
1686 if(!match_ircglob(chan_name, iter_key(it)))
1688 dnr = iter_data(it);
1689 if(dnr->expires && dnr->expires <= now)
1690 dict_remove(mask_dnrs, iter_key(it));
1692 dnrList_append(&list, dnr);
1699 static int dnr_print_func(struct do_not_register *dnr, void *extra)
1701 struct userNode *user;
1702 char buf1[INTERVALLEN];
1703 char buf2[INTERVALLEN];
1710 strftime(buf1, sizeof(buf1), "%d %b %Y", localtime(&feh));
1715 strftime(buf2, sizeof(buf2), "%d %b %Y", localtime(&feh));
1716 send_message(user, chanserv, "CSMSG_DNR_INFO_SET_EXPIRES", dnr->chan_name, buf1, dnr->setter, buf2, dnr->reason);
1720 send_message(user, chanserv, "CSMSG_DNR_INFO_SET", dnr->chan_name, buf1, dnr->setter, dnr->reason);
1723 send_message(user, chanserv, "CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1728 chanserv_show_dnrs(struct userNode *user, struct svccmd *cmd, const char *chan_name, const char *handle)
1730 struct dnrList list;
1733 list = chanserv_find_dnrs(chan_name, handle, UINT_MAX);
1734 for(ii = 0; (ii < list.used) && (ii < 10); ++ii)
1735 dnr_print_func(list.list[ii], user);
1737 reply("CSMSG_MORE_DNRS", list.used - ii);
1742 struct do_not_register *
1743 chanserv_is_dnr(const char *chan_name, struct handle_info *handle)
1745 struct dnrList list;
1746 struct do_not_register *dnr;
1748 list = chanserv_find_dnrs(chan_name, handle ? handle->handle : NULL, 1);
1749 dnr = list.used ? list.list[0] : NULL;
1754 static unsigned int send_dnrs(struct userNode *user, dict_t dict)
1756 struct do_not_register *dnr;
1757 dict_iterator_t it, next;
1758 unsigned int matches = 0;
1760 for(it = dict_first(dict); it; it = next)
1762 dnr = iter_data(it);
1763 next = iter_next(it);
1764 if(dnr->expires && dnr->expires <= now)
1766 dict_remove(dict, iter_key(it));
1769 dnr_print_func(dnr, user);
1776 static CHANSERV_FUNC(cmd_noregister)
1780 unsigned long expiry, duration;
1781 unsigned int matches;
1785 reply("CSMSG_DNR_SEARCH_RESULTS");
1786 matches = send_dnrs(user, handle_dnrs);
1787 matches += send_dnrs(user, plain_dnrs);
1788 matches += send_dnrs(user, mask_dnrs);
1790 reply("MSG_MATCH_COUNT", matches);
1792 reply("MSG_NO_MATCHES");
1798 if(!IsChannelName(target) && (*target != '*'))
1800 reply("CSMSG_NOT_DNR", target);
1808 reply("MSG_INVALID_DURATION", argv[2]);
1812 if(!strcmp(argv[2], "0"))
1814 else if((duration = ParseInterval(argv[2])))
1815 expiry = now + duration;
1818 reply("MSG_INVALID_DURATION", argv[2]);
1822 reason = unsplit_string(argv + 3, argc - 3, NULL);
1823 if((*target == '*') && !get_handle_info(target + 1))
1825 reply("MSG_HANDLE_UNKNOWN", target + 1);
1828 chanserv_add_dnr(target, user->handle_info->handle, expiry, reason);
1829 reply("CSMSG_NOREGISTER_CHANNEL", target);
1833 reply("CSMSG_DNR_SEARCH_RESULTS");
1835 matches = chanserv_show_dnrs(user, cmd, NULL, target + 1);
1837 matches = chanserv_show_dnrs(user, cmd, target, NULL);
1839 reply("MSG_NO_MATCHES");
1843 static CHANSERV_FUNC(cmd_allowregister)
1845 const char *chan_name = argv[1];
1847 if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
1848 || dict_remove(plain_dnrs, chan_name)
1849 || dict_remove(mask_dnrs, chan_name))
1851 reply("CSMSG_DNR_REMOVED", chan_name);
1854 reply("CSMSG_NO_SUCH_DNR", chan_name);
1859 struct userNode *source;
1863 unsigned long min_set, max_set;
1864 unsigned long min_expires, max_expires;
1869 dnr_search_matches(const struct do_not_register *dnr, const struct dnr_search *search)
1871 return !((dnr->set < search->min_set)
1872 || (dnr->set > search->max_set)
1873 || (dnr->expires < search->min_expires)
1874 || (search->max_expires
1875 && ((dnr->expires == 0)
1876 || (dnr->expires > search->max_expires)))
1877 || (search->chan_mask
1878 && !match_ircglob(dnr->chan_name, search->chan_mask))
1879 || (search->setter_mask
1880 && !match_ircglob(dnr->setter, search->setter_mask))
1881 || (search->reason_mask
1882 && !match_ircglob(dnr->reason, search->reason_mask)));
1885 static struct dnr_search *
1886 dnr_search_create(struct userNode *user, struct svccmd *cmd, unsigned int argc, char *argv[])
1888 struct dnr_search *discrim;
1891 discrim = calloc(1, sizeof(*discrim));
1892 discrim->source = user;
1893 discrim->chan_mask = NULL;
1894 discrim->setter_mask = NULL;
1895 discrim->reason_mask = NULL;
1896 discrim->max_set = INT_MAX;
1897 discrim->limit = 50;
1899 for(ii=0; ii<argc; ++ii)
1903 reply("MSG_MISSING_PARAMS", argv[ii]);
1906 else if(0 == irccasecmp(argv[ii], "channel"))
1908 discrim->chan_mask = argv[++ii];
1910 else if(0 == irccasecmp(argv[ii], "setter"))
1912 discrim->setter_mask = argv[++ii];
1914 else if(0 == irccasecmp(argv[ii], "reason"))
1916 discrim->reason_mask = argv[++ii];
1918 else if(0 == irccasecmp(argv[ii], "limit"))
1920 discrim->limit = strtoul(argv[++ii], NULL, 0);
1922 else if(0 == irccasecmp(argv[ii], "set"))
1924 const char *cmp = argv[++ii];
1927 discrim->min_set = now - ParseInterval(cmp + 2);
1929 discrim->min_set = now - (ParseInterval(cmp + 1) - 1);
1930 } else if(cmp[0] == '=') {
1931 discrim->min_set = discrim->max_set = now - ParseInterval(cmp + 1);
1932 } else if(cmp[0] == '>') {
1934 discrim->max_set = now - ParseInterval(cmp + 2);
1936 discrim->max_set = now - (ParseInterval(cmp + 1) - 1);
1938 discrim->max_set = now - (ParseInterval(cmp) - 1);
1941 else if(0 == irccasecmp(argv[ii], "expires"))
1943 const char *cmp = argv[++ii];
1946 discrim->max_expires = now + ParseInterval(cmp + 2);
1948 discrim->max_expires = now + (ParseInterval(cmp + 1) - 1);
1949 } else if(cmp[0] == '=') {
1950 discrim->min_expires = discrim->max_expires = now + ParseInterval(cmp + 1);
1951 } else if(cmp[0] == '>') {
1953 discrim->min_expires = now + ParseInterval(cmp + 2);
1955 discrim->min_expires = now + (ParseInterval(cmp + 1) - 1);
1957 discrim->min_expires = now + (ParseInterval(cmp) - 1);
1962 reply("MSG_INVALID_CRITERIA", argv[ii]);
1973 typedef int (*dnr_search_func)(struct do_not_register *match, void *extra);
1976 dnr_search(struct dnr_search *discrim, dnr_search_func dsf, void *data)
1978 struct do_not_register *dnr;
1979 dict_iterator_t next;
1984 /* Initialize local variables. */
1987 if(discrim->chan_mask)
1989 int shift = (discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*') ? 2 : 0;
1990 if('\0' == discrim->chan_mask[shift + strcspn(discrim->chan_mask+shift, "*?")])
1994 if(target_fixed && discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*')
1996 /* Check against account-based DNRs. */
1997 dnr = dict_find(handle_dnrs, discrim->chan_mask + 2, NULL);
1998 if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
2001 else if(target_fixed)
2003 /* Check against channel-based DNRs. */
2004 dnr = dict_find(plain_dnrs, discrim->chan_mask, NULL);
2005 if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
2010 /* Exhaustively search account DNRs. */
2011 for(it = dict_first(handle_dnrs); it; it = next)
2013 next = iter_next(it);
2014 dnr = iter_data(it);
2015 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
2019 /* Do the same for channel DNRs. */
2020 for(it = dict_first(plain_dnrs); it; it = next)
2022 next = iter_next(it);
2023 dnr = iter_data(it);
2024 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
2028 /* Do the same for wildcarded channel DNRs. */
2029 for(it = dict_first(mask_dnrs); it; it = next)
2031 next = iter_next(it);
2032 dnr = iter_data(it);
2033 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
2041 dnr_remove_func(struct do_not_register *match, void *extra)
2043 struct userNode *user;
2046 chan_name = alloca(strlen(match->chan_name) + 1);
2047 strcpy(chan_name, match->chan_name);
2049 if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
2050 || dict_remove(plain_dnrs, chan_name)
2051 || dict_remove(mask_dnrs, chan_name))
2053 send_message(user, chanserv, "CSMSG_DNR_REMOVED", chan_name);
2059 dnr_count_func(struct do_not_register *match, void *extra)
2061 return 0; (void)match; (void)extra;
2064 static MODCMD_FUNC(cmd_dnrsearch)
2066 struct dnr_search *discrim;
2067 dnr_search_func action;
2068 struct svccmd *subcmd;
2069 unsigned int matches;
2072 sprintf(buf, "dnrsearch %s", argv[1]);
2073 subcmd = dict_find(cmd->parent->commands, buf, NULL);
2076 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
2079 if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
2081 if(!irccasecmp(argv[1], "print"))
2082 action = dnr_print_func;
2083 else if(!irccasecmp(argv[1], "remove"))
2084 action = dnr_remove_func;
2085 else if(!irccasecmp(argv[1], "count"))
2086 action = dnr_count_func;
2089 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
2093 discrim = dnr_search_create(user, cmd, argc-2, argv+2);
2097 if(action == dnr_print_func)
2098 reply("CSMSG_DNR_SEARCH_RESULTS");
2099 matches = dnr_search(discrim, action, user);
2101 reply("MSG_MATCH_COUNT", matches);
2103 reply("MSG_NO_MATCHES");
2109 chanserv_get_owned_count(struct handle_info *hi)
2111 struct userData *cList;
2114 for(owned=0, cList=hi->channels; cList; cList=cList->u_next)
2115 if(cList->access == UL_OWNER)
2120 static CHANSERV_FUNC(cmd_register)
2122 struct handle_info *handle;
2123 struct chanData *cData;
2124 struct modeNode *mn;
2125 char reason[MAXLEN];
2127 unsigned int new_channel, force=0;
2128 struct do_not_register *dnr;
2132 if(channel->channel_info)
2134 reply("CSMSG_ALREADY_REGGED", channel->name);
2138 if(channel->bad_channel)
2140 reply("CSMSG_ILLEGAL_CHANNEL", channel->name);
2145 && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
2147 reply("CSMSG_MUST_BE_OPPED", channel->name);
2152 chan_name = channel->name;
2156 if((argc < 2) || !IsChannelName(argv[1]))
2158 reply("MSG_NOT_CHANNEL_NAME");
2162 if(opserv_bad_channel(argv[1]))
2164 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2169 chan_name = argv[1];
2172 if(argc >= (new_channel+2))
2174 if(!IsHelping(user))
2176 reply("CSMSG_PROXY_FORBIDDEN");
2180 if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
2182 force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
2183 dnr = chanserv_is_dnr(chan_name, handle);
2187 handle = user->handle_info;
2188 dnr = chanserv_is_dnr(chan_name, handle);
2192 if(!IsHelping(user))
2193 reply("CSMSG_DNR_CHANNEL", chan_name);
2195 chanserv_show_dnrs(user, cmd, chan_name, handle->handle);
2199 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2201 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
2206 channel = AddChannel(argv[1], now, NULL, NULL);
2208 cData = register_channel(channel, user->handle_info->handle);
2209 scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL), NULL);
2210 cData->modes = chanserv_conf.default_modes;
2212 cData->modes.modes_set |= MODE_REGISTERED;
2213 if (IsOffChannel(cData))
2215 mod_chanmode_announce(chanserv, channel, &cData->modes);
2219 struct mod_chanmode *change = mod_chanmode_dup(&cData->modes, 1);
2220 change->args[change->argc].mode = MODE_CHANOP;
2221 change->args[change->argc].u.member = AddChannelUser(chanserv, channel);
2223 mod_chanmode_announce(chanserv, channel, change);
2224 mod_chanmode_free(change);
2227 /* Initialize the channel's max user record. */
2228 cData->max = channel->members.used;
2229 cData->max_time = 0;
2231 if(handle != user->handle_info)
2232 reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
2234 reply("CSMSG_REG_SUCCESS", channel->name);
2236 sprintf(reason, "%s registered to %s by %s.", channel->name, handle->handle, user->handle_info->handle);
2237 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2242 make_confirmation_string(struct userData *uData)
2244 static char strbuf[16];
2249 for(src = uData->handle->handle; *src; )
2250 accum = accum * 31 + toupper(*src++);
2252 for(src = uData->channel->channel->name; *src; )
2253 accum = accum * 31 + toupper(*src++);
2254 sprintf(strbuf, "%08x", accum);
2258 static CHANSERV_FUNC(cmd_unregister)
2261 char reason[MAXLEN];
2262 struct chanData *cData;
2263 struct userData *uData;
2265 cData = channel->channel_info;
2268 reply("CSMSG_NOT_REGISTERED", channel->name);
2272 uData = GetChannelUser(cData, user->handle_info);
2273 if(!uData || (uData->access < UL_OWNER))
2275 reply("CSMSG_NO_ACCESS");
2279 if(IsProtected(cData))
2281 reply("CSMSG_UNREG_NODELETE", channel->name);
2285 if(!IsHelping(user))
2287 const char *confirm_string;
2288 if(IsSuspended(cData))
2290 reply("CSMSG_CHAN_SUSPENDED", channel->name, cData->suspended->reason);
2293 confirm_string = make_confirmation_string(uData);
2294 if((argc < 2) || strcmp(argv[1], confirm_string))
2296 reply("CSMSG_CONFIRM_UNREG", confirm_string);
2301 sprintf(reason, "unregistered by %s.", user->handle_info->handle);
2302 name = strdup(channel->name);
2303 unregister_channel(cData, reason);
2304 spamserv_cs_unregister(user, channel, manually, "unregistered");
2305 reply("CSMSG_UNREG_SUCCESS", name);
2311 ss_cs_join_channel(struct chanNode *channel, int spamserv_join)
2313 extern struct userNode *spamserv;
2314 struct mod_chanmode *change;
2316 if(spamserv && spamserv_join && get_chanInfo(channel->name))
2318 change = mod_chanmode_alloc(2);
2320 change->args[0].mode = MODE_CHANOP;
2321 change->args[0].u.member = AddChannelUser(chanserv, channel);
2322 change->args[1].mode = MODE_CHANOP;
2323 change->args[1].u.member = AddChannelUser(spamserv, channel);
2327 change = mod_chanmode_alloc(1);
2329 change->args[0].mode = MODE_CHANOP;
2330 change->args[0].u.member = AddChannelUser(chanserv, channel);
2333 mod_chanmode_announce(chanserv, channel, change);
2334 mod_chanmode_free(change);
2337 static CHANSERV_FUNC(cmd_move)
2339 struct mod_chanmode change;
2340 struct chanNode *target;
2341 struct modeNode *mn;
2342 struct userData *uData;
2343 char reason[MAXLEN];
2344 struct do_not_register *dnr;
2345 int chanserv_join = 0, spamserv_join;
2349 if(IsProtected(channel->channel_info))
2351 reply("CSMSG_MOVE_NODELETE", channel->name);
2355 if(!IsChannelName(argv[1]))
2357 reply("MSG_NOT_CHANNEL_NAME");
2361 if(opserv_bad_channel(argv[1]))
2363 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2367 if(!IsHelping(user) || (argc < 3) || irccasecmp(argv[2], "force"))
2369 for(uData = channel->channel_info->users; uData; uData = uData->next)
2371 if((uData->access == UL_OWNER) && (dnr = chanserv_is_dnr(argv[1], uData->handle)))
2373 if(!IsHelping(user))
2374 reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]);
2376 chanserv_show_dnrs(user, cmd, argv[1], uData->handle->handle);
2382 mod_chanmode_init(&change);
2383 if(!(target = GetChannel(argv[1])))
2385 target = AddChannel(argv[1], now, NULL, NULL);
2386 if(!IsSuspended(channel->channel_info))
2389 else if(target->channel_info)
2391 reply("CSMSG_ALREADY_REGGED", target->name);
2394 else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
2395 && !IsHelping(user))
2397 reply("CSMSG_MUST_BE_OPPED", target->name);
2400 else if(!IsSuspended(channel->channel_info))
2405 /* Clear MODE_REGISTERED from old channel, add it to new. */
2407 change.modes_clear = MODE_REGISTERED;
2408 mod_chanmode_announce(chanserv, channel, &change);
2409 change.modes_clear = 0;
2410 change.modes_set = MODE_REGISTERED;
2411 mod_chanmode_announce(chanserv, target, &change);
2414 /* Move the channel_info to the target channel; it
2415 shouldn't be necessary to clear timeq callbacks
2416 for the old channel. */
2417 target->channel_info = channel->channel_info;
2418 target->channel_info->channel = target;
2419 channel->channel_info = NULL;
2421 /* Check whether users are present in the new channel. */
2422 for(uData = target->channel_info->users; uData; uData = uData->next)
2423 scan_user_presence(uData, NULL);
2425 spamserv_join = spamserv_cs_move_merge(user, channel, target, 1);
2428 ss_cs_join_channel(target, spamserv_join);
2430 sprintf(reason, "%s moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
2431 if(!IsSuspended(target->channel_info))
2433 char reason2[MAXLEN];
2434 sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
2435 DelChannelUser(chanserv, channel, reason2, 0);
2437 UnlockChannel(channel);
2438 LockChannel(target);
2439 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2440 reply("CSMSG_MOVE_SUCCESS", target->name);
2445 merge_users(struct chanData *source, struct chanData *target)
2447 struct userData *suData, *tuData, *next;
2453 /* Insert the source's users into the scratch area. */
2454 for(suData = source->users; suData; suData = suData->next)
2455 dict_insert(merge, suData->handle->handle, suData);
2457 /* Iterate through the target's users, looking for
2458 users common to both channels. The lower access is
2459 removed from either the scratch area or target user
2461 for(tuData = target->users; tuData; tuData = next)
2463 struct userData *choice;
2465 next = tuData->next;
2467 /* If a source user exists with the same handle as a target
2468 channel's user, resolve the conflict by removing one. */
2469 suData = dict_find(merge, tuData->handle->handle, NULL);
2473 /* Pick the data we want to keep. */
2474 /* If the access is the same, use the later seen time. */
2475 if(suData->access == tuData->access)
2476 choice = (suData->seen > tuData->seen) ? suData : tuData;
2477 else /* Otherwise, keep the higher access level. */
2478 choice = (suData->access > tuData->access) ? suData : tuData;
2479 /* Use the later seen time. */
2480 if(suData->seen < tuData->seen)
2481 suData->seen = tuData->seen;
2483 tuData->seen = suData->seen;
2485 /* Remove the user that wasn't picked. */
2486 if(choice == tuData)
2488 dict_remove(merge, suData->handle->handle);
2489 del_channel_user(suData, 0);
2492 del_channel_user(tuData, 0);
2495 /* Move the remaining users to the target channel. */
2496 for(it = dict_first(merge); it; it = iter_next(it))
2498 suData = iter_data(it);
2500 /* Insert the user into the target channel's linked list. */
2501 suData->prev = NULL;
2502 suData->next = target->users;
2503 suData->channel = target;
2506 target->users->prev = suData;
2507 target->users = suData;
2509 /* Update the user counts for the target channel; the
2510 source counts are left alone. */
2511 target->userCount++;
2513 /* Check whether the user is in the target channel. */
2514 scan_user_presence(suData, NULL);
2517 /* Possible to assert (source->users == NULL) here. */
2518 source->users = NULL;
2523 merge_bans(struct chanData *source, struct chanData *target)
2525 struct banData *sbData, *tbData, *sNext, *tNext, *tFront;
2527 /* Hold on to the original head of the target ban list
2528 to avoid comparing source bans with source bans. */
2529 tFront = target->bans;
2531 /* Perform a totally expensive O(n*m) merge, ick. */
2532 for(sbData = source->bans; sbData; sbData = sNext)
2534 /* Flag to track whether the ban's been moved
2535 to the destination yet. */
2538 /* Possible to assert (sbData->prev == NULL) here. */
2539 sNext = sbData->next;
2541 for(tbData = tFront; tbData; tbData = tNext)
2543 tNext = tbData->next;
2545 /* Perform two comparisons between each source
2546 and target ban, conflicts are resolved by
2547 keeping the broader ban and copying the later
2548 expiration and triggered time. */
2549 if(match_ircglobs(tbData->mask, sbData->mask))
2551 /* There is a broader ban in the target channel that
2552 overrides one in the source channel; remove the
2553 source ban and break. */
2554 if(sbData->expires > tbData->expires)
2555 tbData->expires = sbData->expires;
2556 if(sbData->triggered > tbData->triggered)
2557 tbData->triggered = sbData->triggered;
2558 del_channel_ban(sbData);
2561 else if(match_ircglobs(sbData->mask, tbData->mask))
2563 /* There is a broader ban in the source channel that
2564 overrides one in the target channel; remove the
2565 target ban, fall through and move the source over. */
2566 if(tbData->expires > sbData->expires)
2567 sbData->expires = tbData->expires;
2568 if(tbData->triggered > sbData->triggered)
2569 sbData->triggered = tbData->triggered;
2570 if(tbData == tFront)
2572 del_channel_ban(tbData);
2575 /* Source bans can override multiple target bans, so
2576 we allow a source to run through this loop multiple
2577 times, but we can only move it once. */
2582 /* Remove the source ban from the source ban list. */
2584 sbData->next->prev = sbData->prev;
2586 /* Modify the source ban's associated channel. */
2587 sbData->channel = target;
2589 /* Insert the ban into the target channel's linked list. */
2590 sbData->prev = NULL;
2591 sbData->next = target->bans;
2594 target->bans->prev = sbData;
2595 target->bans = sbData;
2597 /* Update the user counts for the target channel. */
2602 /* Possible to assert (source->bans == NULL) here. */
2603 source->bans = NULL;
2607 merge_data(struct chanData *source, struct chanData *target)
2609 /* Use more recent visited and owner-transfer time; use older
2610 * registered time. Bitwise or may_opchan. Use higher max.
2611 * Do not touch last_refresh, ban count or user counts.
2613 if(source->visited > target->visited)
2614 target->visited = source->visited;
2615 if(source->registered < target->registered)
2616 target->registered = source->registered;
2617 if(source->ownerTransfer > target->ownerTransfer)
2618 target->ownerTransfer = source->ownerTransfer;
2619 if(source->may_opchan)
2620 target->may_opchan = 1;
2621 if(source->max > target->max) {
2622 target->max = source->max;
2623 target->max_time = source->max_time;
2628 merge_channel(struct chanData *source, struct chanData *target)
2630 merge_users(source, target);
2631 merge_bans(source, target);
2632 merge_data(source, target);
2635 static CHANSERV_FUNC(cmd_merge)
2637 struct userData *target_user;
2638 struct chanNode *target;
2639 char reason[MAXLEN];
2643 /* Make sure the target channel exists and is registered to the user
2644 performing the command. */
2645 if(!(target = GetChannel(argv[1])))
2647 reply("MSG_INVALID_CHANNEL");
2651 if(!target->channel_info)
2653 reply("CSMSG_NOT_REGISTERED", target->name);
2657 if(IsProtected(channel->channel_info))
2659 reply("CSMSG_MERGE_NODELETE");
2663 if(IsSuspended(target->channel_info))
2665 reply("CSMSG_MERGE_SUSPENDED");
2669 if(channel == target)
2671 reply("CSMSG_MERGE_SELF");
2675 target_user = GetChannelUser(target->channel_info, user->handle_info);
2676 if(!target_user || (target_user->access < UL_OWNER))
2678 reply("CSMSG_MERGE_NOT_OWNER");
2682 /* Merge the channel structures and associated data. */
2683 merge_channel(channel->channel_info, target->channel_info);
2684 spamserv_cs_move_merge(user, channel, target, 0);
2685 sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
2686 unregister_channel(channel->channel_info, reason);
2687 reply("CSMSG_MERGE_SUCCESS", target->name);
2691 static CHANSERV_FUNC(cmd_opchan)
2693 struct mod_chanmode change;
2694 if(!IsHelping(user) && !channel->channel_info->may_opchan)
2696 reply("CSMSG_ALREADY_OPCHANNED", channel->name);
2699 channel->channel_info->may_opchan = 0;
2700 mod_chanmode_init(&change);
2702 change.args[0].mode = MODE_CHANOP;
2703 change.args[0].u.member = GetUserMode(channel, chanserv);
2704 if(!change.args[0].u.member)
2706 reply("CSMSG_OUT_OF_CHANNEL", channel->name);
2709 mod_chanmode_announce(chanserv, channel, &change);
2710 reply("CSMSG_OPCHAN_DONE", channel->name);
2714 static CHANSERV_FUNC(cmd_adduser)
2716 struct userData *actee;
2717 struct userData *actor, *real_actor;
2718 struct handle_info *handle;
2719 unsigned short access_level, override = 0;
2723 if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
2725 reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
2729 access_level = user_level_from_name(argv[2], UL_OWNER);
2732 reply("CSMSG_INVALID_ACCESS", argv[2]);
2736 actor = GetChannelUser(channel->channel_info, user->handle_info);
2737 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2739 if(actor->access <= access_level)
2741 reply("CSMSG_NO_BUMP_ACCESS");
2745 /* Trying to add someone with equal/more access? */
2746 if (!real_actor || real_actor->access <= access_level)
2747 override = CMD_LOG_OVERRIDE;
2749 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2752 if((actee = GetTrueChannelAccess(channel->channel_info, handle)))
2754 reply("CSMSG_USER_EXISTS", handle->handle, channel->name, actee->access);
2758 actee = add_channel_user(channel->channel_info, handle, access_level, 0, NULL);
2759 scan_user_presence(actee, NULL);
2760 reply("CSMSG_ADDED_USER", handle->handle, channel->name, access_level);
2761 return 1 | override;
2764 static CHANSERV_FUNC(cmd_clvl)
2766 struct handle_info *handle;
2767 struct userData *victim;
2768 struct userData *actor, *real_actor;
2769 unsigned short new_access, override = 0;
2770 int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
2774 actor = GetChannelUser(channel->channel_info, user->handle_info);
2775 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2777 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2780 if(handle == user->handle_info && !privileged)
2782 reply("CSMSG_NO_SELF_CLVL");
2786 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2788 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2792 if(actor->access <= victim->access && !privileged)
2794 reply("MSG_USER_OUTRANKED", handle->handle);
2798 new_access = user_level_from_name(argv[2], UL_OWNER);
2802 reply("CSMSG_INVALID_ACCESS", argv[2]);
2806 if(new_access >= actor->access && !privileged)
2808 reply("CSMSG_NO_BUMP_ACCESS");
2812 /* Trying to clvl a equal/higher user? */
2813 if(!real_actor || (real_actor->access <= victim->access && handle != user->handle_info))
2814 override = CMD_LOG_OVERRIDE;
2815 /* Trying to clvl someone to equal/higher access? */
2816 if(!real_actor || new_access >= real_actor->access)
2817 override = CMD_LOG_OVERRIDE;
2818 /* Helpers clvling themselves get caught by the "clvl someone to equal/higher access" check.
2819 * If they lower their own access it's not a big problem.
2822 victim->access = new_access;
2823 reply("CSMSG_CHANGED_ACCESS", handle->handle, new_access, channel->name);
2824 return 1 | override;
2827 static CHANSERV_FUNC(cmd_deluser)
2829 struct handle_info *handle;
2830 struct userData *victim;
2831 struct userData *actor, *real_actor;
2832 unsigned short access_level, override = 0;
2837 actor = GetChannelUser(channel->channel_info, user->handle_info);
2838 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2840 if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
2843 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2845 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2851 access_level = user_level_from_name(argv[1], UL_OWNER);
2854 reply("CSMSG_INVALID_ACCESS", argv[1]);
2857 if(access_level != victim->access)
2859 reply("CSMSG_INCORRECT_ACCESS", handle->handle, victim->access, argv[1]);
2865 access_level = victim->access;
2868 if((actor->access <= victim->access) && !IsHelping(user))
2870 reply("MSG_USER_OUTRANKED", victim->handle->handle);
2874 /* If people delete themselves it is an override, but they
2875 * could've used deleteme so we don't log it as an override
2877 if(!real_actor || (real_actor->access <= victim->access && real_actor != victim))
2878 override = CMD_LOG_OVERRIDE;
2880 chan_name = strdup(channel->name);
2881 del_channel_user(victim, 1);
2882 reply("CSMSG_DELETED_USER", handle->handle, access_level, chan_name);
2884 return 1 | override;
2888 cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
2890 struct userData *actor, *real_actor, *uData, *next;
2891 unsigned int override = 0;
2893 actor = GetChannelUser(channel->channel_info, user->handle_info);
2894 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2896 if(min_access > max_access)
2898 reply("CSMSG_BAD_RANGE", min_access, max_access);
2902 if(actor->access <= max_access)
2904 reply("CSMSG_NO_ACCESS");
2908 if(!real_actor || real_actor->access <= max_access)
2909 override = CMD_LOG_OVERRIDE;
2911 for(uData = channel->channel_info->users; uData; uData = next)
2915 if((uData->access >= min_access)
2916 && (uData->access <= max_access)
2917 && match_ircglob(uData->handle->handle, mask))
2918 del_channel_user(uData, 1);
2921 reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
2922 return 1 | override;
2925 static CHANSERV_FUNC(cmd_mdelowner)
2927 return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
2930 static CHANSERV_FUNC(cmd_mdelcoowner)
2932 return cmd_mdel_user(user, channel, UL_COOWNER, UL_COOWNER, argv[1], cmd);
2935 static CHANSERV_FUNC(cmd_mdelmaster)
2937 return cmd_mdel_user(user, channel, UL_MASTER, UL_MASTER, argv[1], cmd);
2940 static CHANSERV_FUNC(cmd_mdelop)
2942 return cmd_mdel_user(user, channel, UL_OP, UL_OP, argv[1], cmd);
2945 static CHANSERV_FUNC(cmd_mdelpeon)
2947 return cmd_mdel_user(user, channel, UL_PEON, UL_PEON, argv[1], cmd);
2951 cmd_trim_bans(struct userNode *user, struct chanNode *channel, unsigned long duration)
2953 struct banData *bData, *next;
2954 char interval[INTERVALLEN];
2956 unsigned long limit;
2959 limit = now - duration;
2960 for(bData = channel->channel_info->bans; bData; bData = next)
2964 if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
2967 del_channel_ban(bData);
2971 intervalString(interval, duration, user->handle_info);
2972 send_message(user, chanserv, "CSMSG_TRIMMED_BANS", count, channel->name, interval);
2977 cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration, int vacation)
2979 struct userData *actor, *uData, *next;
2980 char interval[INTERVALLEN];
2982 unsigned long limit;
2984 actor = GetChannelAccess(channel->channel_info, user->handle_info);
2985 if(min_access > max_access)
2987 send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
2991 if(!actor || actor->access <= max_access)
2993 send_message(user, chanserv, "CSMSG_NO_ACCESS");
2998 limit = now - duration;
2999 for(uData = channel->channel_info->users; uData; uData = next)
3003 if((uData->seen > limit)
3005 || (HANDLE_FLAGGED(uData->handle, FROZEN) && !vacation))
3008 if(((uData->access >= min_access) && (uData->access <= max_access))
3009 || (!max_access && (uData->access < actor->access)))
3011 del_channel_user(uData, 1);
3019 max_access = (actor->access > UL_OWNER) ? UL_OWNER : (actor->access - 1);
3021 send_message(user, chanserv, "CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration, user->handle_info));
3025 static CHANSERV_FUNC(cmd_trim)
3027 unsigned long duration;
3028 unsigned short min_level, max_level;
3033 vacation = argc > 3 && !strcmp(argv[3], "vacation");
3034 duration = ParseInterval(argv[2]);
3037 reply("CSMSG_CANNOT_TRIM");
3041 if(!irccasecmp(argv[1], "bans"))
3043 cmd_trim_bans(user, channel, duration);
3046 else if(!irccasecmp(argv[1], "users"))
3048 cmd_trim_users(user, channel, 0, 0, duration, vacation);
3051 else if(parse_level_range(&min_level, &max_level, argv[1]))
3053 cmd_trim_users(user, channel, min_level, max_level, duration, vacation);
3056 else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
3058 cmd_trim_users(user, channel, min_level, min_level, duration, vacation);
3063 reply("CSMSG_INVALID_TRIM", argv[1]);
3068 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
3069 to the user. cmd_all takes advantage of this. */
3070 static CHANSERV_FUNC(cmd_up)
3072 struct mod_chanmode change;
3073 struct userData *uData;
3076 mod_chanmode_init(&change);
3078 change.args[0].u.member = GetUserMode(channel, user);
3079 if(!change.args[0].u.member)
3082 reply("MSG_CHANNEL_ABSENT", channel->name);
3086 uData = GetChannelAccess(channel->channel_info, user->handle_info);
3090 reply("CSMSG_GODMODE_UP", argv[0]);
3093 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveOps])
3095 change.args[0].mode = MODE_CHANOP;
3096 errmsg = "CSMSG_ALREADY_OPPED";
3098 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveVoice])
3100 change.args[0].mode = MODE_VOICE;
3101 errmsg = "CSMSG_ALREADY_VOICED";
3106 reply("CSMSG_NO_ACCESS");
3109 change.args[0].mode &= ~change.args[0].u.member->modes;
3110 if(!change.args[0].mode)
3113 reply(errmsg, channel->name);
3116 modcmd_chanmode_announce(&change);
3120 static CHANSERV_FUNC(cmd_down)
3122 struct mod_chanmode change;
3124 mod_chanmode_init(&change);
3126 change.args[0].u.member = GetUserMode(channel, user);
3127 if(!change.args[0].u.member)
3130 reply("MSG_CHANNEL_ABSENT", channel->name);
3134 if(!change.args[0].u.member->modes)
3137 reply("CSMSG_ALREADY_DOWN", channel->name);
3141 change.args[0].mode = MODE_REMOVE | change.args[0].u.member->modes;
3142 modcmd_chanmode_announce(&change);
3146 static int cmd_all(struct userNode *user, UNUSED_ARG(struct chanNode *channel), UNUSED_ARG(unsigned int argc), UNUSED_ARG(char *argv[]), struct svccmd *cmd, modcmd_func_t mcmd)
3148 struct userData *cList;
3150 for(cList = user->handle_info->channels; cList; cList = cList->u_next)
3152 if(IsSuspended(cList->channel)
3153 || IsUserSuspended(cList)
3154 || !GetUserMode(cList->channel->channel, user))
3157 mcmd(user, cList->channel->channel, 0, NULL, cmd);
3163 static CHANSERV_FUNC(cmd_upall)
3165 return cmd_all(CSFUNC_ARGS, cmd_up);
3168 static CHANSERV_FUNC(cmd_downall)
3170 return cmd_all(CSFUNC_ARGS, cmd_down);
3173 typedef int validate_func_t(struct userNode *user, struct chanNode *channel, struct userNode *victim);
3174 typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
3177 modify_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, validate_func_t validate, chan_mode_t mode, char *action)
3179 unsigned int ii, valid;
3180 struct userNode *victim;
3181 struct mod_chanmode *change;
3183 change = mod_chanmode_alloc(argc - 1);
3185 for(ii=valid=0; ++ii < argc; )
3187 if(!(victim = GetUserH(argv[ii])))
3189 change->args[valid].mode = mode;
3190 change->args[valid].u.member = GetUserMode(channel, victim);
3191 if(!change->args[valid].u.member)
3193 if(validate && !validate(user, channel, victim))
3198 change->argc = valid;
3199 if(valid < (argc-1))
3200 reply("CSMSG_PROCESS_FAILED");
3203 modcmd_chanmode_announce(change);
3204 reply(action, channel->name);
3206 mod_chanmode_free(change);
3210 static CHANSERV_FUNC(cmd_op)
3212 return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
3215 static CHANSERV_FUNC(cmd_deop)
3217 return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
3220 static CHANSERV_FUNC(cmd_voice)
3222 return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
3225 static CHANSERV_FUNC(cmd_devoice)
3227 return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
3231 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, unsigned int *victimCount, struct modeNode **victims)
3237 for(ii=0; ii<channel->members.used; ii++)
3239 struct modeNode *mn = channel->members.list[ii];
3241 if(IsService(mn->user))
3244 if(!user_matches_glob(mn->user, ban, MATCH_USENICK | MATCH_VISIBLE))
3247 if(protect_user(mn->user, user, channel->channel_info))
3251 victims[(*victimCount)++] = mn;
3257 eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3259 struct userNode *victim;
3260 struct modeNode **victims;
3261 unsigned int offset, n, victimCount, duration = 0;
3262 char *reason = "Bye.", *ban, *name;
3263 char interval[INTERVALLEN];
3265 offset = (action & ACTION_ADD_TIMED_BAN) ? 3 : 2;
3266 REQUIRE_PARAMS(offset);
3269 reason = unsplit_string(argv + offset, argc - offset, NULL);
3270 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
3272 /* Truncate the reason to a length of TOPICLEN, as
3273 the ircd does; however, leave room for an ellipsis
3274 and the kicker's nick. */
3275 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
3279 if((victim = GetUserH(argv[1])))
3281 victims = alloca(sizeof(victims[0]));
3282 victims[0] = GetUserMode(channel, victim);
3283 /* XXX: The comparison with ACTION_KICK is just because all
3284 * other actions can work on users outside the channel, and we
3285 * want to allow those (e.g. unbans) in that case. If we add
3286 * some other ejection action for in-channel users, change
3288 victimCount = victims[0] ? 1 : 0;
3290 if(IsService(victim))
3292 reply("MSG_SERVICE_IMMUNE", victim->nick);
3296 if((action == ACTION_KICK) && !victimCount)
3298 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
3302 if(protect_user(victim, user, channel->channel_info))
3304 reply("CSMSG_USER_PROTECTED", victim->nick);
3308 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
3309 name = victim->nick;
3311 else if(!is_ircmask(argv[1]) && (*argv[1] == '*'))
3313 struct handle_info *hi;
3314 extern const char *titlehost_suffix;
3315 char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
3316 const char *accountname = argv[1] + 1;
3318 if(!(hi = get_handle_info(accountname)))
3320 reply("MSG_HANDLE_UNKNOWN", accountname);
3324 snprintf(banmask, sizeof(banmask), "*!*@%s.*.%s", hi->handle, titlehost_suffix);
3325 victims = alloca(sizeof(victims[0]) * channel->members.used);
3327 if(bad_channel_ban(channel, user, banmask, &victimCount, victims))
3329 reply("CSMSG_MASK_PROTECTED", banmask);
3333 if((action == ACTION_KICK) && (victimCount == 0))
3335 reply("CSMSG_NO_MATCHING_USERS", channel->name, banmask);
3339 name = ban = strdup(banmask);
3343 if(!is_ircmask(argv[1]))
3345 reply("MSG_NICK_UNKNOWN", argv[1]);
3349 victims = alloca(sizeof(victims[0]) * channel->members.used);
3351 if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
3353 reply("CSMSG_MASK_PROTECTED", argv[1]);
3357 if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
3359 reply("CSMSG_LAME_MASK", argv[1]);
3363 if((action == ACTION_KICK) && (victimCount == 0))
3365 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
3369 name = ban = strdup(argv[1]);
3372 /* Truncate the ban in place if necessary; we must ensure
3373 that 'ban' is a valid ban mask before sanitizing it. */
3374 sanitize_ircmask(ban);
3376 if(action & ACTION_ADD_BAN)
3378 struct banData *bData, *next;
3380 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans)
3382 reply("CSMSG_MAXIMUM_BANS", chanserv_conf.max_chan_bans);
3387 if(action & ACTION_ADD_TIMED_BAN)
3389 duration = ParseInterval(argv[2]);
3393 reply("CSMSG_DURATION_TOO_LOW");
3397 else if(duration > (86400 * 365 * 2))
3399 reply("CSMSG_DURATION_TOO_HIGH");
3405 for(bData = channel->channel_info->bans; bData; bData = next)
3407 if(match_ircglobs(bData->mask, ban))
3409 int exact = !irccasecmp(bData->mask, ban);
3411 /* The ban is redundant; there is already a ban
3412 with the same effect in place. */
3416 free(bData->reason);
3417 bData->reason = strdup(reason);
3418 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
3420 reply("CSMSG_REASON_CHANGE", ban);
3424 if(exact && bData->expires)
3428 /* If the ban matches an existing one exactly,
3429 extend the expiration time if the provided
3430 duration is longer. */
3431 if(duration && (now + duration > bData->expires))
3433 bData->expires = now + duration;
3444 /* Delete the expiration timeq entry and
3445 requeue if necessary. */
3446 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
3449 timeq_add(bData->expires, expire_ban, bData);
3453 /* automated kickban */
3456 reply("CSMSG_BAN_EXTENDED", ban, intervalString(interval, duration, user->handle_info));
3458 reply("CSMSG_BAN_ADDED", name, channel->name);
3464 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3471 if(match_ircglobs(ban, bData->mask))
3473 /* The ban we are adding makes previously existing
3474 bans redundant; silently remove them. */
3475 del_channel_ban(bData);
3479 bData = add_channel_ban(channel->channel_info, ban, (user->handle_info ? user->handle_info->handle : user->nick), now, (victimCount ? now : 0), (duration ? now + duration : 0), reason);
3481 name = ban = strdup(bData->mask);
3485 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
3487 extern const char *hidden_host_suffix;
3488 const char *old_name = chanserv_conf.old_ban_names->list[n];
3490 unsigned int l1, l2;
3493 l2 = strlen(old_name);
3496 if(irccasecmp(ban + l1 - l2, old_name))
3498 new_mask = malloc(MAXLEN);
3499 sprintf(new_mask, "%.*s%s", (int)(l1-l2), ban, hidden_host_suffix);
3501 name = ban = new_mask;
3506 if(action & ACTION_BAN)
3508 unsigned int exists;
3509 struct mod_chanmode *change;
3511 if(channel->banlist.used >= MAXBANS)
3514 reply("CSMSG_BANLIST_FULL", channel->name);
3519 exists = ChannelBanExists(channel, ban);
3520 change = mod_chanmode_alloc(victimCount + 1);
3521 for(n = 0; n < victimCount; ++n)
3523 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_VOICE;
3524 change->args[n].u.member = victims[n];
3528 change->args[n].mode = MODE_BAN;
3529 change->args[n++].u.hostmask = ban;
3533 modcmd_chanmode_announce(change);
3535 mod_chanmode_announce(chanserv, channel, change);
3536 mod_chanmode_free(change);
3538 if(exists && (action == ACTION_BAN))
3541 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3547 if(action & ACTION_KICK)
3549 char kick_reason[MAXLEN];
3550 sprintf(kick_reason, "(%s) %s", user->nick, reason);
3552 for(n = 0; n < victimCount; n++)
3553 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
3558 /* No response, since it was automated. */
3560 else if(action & ACTION_ADD_BAN)
3563 reply("CSMSG_TIMED_BAN_ADDED", name, channel->name, intervalString(interval, duration, user->handle_info));
3565 reply("CSMSG_BAN_ADDED", name, channel->name);
3567 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
3568 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
3569 else if(action & ACTION_BAN)
3570 reply("CSMSG_BAN_DONE", name, channel->name);
3571 else if(action & ACTION_KICK && victimCount)
3572 reply("CSMSG_KICK_DONE", name, channel->name);
3578 static CHANSERV_FUNC(cmd_kickban)
3580 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
3583 static CHANSERV_FUNC(cmd_kick)
3585 return eject_user(CSFUNC_ARGS, ACTION_KICK);
3588 static CHANSERV_FUNC(cmd_ban)
3590 return eject_user(CSFUNC_ARGS, ACTION_BAN);
3593 static CHANSERV_FUNC(cmd_addban)
3595 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN);
3598 static CHANSERV_FUNC(cmd_addtimedban)
3600 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN);
3603 static struct mod_chanmode *
3604 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
3606 struct mod_chanmode *change;
3607 unsigned char *match;
3608 unsigned int ii, count;
3610 match = alloca(bans->used);
3613 for(ii = count = 0; ii < bans->used; ++ii)
3615 match[ii] = user_matches_glob(actee, bans->list[ii]->ban,
3616 MATCH_USENICK | MATCH_VISIBLE);
3623 for(ii = count = 0; ii < bans->used; ++ii)
3625 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
3632 change = mod_chanmode_alloc(count);
3633 for(ii = count = 0; ii < bans->used; ++ii)
3637 change->args[count].mode = MODE_REMOVE | MODE_BAN;
3638 change->args[count++].u.hostmask = strdup(bans->list[ii]->ban);
3640 assert(count == change->argc);
3645 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3647 struct userNode *actee;
3653 /* may want to allow a comma delimited list of users... */
3654 if(!(actee = GetUserH(argv[1])))
3656 if(!is_ircmask(argv[1]) && *argv[1] == '*')
3658 char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
3659 const char *accountname = argv[1] + 1;
3661 snprintf(banmask, sizeof(banmask), "*!*@%s.*", accountname);
3662 mask = strdup(banmask);
3664 else if(!is_ircmask(argv[1]))
3666 reply("MSG_NICK_UNKNOWN", argv[1]);
3671 mask = strdup(argv[1]);
3675 /* We don't sanitize the mask here because ircu
3677 if(action & ACTION_UNBAN)
3679 struct mod_chanmode *change;
3680 change = find_matching_bans(&channel->banlist, actee, mask);
3685 modcmd_chanmode_announce(change);
3686 for(ii = 0; ii < change->argc; ++ii)
3687 free((char*)change->args[ii].u.hostmask);
3688 mod_chanmode_free(change);
3693 if(action & ACTION_DEL_BAN)
3695 struct banData *ban, *next;
3697 ban = channel->channel_info->bans;
3701 for( ; ban && !user_matches_glob(actee, ban->mask,
3702 MATCH_USENICK | MATCH_VISIBLE);
3705 for( ; ban && !match_ircglobs(mask, ban->mask);
3710 del_channel_ban(ban);
3717 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3719 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3725 static CHANSERV_FUNC(cmd_unban)
3727 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3730 static CHANSERV_FUNC(cmd_delban)
3732 /* it doesn't necessarily have to remove the channel ban - may want
3733 to make that an option. */
3734 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_BAN);
3737 static CHANSERV_FUNC(cmd_unbanme)
3739 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3740 long flags = ACTION_UNBAN;
3742 /* remove permanent bans if the user has the proper access. */
3743 if(uData->access >= UL_MASTER)
3744 flags |= ACTION_DEL_BAN;
3746 argv[1] = user->nick;
3747 return unban_user(user, channel, 2, argv, cmd, flags);
3750 static CHANSERV_FUNC(cmd_unbanall)
3752 struct mod_chanmode *change;
3755 if(!channel->banlist.used)
3757 reply("CSMSG_NO_BANS", channel->name);
3761 change = mod_chanmode_alloc(channel->banlist.used);
3762 for(ii=0; ii<channel->banlist.used; ii++)
3764 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3765 change->args[ii].u.hostmask = strdup(channel->banlist.list[ii]->ban);
3767 modcmd_chanmode_announce(change);
3768 for(ii = 0; ii < change->argc; ++ii)
3769 free((char*)change->args[ii].u.hostmask);
3770 mod_chanmode_free(change);
3771 reply("CSMSG_BANS_REMOVED", channel->name);
3775 static CHANSERV_FUNC(cmd_open)
3777 struct mod_chanmode *change;
3780 change = find_matching_bans(&channel->banlist, user, NULL);
3782 change = mod_chanmode_alloc(0);
3783 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3784 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3785 && channel->channel_info->modes.modes_set)
3786 change->modes_clear &= ~channel->channel_info->modes.modes_set;
3787 modcmd_chanmode_announce(change);
3788 reply("CSMSG_CHANNEL_OPENED", channel->name);
3789 for(ii = 0; ii < change->argc; ++ii)
3790 free((char*)change->args[ii].u.hostmask);
3791 mod_chanmode_free(change);
3795 static CHANSERV_FUNC(cmd_myaccess)
3797 static struct string_buffer sbuf;
3798 struct handle_info *target_handle;
3799 struct userData *uData;
3803 target_handle = user->handle_info;
3804 else if(!IsStaff(user))
3806 reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
3809 else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3812 if(!oper_outranks(user, target_handle))
3815 if(!target_handle->channels)
3817 reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
3821 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3822 for(uData = target_handle->channels; uData; uData = uData->u_next)
3824 struct chanData *cData = uData->channel;
3827 if(uData->access > UL_OWNER)
3829 if(IsProtected(cData)
3830 && (target_handle != user->handle_info)
3831 && !GetTrueChannelAccess(cData, user->handle_info))
3834 string_buffer_append_printf(&sbuf, "[%s (%d", cData->channel->name, uData->access);
3835 if(uData->flags != USER_AUTO_OP)
3836 string_buffer_append(&sbuf, ',');
3837 if(IsUserSuspended(uData))
3838 string_buffer_append(&sbuf, 's');
3839 if(IsUserAutoOp(uData))
3841 if(uData->access >= cData->lvlOpts[lvlGiveOps])
3842 string_buffer_append(&sbuf, 'o');
3843 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
3844 string_buffer_append(&sbuf, 'v');
3846 if(IsUserAutoInvite(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe]))
3847 string_buffer_append(&sbuf, 'i');
3849 string_buffer_append_printf(&sbuf, ")] %s", uData->info);
3851 string_buffer_append_string(&sbuf, ")]");
3852 string_buffer_append(&sbuf, '\0');
3853 send_message_type(4, user, cmd->parent->bot, "%s", sbuf.list);
3857 reply("CSMSG_MYACCESS_COUNT_1", target_handle->handle, ccount);
3859 reply("CSMSG_MYACCESS_COUNT", target_handle->handle, ccount);
3865 static CHANSERV_FUNC(cmd_access)
3867 struct userNode *target;
3868 struct handle_info *target_handle;
3869 struct userData *uData;
3871 char prefix[MAXLEN];
3876 target_handle = target->handle_info;
3878 else if((target = GetUserH(argv[1])))
3880 target_handle = target->handle_info;
3882 else if(argv[1][0] == '*')
3884 if(!(target_handle = get_handle_info(argv[1]+1)))
3886 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3892 reply("MSG_NICK_UNKNOWN", argv[1]);
3896 assert(target || target_handle);
3898 if(target == chanserv)
3900 reply("CSMSG_IS_CHANSERV");
3908 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
3913 reply("MSG_USER_AUTHENTICATE", target->nick);
3916 reply("MSG_AUTHENTICATE");
3922 const char *epithet = NULL, *type = NULL;
3925 epithet = chanserv_conf.irc_operator_epithet;
3926 type = user_find_message(user, "CSMSG_OPERATOR_TITLE");
3928 else if(IsNetworkHelper(target))
3930 epithet = chanserv_conf.network_helper_epithet;
3931 type = user_find_message(user, "CSMSG_UC_H_TITLE");
3933 else if(IsSupportHelper(target))
3935 epithet = chanserv_conf.support_helper_epithet;
3936 type = user_find_message(user, "CSMSG_LC_H_TITLE");
3940 if(target_handle->epithet)
3941 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
3943 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
3945 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
3949 sprintf(prefix, "%s", target_handle->handle);
3952 if(!channel->channel_info)
3954 reply("CSMSG_NOT_REGISTERED", channel->name);
3958 helping = HANDLE_FLAGGED(target_handle, HELPING)
3959 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
3960 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
3962 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
3963 /* To prevent possible information leaks, only show infolines
3964 * if the requestor is in the channel or it's their own
3966 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
3968 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
3970 /* Likewise, only say it's suspended if the user has active
3971 * access in that channel or it's their own entry. */
3972 if(IsUserSuspended(uData)
3973 && (GetChannelUser(channel->channel_info, user->handle_info)
3974 || (user->handle_info == uData->handle)))
3976 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
3981 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
3988 zoot_list(struct listData *list)
3990 struct userData *uData;
3991 unsigned int start, curr, highest, lowest;
3992 struct helpfile_table tmp_table;
3993 const char **temp, *msg;
3995 if(list->table.length == 1)
3998 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
4000 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
4001 msg = user_find_message(list->user, "MSG_NONE");
4002 send_message_type(4, list->user, list->bot, " %s", msg);
4004 tmp_table.width = list->table.width;
4005 tmp_table.flags = list->table.flags;
4006 list->table.contents[0][0] = " ";
4007 highest = list->highest;
4008 if(list->lowest != 0)
4009 lowest = list->lowest;
4010 else if(highest < 100)
4013 lowest = highest - 100;
4014 for(start = curr = 1; curr < list->table.length; )
4016 uData = list->users[curr-1];
4017 list->table.contents[curr++][0] = " ";
4018 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
4021 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, lowest, highest, list->search);
4023 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, lowest, highest);
4024 temp = list->table.contents[--start];
4025 list->table.contents[start] = list->table.contents[0];
4026 tmp_table.contents = list->table.contents + start;
4027 tmp_table.length = curr - start;
4028 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
4029 list->table.contents[start] = temp;
4031 highest = lowest - 1;
4032 lowest = (highest < 100) ? 0 : (highest - 99);
4038 def_list(struct listData *list)
4042 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
4044 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
4045 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4046 if(list->table.length == 1)
4048 msg = user_find_message(list->user, "MSG_NONE");
4049 send_message_type(4, list->user, list->bot, " %s", msg);
4054 userData_access_comp(const void *arg_a, const void *arg_b)
4056 const struct userData *a = *(struct userData**)arg_a;
4057 const struct userData *b = *(struct userData**)arg_b;
4059 if(a->access != b->access)
4060 res = b->access - a->access;
4062 res = irccasecmp(a->handle->handle, b->handle->handle);
4067 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
4069 void (*send_list)(struct listData *);
4070 struct userData *uData;
4071 struct listData lData;
4072 unsigned int matches;
4076 lData.bot = cmd->parent->bot;
4077 lData.channel = channel;
4078 lData.lowest = lowest;
4079 lData.highest = highest;
4080 lData.search = (argc > 1) ? argv[1] : NULL;
4081 send_list = def_list;
4082 (void)zoot_list; /* since it doesn't show user levels */
4084 if(user->handle_info)
4086 switch(user->handle_info->userlist_style)
4088 case HI_STYLE_DEF: send_list = def_list; break;
4089 case HI_STYLE_ZOOT: send_list = def_list; break;
4093 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
4095 for(uData = channel->channel_info->users; uData; uData = uData->next)
4097 if((uData->access < lowest)
4098 || (uData->access > highest)
4099 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
4101 lData.users[matches++] = uData;
4103 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
4105 lData.table.length = matches+1;
4106 lData.table.width = 4;
4107 lData.table.flags = TABLE_NO_FREE;
4108 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
4109 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
4110 lData.table.contents[0] = ary;
4113 ary[2] = "Last Seen";
4115 for(matches = 1; matches < lData.table.length; ++matches)
4117 char seen[INTERVALLEN];
4119 uData = lData.users[matches-1];
4120 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
4121 lData.table.contents[matches] = ary;
4122 ary[0] = strtab(uData->access);
4123 ary[1] = uData->handle->handle;
4126 else if(!uData->seen)
4129 ary[2] = intervalString(seen, now - uData->seen, user->handle_info);
4130 ary[2] = strdup(ary[2]);
4131 if(IsUserSuspended(uData))
4132 ary[3] = "Suspended";
4133 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
4134 ary[3] = "Vacation";
4135 else if(HANDLE_FLAGGED(uData->handle, BOT))
4141 for(matches = 1; matches < lData.table.length; ++matches)
4143 free((char*)lData.table.contents[matches][2]);
4144 free(lData.table.contents[matches]);
4146 free(lData.table.contents[0]);
4147 free(lData.table.contents);
4151 static CHANSERV_FUNC(cmd_users)
4153 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
4156 static CHANSERV_FUNC(cmd_wlist)
4158 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
4161 static CHANSERV_FUNC(cmd_clist)
4163 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
4166 static CHANSERV_FUNC(cmd_mlist)
4168 return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
4171 static CHANSERV_FUNC(cmd_olist)
4173 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
4176 static CHANSERV_FUNC(cmd_plist)
4178 return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
4181 static CHANSERV_FUNC(cmd_bans)
4183 struct userNode *search_u = NULL;
4184 struct helpfile_table tbl;
4185 unsigned int matches = 0, timed = 0, search_wilds = 0, ii;
4186 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
4187 const char *msg_never, *triggered, *expires;
4188 struct banData *ban, **bans;
4192 else if(strchr(search = argv[1], '!'))
4195 search_wilds = search[strcspn(search, "?*")];
4197 else if(!(search_u = GetUserH(search)))
4198 reply("MSG_NICK_UNKNOWN", search);
4200 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
4202 for(ban = channel->channel_info->bans; ban; ban = ban->next)
4206 if(!user_matches_glob(search_u, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
4211 if(search_wilds ? !match_ircglobs(search, ban->mask) : !match_ircglob(search, ban->mask))
4214 bans[matches++] = ban;
4219 tbl.length = matches + 1;
4220 tbl.width = 4 + timed;
4222 tbl.flags = TABLE_NO_FREE;
4223 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
4224 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4225 tbl.contents[0][0] = "Mask";
4226 tbl.contents[0][1] = "Set By";
4227 tbl.contents[0][2] = "Triggered";
4230 tbl.contents[0][3] = "Expires";
4231 tbl.contents[0][4] = "Reason";
4234 tbl.contents[0][3] = "Reason";
4237 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4239 free(tbl.contents[0]);
4244 msg_never = user_find_message(user, "MSG_NEVER");
4245 for(ii = 0; ii < matches; )
4251 else if(ban->expires)
4252 expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
4254 expires = msg_never;
4257 triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
4259 triggered = msg_never;
4261 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4262 tbl.contents[ii][0] = ban->mask;
4263 tbl.contents[ii][1] = ban->owner;
4264 tbl.contents[ii][2] = strdup(triggered);
4267 tbl.contents[ii][3] = strdup(expires);
4268 tbl.contents[ii][4] = ban->reason;
4271 tbl.contents[ii][3] = ban->reason;
4273 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4274 reply("MSG_MATCH_COUNT", matches);
4275 for(ii = 1; ii < tbl.length; ++ii)
4277 free((char*)tbl.contents[ii][2]);
4279 free((char*)tbl.contents[ii][3]);
4280 free(tbl.contents[ii]);
4282 free(tbl.contents[0]);
4288 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
4290 struct chanData *cData = channel->channel_info;
4291 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
4293 if(cData->topic_mask)
4294 return !match_ircglob(new_topic, cData->topic_mask);
4295 else if(cData->topic)
4296 return irccasecmp(new_topic, cData->topic);
4301 static CHANSERV_FUNC(cmd_topic)
4303 struct chanData *cData;
4306 cData = channel->channel_info;
4311 SetChannelTopic(channel, chanserv, cData->topic, 1);
4312 reply("CSMSG_TOPIC_SET", cData->topic);
4316 reply("CSMSG_NO_TOPIC", channel->name);
4320 topic = unsplit_string(argv + 1, argc - 1, NULL);
4321 /* If they say "!topic *", use an empty topic. */
4322 if((topic[0] == '*') && (topic[1] == 0))
4324 if(bad_topic(channel, user, topic))
4326 char *topic_mask = cData->topic_mask;
4329 char new_topic[TOPICLEN+1], tchar;
4330 int pos=0, starpos=-1, dpos=0, len;
4332 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
4339 len = strlen(topic);
4340 if((dpos + len) > TOPICLEN)
4341 len = TOPICLEN + 1 - dpos;
4342 memcpy(new_topic+dpos, topic, len);
4346 case '\\': tchar = topic_mask[pos++]; /* and fall through */
4347 default: new_topic[dpos++] = tchar; break;
4350 if((dpos > TOPICLEN) || tchar)
4353 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
4354 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
4357 new_topic[dpos] = 0;
4358 SetChannelTopic(channel, chanserv, new_topic, 1);
4360 reply("CSMSG_TOPIC_LOCKED", channel->name);
4365 SetChannelTopic(channel, chanserv, topic, 1);
4367 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
4369 /* Grab the topic and save it as the default topic. */
4371 cData->topic = strdup(channel->topic);
4377 static CHANSERV_FUNC(cmd_mode)
4379 struct userData *uData;
4380 struct mod_chanmode *change;
4386 change = &channel->channel_info->modes;
4387 if(change->modes_set || change->modes_clear) {
4388 modcmd_chanmode_announce(change);
4389 reply("CSMSG_DEFAULTED_MODES", channel->name);
4391 reply("CSMSG_NO_MODES", channel->name);
4395 uData = GetChannelUser(channel->channel_info, user->handle_info);
4397 base_oplevel = MAXOPLEVEL;
4398 else if (uData->access >= UL_OWNER)
4401 base_oplevel = 1 + UL_OWNER - uData->access;
4402 change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED|MCP_NO_APASS, base_oplevel);
4405 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
4409 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
4410 && mode_lock_violated(&channel->channel_info->modes, change))
4413 mod_chanmode_format(&channel->channel_info->modes, modes);
4414 reply("CSMSG_MODE_LOCKED", modes, channel->name);
4418 modcmd_chanmode_announce(change);
4419 mod_chanmode_format(change, fmt);
4420 mod_chanmode_free(change);
4421 reply("CSMSG_MODES_SET", fmt);
4426 chanserv_del_invite_mark(void *data)
4428 struct ChanUser *chanuser = data;
4429 struct chanNode *channel = chanuser->chan;
4431 if(!channel) return;
4432 for(i = 0; i < channel->invited.used; i++)
4434 if(channel->invited.list[i] == chanuser->user) {
4435 userList_remove(&channel->invited, chanuser->user);
4441 static CHANSERV_FUNC(cmd_invite)
4443 struct userData *uData;
4444 struct userNode *invite;
4445 struct ChanUser *chanuser;
4448 uData = GetChannelUser(channel->channel_info, user->handle_info);
4452 if(!(invite = GetUserH(argv[1])))
4454 reply("MSG_NICK_UNKNOWN", argv[1]);
4461 if(GetUserMode(channel, invite))
4463 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
4467 for(i = 0; i < channel->invited.used; i++)
4469 if(channel->invited.list[i] == invite) {
4470 reply("CSMSG_ALREADY_INVITED", invite->nick, channel->name);
4479 char *reason = unsplit_string(argv + 2, argc - 2, NULL);
4480 send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
4483 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
4485 irc_invite(chanserv, invite, channel);
4487 reply("CSMSG_INVITED_USER", argv[1], channel->name);
4489 userList_append(&channel->invited, invite);
4490 chanuser = calloc(1, sizeof(*chanuser));
4491 chanuser->user=invite;
4492 chanuser->chan=channel;
4493 timeq_add(now + chanserv_conf.invited_timeout, chanserv_del_invite_mark, chanuser);
4498 static CHANSERV_FUNC(cmd_inviteme)
4500 if(GetUserMode(channel, user))
4502 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
4505 if(channel->channel_info
4506 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
4508 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
4511 irc_invite(cmd->parent->bot, user, channel);
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 changes->args[used].mode = MODE_CHANOP;
4834 changes->args[used++].u.member = mn;
4837 else if(!cData->lvlOpts[lvlGiveVoice]
4838 || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice]))
4840 if(mn->modes & MODE_CHANOP)
4842 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4843 changes->args[used++].u.member = mn;
4845 if(!(mn->modes & MODE_VOICE))
4847 changes->args[used].mode = MODE_VOICE;
4848 changes->args[used++].u.member = mn;
4855 changes->args[used].mode = MODE_REMOVE | mn->modes;
4856 changes->args[used++].u.member = mn;
4860 changes->argc = used;
4861 modcmd_chanmode_announce(changes);
4862 mod_chanmode_free(changes);
4863 reply("CSMSG_RESYNCED_USERS", channel->name);
4867 static CHANSERV_FUNC(cmd_seen)
4869 struct userData *uData;
4870 struct handle_info *handle;
4871 char seen[INTERVALLEN];
4875 if(!irccasecmp(argv[1], chanserv->nick))
4877 reply("CSMSG_IS_CHANSERV");
4881 if(!(handle = get_handle_info(argv[1])))
4883 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4887 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4889 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4894 reply("CSMSG_USER_PRESENT", handle->handle);
4895 else if(uData->seen)
4896 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
4898 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4900 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4901 reply("CSMSG_USER_VACATION", handle->handle);
4906 static MODCMD_FUNC(cmd_names)
4908 struct userNode *targ;
4909 struct userData *targData;
4910 unsigned int ii, pos;
4913 for(ii=pos=0; ii<channel->members.used; ++ii)
4915 targ = channel->members.list[ii]->user;
4916 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4919 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
4922 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4926 if(IsUserSuspended(targData))
4928 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4931 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4932 reply("CSMSG_END_NAMES", channel->name);
4937 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
4939 switch(ntype->visible_type)
4941 case NOTE_VIS_ALL: return 1;
4942 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
4943 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
4948 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
4950 struct userData *uData;
4952 switch(ntype->set_access_type)
4954 case NOTE_SET_CHANNEL_ACCESS:
4955 if(!user->handle_info)
4957 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
4959 return uData->access >= ntype->set_access.min_ulevel;
4960 case NOTE_SET_CHANNEL_SETTER:
4961 return check_user_level(channel, user, lvlSetters, 1, 0);
4962 case NOTE_SET_PRIVILEGED: default:
4963 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
4967 static CHANSERV_FUNC(cmd_note)
4969 struct chanData *cData;
4971 struct note_type *ntype;
4973 cData = channel->channel_info;
4976 reply("CSMSG_NOT_REGISTERED", channel->name);
4980 /* If no arguments, show all visible notes for the channel. */
4986 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
4988 note = iter_data(it);
4989 if(!note_type_visible_to_user(cData, note->type, user))
4992 reply("CSMSG_NOTELIST_HEADER", channel->name);
4993 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
4996 reply("CSMSG_NOTELIST_END", channel->name);
4998 reply("CSMSG_NOTELIST_EMPTY", channel->name);
5000 /* If one argument, show the named note. */
5003 if((note = dict_find(cData->notes, argv[1], NULL))
5004 && note_type_visible_to_user(cData, note->type, user))
5006 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
5008 else if((ntype = dict_find(note_types, argv[1], NULL))
5009 && note_type_visible_to_user(NULL, ntype, user))
5011 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
5016 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
5020 /* Assume they're trying to set a note. */
5024 ntype = dict_find(note_types, argv[1], NULL);
5027 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
5030 else if(note_type_settable_by_user(channel, ntype, user))
5032 note_text = unsplit_string(argv+2, argc-2, NULL);
5033 if((note = dict_find(cData->notes, argv[1], NULL)))
5034 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
5035 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
5036 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
5038 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
5040 /* The note is viewable to staff only, so return 0
5041 to keep the invocation from getting logged (or
5042 regular users can see it in !events). */
5048 reply("CSMSG_NO_ACCESS");
5055 static CHANSERV_FUNC(cmd_delnote)
5060 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
5061 || !note_type_settable_by_user(channel, note->type, user))
5063 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
5066 dict_remove(channel->channel_info->notes, note->type->name);
5067 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
5071 static CHANSERV_FUNC(cmd_events)
5073 struct logSearch discrim;
5074 struct logReport report;
5075 unsigned int matches, limit;
5077 limit = (argc > 1) ? atoi(argv[1]) : 10;
5078 if(limit < 1 || limit > 200)
5081 memset(&discrim, 0, sizeof(discrim));
5082 discrim.masks.bot = chanserv;
5083 discrim.masks.channel_name = channel->name;
5085 discrim.masks.command = argv[2];
5086 discrim.limit = limit;
5087 discrim.max_time = INT_MAX;
5088 discrim.severities = 1 << LOG_COMMAND;
5089 report.reporter = chanserv;
5091 reply("CSMSG_EVENT_SEARCH_RESULTS");
5092 matches = log_entry_search(&discrim, log_report_entry, &report);
5094 reply("MSG_MATCH_COUNT", matches);
5096 reply("MSG_NO_MATCHES");
5100 static CHANSERV_FUNC(cmd_say)
5106 msg = unsplit_string(argv + 1, argc - 1, NULL);
5107 send_channel_message(channel, cmd->parent->bot, "%s", msg);
5109 else if(*argv[1] == '*' && argv[1][1] != '\0')
5111 struct handle_info *hi;
5112 struct userNode *authed;
5115 msg = unsplit_string(argv + 2, argc - 2, NULL);
5117 if (!(hi = get_handle_info(argv[1] + 1)))
5119 reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
5123 for (authed = hi->users; authed; authed = authed->next_authed)
5124 send_target_message(5, authed->nick, cmd->parent->bot, "%s", msg);
5126 else if(GetUserH(argv[1]))
5129 msg = unsplit_string(argv + 2, argc - 2, NULL);
5130 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
5134 reply("MSG_NOT_TARGET_NAME");
5140 static CHANSERV_FUNC(cmd_emote)
5146 /* CTCP is so annoying. */
5147 msg = unsplit_string(argv + 1, argc - 1, NULL);
5148 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
5150 else if(*argv[1] == '*' && argv[1][1] != '\0')
5152 struct handle_info *hi;
5153 struct userNode *authed;
5156 msg = unsplit_string(argv + 2, argc - 2, NULL);
5158 if (!(hi = get_handle_info(argv[1] + 1)))
5160 reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
5164 for (authed = hi->users; authed; authed = authed->next_authed)
5165 send_target_message(5, authed->nick, cmd->parent->bot, "\001ACTION %s\001", msg);
5167 else if(GetUserH(argv[1]))
5169 msg = unsplit_string(argv + 2, argc - 2, NULL);
5170 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
5174 reply("MSG_NOT_TARGET_NAME");
5180 struct channelList *
5181 chanserv_support_channels(void)
5183 return &chanserv_conf.support_channels;
5186 static CHANSERV_FUNC(cmd_expire)
5188 int channel_count = registered_channels;
5189 expire_channels(NULL);
5190 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
5195 chanserv_expire_suspension(void *data)
5197 struct suspended *suspended = data;
5198 struct chanNode *channel;
5201 /* Update the channel registration data structure. */
5202 if(!suspended->expires || (now < suspended->expires))
5203 suspended->revoked = now;
5204 channel = suspended->cData->channel;
5205 suspended->cData->channel = channel;
5206 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
5208 /* If appropriate, re-join ChanServ to the channel. */
5209 if(!IsOffChannel(suspended->cData))
5211 spamserv_cs_suspend(channel, 0, 0, NULL);
5212 ss_cs_join_channel(channel, 1);
5215 /* Mark everyone currently in the channel as present. */
5216 for(ii = 0; ii < channel->members.used; ++ii)
5218 struct userData *uData = GetChannelAccess(suspended->cData, channel->members.list[ii]->user->handle_info);
5227 static CHANSERV_FUNC(cmd_csuspend)
5229 struct suspended *suspended;
5230 char reason[MAXLEN];
5231 unsigned long expiry, duration;
5232 struct userData *uData;
5236 if(IsProtected(channel->channel_info))
5238 reply("CSMSG_SUSPEND_NODELETE", channel->name);
5242 if(argv[1][0] == '!')
5244 else if(IsSuspended(channel->channel_info))
5246 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
5247 show_suspension_info(cmd, user, channel->channel_info->suspended);
5251 if(!strcmp(argv[1], "0"))
5253 else if((duration = ParseInterval(argv[1])))
5254 expiry = now + duration;
5257 reply("MSG_INVALID_DURATION", argv[1]);
5261 unsplit_string(argv + 2, argc - 2, reason);
5263 suspended = calloc(1, sizeof(*suspended));
5264 suspended->revoked = 0;
5265 suspended->issued = now;
5266 suspended->suspender = strdup(user->handle_info->handle);
5267 suspended->expires = expiry;
5268 suspended->reason = strdup(reason);
5269 suspended->cData = channel->channel_info;
5270 suspended->previous = suspended->cData->suspended;
5271 suspended->cData->suspended = suspended;
5273 if(suspended->expires)
5274 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
5276 if(IsSuspended(channel->channel_info))
5278 suspended->previous->revoked = now;
5279 if(suspended->previous->expires)
5280 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
5281 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
5282 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5286 /* Mark all users in channel as absent. */
5287 for(uData = channel->channel_info->users; uData; uData = uData->next)
5296 /* Mark the channel as suspended, then part. */
5297 channel->channel_info->flags |= CHANNEL_SUSPENDED;
5298 spamserv_cs_suspend(channel, expiry, 1, suspended->reason);
5299 DelChannelUser(chanserv, channel, suspended->reason, 0);
5300 reply("CSMSG_SUSPENDED", channel->name);
5301 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
5302 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5307 static CHANSERV_FUNC(cmd_cunsuspend)
5309 struct suspended *suspended;
5310 char message[MAXLEN];
5312 if(!IsSuspended(channel->channel_info))
5314 reply("CSMSG_NOT_SUSPENDED", channel->name);
5318 suspended = channel->channel_info->suspended;
5320 /* Expire the suspension and join ChanServ to the channel. */
5321 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
5322 chanserv_expire_suspension(suspended);
5323 reply("CSMSG_UNSUSPENDED", channel->name);
5324 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
5325 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
5329 typedef struct chanservSearch
5334 unsigned long unvisited;
5335 unsigned long registered;
5337 unsigned long flags;
5341 typedef void (*channel_search_func)(struct chanData *channel, void *data);
5344 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
5349 search = malloc(sizeof(struct chanservSearch));
5350 memset(search, 0, sizeof(*search));
5353 for(i = 0; i < argc; i++)
5355 /* Assume all criteria require arguments. */
5358 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
5362 if(!irccasecmp(argv[i], "name"))
5363 search->name = argv[++i];
5364 else if(!irccasecmp(argv[i], "registrar"))
5365 search->registrar = argv[++i];
5366 else if(!irccasecmp(argv[i], "unvisited"))
5367 search->unvisited = ParseInterval(argv[++i]);
5368 else if(!irccasecmp(argv[i], "registered"))
5369 search->registered = ParseInterval(argv[++i]);
5370 else if(!irccasecmp(argv[i], "flags"))
5373 if(!irccasecmp(argv[i], "nodelete"))
5374 search->flags |= CHANNEL_NODELETE;
5375 else if(!irccasecmp(argv[i], "suspended"))
5376 search->flags |= CHANNEL_SUSPENDED;
5377 else if(!irccasecmp(argv[i], "unreviewed"))
5378 search->flags |= CHANNEL_UNREVIEWED;
5381 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
5385 else if(!irccasecmp(argv[i], "limit"))
5386 search->limit = strtoul(argv[++i], NULL, 10);
5389 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
5394 if(search->name && !strcmp(search->name, "*"))
5396 if(search->registrar && !strcmp(search->registrar, "*"))
5397 search->registrar = 0;
5406 chanserv_channel_match(struct chanData *channel, search_t search)
5408 const char *name = channel->channel->name;
5409 if((search->name && !match_ircglob(name, search->name)) ||
5410 (search->registrar && !channel->registrar) ||
5411 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
5412 (search->unvisited && (now - channel->visited) < search->unvisited) ||
5413 (search->registered && (now - channel->registered) > search->registered) ||
5414 (search->flags && ((search->flags & channel->flags) != search->flags)))
5421 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
5423 struct chanData *channel;
5424 unsigned int matches = 0;
5426 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
5428 if(!chanserv_channel_match(channel, search))
5438 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
5443 search_print(struct chanData *channel, void *data)
5445 send_message_type(4, data, chanserv, "%s", channel->channel->name);
5448 static CHANSERV_FUNC(cmd_search)
5451 unsigned int matches;
5452 channel_search_func action;
5456 if(!irccasecmp(argv[1], "count"))
5457 action = search_count;
5458 else if(!irccasecmp(argv[1], "print"))
5459 action = search_print;
5462 reply("CSMSG_ACTION_INVALID", argv[1]);
5466 search = chanserv_search_create(user, argc - 2, argv + 2);
5470 if(action == search_count)
5471 search->limit = INT_MAX;
5473 if(action == search_print)
5474 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
5476 matches = chanserv_channel_search(search, action, user);
5479 reply("MSG_MATCH_COUNT", matches);
5481 reply("MSG_NO_MATCHES");
5487 static CHANSERV_FUNC(cmd_unvisited)
5489 struct chanData *cData;
5490 unsigned long interval = chanserv_conf.channel_expire_delay;
5491 char buffer[INTERVALLEN];
5492 unsigned int limit = 25, matches = 0;
5496 interval = ParseInterval(argv[1]);
5498 limit = atoi(argv[2]);
5501 intervalString(buffer, interval, user->handle_info);
5502 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
5504 for(cData = channelList; cData && matches < limit; cData = cData->next)
5506 if((now - cData->visited) < interval)
5509 intervalString(buffer, now - cData->visited, user->handle_info);
5510 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
5517 static MODCMD_FUNC(chan_opt_defaulttopic)
5523 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5525 reply("CSMSG_TOPIC_LOCKED", channel->name);
5529 topic = unsplit_string(argv+1, argc-1, NULL);
5531 free(channel->channel_info->topic);
5532 if(topic[0] == '*' && topic[1] == 0)
5534 topic = channel->channel_info->topic = NULL;
5538 topic = channel->channel_info->topic = strdup(topic);
5539 if(channel->channel_info->topic_mask
5540 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
5541 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5543 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
5546 if(channel->channel_info->topic)
5547 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
5549 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
5553 static MODCMD_FUNC(chan_opt_topicmask)
5557 struct chanData *cData = channel->channel_info;
5560 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5562 reply("CSMSG_TOPIC_LOCKED", channel->name);
5566 mask = unsplit_string(argv+1, argc-1, NULL);
5568 if(cData->topic_mask)
5569 free(cData->topic_mask);
5570 if(mask[0] == '*' && mask[1] == 0)
5572 cData->topic_mask = 0;
5576 cData->topic_mask = strdup(mask);
5578 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
5579 else if(!match_ircglob(cData->topic, cData->topic_mask))
5580 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5584 if(channel->channel_info->topic_mask)
5585 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
5587 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
5591 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
5595 char *greeting = unsplit_string(argv+1, argc-1, NULL);
5599 if(greeting[0] == '*' && greeting[1] == 0)
5603 unsigned int length = strlen(greeting);
5604 if(length > chanserv_conf.greeting_length)
5606 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
5609 *data = strdup(greeting);
5618 reply(name, user_find_message(user, "MSG_NONE"));
5622 static MODCMD_FUNC(chan_opt_greeting)
5624 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
5627 static MODCMD_FUNC(chan_opt_usergreeting)
5629 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
5632 static MODCMD_FUNC(chan_opt_modes)
5634 struct mod_chanmode *new_modes;
5639 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
5641 reply("CSMSG_NO_ACCESS");
5644 if(argv[1][0] == '*' && argv[1][1] == 0)
5646 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
5648 else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED|MCP_NO_APASS, 0)))
5650 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5653 else if(new_modes->argc > 1)
5655 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5656 mod_chanmode_free(new_modes);
5661 channel->channel_info->modes = *new_modes;
5662 modcmd_chanmode_announce(new_modes);
5663 mod_chanmode_free(new_modes);
5667 mod_chanmode_format(&channel->channel_info->modes, modes);
5669 reply("CSMSG_SET_MODES", modes);
5671 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
5675 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5677 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5679 struct chanData *cData = channel->channel_info;
5684 /* Set flag according to value. */
5685 if(enabled_string(argv[1]))
5687 cData->flags |= mask;
5690 else if(disabled_string(argv[1]))
5692 cData->flags &= ~mask;
5697 reply("MSG_INVALID_BINARY", argv[1]);
5703 /* Find current option value. */
5704 value = (cData->flags & mask) ? 1 : 0;
5708 reply(name, user_find_message(user, "MSG_ON"));
5710 reply(name, user_find_message(user, "MSG_OFF"));
5714 static MODCMD_FUNC(chan_opt_nodelete)
5716 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
5718 reply("MSG_SETTING_PRIVILEGED", argv[0]);
5722 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
5725 static MODCMD_FUNC(chan_opt_dynlimit)
5727 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
5730 static MODCMD_FUNC(chan_opt_offchannel)
5732 struct chanData *cData = channel->channel_info;
5737 /* Set flag according to value. */
5738 if(enabled_string(argv[1]))
5740 if(!IsOffChannel(cData))
5741 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
5742 cData->flags |= CHANNEL_OFFCHANNEL;
5745 else if(disabled_string(argv[1]))
5747 if(IsOffChannel(cData))
5749 struct mod_chanmode change;
5750 mod_chanmode_init(&change);
5752 change.args[0].mode = MODE_CHANOP;
5753 change.args[0].u.member = AddChannelUser(chanserv, channel);
5754 mod_chanmode_announce(chanserv, channel, &change);
5756 cData->flags &= ~CHANNEL_OFFCHANNEL;
5761 reply("MSG_INVALID_BINARY", argv[1]);
5767 /* Find current option value. */
5768 value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
5772 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
5774 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
5778 static MODCMD_FUNC(chan_opt_unreviewed)
5780 struct chanData *cData = channel->channel_info;
5781 int value = (cData->flags & CHANNEL_UNREVIEWED) ? 1 : 0;
5787 /* The two directions can have different ACLs. */
5788 if(enabled_string(argv[1]))
5790 else if(disabled_string(argv[1]))
5794 reply("MSG_INVALID_BINARY", argv[1]);
5798 if (new_value != value)
5800 struct svccmd *subcmd;
5801 char subcmd_name[32];
5803 snprintf(subcmd_name, sizeof(subcmd_name), "%s %s", argv[0], (new_value ? "on" : "off"));
5804 subcmd = dict_find(cmd->parent->commands, subcmd_name, NULL);
5807 reply("MSG_COMMAND_DISABLED", subcmd_name);
5810 else if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
5814 cData->flags |= CHANNEL_UNREVIEWED;
5817 free(cData->registrar);
5818 cData->registrar = strdup(user->handle_info->handle);
5819 cData->flags &= ~CHANNEL_UNREVIEWED;
5826 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_ON"));
5828 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_OFF"));
5832 static MODCMD_FUNC(chan_opt_defaults)
5834 struct userData *uData;
5835 struct chanData *cData;
5836 const char *confirm;
5837 enum levelOption lvlOpt;
5838 enum charOption chOpt;
5840 cData = channel->channel_info;
5841 uData = GetChannelUser(cData, user->handle_info);
5842 if(!uData || (uData->access < UL_OWNER))
5844 reply("CSMSG_OWNER_DEFAULTS", channel->name);
5847 confirm = make_confirmation_string(uData);
5848 if((argc < 2) || strcmp(argv[1], confirm))
5850 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
5853 cData->flags = (CHANNEL_DEFAULT_FLAGS & ~CHANNEL_PRESERVED_FLAGS)
5854 | (cData->flags & CHANNEL_PRESERVED_FLAGS);
5855 cData->modes = chanserv_conf.default_modes;
5856 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
5857 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
5858 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
5859 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
5860 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
5865 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5867 struct chanData *cData = channel->channel_info;
5868 struct userData *uData;
5869 unsigned short value;
5873 if(!check_user_level(channel, user, option, 1, 1))
5875 reply("CSMSG_CANNOT_SET");
5878 value = user_level_from_name(argv[1], UL_OWNER+1);
5879 if(!value && strcmp(argv[1], "0"))
5881 reply("CSMSG_INVALID_ACCESS", argv[1]);
5884 uData = GetChannelUser(cData, user->handle_info);
5885 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
5887 reply("CSMSG_BAD_SETLEVEL");
5893 if(value > cData->lvlOpts[lvlGiveOps])
5895 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
5900 if(value < cData->lvlOpts[lvlGiveVoice])
5902 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
5907 /* This test only applies to owners, since non-owners
5908 * trying to set an option to above their level get caught
5909 * by the CSMSG_BAD_SETLEVEL test above.
5911 if(value > uData->access)
5913 reply("CSMSG_BAD_SETTERS");
5920 cData->lvlOpts[option] = value;
5922 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
5926 static MODCMD_FUNC(chan_opt_enfops)
5928 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
5931 static MODCMD_FUNC(chan_opt_giveops)
5933 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
5936 static MODCMD_FUNC(chan_opt_enfmodes)
5938 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
5941 static MODCMD_FUNC(chan_opt_enftopic)
5943 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
5946 static MODCMD_FUNC(chan_opt_pubcmd)
5948 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
5951 static MODCMD_FUNC(chan_opt_setters)
5953 return channel_level_option(lvlSetters, CSFUNC_ARGS);
5956 static MODCMD_FUNC(chan_opt_ctcpusers)
5958 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
5961 static MODCMD_FUNC(chan_opt_userinfo)
5963 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
5966 static MODCMD_FUNC(chan_opt_givevoice)
5968 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
5971 static MODCMD_FUNC(chan_opt_topicsnarf)
5973 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
5976 static MODCMD_FUNC(chan_opt_vote)
5978 return channel_level_option(lvlVote, CSFUNC_ARGS);
5981 static MODCMD_FUNC(chan_opt_inviteme)
5983 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
5987 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5989 struct chanData *cData = channel->channel_info;
5990 int count = charOptions[option].count, idx;
5994 idx = atoi(argv[1]);
5996 if(!isdigit(argv[1][0]) || (idx < 0) || (idx >= count))
5998 reply("CSMSG_INVALID_NUMERIC", idx);
5999 /* Show possible values. */
6000 for(idx = 0; idx < count; idx++)
6001 reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
6005 cData->chOpts[option] = charOptions[option].values[idx].value;
6009 /* Find current option value. */
6012 (idx < count) && (cData->chOpts[option] != charOptions[option].values[idx].value);
6016 /* Somehow, the option value is corrupt; reset it to the default. */
6017 cData->chOpts[option] = charOptions[option].default_value;
6022 reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
6026 static MODCMD_FUNC(chan_opt_protect)
6028 return channel_multiple_option(chProtect, CSFUNC_ARGS);
6031 static MODCMD_FUNC(chan_opt_toys)
6033 return channel_multiple_option(chToys, CSFUNC_ARGS);
6036 static MODCMD_FUNC(chan_opt_ctcpreaction)
6038 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
6041 static MODCMD_FUNC(chan_opt_topicrefresh)
6043 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
6046 static struct svccmd_list set_shows_list;
6049 handle_svccmd_unbind(struct svccmd *target) {
6051 for(ii=0; ii<set_shows_list.used; ++ii)
6052 if(target == set_shows_list.list[ii])
6053 set_shows_list.used = 0;
6056 static CHANSERV_FUNC(cmd_set)
6058 struct svccmd *subcmd;
6062 /* Check if we need to (re-)initialize set_shows_list. */
6063 if(!set_shows_list.used)
6065 if(!set_shows_list.size)
6067 set_shows_list.size = chanserv_conf.set_shows->used;
6068 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
6070 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
6072 const char *name = chanserv_conf.set_shows->list[ii];
6073 sprintf(buf, "%s %s", argv[0], name);
6074 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6077 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
6080 svccmd_list_append(&set_shows_list, subcmd);
6086 reply("CSMSG_CHANNEL_OPTIONS");
6087 for(ii = 0; ii < set_shows_list.used; ii++)
6089 subcmd = set_shows_list.list[ii];
6090 subcmd->command->func(user, channel, 1, argv+1, subcmd);
6095 sprintf(buf, "%s %s", argv[0], argv[1]);
6096 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6099 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
6102 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
6104 reply("CSMSG_NO_ACCESS");
6110 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
6114 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
6116 struct userData *uData;
6118 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6121 reply("CSMSG_NOT_USER", channel->name);
6127 /* Just show current option value. */
6129 else if(enabled_string(argv[1]))
6131 uData->flags |= mask;
6133 else if(disabled_string(argv[1]))
6135 uData->flags &= ~mask;
6139 reply("MSG_INVALID_BINARY", argv[1]);
6143 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
6147 static MODCMD_FUNC(user_opt_noautoop)
6149 struct userData *uData;
6151 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6154 reply("CSMSG_NOT_USER", channel->name);
6157 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
6158 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
6160 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
6163 static MODCMD_FUNC(user_opt_autoinvite)
6165 if((argc > 1) && !check_user_level(channel, user, lvlInviteMe, 1, 0))
6167 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
6169 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
6172 static MODCMD_FUNC(user_opt_info)
6174 struct userData *uData;
6177 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6181 /* If they got past the command restrictions (which require access)
6182 * but fail this test, we have some fool with security override on.
6184 reply("CSMSG_NOT_USER", channel->name);
6191 infoline = unsplit_string(argv + 1, argc - 1, NULL);
6192 if(strlen(infoline) > chanserv_conf.max_userinfo_length)
6194 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
6197 bp = strcspn(infoline, "\001");
6200 reply("CSMSG_BAD_INFOLINE", infoline[bp]);
6205 if(infoline[0] == '*' && infoline[1] == 0)
6208 uData->info = strdup(infoline);
6211 reply("CSMSG_USET_INFO", uData->info);
6213 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
6217 struct svccmd_list uset_shows_list;
6219 static CHANSERV_FUNC(cmd_uset)
6221 struct svccmd *subcmd;
6225 /* Check if we need to (re-)initialize uset_shows_list. */
6226 if(!uset_shows_list.used)
6230 "NoAutoOp", "AutoInvite", "Info"
6233 if(!uset_shows_list.size)
6235 uset_shows_list.size = ArrayLength(options);
6236 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
6238 for(ii = 0; ii < ArrayLength(options); ii++)
6240 const char *name = options[ii];
6241 sprintf(buf, "%s %s", argv[0], name);
6242 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6245 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
6248 svccmd_list_append(&uset_shows_list, subcmd);
6254 /* Do this so options are presented in a consistent order. */
6255 reply("CSMSG_USER_OPTIONS");
6256 for(ii = 0; ii < uset_shows_list.used; ii++)
6257 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
6261 sprintf(buf, "%s %s", argv[0], argv[1]);
6262 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6265 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
6269 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
6272 static CHANSERV_FUNC(cmd_giveownership)
6274 struct handle_info *new_owner_hi;
6275 struct userData *new_owner;
6276 struct userData *curr_user;
6277 struct userData *invoker;
6278 struct chanData *cData = channel->channel_info;
6279 struct do_not_register *dnr;
6280 const char *confirm;
6282 unsigned short co_access;
6283 char reason[MAXLEN];
6286 curr_user = GetChannelAccess(cData, user->handle_info);
6287 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
6288 if(!curr_user || (curr_user->access != UL_OWNER))
6290 struct userData *owner = NULL;
6291 for(curr_user = channel->channel_info->users;
6293 curr_user = curr_user->next)
6295 if(curr_user->access != UL_OWNER)
6299 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
6306 else if(!force && (now < cData->ownerTransfer + chanserv_conf.giveownership_period))
6308 char delay[INTERVALLEN];
6309 intervalString(delay, cData->ownerTransfer + chanserv_conf.giveownership_period - now, user->handle_info);
6310 reply("CSMSG_TRANSFER_WAIT", delay, channel->name);
6313 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
6315 if(new_owner_hi == user->handle_info)
6317 reply("CSMSG_NO_TRANSFER_SELF");
6320 new_owner = GetChannelAccess(cData, new_owner_hi);
6325 new_owner = add_channel_user(cData, new_owner_hi, UL_OWNER - 1, 0, NULL);
6329 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
6333 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
6335 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
6338 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
6339 if(!IsHelping(user))
6340 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
6342 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi->handle);
6345 invoker = GetChannelUser(cData, user->handle_info);
6346 if(invoker->access <= UL_OWNER)
6348 confirm = make_confirmation_string(curr_user);
6349 if((argc < 3) || strcmp(argv[2], confirm))
6351 reply("CSMSG_CONFIRM_GIVEOWNERSHIP", new_owner_hi->handle, confirm);
6355 if(new_owner->access >= UL_COOWNER)
6356 co_access = new_owner->access;
6358 co_access = UL_COOWNER;
6359 new_owner->access = UL_OWNER;
6361 curr_user->access = co_access;
6362 cData->ownerTransfer = now;
6363 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
6364 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
6365 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
6369 static CHANSERV_FUNC(cmd_suspend)
6371 struct handle_info *hi;
6372 struct userData *actor, *real_actor, *target;
6373 unsigned int override = 0;
6376 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6377 actor = GetChannelUser(channel->channel_info, user->handle_info);
6378 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
6379 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6381 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6384 if(target->access >= actor->access)
6386 reply("MSG_USER_OUTRANKED", hi->handle);
6389 if(target->flags & USER_SUSPENDED)
6391 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
6396 target->present = 0;
6399 if(!real_actor || target->access >= real_actor->access)
6400 override = CMD_LOG_OVERRIDE;
6401 target->flags |= USER_SUSPENDED;
6402 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
6403 return 1 | override;
6406 static CHANSERV_FUNC(cmd_unsuspend)
6408 struct handle_info *hi;
6409 struct userData *actor, *real_actor, *target;
6410 unsigned int override = 0;
6413 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6414 actor = GetChannelUser(channel->channel_info, user->handle_info);
6415 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
6416 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6418 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6421 if(target->access >= actor->access)
6423 reply("MSG_USER_OUTRANKED", hi->handle);
6426 if(!(target->flags & USER_SUSPENDED))
6428 reply("CSMSG_NOT_SUSPENDED", hi->handle);
6431 if(!real_actor || target->access >= real_actor->access)
6432 override = CMD_LOG_OVERRIDE;
6433 target->flags &= ~USER_SUSPENDED;
6434 scan_user_presence(target, NULL);
6435 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
6436 return 1 | override;
6439 static MODCMD_FUNC(cmd_deleteme)
6441 struct handle_info *hi;
6442 struct userData *target;
6443 const char *confirm_string;
6444 unsigned short access_level;
6447 hi = user->handle_info;
6448 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6450 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6453 if(target->access == UL_OWNER)
6455 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
6458 confirm_string = make_confirmation_string(target);
6459 if((argc < 2) || strcmp(argv[1], confirm_string))
6461 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
6464 access_level = target->access;
6465 channel_name = strdup(channel->name);
6466 del_channel_user(target, 1);
6467 reply("CSMSG_DELETED_YOU", access_level, channel_name);
6472 static CHANSERV_FUNC(cmd_addvote)
6474 struct chanData *cData = channel->channel_info;
6475 struct userData *uData, *target;
6476 struct handle_info *hi;
6477 if (!cData) return 0;
6479 hi = user->handle_info;
6480 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6482 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6485 if(target->access < 300) {
6486 reply("CSMSG_NO_ACCESS");
6490 reply("CSMSG_ADDVOTE_FULL");
6494 msg = unsplit_string(argv + 1, argc - 1, NULL);
6495 cData->vote = strdup(msg);
6496 cData->vote_start=0;
6497 dict_delete(cData->vote_options);
6498 cData->vote_options = dict_new();
6499 dict_set_free_data(cData->vote_options, free_vote_options);
6500 for(uData = channel->channel_info->users; uData; uData = uData->next)
6505 reply("CSMSG_ADDVOTE_DONE");
6509 static CHANSERV_FUNC(cmd_delvote)
6511 struct chanData *cData = channel->channel_info;
6512 struct userData *target;
6513 struct handle_info *hi;
6514 if (!cData) return 0;
6515 hi = user->handle_info;
6516 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6518 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6521 if(target->access < 300) {
6522 reply("CSMSG_NO_ACCESS");
6526 reply("CSMSG_NO_VOTE");
6531 reply("CSMSG_DELVOTE_DONE");
6535 static CHANSERV_FUNC(cmd_addoption)
6537 struct chanData *cData = channel->channel_info;
6538 struct userData *target;
6539 struct handle_info *hi;
6540 if (!cData) return 0;
6542 hi = user->handle_info;
6543 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6545 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6548 if(target->access < 300) {
6549 reply("CSMSG_NO_ACCESS");
6553 reply("CSMSG_NO_VOTE");
6559 msg = unsplit_string(argv + 1, argc - 1, NULL);
6562 unsigned int lastid = 1;
6563 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6564 struct vote_option *cvOpt = iter_data(it);
6565 if(cvOpt->option_id > lastid)
6566 lastid = cvOpt->option_id;
6568 struct vote_option *vOpt;
6569 vOpt = calloc(1, sizeof(*vOpt));
6570 vOpt->name = strdup(msg);
6571 vOpt->option_id = (lastid + 1);
6573 sprintf(str,"%i",(lastid + 1));
6574 vOpt->option_str = strdup(str);
6576 dict_insert(cData->vote_options,vOpt->option_str,vOpt);
6578 reply("CSMSG_ADDOPTION_DONE",dict_size(cData->vote_options),lastid,(lastid + 1));
6582 static CHANSERV_FUNC(cmd_deloption)
6584 struct chanData *cData = channel->channel_info;
6585 struct userData *uData, *target;
6586 struct handle_info *hi;
6587 if (!cData) return 0;
6589 hi = user->handle_info;
6590 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6592 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6595 if(target->access < 300) {
6596 reply("CSMSG_NO_ACCESS");
6600 reply("CSMSG_NO_VOTE");
6603 if(cData->vote_start) {
6604 if(dict_size(cData->vote_options) < 3) {
6605 reply("CSMSG_VOTE_NEED_OPTIONS");
6610 int find_id = atoi(argv[1]);
6612 unsigned int found = 0;
6615 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6617 if (find_id == ii) {
6618 struct vote_option *vOpt = iter_data(it);
6619 found = vOpt->option_id;
6621 sprintf(str,"%i",vOpt->option_id);
6622 dict_remove(cData->vote_options, str);
6627 for(uData = channel->channel_info->users; uData; uData = uData->next) {
6628 if(uData->votefor == found) {
6633 reply("CSMSG_DELOPTION_DONE");
6636 reply("CSMSG_DELOPTION_NONE");
6641 static CHANSERV_FUNC(cmd_vote)
6643 struct chanData *cData = channel->channel_info;
6644 struct userData *target;
6645 struct handle_info *hi;
6646 unsigned int votedfor = 0;
6647 char *votedfor_str = NULL;
6649 if (!cData || !cData->vote) {
6650 reply("CSMSG_NO_VOTE");
6653 if(argc > 1 && cData->vote_start) {
6654 hi = user->handle_info;
6655 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6657 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6660 if(!check_user_level(channel, user, lvlVote, 1, 0)) {
6661 reply("CSMSG_NO_ACCESS");
6665 reply("CSMSG_VOTE_VOTED");
6668 int find_id = atoi(argv[1]);
6671 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6673 if (find_id == ii) {
6674 struct vote_option *vOpt = iter_data(it);
6677 target->votefor = vOpt->option_id;
6678 votedfor = vOpt->option_id;
6679 votedfor_str = vOpt->name;
6683 reply("CSMSG_VOTE_INVALID");
6687 if (!cData->vote_start) {
6688 reply("CSMSG_VOTE_NOT_STARTED");
6690 reply("CSMSG_VOTE_QUESTION",cData->vote);
6692 unsigned int voteid = 0;
6695 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6696 struct vote_option *vOpt = iter_data(it);
6698 reply("CSMSG_VOTE_OPTION",voteid,vOpt->name,vOpt->voted);
6700 if(argc > 1 && cData->vote_start && votedfor_str) {
6701 reply("CSMSG_VOTE_DONE",votedfor_str);
6706 static CHANSERV_FUNC(cmd_startvote)
6708 struct chanData *cData = channel->channel_info;
6709 struct userData *target;
6710 struct handle_info *hi;
6711 if (!cData) return 0;
6712 hi = user->handle_info;
6713 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6715 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6718 if(target->access < 300) {
6719 reply("CSMSG_NO_ACCESS");
6723 reply("CSMSG_NO_VOTE");
6726 if(cData->vote_start) {
6727 reply("CSMSG_STARTVOTE_RUNNING");
6730 if(dict_size(cData->vote_options) < 2) {
6731 reply("CSMSG_VOTE_NEED_OPTIONS");
6734 cData->vote_start = 1;
6735 char response[MAXLEN];
6736 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_TOP"), user->nick);
6737 irc_privmsg(cmd->parent->bot, channel->name, response);
6738 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_QUESTION"), cData->vote);
6739 irc_privmsg(cmd->parent->bot, channel->name, response);
6740 unsigned int voteid = 0;
6742 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6743 struct vote_option *vOpt = iter_data(it);
6745 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_OPTION"), voteid, vOpt->name);
6746 irc_privmsg(cmd->parent->bot, channel->name, response);
6748 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_ACCESS"), cData->lvlOpts[lvlVote]); //Todo
6749 irc_privmsg(cmd->parent->bot, channel->name, response);
6750 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_HOWTO")); //Todo
6751 irc_privmsg(cmd->parent->bot, channel->name, response);
6755 static CHANSERV_FUNC(cmd_endvote)
6757 struct chanData *cData = channel->channel_info;
6758 struct userData *target;
6759 struct handle_info *hi;
6760 if (!cData) return 0;
6761 hi = user->handle_info;
6762 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6764 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6767 if(target->access < 300) {
6768 reply("CSMSG_NO_ACCESS");
6772 reply("CSMSG_NO_VOTE");
6775 if(!cData->vote_start) {
6776 reply("CSMSG_ENDVOTE_STOPPED");
6779 cData->vote_start = 0;
6780 reply("CSMSG_ENDVOTE_DONE");
6784 static CHANSERV_FUNC(cmd_voteresults)
6786 struct chanData *cData = channel->channel_info;
6787 struct userData *target;
6788 struct handle_info *hi;
6789 if (!cData) return 0;
6791 reply("CSMSG_NO_VOTE");
6794 if (argc > 1 && !irccasecmp(argv[1], "*")) {
6795 hi = user->handle_info;
6796 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6798 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6801 if(target->access < 300) {
6802 reply("CSMSG_NO_ACCESS");
6805 char response[MAXLEN];
6806 sprintf(response, user_find_message(user, "CSMSG_VOTERES_QUESTION"), cData->vote);
6807 irc_privmsg(cmd->parent->bot, channel->name, response);
6808 unsigned int voteid = 0;
6810 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6811 struct vote_option *vOpt = iter_data(it);
6813 sprintf(response, user_find_message(user, "CSMSG_VOTERES_OPTION"), voteid, vOpt->name, vOpt->voted);
6814 irc_privmsg(cmd->parent->bot, channel->name, response);
6817 reply("CSMSG_VOTE_QUESTION",cData->vote);
6818 unsigned int voteid = 0;
6820 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6821 struct vote_option *vOpt = iter_data(it);
6823 reply("CSMSG_VOTE_OPTION",voteid,vOpt->name,vOpt->voted);
6830 chanserv_refresh_topics(UNUSED_ARG(void *data))
6832 unsigned int refresh_num = (now - self->link_time) / chanserv_conf.refresh_period;
6833 struct chanData *cData;
6836 for(cData = channelList; cData; cData = cData->next)
6838 if(IsSuspended(cData))
6840 opt = cData->chOpts[chTopicRefresh];
6843 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
6846 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
6847 cData->last_refresh = refresh_num;
6849 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
6852 static CHANSERV_FUNC(cmd_unf)
6856 char response[MAXLEN];
6857 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
6858 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6859 irc_privmsg(cmd->parent->bot, channel->name, response);
6862 reply("CSMSG_UNF_RESPONSE");
6866 static CHANSERV_FUNC(cmd_ping)
6870 char response[MAXLEN];
6871 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
6872 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6873 irc_privmsg(cmd->parent->bot, channel->name, response);
6876 reply("CSMSG_PING_RESPONSE");
6880 static CHANSERV_FUNC(cmd_wut)
6884 char response[MAXLEN];
6885 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
6886 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6887 irc_privmsg(cmd->parent->bot, channel->name, response);
6890 reply("CSMSG_WUT_RESPONSE");
6894 static CHANSERV_FUNC(cmd_8ball)
6896 unsigned int i, j, accum;
6901 for(i=1; i<argc; i++)
6902 for(j=0; argv[i][j]; j++)
6903 accum = (accum << 5) - accum + toupper(argv[i][j]);
6904 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
6907 char response[MAXLEN];
6908 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
6909 irc_privmsg(cmd->parent->bot, channel->name, response);
6912 send_message_type(4, user, cmd->parent->bot, "%s", resp);
6916 static CHANSERV_FUNC(cmd_d)
6918 unsigned long sides, count, modifier, ii, total;
6919 char response[MAXLEN], *sep;
6923 if((count = strtoul(argv[1], &sep, 10)) < 1)
6933 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
6934 && (sides = strtoul(sep+1, &sep, 10)) > 1)
6938 else if((sep[0] == '-') && isdigit(sep[1]))
6939 modifier = strtoul(sep, NULL, 10);
6940 else if((sep[0] == '+') && isdigit(sep[1]))
6941 modifier = strtoul(sep+1, NULL, 10);
6948 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
6953 reply("CSMSG_BAD_DICE_COUNT", count, 10);
6956 for(total = ii = 0; ii < count; ++ii)
6957 total += (rand() % sides) + 1;
6960 if((count > 1) || modifier)
6962 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
6963 sprintf(response, fmt, total, count, sides, modifier);
6967 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
6968 sprintf(response, fmt, total, sides);
6971 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
6973 send_message_type(4, user, cmd->parent->bot, "%s", response);
6977 static CHANSERV_FUNC(cmd_huggle)
6979 /* CTCP must be via PRIVMSG, never notice */
6981 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
6983 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
6988 chanserv_adjust_limit(void *data)
6990 struct mod_chanmode change;
6991 struct chanData *cData = data;
6992 struct chanNode *channel = cData->channel;
6995 if(IsSuspended(cData))
6998 cData->limitAdjusted = now;
6999 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
7000 if(cData->modes.modes_set & MODE_LIMIT)
7002 if(limit > cData->modes.new_limit)
7003 limit = cData->modes.new_limit;
7004 else if(limit == cData->modes.new_limit)
7008 mod_chanmode_init(&change);
7009 change.modes_set = MODE_LIMIT;
7010 change.new_limit = limit;
7011 mod_chanmode_announce(chanserv, channel, &change);
7015 handle_new_channel(struct chanNode *channel)
7017 struct chanData *cData;
7019 if(!(cData = channel->channel_info))
7022 if(cData->modes.modes_set || cData->modes.modes_clear)
7023 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
7025 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
7026 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
7029 /* Welcome to my worst nightmare. Warning: Read (or modify)
7030 the code below at your own risk. */
7032 handle_join(struct modeNode *mNode)
7034 struct mod_chanmode change;
7035 struct userNode *user = mNode->user;
7036 struct chanNode *channel = mNode->channel;
7037 struct chanData *cData;
7038 struct userData *uData = NULL;
7039 struct banData *bData;
7040 struct handle_info *handle;
7041 unsigned int modes = 0, info = 0;
7045 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
7048 cData = channel->channel_info;
7049 if(channel->members.used > cData->max) {
7050 cData->max = channel->members.used;
7051 cData->max_time = now;
7054 for(i = 0; i < channel->invited.used; i++)
7056 if(channel->invited.list[i] == user) {
7057 userList_remove(&channel->invited, user);
7061 /* Check for bans. If they're joining through a ban, one of two
7063 * 1: Join during a netburst, by riding the break. Kick them
7064 * unless they have ops or voice in the channel.
7065 * 2: They're allowed to join through the ban (an invite in
7066 * ircu2.10, or a +e on Hybrid, or something).
7067 * If they're not joining through a ban, and the banlist is not
7068 * full, see if they're on the banlist for the channel. If so,
7071 if(user->uplink->burst && !mNode->modes)
7074 for(ii = 0; ii < channel->banlist.used; ii++)
7076 if(user_matches_glob(user, channel->banlist.list[ii]->ban, MATCH_USENICK))
7078 /* Riding a netburst. Naughty. */
7079 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
7085 mod_chanmode_init(&change);
7087 if(channel->banlist.used < MAXBANS)
7089 /* Not joining through a ban. */
7090 for(bData = cData->bans;
7091 bData && !user_matches_glob(user, bData->mask, MATCH_USENICK);
7092 bData = bData->next);
7096 char kick_reason[MAXLEN];
7097 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
7099 bData->triggered = now;
7100 if(bData != cData->bans)
7102 /* Shuffle the ban to the head of the list. */
7104 bData->next->prev = bData->prev;
7106 bData->prev->next = bData->next;
7109 bData->next = cData->bans;
7112 cData->bans->prev = bData;
7113 cData->bans = bData;
7116 change.args[0].mode = MODE_BAN;
7117 change.args[0].u.hostmask = bData->mask;
7118 mod_chanmode_announce(chanserv, channel, &change);
7119 KickChannelUser(user, channel, chanserv, kick_reason);
7124 /* ChanServ will not modify the limits in join-flooded channels,
7125 or when there are enough slots left below the limit. */
7126 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
7127 && !channel->join_flooded
7128 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
7130 /* The user count has begun "bumping" into the channel limit,
7131 so set a timer to raise the limit a bit. Any previous
7132 timers are removed so three incoming users within the delay
7133 results in one limit change, not three. */
7135 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7136 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7139 if(channel->join_flooded)
7141 /* don't automatically give ops or voice during a join flood */
7143 else if(cData->lvlOpts[lvlGiveOps] == 0)
7144 modes |= MODE_CHANOP;
7145 else if(cData->lvlOpts[lvlGiveVoice] == 0)
7146 modes |= MODE_VOICE;
7148 greeting = cData->greeting;
7149 if(user->handle_info)
7151 handle = user->handle_info;
7153 if(IsHelper(user) && !IsHelping(user))
7156 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7158 if(channel == chanserv_conf.support_channels.list[ii])
7160 HANDLE_SET_FLAG(user->handle_info, HELPING);
7166 uData = GetTrueChannelAccess(cData, handle);
7167 if(uData && !IsUserSuspended(uData))
7169 /* Ops and above were handled by the above case. */
7170 if(IsUserAutoOp(uData))
7172 if(uData->access >= cData->lvlOpts[lvlGiveOps])
7173 modes |= MODE_CHANOP;
7174 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
7175 modes |= MODE_VOICE;
7177 if(uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
7178 cData->visited = now;
7179 if(cData->user_greeting)
7180 greeting = cData->user_greeting;
7182 && (uData->access >= cData->lvlOpts[lvlUserInfo])
7183 && ((now - uData->seen) >= chanserv_conf.info_delay)
7191 /* If user joining normally (not during burst), apply op or voice,
7192 * and send greeting/userinfo as appropriate.
7194 if(!user->uplink->burst)
7198 if(modes & MODE_CHANOP)
7199 modes &= ~MODE_VOICE;
7200 change.args[0].mode = modes;
7201 change.args[0].u.member = mNode;
7202 mod_chanmode_announce(chanserv, channel, &change);
7205 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
7206 if(uData && info && (modes || !(channel->modes & MODE_DELAYJOINS)))
7207 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
7213 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
7215 struct mod_chanmode change;
7216 struct userData *channel;
7217 unsigned int ii, jj;
7219 if(!user->handle_info)
7222 mod_chanmode_init(&change);
7224 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
7226 struct chanNode *cn;
7227 struct modeNode *mn;
7228 if(IsUserSuspended(channel)
7229 || IsSuspended(channel->channel)
7230 || !(cn = channel->channel->channel))
7233 mn = GetUserMode(cn, user);
7236 if(!IsUserSuspended(channel)
7237 && IsUserAutoInvite(channel)
7238 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
7240 && !user->uplink->burst)
7241 irc_invite(chanserv, user, cn);
7245 if(channel->access >= UL_PRESENT && !HANDLE_FLAGGED(channel->handle, BOT))
7246 channel->channel->visited = now;
7248 if(IsUserAutoOp(channel))
7250 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
7251 change.args[0].mode = MODE_CHANOP;
7252 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
7253 change.args[0].mode = MODE_VOICE;
7255 change.args[0].mode = 0;
7256 change.args[0].u.member = mn;
7257 if(change.args[0].mode)
7258 mod_chanmode_announce(chanserv, cn, &change);
7261 channel->seen = now;
7262 channel->present = 1;
7265 for(ii = 0; ii < user->channels.used; ++ii)
7267 struct chanNode *chan = user->channels.list[ii]->channel;
7268 struct banData *ban;
7270 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
7271 || !chan->channel_info
7272 || IsSuspended(chan->channel_info))
7274 for(jj = 0; jj < chan->banlist.used; ++jj)
7275 if(user_matches_glob(user, chan->banlist.list[jj]->ban, MATCH_USENICK))
7277 if(jj < chan->banlist.used)
7279 for(ban = chan->channel_info->bans; ban; ban = ban->next)
7281 char kick_reason[MAXLEN];
7282 if(!user_matches_glob(user, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
7284 change.args[0].mode = MODE_BAN;
7285 change.args[0].u.hostmask = ban->mask;
7286 mod_chanmode_announce(chanserv, chan, &change);
7287 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
7288 KickChannelUser(user, chan, chanserv, kick_reason);
7289 ban->triggered = now;
7294 if(IsSupportHelper(user))
7296 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7298 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
7300 HANDLE_SET_FLAG(user->handle_info, HELPING);
7308 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
7310 struct chanData *cData;
7311 struct userData *uData;
7313 cData = mn->channel->channel_info;
7314 if(!cData || IsSuspended(cData) || IsLocal(mn->user))
7317 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
7319 /* Allow for a bit of padding so that the limit doesn't
7320 track the user count exactly, which could get annoying. */
7321 if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
7323 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7324 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7328 if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
7330 scan_user_presence(uData, mn->user);
7332 if (uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
7333 cData->visited = now;
7336 if(IsHelping(mn->user) && IsSupportHelper(mn->user))
7339 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii) {
7340 struct chanNode *channel;
7341 struct userNode *exclude;
7342 /* When looking at the channel that is being /part'ed, we
7343 * have to skip over the client that is leaving. For
7344 * other channels, we must not do that.
7346 channel = chanserv_conf.support_channels.list[ii];
7347 exclude = (channel == mn->channel) ? mn->user : NULL;
7348 if(find_handle_in_channel(channel, mn->user->handle_info, exclude))
7351 if(ii == chanserv_conf.support_channels.used)
7352 HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
7357 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
7359 struct userData *uData;
7361 if(!channel->channel_info || !kicker || IsService(kicker)
7362 || (kicker == victim) || IsSuspended(channel->channel_info)
7363 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
7366 if(protect_user(victim, kicker, channel->channel_info))
7368 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED_2");
7369 KickChannelUser(kicker, channel, chanserv, reason);
7372 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
7377 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
7379 struct chanData *cData;
7381 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
7384 cData = channel->channel_info;
7385 if(bad_topic(channel, user, channel->topic))
7387 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
7388 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
7389 SetChannelTopic(channel, chanserv, old_topic, 1);
7390 else if(cData->topic)
7391 SetChannelTopic(channel, chanserv, cData->topic, 1);
7394 /* With topicsnarf, grab the topic and save it as the default topic. */
7395 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
7398 cData->topic = strdup(channel->topic);
7404 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
7406 struct mod_chanmode *bounce = NULL;
7407 unsigned int bnc, ii;
7410 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
7413 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
7414 && mode_lock_violated(&channel->channel_info->modes, change))
7416 char correct[MAXLEN];
7417 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
7418 mod_chanmode_format(&channel->channel_info->modes, correct);
7419 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
7421 for(ii = bnc = 0; ii < change->argc; ++ii)
7423 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
7425 const struct userNode *victim = change->args[ii].u.member->user;
7426 if(!protect_user(victim, user, channel->channel_info))
7429 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7432 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
7433 bounce->args[bnc].u.member = GetUserMode(channel, user);
7434 if(bounce->args[bnc].u.member)
7438 bounce->args[bnc].mode = MODE_CHANOP;
7439 bounce->args[bnc].u.member = change->args[ii].u.member;
7441 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
7443 else if(change->args[ii].mode & MODE_CHANOP)
7445 const struct userNode *victim = change->args[ii].u.member->user;
7446 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
7449 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7450 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
7451 bounce->args[bnc].u.member = change->args[ii].u.member;
7454 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
7456 const char *ban = change->args[ii].u.hostmask;
7457 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
7460 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7461 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
7462 bounce->args[bnc].u.hostmask = strdup(ban);
7464 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
7469 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
7470 mod_chanmode_announce(chanserv, channel, bounce);
7471 for(ii = 0; ii < change->argc; ++ii)
7472 if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
7473 free((char*)bounce->args[ii].u.hostmask);
7474 mod_chanmode_free(bounce);
7479 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
7481 struct chanNode *channel;
7482 struct banData *bData;
7483 struct mod_chanmode change;
7484 unsigned int ii, jj;
7485 char kick_reason[MAXLEN];
7487 mod_chanmode_init(&change);
7489 change.args[0].mode = MODE_BAN;
7490 for(ii = 0; ii < user->channels.used; ++ii)
7492 channel = user->channels.list[ii]->channel;
7493 /* Need not check for bans if they're opped or voiced. */
7494 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
7496 /* Need not check for bans unless channel registration is active. */
7497 if(!channel->channel_info || IsSuspended(channel->channel_info))
7499 /* Look for a matching ban already on the channel. */
7500 for(jj = 0; jj < channel->banlist.used; ++jj)
7501 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
7503 /* Need not act if we found one. */
7504 if(jj < channel->banlist.used)
7506 /* Look for a matching ban in this channel. */
7507 for(bData = channel->channel_info->bans; bData; bData = bData->next)
7509 if(!user_matches_glob(user, bData->mask, MATCH_USENICK | MATCH_VISIBLE))
7511 change.args[0].u.hostmask = bData->mask;
7512 mod_chanmode_announce(chanserv, channel, &change);
7513 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
7514 KickChannelUser(user, channel, chanserv, kick_reason);
7515 bData->triggered = now;
7516 break; /* we don't need to check any more bans in the channel */
7521 static void handle_rename(struct handle_info *handle, const char *old_handle)
7523 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
7527 dict_remove2(handle_dnrs, old_handle, 1);
7528 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
7529 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
7534 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
7536 struct userNode *h_user;
7538 if(handle->channels)
7540 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
7541 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
7543 while(handle->channels)
7544 del_channel_user(handle->channels, 1);
7549 handle_server_link(UNUSED_ARG(struct server *server))
7551 struct chanData *cData;
7553 for(cData = channelList; cData; cData = cData->next)
7555 if(!IsSuspended(cData))
7556 cData->may_opchan = 1;
7557 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
7558 && !cData->channel->join_flooded
7559 && ((cData->channel->limit - cData->channel->members.used)
7560 < chanserv_conf.adjust_threshold))
7562 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7563 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7569 chanserv_conf_read(void)
7573 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
7574 struct mod_chanmode *change;
7575 struct string_list *strlist;
7576 struct chanNode *chan;
7579 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
7581 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
7584 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7585 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7586 chanserv_conf.support_channels.used = 0;
7587 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
7589 for(ii = 0; ii < strlist->used; ++ii)
7591 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7594 chan = AddChannel(strlist->list[ii], now, str2, NULL);
7596 channelList_append(&chanserv_conf.support_channels, chan);
7599 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
7602 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7605 chan = AddChannel(str, now, str2, NULL);
7607 channelList_append(&chanserv_conf.support_channels, chan);
7609 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
7610 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
7611 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
7612 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
7613 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
7614 chanserv_conf.greeting_length = str ? atoi(str) : 200;
7615 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
7616 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
7617 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
7618 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
7619 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
7620 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
7621 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
7622 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
7623 str = database_get_data(conf_node, KEY_DNR_EXPIRE_FREQ, RECDB_QSTRING);
7624 chanserv_conf.dnr_expire_frequency = str ? ParseInterval(str) : 3600;
7625 str = database_get_data(conf_node, KEY_INVITED_INTERVAL, RECDB_QSTRING);
7626 chanserv_conf.invited_timeout = str ? ParseInterval(str) : 600*2;
7627 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
7628 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
7629 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
7630 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
7631 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
7632 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
7633 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
7634 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
7635 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
7637 NickChange(chanserv, str, 0);
7638 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
7639 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
7640 str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
7641 chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
7642 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
7643 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
7644 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
7645 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
7646 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
7647 chanserv_conf.max_owned = str ? atoi(str) : 5;
7648 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
7649 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
7650 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
7651 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
7652 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
7653 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
7654 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
7657 safestrncpy(mode_line, str, sizeof(mode_line));
7658 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
7659 if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE|MCP_NO_APASS, 0))
7660 && (change->argc < 2))
7662 chanserv_conf.default_modes = *change;
7663 mod_chanmode_free(change);
7665 free_string_list(chanserv_conf.set_shows);
7666 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
7668 strlist = string_list_copy(strlist);
7671 static const char *list[] = {
7672 /* free form text */
7673 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
7674 /* options based on user level */
7675 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
7676 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
7677 /* multiple choice options */
7678 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
7679 /* binary options */
7680 "DynLimit", "NoDelete", "expire", "Vote",
7684 strlist = alloc_string_list(ArrayLength(list)-1);
7685 for(ii=0; list[ii]; ii++)
7686 string_list_append(strlist, strdup(list[ii]));
7688 chanserv_conf.set_shows = strlist;
7689 /* We don't look things up now, in case the list refers to options
7690 * defined by modules initialized after this point. Just mark the
7691 * function list as invalid, so it will be initialized.
7693 set_shows_list.used = 0;
7694 free_string_list(chanserv_conf.eightball);
7695 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
7698 strlist = string_list_copy(strlist);
7702 strlist = alloc_string_list(4);
7703 string_list_append(strlist, strdup("Yes."));
7704 string_list_append(strlist, strdup("No."));
7705 string_list_append(strlist, strdup("Maybe so."));
7707 chanserv_conf.eightball = strlist;
7708 free_string_list(chanserv_conf.old_ban_names);
7709 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
7711 strlist = string_list_copy(strlist);
7713 strlist = alloc_string_list(2);
7714 chanserv_conf.old_ban_names = strlist;
7715 str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
7716 off_channel = str ? atoi(str) : 0;
7720 chanserv_note_type_read(const char *key, struct record_data *rd)
7723 struct note_type *ntype;
7726 if(!(obj = GET_RECORD_OBJECT(rd)))
7728 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
7731 if(!(ntype = chanserv_create_note_type(key)))
7733 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
7737 /* Figure out set access */
7738 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
7740 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7741 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
7743 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
7745 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
7746 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
7748 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
7750 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
7754 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
7755 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7756 ntype->set_access.min_opserv = 0;
7759 /* Figure out visibility */
7760 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
7761 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7762 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
7763 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7764 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
7765 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
7766 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
7767 ntype->visible_type = NOTE_VIS_ALL;
7769 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7771 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
7772 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
7776 vote_option_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7778 struct vote_option *vOpt;
7781 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7783 log_module(CS_LOG, LOG_ERROR, "Invalid vote option in %s.", chan->channel->name);
7787 vOpt = calloc(1, sizeof(*vOpt));
7788 vOpt->name = strdup(database_get_data(rd->d.object, KEY_VOTE_OPTION_NAME, RECDB_QSTRING));
7789 str = database_get_data(rd->d.object, KEY_VOTE_OPTION_VOTED, RECDB_QSTRING);
7790 vOpt->voted = str ? atoi(str) : 0;
7791 vOpt->option_id = str ? atoi(key) : 0;
7792 vOpt->option_str = strdup(key);
7793 dict_insert(chan->vote_options,vOpt->option_str,vOpt);
7797 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7799 struct handle_info *handle;
7800 struct userData *uData;
7801 char *seen, *inf, *flags, *voted, *votefor;
7802 unsigned long last_seen;
7803 unsigned short access_level;
7805 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7807 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
7811 access_level = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
7812 if(access_level > UL_OWNER)
7814 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
7818 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
7819 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
7820 last_seen = seen ? strtoul(seen, NULL, 0) : now;
7821 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
7822 voted = database_get_data(rd->d.object, KEY_VOTE_VOTED, RECDB_QSTRING);
7823 votefor = database_get_data(rd->d.object, KEY_VOTE_VOTEDFOR, RECDB_QSTRING);
7824 handle = get_handle_info(key);
7827 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
7831 uData = add_channel_user(chan, handle, access_level, last_seen, inf);
7832 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
7834 uData->voted = voted ? strtoul(voted, NULL, 0) : 0;
7835 uData->votefor = votefor ? strtoul(votefor, NULL, 0) : 0;
7843 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7845 struct banData *bData;
7846 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
7847 unsigned long set_time, triggered_time, expires_time;
7849 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7851 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
7855 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
7856 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
7857 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
7858 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
7859 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
7860 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
7861 if (!reason || !owner)
7864 set_time = set ? strtoul(set, NULL, 0) : now;
7865 triggered_time = triggered ? strtoul(triggered, NULL, 0) : 0;
7867 expires_time = strtoul(s_expires, NULL, 0);
7869 expires_time = set_time + atoi(s_duration);
7873 if(!reason || (expires_time && (expires_time < now)))
7876 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
7879 static struct suspended *
7880 chanserv_read_suspended(dict_t obj)
7882 struct suspended *suspended = calloc(1, sizeof(*suspended));
7886 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
7887 suspended->expires = str ? strtoul(str, NULL, 0) : 0;
7888 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
7889 suspended->revoked = str ? strtoul(str, NULL, 0) : 0;
7890 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
7891 suspended->issued = str ? strtoul(str, NULL, 0) : 0;
7892 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
7893 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
7894 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
7895 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
7900 chanserv_channel_read(const char *key, struct record_data *hir)
7902 struct suspended *suspended;
7903 struct mod_chanmode *modes;
7904 struct chanNode *cNode;
7905 struct chanData *cData;
7906 struct dict *channel, *obj;
7907 char *str, *argv[10];
7911 channel = hir->d.object;
7913 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
7916 cNode = AddChannel(key, now, NULL, NULL);
7919 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
7922 cData = register_channel(cNode, str);
7925 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
7929 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
7931 enum levelOption lvlOpt;
7932 enum charOption chOpt;
7934 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
7935 cData->flags = atoi(str);
7937 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7939 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
7941 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
7942 else if(levelOptions[lvlOpt].old_flag)
7944 if(cData->flags & levelOptions[lvlOpt].old_flag)
7945 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
7947 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
7951 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7953 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
7955 cData->chOpts[chOpt] = str[0];
7958 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
7960 enum levelOption lvlOpt;
7961 enum charOption chOpt;
7964 cData->flags = base64toint(str, 5);
7965 count = strlen(str += 5);
7966 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7969 if(levelOptions[lvlOpt].old_flag)
7971 if(cData->flags & levelOptions[lvlOpt].old_flag)
7972 lvl = levelOptions[lvlOpt].flag_value;
7974 lvl = levelOptions[lvlOpt].default_value;
7976 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
7978 case 'c': lvl = UL_COOWNER; break;
7979 case 'm': lvl = UL_MASTER; break;
7980 case 'n': lvl = UL_OWNER+1; break;
7981 case 'o': lvl = UL_OP; break;
7982 case 'p': lvl = UL_PEON; break;
7983 case 'w': lvl = UL_OWNER; break;
7984 default: lvl = 0; break;
7986 cData->lvlOpts[lvlOpt] = lvl;
7988 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7989 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
7992 if((str = database_get_data(hir->d.object, KEY_EXPIRE, RECDB_QSTRING)))
7994 cData->expiry = atoi(str);
7995 if(cData->expiry > 0) {
7996 if(cData->expiry > now) {
7997 timeq_add(cData->expiry, chanserv_expire_channel, cData);
7999 timeq_add(1, chanserv_expire_channel, cData);
8006 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
8008 suspended = chanserv_read_suspended(obj);
8009 cData->suspended = suspended;
8010 suspended->cData = cData;
8011 /* We could use suspended->expires and suspended->revoked to
8012 * set the CHANNEL_SUSPENDED flag, but we don't. */
8014 else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
8016 suspended = calloc(1, sizeof(*suspended));
8017 suspended->issued = 0;
8018 suspended->revoked = 0;
8019 suspended->suspender = strdup(str);
8020 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
8021 suspended->expires = str ? atoi(str) : 0;
8022 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
8023 suspended->reason = strdup(str ? str : "No reason");
8024 suspended->previous = NULL;
8025 cData->suspended = suspended;
8026 suspended->cData = cData;
8030 cData->flags &= ~CHANNEL_SUSPENDED;
8031 suspended = NULL; /* to squelch a warning */
8034 if(IsSuspended(cData)) {
8035 if(suspended->expires > now)
8036 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
8037 else if(suspended->expires)
8038 cData->flags &= ~CHANNEL_SUSPENDED;
8041 if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
8042 struct mod_chanmode change;
8043 mod_chanmode_init(&change);
8045 change.args[0].mode = MODE_CHANOP;
8046 change.args[0].u.member = AddChannelUser(chanserv, cNode);
8047 mod_chanmode_announce(chanserv, cNode, &change);
8050 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
8051 cData->registered = str ? strtoul(str, NULL, 0) : now;
8052 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
8053 cData->visited = str ? strtoul(str, NULL, 0) : now;
8054 str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
8055 cData->ownerTransfer = str ? strtoul(str, NULL, 0) : 0;
8056 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
8057 cData->max = str ? atoi(str) : 0;
8058 str = database_get_data(channel, KEY_MAX_TIME, RECDB_QSTRING);
8059 cData->max_time = str ? atoi(str) : 0;
8060 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
8061 cData->greeting = str ? strdup(str) : NULL;
8062 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
8063 cData->user_greeting = str ? strdup(str) : NULL;
8064 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
8065 cData->topic_mask = str ? strdup(str) : NULL;
8066 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
8067 cData->topic = str ? strdup(str) : NULL;
8069 str = database_get_data(channel, KEY_VOTE, RECDB_QSTRING);
8071 cData->vote = str ? strdup(str) : NULL;
8072 dict_delete(cData->vote_options);
8073 cData->vote_options = dict_new();
8074 dict_set_free_data(cData->vote_options, free_vote_options);
8075 str = database_get_data(channel, KEY_VOTE_START, RECDB_QSTRING);
8076 cData->vote_start = str ? atoi(str) : 0;
8077 obj = database_get_data(channel, KEY_VOTE_OPTIONS, RECDB_OBJECT);
8078 for(it = dict_first(obj); it; it = iter_next(it)) {
8079 vote_option_read_helper(iter_key(it), iter_data(it), cData);
8083 if(!IsSuspended(cData)
8084 && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
8085 && (argc = split_line(str, 0, ArrayLength(argv), argv))
8086 && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE|MCP_NO_APASS, 0))) {
8087 cData->modes = *modes;
8089 cData->modes.modes_set |= MODE_REGISTERED;
8090 if(cData->modes.argc > 1)
8091 cData->modes.argc = 1;
8092 mod_chanmode_announce(chanserv, cNode, &cData->modes);
8093 mod_chanmode_free(modes);
8096 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
8097 for(it = dict_first(obj); it; it = iter_next(it))
8098 user_read_helper(iter_key(it), iter_data(it), cData);
8100 if(!cData->users && !IsProtected(cData))
8102 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
8103 unregister_channel(cData, "has empty user list.");
8107 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
8108 for(it = dict_first(obj); it; it = iter_next(it))
8109 ban_read_helper(iter_key(it), iter_data(it), cData);
8111 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
8112 for(it = dict_first(obj); it; it = iter_next(it))
8114 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
8115 struct record_data *rd = iter_data(it);
8116 const char *note, *setter;
8118 if(rd->type != RECDB_OBJECT)
8120 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
8124 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
8126 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
8128 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
8132 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
8133 if(!setter) setter = "<unknown>";
8134 chanserv_add_channel_note(cData, ntype, setter, note);
8142 chanserv_dnr_read(const char *key, struct record_data *hir)
8144 const char *setter, *reason, *str;
8145 struct do_not_register *dnr;
8146 unsigned long expiry;
8148 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
8151 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
8154 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
8157 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
8160 str = database_get_data(hir->d.object, KEY_EXPIRES, RECDB_QSTRING);
8161 expiry = str ? strtoul(str, NULL, 0) : 0;
8162 if(expiry && expiry <= now)
8164 dnr = chanserv_add_dnr(key, setter, expiry, reason);
8167 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
8169 dnr->set = atoi(str);
8175 chanserv_saxdb_read(struct dict *database)
8177 struct dict *section;
8180 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
8181 for(it = dict_first(section); it; it = iter_next(it))
8182 chanserv_note_type_read(iter_key(it), iter_data(it));
8184 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
8185 for(it = dict_first(section); it; it = iter_next(it))
8186 chanserv_channel_read(iter_key(it), iter_data(it));
8188 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
8189 for(it = dict_first(section); it; it = iter_next(it))
8190 chanserv_dnr_read(iter_key(it), iter_data(it));
8196 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
8198 int high_present = 0;
8199 saxdb_start_record(ctx, KEY_USERS, 1);
8200 for(; uData; uData = uData->next)
8202 if((uData->access >= UL_PRESENT) && uData->present && !HANDLE_FLAGGED(uData->handle, BOT))
8204 saxdb_start_record(ctx, uData->handle->handle, 0);
8205 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
8206 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
8208 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
8209 if(uData->channel->vote && uData->voted)
8210 saxdb_write_int(ctx, KEY_VOTE_VOTED, uData->voted);
8211 if(uData->channel->vote && uData->votefor)
8212 saxdb_write_int(ctx, KEY_VOTE_VOTEDFOR, uData->votefor);
8214 saxdb_write_string(ctx, KEY_INFO, uData->info);
8215 saxdb_end_record(ctx);
8217 saxdb_end_record(ctx);
8218 return high_present;
8222 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
8226 saxdb_start_record(ctx, KEY_BANS, 1);
8227 for(; bData; bData = bData->next)
8229 saxdb_start_record(ctx, bData->mask, 0);
8230 saxdb_write_int(ctx, KEY_SET, bData->set);
8231 if(bData->triggered)
8232 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
8234 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
8236 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
8238 saxdb_write_string(ctx, KEY_REASON, bData->reason);
8239 saxdb_end_record(ctx);
8241 saxdb_end_record(ctx);
8245 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
8247 saxdb_start_record(ctx, name, 0);
8248 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
8249 saxdb_write_string(ctx, KEY_REASON, susp->reason);
8251 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
8253 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
8255 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
8257 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
8258 saxdb_end_record(ctx);
8262 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
8266 enum levelOption lvlOpt;
8267 enum charOption chOpt;
8270 saxdb_start_record(ctx, channel->channel->name, 1);
8272 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
8273 saxdb_write_int(ctx, KEY_MAX, channel->max);
8274 saxdb_write_int(ctx, KEY_MAX_TIME, channel->max_time);
8276 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
8277 if(channel->registrar)
8278 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
8279 if(channel->greeting)
8280 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
8281 if(channel->user_greeting)
8282 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
8283 if(channel->topic_mask)
8284 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
8285 if(channel->suspended)
8286 chanserv_write_suspended(ctx, "suspended", channel->suspended);
8288 saxdb_write_int(ctx, KEY_EXPIRE, channel->expiry);
8291 saxdb_write_string(ctx, KEY_VOTE, channel->vote);
8292 if(channel->vote_start)
8293 saxdb_write_int(ctx, KEY_VOTE_START, channel->vote_start);
8294 if (dict_size(channel->vote_options)) {
8295 saxdb_start_record(ctx, KEY_VOTE_OPTIONS, 1);
8296 for (it = dict_first(channel->vote_options); it; it = iter_next(it)) {
8297 struct vote_option *vOpt = iter_data(it);
8299 sprintf(str,"%i",vOpt->option_id);
8300 saxdb_start_record(ctx, str, 0);
8302 saxdb_write_int(ctx, KEY_VOTE_OPTION_VOTED, vOpt->voted);
8304 saxdb_write_string(ctx, KEY_VOTE_OPTION_NAME, vOpt->name);
8305 saxdb_end_record(ctx);
8307 saxdb_end_record(ctx);
8311 saxdb_start_record(ctx, KEY_OPTIONS, 0);
8312 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
8313 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8314 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
8315 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8317 buf[0] = channel->chOpts[chOpt];
8319 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
8321 saxdb_end_record(ctx);
8323 if(channel->modes.modes_set || channel->modes.modes_clear)
8325 mod_chanmode_format(&channel->modes, buf);
8326 saxdb_write_string(ctx, KEY_MODES, buf);
8329 high_present = chanserv_write_users(ctx, channel->users);
8330 chanserv_write_bans(ctx, channel->bans);
8332 if(dict_size(channel->notes))
8336 saxdb_start_record(ctx, KEY_NOTES, 1);
8337 for(it = dict_first(channel->notes); it; it = iter_next(it))
8339 struct note *note = iter_data(it);
8340 saxdb_start_record(ctx, iter_key(it), 0);
8341 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
8342 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
8343 saxdb_end_record(ctx);
8345 saxdb_end_record(ctx);
8348 if(channel->ownerTransfer)
8349 saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
8350 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
8351 saxdb_end_record(ctx);
8355 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
8359 saxdb_start_record(ctx, ntype->name, 0);
8360 switch(ntype->set_access_type)
8362 case NOTE_SET_CHANNEL_ACCESS:
8363 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
8365 case NOTE_SET_CHANNEL_SETTER:
8366 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
8368 case NOTE_SET_PRIVILEGED: default:
8369 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
8372 switch(ntype->visible_type)
8374 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
8375 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
8376 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
8378 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
8379 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
8380 saxdb_end_record(ctx);
8384 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
8386 struct do_not_register *dnr;
8387 dict_iterator_t it, next;
8389 for(it = dict_first(dnrs); it; it = next)
8391 next = iter_next(it);
8392 dnr = iter_data(it);
8393 if(dnr->expires && dnr->expires <= now)
8395 dict_remove(dnrs, iter_key(it));
8398 saxdb_start_record(ctx, dnr->chan_name, 0);
8400 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
8402 saxdb_write_int(ctx, KEY_EXPIRES, dnr->expires);
8403 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
8404 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
8405 saxdb_end_record(ctx);
8410 chanserv_saxdb_write(struct saxdb_context *ctx)
8413 struct chanData *channel;
8416 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
8417 for(it = dict_first(note_types); it; it = iter_next(it))
8418 chanserv_write_note_type(ctx, iter_data(it));
8419 saxdb_end_record(ctx);
8422 saxdb_start_record(ctx, KEY_DNR, 1);
8423 write_dnrs_helper(ctx, handle_dnrs);
8424 write_dnrs_helper(ctx, plain_dnrs);
8425 write_dnrs_helper(ctx, mask_dnrs);
8426 saxdb_end_record(ctx);
8429 saxdb_start_record(ctx, KEY_CHANNELS, 1);
8430 for(channel = channelList; channel; channel = channel->next)
8431 chanserv_write_channel(ctx, channel);
8432 saxdb_end_record(ctx);
8438 chanserv_db_cleanup(void) {
8440 unreg_part_func(handle_part);
8442 unregister_channel(channelList, "terminating.");
8443 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
8444 UnlockChannel(chanserv_conf.support_channels.list[ii]);
8445 free(chanserv_conf.support_channels.list);
8446 dict_delete(handle_dnrs);
8447 dict_delete(plain_dnrs);
8448 dict_delete(mask_dnrs);
8449 dict_delete(note_types);
8450 free_string_list(chanserv_conf.eightball);
8451 free_string_list(chanserv_conf.old_ban_names);
8452 free_string_list(chanserv_conf.set_shows);
8453 free(set_shows_list.list);
8454 free(uset_shows_list.list);
8457 struct userData *helper = helperList;
8458 helperList = helperList->next;
8463 #if defined(GCC_VARMACROS)
8464 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ARGS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ARGS)
8465 #elif defined(C99_VARMACROS)
8466 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, __VA_ARGS__)
8468 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
8469 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
8472 init_chanserv(const char *nick)
8474 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
8475 conf_register_reload(chanserv_conf_read);
8479 reg_server_link_func(handle_server_link);
8480 reg_new_channel_func(handle_new_channel);
8481 reg_join_func(handle_join);
8482 reg_part_func(handle_part);
8483 reg_kick_func(handle_kick);
8484 reg_topic_func(handle_topic);
8485 reg_mode_change_func(handle_mode);
8486 reg_nick_change_func(handle_nick_change);
8487 reg_auth_func(handle_auth);
8490 reg_handle_rename_func(handle_rename);
8491 reg_unreg_func(handle_unreg);
8493 handle_dnrs = dict_new();
8494 dict_set_free_data(handle_dnrs, free);
8495 plain_dnrs = dict_new();
8496 dict_set_free_data(plain_dnrs, free);
8497 mask_dnrs = dict_new();
8498 dict_set_free_data(mask_dnrs, free);
8500 reg_svccmd_unbind_func(handle_svccmd_unbind);
8501 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
8502 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
8503 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
8504 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
8505 DEFINE_COMMAND(dnrsearch, 3, 0, "template", "noregister", NULL);
8506 modcmd_register(chanserv_module, "dnrsearch print", NULL, 0, 0, NULL);
8507 modcmd_register(chanserv_module, "dnrsearch remove", NULL, 0, 0, NULL);
8508 modcmd_register(chanserv_module, "dnrsearch count", NULL, 0, 0, NULL);
8509 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
8510 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
8511 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
8512 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
8513 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
8515 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
8516 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
8518 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8519 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8520 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8521 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8522 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
8524 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
8525 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
8526 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
8527 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8528 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8530 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8531 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
8532 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8533 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
8535 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
8536 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
8537 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8538 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8539 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
8540 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8541 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8542 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8544 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8545 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8546 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8547 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
8548 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
8549 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
8550 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
8551 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
8552 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8553 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
8554 DEFINE_COMMAND(invitemeall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8555 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
8556 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
8557 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8558 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8560 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
8561 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8562 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8563 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8564 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
8566 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
8567 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
8569 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
8570 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8571 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8572 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8573 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8574 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8575 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8576 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8577 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8578 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8579 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8581 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
8582 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
8584 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
8585 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
8586 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
8587 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
8589 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
8590 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
8591 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
8592 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
8593 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
8595 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8596 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8597 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8598 DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8599 DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8600 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8602 DEFINE_COMMAND(addvote, 1, MODCMD_REQUIRE_AUTHED, NULL);
8603 DEFINE_COMMAND(delvote, 1, MODCMD_REQUIRE_AUTHED, NULL);
8604 DEFINE_COMMAND(addoption, 1, MODCMD_REQUIRE_AUTHED, NULL);
8605 DEFINE_COMMAND(deloption, 1, MODCMD_REQUIRE_AUTHED, NULL);
8606 DEFINE_COMMAND(vote, 1, MODCMD_REQUIRE_AUTHED, NULL);
8607 DEFINE_COMMAND(startvote, 1, MODCMD_REQUIRE_AUTHED, NULL);
8608 DEFINE_COMMAND(endvote, 1, MODCMD_REQUIRE_AUTHED, NULL);
8609 DEFINE_COMMAND(voteresults, 1, MODCMD_REQUIRE_AUTHED, NULL);
8611 /* Channel options */
8612 DEFINE_CHANNEL_OPTION(defaulttopic);
8613 DEFINE_CHANNEL_OPTION(topicmask);
8614 DEFINE_CHANNEL_OPTION(greeting);
8615 DEFINE_CHANNEL_OPTION(usergreeting);
8616 DEFINE_CHANNEL_OPTION(modes);
8617 DEFINE_CHANNEL_OPTION(enfops);
8618 DEFINE_CHANNEL_OPTION(giveops);
8619 DEFINE_CHANNEL_OPTION(protect);
8620 DEFINE_CHANNEL_OPTION(enfmodes);
8621 DEFINE_CHANNEL_OPTION(enftopic);
8622 DEFINE_CHANNEL_OPTION(pubcmd);
8623 DEFINE_CHANNEL_OPTION(givevoice);
8624 DEFINE_CHANNEL_OPTION(userinfo);
8625 DEFINE_CHANNEL_OPTION(dynlimit);
8626 DEFINE_CHANNEL_OPTION(topicsnarf);
8627 DEFINE_CHANNEL_OPTION(vote);
8628 DEFINE_CHANNEL_OPTION(nodelete);
8629 DEFINE_CHANNEL_OPTION(toys);
8630 DEFINE_CHANNEL_OPTION(setters);
8631 DEFINE_CHANNEL_OPTION(topicrefresh);
8632 DEFINE_CHANNEL_OPTION(ctcpusers);
8633 DEFINE_CHANNEL_OPTION(ctcpreaction);
8634 DEFINE_CHANNEL_OPTION(inviteme);
8635 DEFINE_CHANNEL_OPTION(unreviewed);
8636 modcmd_register(chanserv_module, "set expire", chan_opt_expire, 1, 0, "flags", "+helping", NULL);
8637 modcmd_register(chanserv_module, "set unreviewed on", NULL, 0, 0, "flags", "+helping", NULL);
8638 modcmd_register(chanserv_module, "set unreviewed off", NULL, 0, 0, "flags", "+oper", NULL);
8640 DEFINE_CHANNEL_OPTION(offchannel);
8641 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
8643 /* Alias set topic to set defaulttopic for compatibility. */
8644 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
8647 DEFINE_USER_OPTION(noautoop);
8648 DEFINE_USER_OPTION(autoinvite);
8649 DEFINE_USER_OPTION(info);
8651 /* Alias uset autovoice to uset autoop. */
8652 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
8654 note_types = dict_new();
8655 dict_set_free_data(note_types, chanserv_deref_note_type);
8658 const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
8659 chanserv = AddLocalUser(nick, nick, NULL, "Channel Services", modes);
8660 service_register(chanserv)->trigger = '!';
8661 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
8663 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
8665 if(chanserv_conf.channel_expire_frequency)
8666 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
8668 if(chanserv_conf.dnr_expire_frequency)
8669 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
8671 if(chanserv_conf.refresh_period)
8673 unsigned long next_refresh;
8674 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
8675 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
8678 reg_exit_func(chanserv_db_cleanup);
8679 message_register_table(msgtab);