1 /* chanserv.c - Channel service bot
2 * Copyright 2000-2004 srvx Development Team
4 * This file is part of x3.
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() */
29 #define CHANSERV_CONF_NAME "services/chanserv"
31 /* ChanServ options */
32 #define KEY_SUPPORT_CHANNEL "support_channel"
33 #define KEY_SUPPORT_CHANNEL_MODES "support_channel_modes"
34 #define KEY_DB_BACKUP_FREQ "db_backup_freq"
35 #define KEY_INFO_DELAY "info_delay"
36 #define KEY_MAX_GREETLEN "max_greetlen"
37 #define KEY_ADJUST_THRESHOLD "adjust_threshold"
38 #define KEY_ADJUST_DELAY "adjust_delay"
39 #define KEY_CHAN_EXPIRE_FREQ "chan_expire_freq"
40 #define KEY_CHAN_EXPIRE_DELAY "chan_expire_delay"
41 #define KEY_MAX_CHAN_USERS "max_chan_users"
42 #define KEY_MAX_CHAN_BANS "max_chan_bans"
43 #define KEY_NICK "nick"
44 #define KEY_OLD_CHANSERV_NAME "old_chanserv_name"
45 #define KEY_8BALL_RESPONSES "8ball"
46 #define KEY_OLD_BAN_NAMES "old_ban_names"
47 #define KEY_REFRESH_PERIOD "refresh_period"
48 #define KEY_CTCP_SHORT_BAN_DURATION "ctcp_short_ban_duration"
49 #define KEY_CTCP_LONG_BAN_DURATION "ctcp_long_ban_duration"
50 #define KEY_MAX_OWNED "max_owned"
51 #define KEY_IRC_OPERATOR_EPITHET "irc_operator_epithet"
52 #define KEY_NETWORK_HELPER_EPITHET "network_helper_epithet"
53 #define KEY_SUPPORT_HELPER_EPITHET "support_helper_epithet"
54 #define KEY_NODELETE_LEVEL "nodelete_level"
55 #define KEY_MAX_USERINFO_LENGTH "max_userinfo_length"
56 #define KEY_GIVEOWNERSHIP_PERIOD "giveownership_timeout"
58 /* ChanServ database */
59 #define KEY_VERSION_CONTROL "version_control"
60 #define KEY_CHANNELS "channels"
61 #define KEY_NOTE_TYPES "note_types"
63 /* version control paramiter */
64 #define KEY_VERSION_NUMBER "version_number"
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" /* for lamers */
103 #define KEY_MAX "max"
104 #define KEY_NOTES "notes"
105 #define KEY_TOPIC_MASK "topic_mask"
106 #define KEY_OWNER_TRANSFER "owner_transfer"
109 #define KEY_LEVEL "level"
110 #define KEY_INFO "info"
111 #define KEY_SEEN "seen"
114 #define KEY_OWNER "owner"
115 #define KEY_REASON "reason"
116 #define KEY_SET "set"
117 #define KEY_DURATION "duration"
118 #define KEY_EXPIRES "expires"
119 #define KEY_TRIGGERED "triggered"
121 #define CHANNEL_DEFAULT_FLAGS (CHANNEL_OFFCHANNEL)
122 #define CHANNEL_DEFAULT_OPTIONS "lmoooanpcnat"
124 /* Administrative messages */
125 static const struct message_entry msgtab
[] = {
126 { "CSMSG_CHANNELS_EXPIRED", "%i channels expired." },
128 /* Channel registration */
129 { "CSMSG_REG_SUCCESS", "You now have ownership of $b%s$b." },
130 { "CSMSG_PROXY_SUCCESS", "%s now has ownership of $b%s$b." },
131 { "CSMSG_ALREADY_REGGED", "$b%s$b is registered to someone else." },
132 { "CSMSG_MUST_BE_OPPED", "You must be a channel operator (+o) in $b%s$b to register it." },
133 { "CSMSG_PROXY_FORBIDDEN", "You may not register a channel for someone else." },
134 { "CSMSG_OWN_TOO_MANY", "%s already owns more than the limit of %d channels. Use FORCE to override." },
135 { "CSMSG_YOU_OWN_TOO_MANY", "You already own more than the limit of %d channels. Ask a staff member for help." },
136 { "CSMSG_ANOTHER_SERVICE", "Another service bot is in that channel already. Ask a staff member for help." },
138 /* Do-not-register channels */
139 { "CSMSG_NOT_DNR", "$b%s$b is not a valid channel name or *account." },
140 { "CSMSG_DNR_SEARCH_RESULTS", "$bDo-Not-Register Channels$b" },
141 { "CSMSG_DNR_INFO", "$b%s$b (set by $b%s$b): %s" },
142 { "CSMSG_DNR_INFO_SET", "$b%s$b (set %s ago by $b%s$b): %s" },
143 { "CSMSG_MORE_DNRS", "%d more do-not-register entries skipped." },
144 { "CSMSG_DNR_CHANNEL", "Only network staff may register $b%s$b." },
145 { "CSMSG_DNR_CHANNEL_MOVE", "Only network staff may move $b%s$b." },
146 { "CSMSG_DNR_ACCOUNT", "Only network staff may register channels to $b%s$b." },
147 { "CSMSG_NOREGISTER_CHANNEL", "$b%s$b has been added to the do-not-register list." },
148 { "CSMSG_NO_SUCH_DNR", "$b%s$b is not in the do-not-register list." },
149 { "CSMSG_DNR_REMOVED", "$b%s$b has been removed from the do-not-register list." },
151 /* Channel unregistration */
152 { "CSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
153 { "CSMSG_UNREG_NODELETE", "$b%s$b is protected from unregistration." },
154 { "CSMSG_CHAN_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended (%s)." },
155 { "CSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must use 'unregister %s %s'." },
158 { "CSMSG_MOVE_SUCCESS", "Channel registration has been moved to $b%s$b." },
159 { "CSMSG_MOVE_NODELETE", "$b%s$b is protected from unregistration, and cannot be moved." },
161 /* Channel merging */
162 { "CSMSG_MERGE_SUCCESS", "Channel successfully merged into $b%s$b." },
163 { "CSMSG_MERGE_SELF", "Merging cannot be performed if the source and target channels are the same." },
164 { "CSMSG_MERGE_NODELETE", "You may not merge a channel that is marked NoDelete." },
165 { "CSMSG_MERGE_SUSPENDED", "Merging cannot be performed if the source or target channel is suspended." },
166 { "CSMSG_MERGE_NOT_OWNER", "You must be the owner of the target channel (or a helper) to merge into the channel." },
168 /* Handle unregistration */
169 { "CSMSG_HANDLE_UNREGISTERED", "As a result of your account unregistration, you have been deleted from all of your channels' userlists." },
172 { "CSMSG_NOT_USER", "You lack access to $b%s$b." },
173 { "CSMSG_NO_CHAN_USER", "%s lacks access to $b%s$b." },
174 { "CSMSG_NO_ACCESS", "You lack sufficient access to use this command." },
175 { "CSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$C$b." },
176 { "CSMSG_MAXIMUM_LAMERS", "This channel has reached the lamer count limit of $b%d$b." },
177 { "CSMSG_MAXIMUM_USERS", "This channel has reached the user count limit of $b%d$b." },
178 { "CSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel, and cannot be registered." },
179 { "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." },
180 { "CSMSG_ALREADY_OPPED", "You are already opped in $b%s$b." },
181 { "CSMSG_ALREADY_HALFOPPED", "You are already halfopped in $b%s$b." },
182 { "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." },
183 { "CSMSG_ALREADY_DOWN", "You are not opped, halfopped, or voiced in $b%s$b." },
184 { "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." },
185 { "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." },
187 /* Removing yourself from a channel. */
188 { "CSMSG_NO_OWNER_DELETEME", "You cannot delete your owner access in $b%s$b." },
189 { "CSMSG_CONFIRM_DELETEME", "To really remove yourself, you must use 'deleteme %s'." },
190 { "CSMSG_DELETED_YOU", "Your $b%d$b access has been deleted from $b%s$b." },
192 /* User management */
193 { "CSMSG_ADDED_USER", "Added %s to the %s user list with access %s (%d)." },
194 { "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." },
195 { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be greater than maximum (%d)." },
196 { "CSMSG_DELETED_USERS", "Deleted accounts matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list." },
197 { "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." },
198 { "CSMSG_INCORRECT_ACCESS", "%s has access $b%s$b, not %s." },
199 { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with %s access)." },
200 { "CSMSG_ADDUSER_PENDING", "I have sent him/her a message letting them know, and if they auth or register soon, I will finish adding them automatically." },
201 { "CSMSG_ADDUSER_PENDING_ALREADY", "He or she is already pending addition to %s once he/she auths with $b$N$b." },
202 { "CSMSG_ADDUSER_PENDING_HEADER", "Users to add to channels pending logins:" }, /* Remove after testing? */
203 { "CSMSG_ADDUSER_PENDING_LIST", "Channel %s user %s" }, /* Remove after testing? */
204 { "CSMSG_ADDUSER_PENDING_FOOTER", "--------- End of pending list ----------" }, /* Remove after testing? */
205 /*{ "CSMSG_ADDUSER_PENDING_NOTINCHAN", "That user is not in %s, and is not auth'd." }, */
206 { "CSMSG_ADDUSER_PENDING_TARGET", "Channel Services bot here: %s would like to add you to my userlist in channel %s, but you are not authenticated to $b$N$b. Please authenticate now and you will be added. If you do not have an account, type /msg $N help register" },
207 { "CSMSG_CANNOT_TRIM", "You must include a minimum inactivity duration of at least 60 seconds to trim." },
209 { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." },
210 { "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." },
211 { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
212 { "CSMSG_TRANSFER_WAIT", "You must wait %s before you can give ownership of $b%s$b to someone else." },
213 { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
214 { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
217 { "CSMSG_LAMER_ADDED", "Added $b%s$b to %s LAMERs." },
218 { "CSMSG_TIMED_LAMER_ADDED", "LAMERed $b%s$b on %s for %s." },
219 { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
220 { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
221 { "CSMSG_REASON_CHANGE", "Reason for LAMER $b%s$b changed." },
222 { "CSMSG_LAMER_EXTENDED", "Extended LAMER for $b%s$b, now expires in %s." },
223 { "CSMSG_BAN_REMOVED", "Matching ban(s) and LAMER(s) in $b%s$b were removed." },
224 { "CSMSG_TRIMMED_LAMERS", "Trimmed $b%d LAMERs$b from the %s LAMER list that were inactive for at least %s." },
225 { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
226 { "CSMSG_REDUNDANT_LAMER", "$b%s$b is already LAMER'd in %s." },
227 { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
228 { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
229 { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
230 { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
231 { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
232 { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban or LAMER found: $b%s$b." },
233 { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
235 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
237 /* Channel management */
238 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
239 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
240 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
242 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
243 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
244 { "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" },
245 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic is at most %d characters and matches the topic mask pattern." },
246 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
247 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
248 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
250 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
251 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
252 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
253 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
254 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
255 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
256 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you must use 'set defaults %s'." },
257 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
258 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
260 { "CSMSG_BAD_GIVEVOICE", "You cannot change GiveVoice to above GiveHalfOps (%d)." },
261 { "CSMSG_BAD_GIVEHOPS", "You cannot change GiveHalfOps to below GiveOps (%d)." },
262 { "CSMSG_BAD_GIVEOPS", "You cannot change GiveOps to below GiveVoice (%d)." },
264 { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
265 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
266 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
267 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
268 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
269 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
270 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
271 { "CSMSG_SET_MODES", "$bModes $b %s" },
272 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
273 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s" },
274 { "CSMSG_SET_OFFCHANNEL", "$bOffChannel $b %s" },
275 { "CSMSG_SET_USERINFO", "$bUserInfo $b %d" },
277 { "CSMSG_SET_GIVE_VOICE", "$bGiveVoice $b %d" },
278 { "CSMSG_SET_GIVE_HALFOPS", "$bGiveHalfOps $b %d" },
280 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" },
281 { "CSMSG_SET_INVITEME", "$bInviteMe $b %d" },
282 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d" },
283 { "CSMSG_SET_ENFHALFOPS", "$bEnfHalfOps $b %d" },
285 { "CSMSG_SET_GIVE_OPS", "$bGiveOps $b %d" },
287 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d" },
288 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d" },
289 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d" },
290 { "CSMSG_SET_SETTERS", "$bSetters $b %d" },
291 { "CSMSG_SET_CTCPUSERS", "$bCTCPUsers $b %d" },
292 { "CSMSG_SET_VOICE", "$bvoice $b %d - %s" },
293 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
294 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
295 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
296 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
297 { "CSMSG_USET_AUTOOP", "$bAutoOp $b %s" },
298 { "CSMSG_USET_AUTOVOICE", "$bAutoVoice $b %s" },
299 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
300 { "CSMSG_USET_INFO", "$bInfo $b %s" },
302 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
303 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
304 { "CSMSG_HOPBY_LOCKED", "You may not halfop users who lack halfop 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_HALFOPPED_USERS", "Halfopped users in $b%s$b." },
308 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
309 { "CSMSG_DEHALFOPPED_USERS", "DeHalfopped users in $b%s$b." },
310 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
311 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
312 { "CSMSG_VOICE_NONE", "Noone will be auto-voiced" },
313 { "CSMSG_VOICE_PEON", "PEONs will be auto-voiced" },
314 { "CSMSG_VOICE_ALL", "Everyone will be auto-voiced" },
315 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
316 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
317 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
318 { "CSMSG_PROTECT_NONE", "No users will be protected." },
319 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
320 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
321 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
322 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
323 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
324 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
325 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
326 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
327 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
328 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
329 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
330 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
332 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
333 { "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" },
334 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s." },
335 { "CSMSG_ALREADY_PRESENT", "%s is already in $b%s$b." },
336 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
337 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s to use this command." },
338 { "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
339 { "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
341 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
342 { "CSMSG_NO_BANS", "No bans found on $b%s$b." },
343 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
345 /* Channel userlist */
346 { "CSMSG_ACCESS_ALL_HEADER", "$b%s Users From Level %s To %s$b" },
347 { "CSMSG_ACCESS_SEARCH_HEADER", "$b%s Users From Level %s To %s Matching %s$b" },
348 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
349 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%s$b (%u) in %s." },
350 { "CSMSG_LAMERS_HEADER", "$bLamers in %s$b" },
352 /* Channel note list */
353 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
354 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
355 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
356 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
357 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
358 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
359 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
360 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
361 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
362 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
363 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
364 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
365 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
366 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
367 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
369 /* Channel [un]suspension */
370 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
371 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
372 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
373 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
374 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
375 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
376 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
378 /* Access information */
379 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
380 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
381 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
382 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
383 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
384 { "CSMSG_USER_HAS_ACCESS", "%s has $b%s$b access (%d) in %s." },
385 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
386 { "CSMSG_HELPER_HAS_ACCESS", "%s has $b%s$b access (%d) in %s and has $bsecurity override$b enabled." },
387 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
388 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
389 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
391 /* Seen information */
392 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
393 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
394 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
395 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
397 /* Names information */
398 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
399 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
401 /* Channel information */
402 { "CSMSG_CHANNEL_INFO", "$bInformation About %s$b" },
403 { "CSMSG_BAR", "----------------------------------------"},
404 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
405 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
406 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
407 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
408 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
409 { "CSMSG_CHANNEL_LAMERS", "$bLamer Count: $b%i" },
410 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
411 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
412 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
413 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
414 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
415 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
416 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
417 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
418 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
419 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
420 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
421 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
422 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
423 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
424 { "CSMSG_CHANNEL_END", "---------------End of Info--------------"},
426 { "CSMSG_PEEK_INFO", "$bStatus of %s$b" },
427 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
428 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
429 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d" },
430 { "CSMSG_PEEK_OPS", "$bOps:$b" },
431 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
432 { "CSMSG_PEEK_END", "-------------End of Status--------------" },
434 /* Network information */
435 { "CSMSG_NETWORK_INFO", "Network Information:" },
436 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
437 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
438 { "CSMSG_NETWORK_LAMERS", "$bTotal Lamer Count: $b%i" },
439 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
440 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
441 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
442 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
443 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
446 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
447 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
448 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
450 /* Channel searches */
451 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
452 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
453 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
454 { "CSMSG_CHANNEL_SEARCH_RESULTS", "$bChannels Found Matching Search$b" },
456 /* Channel configuration */
457 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
458 { "CSMSG_CHANNEL_OPTIONS", "$bChannel Options for %s$b" },
459 { "CSMSG_CHANNEL_OPTIONS_END", "-------------End of Options-------------" },
460 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
463 { "CSMSG_USER_OPTIONS", "User Options:" },
464 { "CSMSG_USER_PROTECTED", "That user is protected." },
467 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
468 { "CSMSG_PING_RESPONSE", "Pong!" },
469 { "CSMSG_WUT_RESPONSE", "wut" },
470 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
471 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
472 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
473 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
474 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
475 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
476 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
479 { "CSMSG_EVENT_SEARCH_RESULTS", "$bChannel Events for %s$b" },
480 { "CSMSG_LAST_INVALID", "Invalid argument. must be 1-200" },
484 /* eject_user and unban_user flags */
485 #define ACTION_KICK 0x0001
486 #define ACTION_BAN 0x0002
487 #define ACTION_ADD_LAMER 0x0004
488 #define ACTION_ADD_TIMED_LAMER 0x0008
489 #define ACTION_UNBAN 0x0010
490 #define ACTION_DEL_LAMER 0x0020
492 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
493 #define MODELEN 40 + KEYLEN
497 #define CSFUNC_ARGS user, channel, argc, argv, cmd
499 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
500 #define CHANSERV_SYNTAX() svccmd_send_help_brief(user, chanserv, cmd)
501 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
502 reply("MSG_MISSING_PARAMS", argv[0]); \
506 DECLARE_LIST(dnrList
, struct do_not_register
*);
507 DEFINE_LIST(dnrList
, struct do_not_register
*);
509 static int eject_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
);
511 struct userNode
*chanserv
;
514 static dict_t plain_dnrs
, mask_dnrs
, handle_dnrs
;
515 static struct log_type
*CS_LOG
;
516 struct adduserPending
* adduser_pendings
= NULL
;
517 unsigned int adduser_pendings_count
= 0;
521 struct channelList support_channels
;
522 struct mod_chanmode default_modes
;
524 unsigned long db_backup_frequency
;
525 unsigned long channel_expire_frequency
;
528 unsigned int adjust_delay
;
529 long channel_expire_delay
;
530 unsigned int nodelete_level
;
532 unsigned int adjust_threshold
;
533 int join_flood_threshold
;
535 unsigned int greeting_length
;
536 unsigned int refresh_period
;
537 unsigned int giveownership_period
;
539 unsigned int max_owned
;
540 unsigned int max_chan_users
;
541 unsigned int max_chan_bans
; /* lamers */
542 unsigned int max_userinfo_length
;
544 struct string_list
*set_shows
;
545 struct string_list
*eightball
;
546 struct string_list
*old_ban_names
;
548 const char *ctcp_short_ban_duration
;
549 const char *ctcp_long_ban_duration
;
551 const char *irc_operator_epithet
;
552 const char *network_helper_epithet
;
553 const char *support_helper_epithet
;
558 struct userNode
*user
;
559 struct userNode
*bot
;
560 struct chanNode
*channel
;
562 unsigned short lowest
;
563 unsigned short highest
;
564 struct userData
**users
;
565 struct helpfile_table table
;
568 enum note_access_type
570 NOTE_SET_CHANNEL_ACCESS
,
571 NOTE_SET_CHANNEL_SETTER
,
575 enum note_visible_type
578 NOTE_VIS_CHANNEL_USERS
,
584 enum note_access_type set_access_type
;
586 unsigned int min_opserv
;
587 unsigned short min_ulevel
;
589 enum note_visible_type visible_type
;
590 unsigned int max_length
;
597 struct note_type
*type
;
598 char setter
[NICKSERV_HANDLE_LEN
+1];
602 static unsigned int registered_channels
;
603 static unsigned int banCount
;
605 static const struct {
608 unsigned short level
;
610 } accessLevels
[] = { /* MUST be orderd less to most! */
611 { "peon", "Peon", UL_PEON
, '+' },
612 { "halfop", "HalfOp", UL_HALFOP
, '%' },
613 { "op", "Op", UL_OP
, '@' },
614 { "manager", "Manager", UL_MANAGER
, '%' },
615 { "coowner", "Coowner", UL_COOWNER
, '*' },
616 { "owner", "Owner", UL_OWNER
, '!' },
617 { "helper", "BUG:", UL_HELPER
, 'X' }
620 static const struct {
623 unsigned short default_value
;
624 unsigned int old_idx
;
625 unsigned int old_flag
;
626 unsigned short flag_value
;
628 { "CSMSG_SET_GIVE_VOICE", "givevoice", 100, ~0, CHANNEL_VOICE_ALL
, 0 },
629 { "CSMSG_SET_GIVE_HALFOPS", "givehalfops", 150, ~0, CHANNEL_HOP_ALL
, 0 },
630 { "CSMSG_SET_GIVE_OPS", "giveops", 200, 2, 0, 0 },
631 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
632 { "CSMSG_SET_ENFHALFOPS", "enfhalfops", 300, 1, 0, 0 },
633 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
634 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
635 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
636 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
637 { "CSMSG_SET_CTCPUSERS", "ctcpusers", 0, 9, 0, 0 },
638 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES
, 1 },
639 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE
, 200 },
640 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF
, 1 }
643 struct charOptionValues
{
647 { 'n', "CSMSG_VOICE_NONE" },
648 { 'p', "CSMSG_VOICE_PEON" },
649 { 'a', "CSMSG_VOICE_ALL" }
650 }, protectValues
[] = {
651 { 'a', "CSMSG_PROTECT_ALL" },
652 { 'e', "CSMSG_PROTECT_EQUAL" },
653 { 'l', "CSMSG_PROTECT_LOWER" },
654 { 'n', "CSMSG_PROTECT_NONE" }
656 { 'd', "CSMSG_TOYS_DISABLED" },
657 { 'n', "CSMSG_TOYS_PRIVATE" },
658 { 'p', "CSMSG_TOYS_PUBLIC" }
659 }, topicRefreshValues
[] = {
660 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
661 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
662 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
663 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
664 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
665 }, ctcpReactionValues
[] = {
666 { 'k', "CSMSG_CTCPREACTION_KICK" },
667 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
668 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
669 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
672 static const struct {
676 unsigned int old_idx
;
678 struct charOptionValues
*values
;
680 { "CSMSG_SET_VOICE", "voice", 'p', 99, ArrayLength(voiceValues
), voiceValues
},
681 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues
), protectValues
},
682 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues
), toysValues
},
683 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues
), topicRefreshValues
},
684 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 't', 10, ArrayLength(ctcpReactionValues
), ctcpReactionValues
}
687 struct userData
*helperList
;
688 struct chanData
*channelList
;
689 static struct module *chanserv_module
;
690 static unsigned int userCount
;
691 unsigned int chanserv_read_version
= 0; /* db version control */
693 #define CHANSERV_DB_VERSION 2
695 #define GetChannelUser(channel, handle) _GetChannelUser(channel, handle, 1, 0)
696 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
697 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
700 user_level_from_name(const char *name
, unsigned short clamp_level
)
702 unsigned int level
= 0, ii
;
704 level
= strtoul(name
, NULL
, 10);
705 else for(ii
= 0; (ii
< ArrayLength(accessLevels
)) && !level
; ++ii
)
706 if(!irccasecmp(name
, accessLevels
[ii
].name
))
707 level
= accessLevels
[ii
].level
;
708 if(level
> clamp_level
)
714 user_level_name_from_level(int level
)
722 for(ii
= 0; (ii
< ArrayLength(accessLevels
)); ii
++)
723 if(level
>= accessLevels
[ii
].level
)
724 highest
= accessLevels
[ii
].title
;
730 parse_level_range(unsigned short *minl
, unsigned short *maxl
, const char *arg
)
733 *minl
= strtoul(arg
, &sep
, 10);
741 *maxl
= strtoul(sep
+1, &sep
, 10);
749 _GetChannelUser(struct chanData
*channel
, struct handle_info
*handle
, int override
, int allow_suspended
)
751 struct userData
*uData
, **head
;
753 if(!channel
|| !handle
)
756 if(override
&& HANDLE_FLAGGED(handle
, HELPING
)
757 && ((handle
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
)))
759 for(uData
= helperList
;
760 uData
&& uData
->handle
!= handle
;
761 uData
= uData
->next
);
765 uData
= calloc(1, sizeof(struct userData
));
766 uData
->handle
= handle
;
768 uData
->access
= UL_HELPER
;
774 uData
->next
= helperList
;
776 helperList
->prev
= uData
;
784 for(uData
= channel
->users
; uData
; uData
= uData
->next
)
785 if((uData
->handle
== handle
) && (allow_suspended
|| !IsUserSuspended(uData
)))
788 head
= &(channel
->users
);
791 if(uData
&& (uData
!= *head
))
793 /* Shuffle the user to the head of whatever list he was in. */
795 uData
->next
->prev
= uData
->prev
;
797 uData
->prev
->next
= uData
->next
;
803 (**head
).prev
= uData
;
810 /* Returns non-zero if user has at least the minimum access.
811 * exempt_owner is set when handling !set, so the owner can set things
814 int check_user_level(struct chanNode
*channel
, struct userNode
*user
, enum levelOption opt
, int allow_override
, int exempt_owner
)
816 struct userData
*uData
;
817 struct chanData
*cData
= channel
->channel_info
;
818 unsigned short minimum
= cData
->lvlOpts
[opt
];
821 uData
= _GetChannelUser(cData
, user
->handle_info
, allow_override
, 0);
824 if(minimum
<= uData
->access
)
826 if((minimum
> UL_OWNER
) && (uData
->access
== UL_OWNER
) && exempt_owner
)
831 /* Scan for other users authenticated to the same handle
832 still in the channel. If so, keep them listed as present.
834 user is optional, if not null, it skips checking that userNode
835 (for the handle_part function) */
837 scan_user_presence(struct userData
*uData
, struct userNode
*user
)
841 if(IsSuspended(uData
->channel
)
842 || IsUserSuspended(uData
)
843 || !(mn
= find_handle_in_channel(uData
->channel
->channel
, uData
->handle
, user
)))
855 chanserv_ctcp_check(struct userNode
*user
, struct chanNode
*channel
, char *text
, UNUSED_ARG(struct userNode
*bot
))
857 unsigned int eflags
, argc
;
859 static char *bad_ctcp_reason
= "CTCPs to this channel are forbidden.";
861 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
862 if(!channel
->channel_info
863 || IsSuspended(channel
->channel_info
)
865 || !ircncasecmp(text
, "ACTION ", 7))
867 /* Figure out the minimum level needed to CTCP the channel */
868 if(check_user_level(channel
, user
, lvlCTCPUsers
, 1, 0))
870 /* We need to enforce against them; do so. */
873 argv
[1] = user
->nick
;
875 if(GetUserMode(channel
, user
))
876 eflags
|= ACTION_KICK
;
877 switch(channel
->channel_info
->chOpts
[chCTCPReaction
]) {
878 default: case 'k': /* just do the kick */ break;
880 eflags
|= ACTION_BAN
;
883 eflags
|= ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
;
884 argv
[argc
++] = (char*)chanserv_conf
.ctcp_short_ban_duration
;
887 eflags
|= ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
;
888 argv
[argc
++] = (char*)chanserv_conf
.ctcp_long_ban_duration
;
891 argv
[argc
++] = bad_ctcp_reason
;
892 eject_user(chanserv
, channel
, argc
, argv
, NULL
, eflags
);
896 chanserv_create_note_type(const char *name
)
898 struct note_type
*ntype
= calloc(1, sizeof(*ntype
) + strlen(name
));
899 strcpy(ntype
->name
, name
);
901 dict_insert(note_types
, ntype
->name
, ntype
);
906 chanserv_deref_note_type(void *data
)
908 struct note_type
*ntype
= data
;
910 if(--ntype
->refs
> 0)
916 chanserv_flush_note_type(struct note_type
*ntype
)
918 struct chanData
*cData
;
919 for(cData
= channelList
; cData
; cData
= cData
->next
)
920 dict_remove(cData
->notes
, ntype
->name
);
924 chanserv_truncate_notes(struct note_type
*ntype
)
926 struct chanData
*cData
;
928 unsigned int size
= sizeof(*note
) + ntype
->max_length
;
930 for(cData
= channelList
; cData
; cData
= cData
->next
) {
931 note
= dict_find(cData
->notes
, ntype
->name
, NULL
);
934 if(strlen(note
->note
) <= ntype
->max_length
)
936 dict_remove2(cData
->notes
, ntype
->name
, 1);
937 note
= realloc(note
, size
);
938 note
->note
[ntype
->max_length
] = 0;
939 dict_insert(cData
->notes
, ntype
->name
, note
);
943 static int note_type_visible_to_user(struct chanData
*channel
, struct note_type
*ntype
, struct userNode
*user
);
946 chanserv_add_channel_note(struct chanData
*channel
, struct note_type
*type
, const char *setter
, const char *text
)
949 unsigned int len
= strlen(text
);
951 if(len
> type
->max_length
) len
= type
->max_length
;
952 note
= calloc(1, sizeof(*note
) + len
);
954 strncpy(note
->setter
, setter
, sizeof(note
->setter
)-1);
955 memcpy(note
->note
, text
, len
);
957 dict_insert(channel
->notes
, type
->name
, note
);
963 chanserv_free_note(void *data
)
965 struct note
*note
= data
;
967 chanserv_deref_note_type(note
->type
);
968 assert(note
->type
->refs
> 0); /* must use delnote to remove the type */
972 static MODCMD_FUNC(cmd_createnote
) {
973 struct note_type
*ntype
;
974 unsigned int arg
= 1, existed
= 0, max_length
;
976 if((ntype
= dict_find(note_types
, argv
[1], NULL
)))
979 ntype
= chanserv_create_note_type(argv
[arg
]);
980 if(!irccasecmp(argv
[++arg
], "privileged"))
983 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
984 ntype
->set_access
.min_opserv
= strtoul(argv
[arg
], NULL
, 0);
986 else if(!irccasecmp(argv
[arg
], "channel"))
988 unsigned short ulvl
= user_level_from_name(argv
[++arg
], UL_OWNER
);
991 reply("CSMSG_INVALID_ACCESS", argv
[arg
]);
994 ntype
->set_access_type
= NOTE_SET_CHANNEL_ACCESS
;
995 ntype
->set_access
.min_ulevel
= ulvl
;
997 else if(!irccasecmp(argv
[arg
], "setter"))
999 ntype
->set_access_type
= NOTE_SET_CHANNEL_SETTER
;
1003 reply("CSMSG_BAD_NOTE_ACCESS", argv
[arg
]);
1007 if(!irccasecmp(argv
[++arg
], "privileged"))
1008 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
1009 else if(!irccasecmp(argv
[arg
], "channel_users"))
1010 ntype
->visible_type
= NOTE_VIS_CHANNEL_USERS
;
1011 else if(!irccasecmp(argv
[arg
], "all"))
1012 ntype
->visible_type
= NOTE_VIS_ALL
;
1014 reply("CSMSG_BAD_NOTE_ACCESS", argv
[arg
]);
1018 if((arg
+1) >= argc
) {
1019 reply("MSG_MISSING_PARAMS", argv
[0]);
1022 max_length
= strtoul(argv
[++arg
], NULL
, 0);
1023 if(max_length
< 20 || max_length
> 450)
1025 reply("CSMSG_BAD_MAX_LENGTH", argv
[arg
]);
1028 if(existed
&& (max_length
< ntype
->max_length
))
1030 ntype
->max_length
= max_length
;
1031 chanserv_truncate_notes(ntype
);
1033 ntype
->max_length
= max_length
;
1036 reply("CSMSG_NOTE_MODIFIED", ntype
->name
);
1038 reply("CSMSG_NOTE_CREATED", ntype
->name
);
1043 dict_remove(note_types
, ntype
->name
);
1047 static MODCMD_FUNC(cmd_removenote
) {
1048 struct note_type
*ntype
;
1051 ntype
= dict_find(note_types
, argv
[1], NULL
);
1052 force
= (argc
> 2) && !irccasecmp(argv
[2], "force");
1055 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
1062 reply("CSMSG_NOTE_TYPE_USED", ntype
->name
);
1065 chanserv_flush_note_type(ntype
);
1067 dict_remove(note_types
, argv
[1]);
1068 reply("CSMSG_NOTE_DELETED", argv
[1]);
1073 mode_lock_violated(const struct mod_chanmode
*orig
, const struct mod_chanmode
*change
)
1077 if(orig
->modes_set
& change
->modes_clear
)
1079 if(orig
->modes_clear
& change
->modes_set
)
1081 if((orig
->modes_set
& MODE_KEY
) && (change
->modes_set
& MODE_KEY
)
1082 && strcmp(orig
->new_key
, change
->new_key
))
1084 if((orig
->modes_set
& MODE_LIMIT
) && (change
->modes_set
& MODE_LIMIT
)
1085 && (orig
->new_limit
!= change
->new_limit
))
1090 static char max_length_text
[MAXLEN
+1][16];
1092 static struct helpfile_expansion
1093 chanserv_expand_variable(const char *variable
)
1095 struct helpfile_expansion exp
;
1097 if(!irccasecmp(variable
, "notes"))
1100 exp
.type
= HF_TABLE
;
1101 exp
.value
.table
.length
= 1;
1102 exp
.value
.table
.width
= 3;
1103 exp
.value
.table
.flags
= 0;
1104 exp
.value
.table
.contents
= calloc(dict_size(note_types
)+1, sizeof(char**));
1105 exp
.value
.table
.contents
[0] = calloc(exp
.value
.table
.width
, sizeof(char*));
1106 exp
.value
.table
.contents
[0][0] = "Note Type";
1107 exp
.value
.table
.contents
[0][1] = "Visibility";
1108 exp
.value
.table
.contents
[0][2] = "Max Length";
1109 for(it
=dict_first(note_types
); it
; it
=iter_next(it
))
1111 struct note_type
*ntype
= iter_data(it
);
1114 if(!note_type_visible_to_user(NULL
, ntype
, message_dest
)) continue;
1115 row
= exp
.value
.table
.length
++;
1116 exp
.value
.table
.contents
[row
] = calloc(exp
.value
.table
.width
, sizeof(char*));
1117 exp
.value
.table
.contents
[row
][0] = ntype
->name
;
1118 exp
.value
.table
.contents
[row
][1] = (ntype
->visible_type
== NOTE_VIS_ALL
) ? "all" :
1119 (ntype
->visible_type
== NOTE_VIS_CHANNEL_USERS
) ? "chan users" :
1121 if(!max_length_text
[ntype
->max_length
][0])
1122 snprintf(max_length_text
[ntype
->max_length
], sizeof(max_length_text
[ntype
->max_length
]), "%u", ntype
->max_length
);
1123 exp
.value
.table
.contents
[row
][2] = max_length_text
[ntype
->max_length
];
1128 exp
.type
= HF_STRING
;
1129 exp
.value
.str
= NULL
;
1133 static struct chanData
*
1134 register_channel(struct chanNode
*cNode
, char *registrar
)
1136 struct chanData
*channel
;
1137 enum levelOption lvlOpt
;
1138 enum charOption chOpt
;
1140 channel
= calloc(1, sizeof(struct chanData
));
1142 channel
->notes
= dict_new();
1143 dict_set_free_data(channel
->notes
, chanserv_free_note
);
1145 channel
->registrar
= strdup(registrar
);
1146 channel
->registered
= now
;
1147 channel
->visited
= now
;
1148 channel
->limitAdjusted
= now
;
1149 channel
->ownerTransfer
= now
;
1150 channel
->flags
= CHANNEL_DEFAULT_FLAGS
;
1151 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
1152 channel
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
1153 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
1154 channel
->chOpts
[chOpt
] = charOptions
[chOpt
].default_value
;
1156 channel
->prev
= NULL
;
1157 channel
->next
= channelList
;
1160 channelList
->prev
= channel
;
1161 channelList
= channel
;
1162 registered_channels
++;
1164 channel
->channel
= cNode
;
1166 cNode
->channel_info
= channel
;
1171 static struct userData
*
1172 add_channel_user(struct chanData
*channel
, struct handle_info
*handle
, unsigned short access
, time_t seen
, const char *info
)
1174 struct userData
*ud
;
1176 if(access
> UL_OWNER
)
1179 ud
= calloc(1, sizeof(*ud
));
1180 ud
->channel
= channel
;
1181 ud
->handle
= handle
;
1183 ud
->access
= access
;
1184 ud
->info
= info
? strdup(info
) : NULL
;
1187 ud
->next
= channel
->users
;
1189 channel
->users
->prev
= ud
;
1190 channel
->users
= ud
;
1192 channel
->userCount
++;
1196 ud
->u_next
= ud
->handle
->channels
;
1198 ud
->u_next
->u_prev
= ud
;
1199 ud
->handle
->channels
= ud
;
1201 ud
->flags
= USER_FLAGS_DEFAULT
;
1205 static void unregister_channel(struct chanData
*channel
, const char *reason
);
1208 del_channel_user(struct userData
*user
, int do_gc
)
1210 struct chanData
*channel
= user
->channel
;
1212 channel
->userCount
--;
1216 user
->prev
->next
= user
->next
;
1218 channel
->users
= user
->next
;
1220 user
->next
->prev
= user
->prev
;
1223 user
->u_prev
->u_next
= user
->u_next
;
1225 user
->handle
->channels
= user
->u_next
;
1227 user
->u_next
->u_prev
= user
->u_prev
;
1231 if(do_gc
&& !channel
->users
&& !IsProtected(channel
))
1232 unregister_channel(channel
, "lost all users.");
1235 static struct adduserPending
*
1236 add_adduser_pending(struct chanNode
*channel
, struct userNode
*user
, int level
)
1238 struct adduserPending
*ap
;
1239 ap
= calloc(1,sizeof(struct adduserPending
));
1240 ap
->channel
= channel
;
1243 ap
->created
= time(NULL
);
1245 /* ap->prev defaults to NULL already.. */
1246 ap
->next
= adduser_pendings
;
1247 if(adduser_pendings
)
1248 adduser_pendings
->prev
= ap
;
1249 adduser_pendings
= ap
;
1250 adduser_pendings_count
++;
1255 del_adduser_pending(struct adduserPending
*ap
)
1258 ap
->prev
->next
= ap
->next
;
1260 adduser_pendings
= ap
->next
;
1263 ap
->next
->prev
= ap
->prev
;
1267 static void expire_adduser_pending();
1269 /* find_adduser_pending(channel, user) will find an arbitrary record
1270 * from user, channel, or user and channel.
1271 * if user or channel are NULL, they will match any records.
1273 static struct adduserPending
*
1274 find_adduser_pending(struct chanNode
*channel
, struct userNode
*user
)
1276 struct adduserPending
*ap
;
1278 expire_adduser_pending(); /* why not here.. */
1280 if(!channel
&& !user
) /* 2 nulls matches all */
1281 return(adduser_pendings
);
1282 for(ap
= adduser_pendings
;ap
;ap
= ap
->next
)
1284 if((channel
== ap
->channel
&& (user
== NULL
|| user
== ap
->user
)) || (user
==ap
->user
&& channel
==NULL
))
1291 /* Remove all pendings for a user or channel
1293 * called in nickserv.c DelUser() and proto-* unregister_channel()
1296 wipe_adduser_pending(struct chanNode
*channel
, struct userNode
*user
)
1298 struct adduserPending
*ap
;
1300 /* So this is a bit wastefull, i hate dealing with linked lists.
1301 * if its a problem we'll rewrite it right */
1302 while((ap
= find_adduser_pending(channel
, user
))) {
1303 del_adduser_pending(ap
);
1307 /* Called from nickserv.c cmd_auth after someone auths */
1309 process_adduser_pending(struct userNode
*user
)
1311 struct adduserPending
*ap
;
1312 if(!user
->handle_info
)
1313 return; /* not associated with an account */
1314 while((ap
= find_adduser_pending(NULL
, user
)))
1316 struct userData
*actee
;
1317 if(GetTrueChannelAccess(ap
->channel
->channel_info
, ap
->user
->handle_info
))
1319 /* Already on the userlist. do nothing*/
1323 actee
= add_channel_user(ap
->channel
->channel_info
, ap
->user
->handle_info
, ap
->level
, 0, NULL
);
1324 scan_user_presence(actee
, NULL
);
1326 del_adduser_pending(ap
);
1331 expire_adduser_pending()
1333 struct adduserPending
*ap
, *ap_next
;
1334 ap
= adduser_pendings
;
1337 if((ap
->created
+ ADDUSER_PENDING_EXPIRE
) < time(NULL
))
1339 ap_next
= ap
->next
; /* save next */
1340 del_adduser_pending(ap
); /* free and relink */
1341 ap
= ap_next
; /* advance */
1348 static void expire_ban(void *data
);
1350 static struct banData
*
1351 add_channel_ban(struct chanData
*channel
, const char *mask
, char *owner
, time_t set
, time_t triggered
, time_t expires
, char *reason
)
1354 unsigned int ii
, l1
, l2
;
1359 bd
= malloc(sizeof(struct banData
));
1361 bd
->channel
= channel
;
1363 bd
->triggered
= triggered
;
1364 bd
->expires
= expires
;
1366 for(ii
= 0; ii
< chanserv_conf
.old_ban_names
->used
; ++ii
)
1368 extern const char *hidden_host_suffix
;
1369 const char *old_name
= chanserv_conf
.old_ban_names
->list
[ii
];
1373 l2
= strlen(old_name
);
1376 if(irccasecmp(mask
+ l1
- l2
, old_name
))
1378 new_mask
= alloca(MAXLEN
);
1379 sprintf(new_mask
, "%.*s%s", (int)(l1
-l2
), mask
, hidden_host_suffix
);
1382 safestrncpy(bd
->mask
, mask
, sizeof(bd
->mask
));
1384 safestrncpy(bd
->owner
, owner
, sizeof(bd
->owner
));
1385 bd
->reason
= strdup(reason
);
1388 timeq_add(expires
, expire_ban
, bd
);
1391 bd
->next
= channel
->bans
; /* lamers */
1393 channel
->bans
->prev
= bd
;
1395 channel
->banCount
++;
1402 del_channel_ban(struct banData
*ban
)
1404 ban
->channel
->banCount
--;
1408 ban
->prev
->next
= ban
->next
;
1410 ban
->channel
->bans
= ban
->next
;
1413 ban
->next
->prev
= ban
->prev
;
1416 timeq_del(0, expire_ban
, ban
, TIMEQ_IGNORE_WHEN
);
1425 expire_ban(void *data
) /* lamer.. */
1427 struct banData
*bd
= data
;
1428 if(!IsSuspended(bd
->channel
))
1430 struct banList bans
;
1431 struct mod_chanmode change
;
1433 bans
= bd
->channel
->channel
->banlist
;
1434 mod_chanmode_init(&change
);
1435 for(ii
=0; ii
<bans
.used
; ii
++)
1437 if(!strcmp(bans
.list
[ii
]->ban
, bd
->mask
))
1440 change
.args
[0].mode
= MODE_REMOVE
|MODE_BAN
;
1441 change
.args
[0].u
.hostmask
= bd
->mask
;
1442 mod_chanmode_announce(chanserv
, bd
->channel
->channel
, &change
);
1448 del_channel_ban(bd
);
1451 static void chanserv_expire_suspension(void *data
);
1454 unregister_channel(struct chanData
*channel
, const char *reason
)
1456 struct mod_chanmode change
;
1457 char msgbuf
[MAXLEN
];
1459 /* After channel unregistration, the following must be cleaned
1461 - Channel information.
1463 - Channel bans. (lamers)
1464 - Channel suspension data.
1465 - adduser_pending data.
1466 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1472 timeq_del(0, NULL
, channel
, TIMEQ_IGNORE_FUNC
| TIMEQ_IGNORE_WHEN
);
1476 mod_chanmode_init(&change
);
1477 change
.modes_clear
|= MODE_REGISTERED
;
1478 mod_chanmode_announce(chanserv
, channel
->channel
, &change
);
1481 wipe_adduser_pending(channel
->channel
, NULL
);
1483 while(channel
->users
)
1484 del_channel_user(channel
->users
, 0);
1486 while(channel
->bans
)
1487 del_channel_ban(channel
->bans
);
1489 free(channel
->topic
);
1490 free(channel
->registrar
);
1491 free(channel
->greeting
);
1492 free(channel
->user_greeting
);
1493 free(channel
->topic_mask
);
1496 channel
->prev
->next
= channel
->next
;
1498 channelList
= channel
->next
;
1501 channel
->next
->prev
= channel
->prev
;
1503 if(channel
->suspended
)
1505 struct chanNode
*cNode
= channel
->channel
;
1506 struct suspended
*suspended
, *next_suspended
;
1508 for(suspended
= channel
->suspended
; suspended
; suspended
= next_suspended
)
1510 next_suspended
= suspended
->previous
;
1511 free(suspended
->suspender
);
1512 free(suspended
->reason
);
1513 if(suspended
->expires
)
1514 timeq_del(suspended
->expires
, chanserv_expire_suspension
, suspended
, 0);
1519 cNode
->channel_info
= NULL
;
1521 channel
->channel
->channel_info
= NULL
;
1523 dict_delete(channel
->notes
);
1524 sprintf(msgbuf
, "%s %s", channel
->channel
->name
, reason
);
1525 if(!IsSuspended(channel
))
1526 DelChannelUser(chanserv
, channel
->channel
, msgbuf
, 0);
1527 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, msgbuf
);
1528 UnlockChannel(channel
->channel
);
1530 registered_channels
--;
1534 expire_channels(UNUSED_ARG(void *data
))
1536 struct chanData
*channel
, *next
;
1537 struct userData
*user
;
1538 char delay
[INTERVALLEN
], reason
[INTERVALLEN
+ 64];
1540 intervalString(delay
, chanserv_conf
.channel_expire_delay
, NULL
);
1541 sprintf(reason
, "Channel registration automatically expired after %s of disuse.", delay
);
1543 for(channel
= channelList
; channel
; channel
= next
)
1545 next
= channel
->next
;
1547 /* See if the channel can be expired. */
1548 if(((now
- channel
->visited
) <= chanserv_conf
.channel_expire_delay
)
1549 || IsProtected(channel
))
1552 /* Make sure there are no high-ranking users still in the channel. */
1553 for(user
=channel
->users
; user
; user
=user
->next
)
1554 if(user
->present
&& (user
->access
>= UL_PRESENT
))
1559 /* Unregister the channel */
1560 log_module(CS_LOG
, LOG_INFO
, "(%s) Channel registration expired.", channel
->channel
->name
);
1561 unregister_channel(channel
, "registration expired.");
1564 if(chanserv_conf
.channel_expire_frequency
)
1565 timeq_add(now
+ chanserv_conf
.channel_expire_frequency
, expire_channels
, NULL
);
1569 protect_user(const struct userNode
*victim
, const struct userNode
*aggressor
, struct chanData
*channel
)
1571 char protect
= channel
->chOpts
[chProtect
];
1572 struct userData
*cs_victim
, *cs_aggressor
;
1574 /* Don't protect if no one is to be protected, someone is attacking
1575 himself, or if the aggressor is an IRC Operator. */
1576 if(protect
== 'n' || victim
== aggressor
/* Opers dont get special treatment :/ || IsOper(aggressor) */)
1579 /* Don't protect if the victim isn't authenticated (because they
1580 can't be a channel user), unless we are to protect non-users
1582 cs_victim
= GetChannelAccess(channel
, victim
->handle_info
);
1583 if(protect
!= 'a' && !cs_victim
)
1586 /* Protect if the aggressor isn't a user because at this point,
1587 the aggressor can only be less than or equal to the victim. */
1588 cs_aggressor
= GetChannelAccess(channel
, aggressor
->handle_info
);
1592 /* If the aggressor was a user, then the victim can't be helped. */
1599 if(cs_victim
->access
> cs_aggressor
->access
)
1604 if(cs_victim
->access
>= cs_aggressor
->access
)
1613 validate_op(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1615 struct chanData
*cData
= channel
->channel_info
;
1616 struct userData
*cs_victim
;
1618 if((!(cs_victim
= GetChannelUser(cData
, victim
->handle_info
))
1619 || (cs_victim
->access
< UL_OP
/* cData->lvlOpts[lvlGiveOps]*/))
1620 && !check_user_level(channel
, user
, lvlEnfOps
, 0, 0))
1622 send_message(user
, chanserv
, "CSMSG_OPBY_LOCKED");
1630 validate_halfop(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1632 struct chanData
*cData
= channel
->channel_info
;
1633 struct userData
*cs_victim
;
1635 if((!(cs_victim
= GetChannelUser(cData
, victim
->handle_info
))
1636 || (cs_victim
->access
< UL_HALFOP
/* cData->lvlOpts[lvlGiveHalfOps] */))
1637 && !check_user_level(channel
, user
, lvlEnfHalfOps
, 0, 0))
1639 send_message(user
, chanserv
, "CSMSG_HOPBY_LOCKED");
1648 validate_deop(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1650 if(IsService(victim
))
1652 send_message(user
, chanserv
, "MSG_SERVICE_IMMUNE", victim
->nick
);
1656 if(protect_user(victim
, user
, channel
->channel_info
))
1658 send_message(user
, chanserv
, "CSMSG_USER_PROTECTED", victim
->nick
);
1666 validate_dehop(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1668 if(IsService(victim
))
1670 send_message(user
, chanserv
, "MSG_SERVICE_IMMUNE", victim
->nick
);
1674 if(protect_user(victim
, user
, channel
->channel_info
))
1676 send_message(user
, chanserv
, "CSMSG_USER_PROTECTED", victim
->nick
);
1683 static struct do_not_register
*
1684 chanserv_add_dnr(const char *chan_name
, const char *setter
, const char *reason
)
1686 struct do_not_register
*dnr
= calloc(1, sizeof(*dnr
)+strlen(reason
));
1687 safestrncpy(dnr
->chan_name
, chan_name
, sizeof(dnr
->chan_name
));
1688 safestrncpy(dnr
->setter
, setter
, sizeof(dnr
->setter
));
1689 strcpy(dnr
->reason
, reason
);
1691 if(dnr
->chan_name
[0] == '*')
1692 dict_insert(handle_dnrs
, dnr
->chan_name
+1, dnr
);
1693 else if(strpbrk(dnr
->chan_name
, "*?"))
1694 dict_insert(mask_dnrs
, dnr
->chan_name
, dnr
);
1696 dict_insert(plain_dnrs
, dnr
->chan_name
, dnr
);
1700 static struct dnrList
1701 chanserv_find_dnrs(const char *chan_name
, const char *handle
)
1703 struct dnrList list
;
1705 struct do_not_register
*dnr
;
1707 dnrList_init(&list
);
1708 if(handle
&& (dnr
= dict_find(handle_dnrs
, handle
, NULL
)))
1709 dnrList_append(&list
, dnr
);
1710 if(chan_name
&& (dnr
= dict_find(plain_dnrs
, chan_name
, NULL
)))
1711 dnrList_append(&list
, dnr
);
1713 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1714 if(match_ircglob(chan_name
, iter_key(it
)))
1715 dnrList_append(&list
, iter_data(it
));
1720 chanserv_show_dnrs(struct userNode
*user
, struct svccmd
*cmd
, const char *chan_name
, const char *handle
)
1722 struct dnrList list
;
1723 struct do_not_register
*dnr
;
1725 char buf
[INTERVALLEN
];
1727 list
= chanserv_find_dnrs(chan_name
, handle
);
1728 for(ii
= 0; (ii
< list
.used
) && (ii
< 10); ++ii
)
1730 dnr
= list
.list
[ii
];
1733 strftime(buf
, sizeof(buf
), "%Y %b %d", localtime(&dnr
->set
));
1734 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, buf
, dnr
->setter
, dnr
->reason
);
1737 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1740 reply("CSMSG_MORE_DNRS", list
.used
- ii
);
1745 struct do_not_register
*
1746 chanserv_is_dnr(const char *chan_name
, struct handle_info
*handle
)
1748 struct do_not_register
*dnr
;
1751 if(handle
&& (dnr
= dict_find(handle_dnrs
, handle
->handle
, NULL
)))
1755 if((dnr
= dict_find(plain_dnrs
, chan_name
, NULL
)))
1757 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1758 if(match_ircglob(chan_name
, iter_key(it
)))
1759 return iter_data(it
);
1764 static CHANSERV_FUNC(cmd_noregister
)
1767 struct do_not_register
*dnr
;
1768 char buf
[INTERVALLEN
];
1769 unsigned int matches
;
1775 reply("CSMSG_DNR_SEARCH_RESULTS");
1778 for(it
= dict_first(handle_dnrs
); it
; it
= iter_next(it
))
1780 dnr
= iter_data(it
);
1782 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
1784 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1787 for(it
= dict_first(plain_dnrs
); it
; it
= iter_next(it
))
1789 dnr
= iter_data(it
);
1791 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
1793 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1796 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1798 dnr
= iter_data(it
);
1800 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
1802 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1807 reply("MSG_MATCH_COUNT", matches
);
1809 reply("MSG_NO_MATCHES");
1815 if(!IsChannelName(target
) && (*target
!= '*'))
1817 reply("CSMSG_NOT_DNR", target
);
1823 const char *reason
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
1824 if((*target
== '*') && !get_handle_info(target
+ 1))
1826 reply("MSG_HANDLE_UNKNOWN", target
+ 1);
1829 chanserv_add_dnr(target
, user
->handle_info
->handle
, reason
);
1830 reply("CSMSG_NOREGISTER_CHANNEL", target
);
1834 reply("CSMSG_DNR_SEARCH_RESULTS");
1837 matches
= chanserv_show_dnrs(user
, cmd
, NULL
, target
+ 1);
1839 matches
= chanserv_show_dnrs(user
, cmd
, target
, NULL
);
1841 reply("MSG_NO_MATCHES");
1845 static CHANSERV_FUNC(cmd_allowregister
)
1847 const char *chan_name
= argv
[1];
1849 if((chan_name
[0] == '*') && dict_find(handle_dnrs
, chan_name
+1, NULL
))
1851 dict_remove(handle_dnrs
, chan_name
+1);
1852 reply("CSMSG_DNR_REMOVED", chan_name
);
1854 else if(dict_find(plain_dnrs
, chan_name
, NULL
))
1856 dict_remove(plain_dnrs
, chan_name
);
1857 reply("CSMSG_DNR_REMOVED", chan_name
);
1859 else if(dict_find(mask_dnrs
, chan_name
, NULL
))
1861 dict_remove(mask_dnrs
, chan_name
);
1862 reply("CSMSG_DNR_REMOVED", chan_name
);
1866 reply("CSMSG_NO_SUCH_DNR", chan_name
);
1873 chanserv_get_owned_count(struct handle_info
*hi
)
1875 struct userData
*cList
;
1878 for(owned
=0, cList
=hi
->channels
; cList
; cList
=cList
->u_next
)
1879 if(cList
->access
== UL_OWNER
)
1884 static CHANSERV_FUNC(cmd_register
)
1886 struct handle_info
*handle
;
1887 struct chanData
*cData
;
1888 struct modeNode
*mn
;
1889 char reason
[MAXLEN
];
1891 unsigned int new_channel
, force
=0;
1892 struct do_not_register
*dnr
;
1898 if(channel
->channel_info
)
1900 reply("CSMSG_ALREADY_REGGED", channel
->name
);
1904 if(channel
->bad_channel
)
1906 reply("CSMSG_ILLEGAL_CHANNEL", channel
->name
);
1910 if(!IsHelping(user
) && (!(mn
= GetUserMode(channel
, user
)) || !(mn
->modes
& MODE_CHANOP
)))
1912 reply("CSMSG_MUST_BE_OPPED", channel
->name
);
1917 chan_name
= channel
->name
;
1923 reply("MSG_MISSING_PARAMS", cmd
->name
);
1924 svccmd_send_help_brief(user
, chanserv
, cmd
);
1927 if(!IsChannelName(argv
[1]))
1929 reply("MSG_NOT_CHANNEL_NAME");
1933 if(opserv_bad_channel(argv
[1]))
1935 reply("CSMSG_ILLEGAL_CHANNEL", argv
[1]);
1940 chan_name
= argv
[1];
1943 if(argc
>= (new_channel
+2))
1945 if(!IsHelping(user
))
1947 reply("CSMSG_PROXY_FORBIDDEN");
1951 if(!(handle
= modcmd_get_handle_info(user
, argv
[new_channel
+1])))
1953 force
= (argc
> (new_channel
+2)) && !irccasecmp(argv
[new_channel
+2], "force");
1954 dnr
= chanserv_is_dnr(chan_name
, handle
);
1956 /* Check if they are over the limit.. */
1957 if((chanserv_get_owned_count(handle
) >= chanserv_conf
.max_owned
) && !force
)
1959 reply("CSMSG_OWN_TOO_MANY", handle
->handle
, chanserv_conf
.max_owned
);
1966 handle
= user
->handle_info
;
1967 dnr
= chanserv_is_dnr(chan_name
, handle
);
1968 /* Check if they are over the limit.. */
1969 if((chanserv_get_owned_count(handle
) >= chanserv_conf
.max_owned
) && !force
)
1971 reply("CSMSG_YOU_OWN_TOO_MANY", chanserv_conf
.max_owned
);
1974 /* Check if another service is in the channel */
1976 for(n
= 0; n
< channel
->members
.used
; n
++)
1978 mn
= channel
->members
.list
[n
];
1979 if((mn
&& mn
->user
&& (mn
->user
->modes
& FLAGS_SERVICE
)) || IsLocal(mn
->user
))
1981 reply("CSMSG_ANOTHER_SERVICE");
1988 if(!IsHelping(user
))
1989 reply("CSMSG_DNR_CHANNEL", chan_name
);
1991 chanserv_show_dnrs(user
, cmd
, chan_name
, handle
->handle
);
1995 /* now handled above for message specilization *
1996 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
1998 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
2004 channel
= AddChannel(argv
[1], now
, NULL
, NULL
, NULL
);
2006 cData
= register_channel(channel
, user
->handle_info
->handle
);
2007 scan_user_presence(add_channel_user(cData
, handle
, UL_OWNER
, 0, NULL
), NULL
);
2008 cData
->modes
= chanserv_conf
.default_modes
;
2010 cData
->modes
.modes_set
|= MODE_REGISTERED
;
2011 if (IsOffChannel(cData
))
2013 mod_chanmode_announce(chanserv
, channel
, &cData
->modes
);
2017 struct mod_chanmode
*change
= mod_chanmode_dup(&cData
->modes
, 1);
2018 change
->args
[change
->argc
].mode
= MODE_CHANOP
;
2019 change
->args
[change
->argc
].u
.member
= AddChannelUser(chanserv
, channel
);
2021 mod_chanmode_announce(chanserv
, channel
, change
);
2022 mod_chanmode_free(change
);
2025 /* Initialize the channel's max user record. */
2026 cData
->max
= channel
->members
.used
;
2028 if(handle
!= user
->handle_info
)
2029 reply("CSMSG_PROXY_SUCCESS", handle
->handle
, channel
->name
);
2031 reply("CSMSG_REG_SUCCESS", channel
->name
);
2033 sprintf(reason
, "%s registered to %s by %s.", channel
->name
, handle
->handle
, user
->handle_info
->handle
);
2034 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
2039 make_confirmation_string(struct userData
*uData
)
2041 static char strbuf
[16];
2046 for(src
= uData
->handle
->handle
; *src
; )
2047 accum
= accum
* 31 + toupper(*src
++);
2049 for(src
= uData
->channel
->channel
->name
; *src
; )
2050 accum
= accum
* 31 + toupper(*src
++);
2051 sprintf(strbuf
, "%08x", accum
);
2055 static CHANSERV_FUNC(cmd_unregister
)
2058 char reason
[MAXLEN
];
2059 struct chanData
*cData
;
2060 struct userData
*uData
;
2062 cData
= channel
->channel_info
;
2065 reply("CSMSG_NOT_REGISTERED", channel
->name
);
2069 uData
= GetChannelUser(cData
, user
->handle_info
);
2070 if(!uData
|| (uData
->access
< UL_OWNER
))
2072 reply("CSMSG_NO_ACCESS");
2076 if(IsProtected(cData
))
2078 reply("CSMSG_UNREG_NODELETE", channel
->name
);
2082 if(!IsHelping(user
))
2084 const char *confirm_string
;
2085 if(IsSuspended(cData
))
2087 reply("CSMSG_CHAN_SUSPENDED", channel
->name
, cData
->suspended
->reason
);
2090 confirm_string
= make_confirmation_string(uData
);
2091 if((argc
< 2) || strcmp(argv
[1], confirm_string
))
2093 reply("CSMSG_CONFIRM_UNREG", channel
->name
, confirm_string
);
2098 sprintf(reason
, "unregistered by %s.", user
->handle_info
->handle
);
2099 name
= strdup(channel
->name
);
2100 unregister_channel(cData
, reason
);
2101 reply("CSMSG_UNREG_SUCCESS", name
);
2106 static CHANSERV_FUNC(cmd_move
)
2108 struct mod_chanmode change
;
2109 struct chanNode
*target
;
2110 struct modeNode
*mn
;
2111 struct userData
*uData
;
2112 char reason
[MAXLEN
];
2113 struct do_not_register
*dnr
;
2117 if(IsProtected(channel
->channel_info
))
2119 reply("CSMSG_MOVE_NODELETE", channel
->name
);
2123 if(!IsChannelName(argv
[1]))
2125 reply("MSG_NOT_CHANNEL_NAME");
2129 if(opserv_bad_channel(argv
[1]))
2131 reply("CSMSG_ILLEGAL_CHANNEL", argv
[1]);
2135 if(!IsHelping(user
) || (argc
< 3) || irccasecmp(argv
[2], "force"))
2137 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
2139 if((uData
->access
== UL_OWNER
) && (dnr
= chanserv_is_dnr(argv
[1], uData
->handle
)))
2141 if(!IsHelping(user
))
2142 reply("CSMSG_DNR_CHANNEL_MOVE", argv
[1]);
2144 chanserv_show_dnrs(user
, cmd
, argv
[1], uData
->handle
->handle
);
2150 mod_chanmode_init(&change
);
2151 if(!(target
= GetChannel(argv
[1])))
2153 target
= AddChannel(argv
[1], now
, NULL
, NULL
, NULL
);
2154 if(!IsSuspended(channel
->channel_info
))
2155 AddChannelUser(chanserv
, target
);
2157 else if(target
->channel_info
)
2159 reply("CSMSG_ALREADY_REGGED", target
->name
);
2162 else if((!(mn
= GetUserMode(target
, user
)) || !(mn
->modes
&& MODE_CHANOP
))
2163 && !IsHelping(user
))
2165 reply("CSMSG_MUST_BE_OPPED", target
->name
);
2168 else if(!IsSuspended(channel
->channel_info
))
2171 change
.args
[0].mode
= MODE_CHANOP
;
2172 change
.args
[0].u
.member
= AddChannelUser(chanserv
, target
);
2173 mod_chanmode_announce(chanserv
, target
, &change
);
2178 /* Clear MODE_REGISTERED from old channel, add it to new. */
2180 change
.modes_clear
= MODE_REGISTERED
;
2181 mod_chanmode_announce(chanserv
, channel
, &change
);
2182 change
.modes_clear
= 0;
2183 change
.modes_set
= MODE_REGISTERED
;
2184 mod_chanmode_announce(chanserv
, target
, &change
);
2187 /* Move the channel_info to the target channel; it
2188 shouldn't be necessary to clear timeq callbacks
2189 for the old channel. */
2190 target
->channel_info
= channel
->channel_info
;
2191 target
->channel_info
->channel
= target
;
2192 channel
->channel_info
= NULL
;
2194 reply("CSMSG_MOVE_SUCCESS", target
->name
);
2196 sprintf(reason
, "%s moved to %s by %s.", channel
->name
, target
->name
, user
->handle_info
->handle
);
2197 if(!IsSuspended(target
->channel_info
))
2199 char reason2
[MAXLEN
];
2200 sprintf(reason2
, "Channel moved to %s by %s.", target
->name
, user
->handle_info
->handle
);
2201 DelChannelUser(chanserv
, channel
, reason2
, 0);
2203 UnlockChannel(channel
);
2204 LockChannel(target
);
2205 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
2210 merge_users(struct chanData
*source
, struct chanData
*target
)
2212 struct userData
*suData
, *tuData
, *next
;
2218 /* Insert the source's users into the scratch area. */
2219 for(suData
= source
->users
; suData
; suData
= suData
->next
)
2220 dict_insert(merge
, suData
->handle
->handle
, suData
);
2222 /* Iterate through the target's users, looking for
2223 users common to both channels. The lower access is
2224 removed from either the scratch area or target user
2226 for(tuData
= target
->users
; tuData
; tuData
= next
)
2228 struct userData
*choice
;
2230 next
= tuData
->next
;
2232 /* If a source user exists with the same handle as a target
2233 channel's user, resolve the conflict by removing one. */
2234 suData
= dict_find(merge
, tuData
->handle
->handle
, NULL
);
2238 /* Pick the data we want to keep. */
2239 /* If the access is the same, use the later seen time. */
2240 if(suData
->access
== tuData
->access
)
2241 choice
= (suData
->seen
> tuData
->seen
) ? suData
: tuData
;
2242 else /* Otherwise, keep the higher access level. */
2243 choice
= (suData
->access
> tuData
->access
) ? suData
: tuData
;
2245 /* Remove the user that wasn't picked. */
2246 if(choice
== tuData
)
2248 dict_remove(merge
, suData
->handle
->handle
);
2249 del_channel_user(suData
, 0);
2252 del_channel_user(tuData
, 0);
2255 /* Move the remaining users to the target channel. */
2256 for(it
= dict_first(merge
); it
; it
= iter_next(it
))
2258 suData
= iter_data(it
);
2260 /* Insert the user into the target channel's linked list. */
2261 suData
->prev
= NULL
;
2262 suData
->next
= target
->users
;
2263 suData
->channel
= target
;
2266 target
->users
->prev
= suData
;
2267 target
->users
= suData
;
2269 /* Update the user counts for the target channel; the
2270 source counts are left alone. */
2271 target
->userCount
++;
2274 /* Possible to assert (source->users == NULL) here. */
2275 source
->users
= NULL
;
2280 merge_bans(struct chanData
*source
, struct chanData
*target
)
2282 struct banData
*sbData
, *tbData
, *sNext
, *tNext
, *tFront
;
2284 /* Hold on to the original head of the target ban list
2285 to avoid comparing source bans with source bans. */
2286 tFront
= target
->bans
;
2288 /* Perform a totally expensive O(n*m) merge, ick. */
2289 for(sbData
= source
->bans
; sbData
; sbData
= sNext
)
2291 /* Flag to track whether the ban's been moved
2292 to the destination yet. */
2295 /* Possible to assert (sbData->prev == NULL) here. */
2296 sNext
= sbData
->next
;
2298 for(tbData
= tFront
; tbData
; tbData
= tNext
)
2300 tNext
= tbData
->next
;
2302 /* Perform two comparisons between each source
2303 and target ban, conflicts are resolved by
2304 keeping the broader ban and copying the later
2305 expiration and triggered time. */
2306 if(match_ircglobs(tbData
->mask
, sbData
->mask
))
2308 /* There is a broader ban in the target channel that
2309 overrides one in the source channel; remove the
2310 source ban and break. */
2311 if(sbData
->expires
> tbData
->expires
)
2312 tbData
->expires
= sbData
->expires
;
2313 if(sbData
->triggered
> tbData
->triggered
)
2314 tbData
->triggered
= sbData
->triggered
;
2315 del_channel_ban(sbData
);
2318 else if(match_ircglobs(sbData
->mask
, tbData
->mask
))
2320 /* There is a broader ban in the source channel that
2321 overrides one in the target channel; remove the
2322 target ban, fall through and move the source over. */
2323 if(tbData
->expires
> sbData
->expires
)
2324 sbData
->expires
= tbData
->expires
;
2325 if(tbData
->triggered
> sbData
->triggered
)
2326 sbData
->triggered
= tbData
->triggered
;
2327 if(tbData
== tFront
)
2329 del_channel_ban(tbData
);
2332 /* Source bans can override multiple target bans, so
2333 we allow a source to run through this loop multiple
2334 times, but we can only move it once. */
2339 /* Remove the source ban from the source ban list. */
2341 sbData
->next
->prev
= sbData
->prev
;
2343 /* Modify the source ban's associated channel. */
2344 sbData
->channel
= target
;
2346 /* Insert the ban into the target channel's linked list. */
2347 sbData
->prev
= NULL
;
2348 sbData
->next
= target
->bans
;
2351 target
->bans
->prev
= sbData
;
2352 target
->bans
= sbData
;
2354 /* Update the user counts for the target channel. */
2359 /* Possible to assert (source->bans == NULL) here. */
2360 source
->bans
= NULL
;
2364 merge_data(struct chanData
*source
, struct chanData
*target
)
2366 if(source
->visited
> target
->visited
)
2367 target
->visited
= source
->visited
;
2371 merge_channel(struct chanData
*source
, struct chanData
*target
)
2373 merge_users(source
, target
);
2374 merge_bans(source
, target
);
2375 merge_data(source
, target
);
2378 static CHANSERV_FUNC(cmd_merge
)
2380 struct userData
*target_user
;
2381 struct chanNode
*target
;
2382 char reason
[MAXLEN
];
2386 /* Make sure the target channel exists and is registered to the user
2387 performing the command. */
2388 if(!(target
= GetChannel(argv
[1])))
2390 reply("MSG_INVALID_CHANNEL");
2394 if(!target
->channel_info
)
2396 reply("CSMSG_NOT_REGISTERED", target
->name
);
2400 if(IsProtected(channel
->channel_info
))
2402 reply("CSMSG_MERGE_NODELETE");
2406 if(IsSuspended(target
->channel_info
))
2408 reply("CSMSG_MERGE_SUSPENDED");
2412 if(channel
== target
)
2414 reply("CSMSG_MERGE_SELF");
2418 target_user
= GetChannelUser(target
->channel_info
, user
->handle_info
);
2419 if(!target_user
|| (target_user
->access
< UL_OWNER
))
2421 reply("CSMSG_MERGE_NOT_OWNER");
2425 /* Merge the channel structures and associated data. */
2426 merge_channel(channel
->channel_info
, target
->channel_info
);
2427 sprintf(reason
, "merged into %s by %s.", target
->name
, user
->handle_info
->handle
);
2428 unregister_channel(channel
->channel_info
, reason
);
2429 reply("CSMSG_MERGE_SUCCESS", target
->name
);
2433 static CHANSERV_FUNC(cmd_opchan
)
2435 struct mod_chanmode change
;
2436 if(!IsHelping(user
) && !channel
->channel_info
->may_opchan
)
2438 reply("CSMSG_ALREADY_OPCHANNED", channel
->name
);
2441 channel
->channel_info
->may_opchan
= 0;
2442 mod_chanmode_init(&change
);
2444 change
.args
[0].mode
= MODE_CHANOP
;
2445 change
.args
[0].u
.member
= GetUserMode(channel
, chanserv
);
2446 mod_chanmode_announce(chanserv
, channel
, &change
);
2447 reply("CSMSG_OPCHAN_DONE", channel
->name
);
2451 static CHANSERV_FUNC(cmd_adduser
)
2453 struct userData
*actee
;
2454 struct userData
*actor
;
2455 struct handle_info
*handle
;
2456 unsigned short access
;
2460 if(channel
->channel_info
->userCount
>= chanserv_conf
.max_chan_users
)
2462 reply("CSMSG_MAXIMUM_USERS", chanserv_conf
.max_chan_users
);
2466 access
= user_level_from_name(argv
[2], UL_OWNER
);
2469 reply("CSMSG_INVALID_ACCESS", argv
[2]);
2473 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2474 if(actor
->access
<= access
)
2476 reply("CSMSG_NO_BUMP_ACCESS");
2480 if(!(handle
= modcmd_get_handle_info(user
, argv
[1])))
2482 // 'kevin must first authenticate with AuthServ.' is sent to user
2483 struct userNode
*unode
;
2484 unode
= GetUserH(argv
[1]); /* find user struct by nick */
2487 if(find_adduser_pending(channel
, unode
)) {
2488 reply("CSMSG_ADDUSER_PENDING_ALREADY", channel
->name
);
2491 if(IsInChannel(channel
, unode
)) {
2492 reply("CSMSG_ADDUSER_PENDING");
2493 add_adduser_pending(channel
, unode
, access
);
2494 send_message_type(1,unode
, chanserv
, "CSMSG_ADDUSER_PENDING_TARGET", user
->nick
, channel
->name
);
2496 /* this results in user must auth AND not in chan errors. too confusing..
2498 reply("CSMSG_ADDUSER_PENDING_NOTINCHAN", channel->name);
2506 if((actee
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2508 reply("CSMSG_USER_EXISTS", handle
->handle
, channel
->name
, user_level_name_from_level(actee
->access
));
2512 actee
= add_channel_user(channel
->channel_info
, handle
, access
, 0, NULL
);
2513 scan_user_presence(actee
, NULL
);
2514 reply("CSMSG_ADDED_USER", handle
->handle
, channel
->name
, user_level_name_from_level(access
), access
);
2518 static CHANSERV_FUNC(cmd_clvl
)
2520 struct handle_info
*handle
;
2521 struct userData
*victim
;
2522 struct userData
*actor
;
2523 unsigned short new_access
;
2524 int privileged
= IsHelping(user
) && ((user
->handle_info
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
->channel_info
));
2528 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2530 if(!(handle
= modcmd_get_handle_info(user
, argv
[1])))
2533 if(handle
== user
->handle_info
&& !privileged
)
2535 reply("CSMSG_NO_SELF_CLVL");
2539 if(!(victim
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2541 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
2545 if(actor
->access
<= victim
->access
&& !privileged
)
2547 reply("MSG_USER_OUTRANKED", handle
->handle
);
2551 new_access
= user_level_from_name(argv
[2], UL_OWNER
);
2555 reply("CSMSG_INVALID_ACCESS", argv
[2]);
2559 if(new_access
>= actor
->access
&& !privileged
)
2561 reply("CSMSG_NO_BUMP_ACCESS");
2565 victim
->access
= new_access
;
2566 reply("CSMSG_CHANGED_ACCESS", handle
->handle
, user_level_name_from_level(new_access
), new_access
, channel
->name
);
2570 static CHANSERV_FUNC(cmd_deluser
)
2572 struct handle_info
*handle
;
2573 struct userData
*victim
;
2574 struct userData
*actor
;
2575 unsigned short access
;
2580 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2582 if(!(handle
= modcmd_get_handle_info(user
, argv
[argc
-1])))
2585 if(!(victim
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2587 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
2593 access
= user_level_from_name(argv
[1], UL_OWNER
);
2596 reply("CSMSG_INVALID_ACCESS", argv
[1]);
2599 if(access
!= victim
->access
)
2601 reply("CSMSG_INCORRECT_ACCESS", handle
->handle
, user_level_name_from_level(victim
->access
), argv
[1]);
2607 access
= victim
->access
;
2610 if((actor
->access
<= victim
->access
) && !IsHelping(user
))
2612 reply("MSG_USER_OUTRANKED", victim
->handle
->handle
);
2616 chan_name
= strdup(channel
->name
);
2617 del_channel_user(victim
, 1);
2618 reply("CSMSG_DELETED_USER", handle
->handle
, access
, chan_name
);
2624 cmd_mdel_user(struct userNode
*user
, struct chanNode
*channel
, unsigned short min_access
, unsigned short max_access
, char *mask
, struct svccmd
*cmd
)
2626 struct userData
*actor
, *uData
, *next
;
2628 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2630 if(min_access
> max_access
)
2632 reply("CSMSG_BAD_RANGE", min_access
, max_access
);
2636 if((actor
->access
<= max_access
) && !IsHelping(user
))
2638 reply("CSMSG_NO_ACCESS");
2642 for(uData
= channel
->channel_info
->users
; uData
; uData
= next
)
2646 if((uData
->access
>= min_access
)
2647 && (uData
->access
<= max_access
)
2648 && match_ircglob(uData
->handle
->handle
, mask
))
2649 del_channel_user(uData
, 1);
2652 reply("CSMSG_DELETED_USERS", mask
, min_access
, max_access
, channel
->name
);
2656 static CHANSERV_FUNC(cmd_mdelowner
)
2658 return cmd_mdel_user(user
, channel
, UL_OWNER
, UL_OWNER
, argv
[1], cmd
);
2661 static CHANSERV_FUNC(cmd_mdelcoowner
)
2663 return cmd_mdel_user(user
, channel
, UL_COOWNER
, UL_COOWNER
, argv
[1], cmd
);
2666 static CHANSERV_FUNC(cmd_mdelmanager
)
2668 return cmd_mdel_user(user
, channel
, UL_MANAGER
, UL_MANAGER
, argv
[1], cmd
);
2671 static CHANSERV_FUNC(cmd_mdelop
)
2673 return cmd_mdel_user(user
, channel
, UL_OP
, UL_OP
, argv
[1], cmd
);
2676 static CHANSERV_FUNC(cmd_mdelpeon
)
2678 return cmd_mdel_user(user
, channel
, UL_PEON
, UL_PEON
, argv
[1], cmd
);
2681 static CHANSERV_FUNC(cmd_mdelhalfop
)
2683 return cmd_mdel_user(user
, channel
, UL_HALFOP
, UL_HALFOP
, argv
[1], cmd
);
2689 cmd_trim_bans(struct userNode
*user
, struct chanNode
*channel
, unsigned long duration
)
2691 struct banData
*bData
, *next
;
2692 char interval
[INTERVALLEN
];
2697 limit
= now
- duration
;
2698 for(bData
= channel
->channel_info
->bans
; bData
; bData
= next
)
2702 if((bData
->triggered
&& bData
->triggered
>= limit
) || (bData
->set
&& bData
->set
>= limit
))
2705 del_channel_ban(bData
);
2709 intervalString(interval
, duration
, user
->handle_info
);
2710 send_message(user
, chanserv
, "CSMSG_TRIMMED_LAMERS", count
, channel
->name
, interval
);
2715 cmd_trim_users(struct userNode
*user
, struct chanNode
*channel
, unsigned short min_access
, unsigned short max_access
, unsigned long duration
)
2717 struct userData
*actor
, *uData
, *next
;
2718 char interval
[INTERVALLEN
];
2722 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2723 if(min_access
> max_access
)
2725 send_message(user
, chanserv
, "CSMSG_BAD_RANGE", min_access
, max_access
);
2729 if((actor
->access
<= max_access
) && !IsHelping(user
))
2731 send_message(user
, chanserv
, "CSMSG_NO_ACCESS");
2736 limit
= now
- duration
;
2737 for(uData
= channel
->channel_info
->users
; uData
; uData
= next
)
2741 if((uData
->seen
> limit
) || uData
->present
)
2744 if(((uData
->access
>= min_access
) && (uData
->access
<= max_access
))
2745 || (!max_access
&& (uData
->access
< actor
->access
)))
2747 del_channel_user(uData
, 1);
2755 max_access
= (actor
->access
> UL_OWNER
) ? UL_OWNER
: (actor
->access
- 1);
2757 send_message(user
, chanserv
, "CSMSG_TRIMMED_USERS", count
, min_access
, max_access
, channel
->name
, intervalString(interval
, duration
, user
->handle_info
));
2761 static CHANSERV_FUNC(cmd_trim
)
2763 unsigned long duration
;
2764 unsigned short min_level
, max_level
;
2768 duration
= ParseInterval(argv
[2]);
2771 reply("CSMSG_CANNOT_TRIM");
2775 if(!irccasecmp(argv
[1], "lamers"))
2777 cmd_trim_bans(user
, channel
, duration
); /* trim_lamers.. */
2780 else if(!irccasecmp(argv
[1], "users"))
2782 cmd_trim_users(user
, channel
, 0, 0, duration
);
2785 else if(parse_level_range(&min_level
, &max_level
, argv
[1]))
2787 cmd_trim_users(user
, channel
, min_level
, max_level
, duration
);
2790 else if((min_level
= user_level_from_name(argv
[1], UL_OWNER
)))
2792 cmd_trim_users(user
, channel
, min_level
, min_level
, duration
);
2797 reply("CSMSG_INVALID_TRIM", argv
[1]);
2802 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
2803 to the user. cmd_all takes advantage of this. */
2804 static CHANSERV_FUNC(cmd_up
)
2806 struct mod_chanmode change
;
2807 struct userData
*uData
;
2810 mod_chanmode_init(&change
);
2812 change
.args
[0].u
.member
= GetUserMode(channel
, user
);
2813 if(!change
.args
[0].u
.member
)
2816 reply("MSG_CHANNEL_ABSENT", channel
->name
);
2820 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
2824 reply("CSMSG_GODMODE_UP", argv
[0]);
2827 else if(uData
->access
>= UL_OP
/*channel->channel_info->lvlOpts[lvlGiveOps]*/)
2829 change
.args
[0].mode
= MODE_CHANOP
;
2830 errmsg
= "CSMSG_ALREADY_OPPED";
2832 else if(uData
->access
>= UL_HALFOP
/*channel->channel_info->lvlOpts[lvlGiveHalfOps]*/)
2834 change
.args
[0].mode
= MODE_HALFOP
;
2835 errmsg
= "CSMSG_ALREADY_HALFOPPED";
2837 else if(uData
->access
>= UL_PEON
&& (channel
->channel_info
->chOpts
[chVoice
] == 'p' || channel
->channel_info
->chOpts
[chVoice
] == 'a'))
2839 change
.args
[0].mode
= MODE_VOICE
;
2840 errmsg
= "CSMSG_ALREADY_VOICED";
2845 reply("CSMSG_NO_ACCESS");
2848 change
.args
[0].mode
&= ~change
.args
[0].u
.member
->modes
;
2849 if(!change
.args
[0].mode
)
2852 reply(errmsg
, channel
->name
);
2855 modcmd_chanmode_announce(&change
);
2859 static CHANSERV_FUNC(cmd_down
)
2861 struct mod_chanmode change
;
2863 mod_chanmode_init(&change
);
2865 change
.args
[0].u
.member
= GetUserMode(channel
, user
);
2866 if(!change
.args
[0].u
.member
)
2869 reply("MSG_CHANNEL_ABSENT", channel
->name
);
2873 if(!change
.args
[0].u
.member
->modes
)
2876 reply("CSMSG_ALREADY_DOWN", channel
->name
);
2880 change
.args
[0].mode
= MODE_REMOVE
| change
.args
[0].u
.member
->modes
;
2881 modcmd_chanmode_announce(&change
);
2885 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
)
2887 struct userData
*cList
;
2889 for(cList
= user
->handle_info
->channels
; cList
; cList
= cList
->u_next
)
2891 if(IsSuspended(cList
->channel
)
2892 || IsUserSuspended(cList
)
2893 || !GetUserMode(cList
->channel
->channel
, user
))
2896 mcmd(user
, cList
->channel
->channel
, 0, NULL
, cmd
);
2902 static CHANSERV_FUNC(cmd_upall
)
2904 return cmd_all(CSFUNC_ARGS
, cmd_up
);
2907 static CHANSERV_FUNC(cmd_downall
)
2909 return cmd_all(CSFUNC_ARGS
, cmd_down
);
2912 typedef int validate_func_t(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
);
2913 typedef void process_func_t(unsigned int num
, struct userNode
**newops
, struct chanNode
*channel
, struct userNode
*who
, int announce
);
2916 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
)
2918 unsigned int ii
, valid
;
2919 struct userNode
*victim
;
2920 struct mod_chanmode
*change
;
2922 change
= mod_chanmode_alloc(argc
- 1);
2924 for(ii
=valid
=0; ++ii
< argc
; )
2926 if(!(victim
= GetUserH(argv
[ii
])))
2928 change
->args
[valid
].mode
= mode
;
2929 change
->args
[valid
].u
.member
= GetUserMode(channel
, victim
);
2930 if(!change
->args
[valid
].u
.member
)
2932 if(validate
&& !validate(user
, channel
, victim
))
2937 change
->argc
= valid
;
2938 if(valid
< (argc
-1))
2939 reply("CSMSG_PROCESS_FAILED");
2942 modcmd_chanmode_announce(change
);
2943 reply(action
, channel
->name
);
2945 mod_chanmode_free(change
);
2949 static CHANSERV_FUNC(cmd_op
)
2951 return modify_users(CSFUNC_ARGS
, validate_op
, MODE_CHANOP
, "CSMSG_OPPED_USERS");
2954 static CHANSERV_FUNC(cmd_hop
)
2956 return modify_users(CSFUNC_ARGS
, validate_halfop
, MODE_HALFOP
, "CSMSG_HALFOPPED_USERS");
2959 static CHANSERV_FUNC(cmd_deop
)
2961 return modify_users(CSFUNC_ARGS
, validate_deop
, MODE_REMOVE
|MODE_CHANOP
, "CSMSG_DEOPPED_USERS");
2964 static CHANSERV_FUNC(cmd_dehop
)
2966 return modify_users(CSFUNC_ARGS
, validate_dehop
, MODE_REMOVE
|MODE_HALFOP
, "CSMSG_DEHALFOPPED_USERS");
2969 static CHANSERV_FUNC(cmd_voice
)
2971 return modify_users(CSFUNC_ARGS
, NULL
, MODE_VOICE
, "CSMSG_VOICED_USERS");
2974 static CHANSERV_FUNC(cmd_devoice
)
2976 return modify_users(CSFUNC_ARGS
, NULL
, MODE_REMOVE
|MODE_VOICE
, "CSMSG_DEVOICED_USERS");
2980 bad_channel_ban(struct chanNode
*channel
, struct userNode
*user
, const char *ban
, int *victimCount
, struct modeNode
**victims
)
2986 for(ii
=0; ii
<channel
->members
.used
; ii
++)
2988 struct modeNode
*mn
= channel
->members
.list
[ii
];
2990 if(IsService(mn
->user
))
2993 if(!user_matches_glob(mn
->user
, ban
, 1))
2996 if(protect_user(mn
->user
, user
, channel
->channel_info
))
3000 victims
[(*victimCount
)++] = mn
;
3006 eject_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
)
3008 struct userNode
*victim
;
3009 struct modeNode
**victims
;
3010 unsigned int offset
, n
, victimCount
, duration
= 0;
3011 char *reason
= "Bye.", *ban
, *name
;
3012 char interval
[INTERVALLEN
];
3014 offset
= (action
& ACTION_ADD_TIMED_LAMER
) ? 3 : 2;
3015 REQUIRE_PARAMS(offset
);
3018 reason
= unsplit_string(argv
+ offset
, argc
- offset
, NULL
);
3019 if(strlen(reason
) > (TOPICLEN
- (NICKLEN
+ 3)))
3021 /* Truncate the reason to a length of TOPICLEN, as
3022 the ircd does; however, leave room for an ellipsis
3023 and the kicker's nick. */
3024 sprintf(reason
+ (TOPICLEN
- (NICKLEN
+ 6)), "...");
3028 if((victim
= GetUserH(argv
[1])))
3030 victims
= alloca(sizeof(victims
[0]));
3031 victims
[0] = GetUserMode(channel
, victim
);
3032 /* XXX: The comparison with ACTION_KICK is just because all
3033 * other actions can work on users outside the channel, and we
3034 * want to allow those (e.g. unbans) in that case. If we add
3035 * some other ejection action for in-channel users, change
3037 victimCount
= victims
[0] ? 1 : 0;
3039 if(IsService(victim
))
3041 reply("MSG_SERVICE_IMMUNE", victim
->nick
);
3045 if((action
== ACTION_KICK
) && !victimCount
)
3047 reply("MSG_CHANNEL_USER_ABSENT", victim
->nick
, channel
->name
);
3051 if(protect_user(victim
, user
, channel
->channel_info
))
3053 reply("CSMSG_USER_PROTECTED", victim
->nick
);
3057 ban
= generate_hostmask(victim
, GENMASK_STRICT_HOST
|GENMASK_ANY_IDENT
);
3058 name
= victim
->nick
;
3062 if(!is_ircmask(argv
[1]))
3064 reply("MSG_NICK_UNKNOWN", argv
[1]);
3068 victims
= alloca(sizeof(victims
[0]) * channel
->members
.used
);
3070 if(bad_channel_ban(channel
, user
, argv
[1], &victimCount
, victims
))
3072 reply("CSMSG_MASK_PROTECTED", argv
[1]);
3075 /* We dont actually want a victem count if were banning a mask manually, IMO -Rubin*/
3077 victimCount
= 0; /* Dont deop etc ppl who match this */
3079 #ifdef entropy_lameness
3080 if((victimCount
> 4) && ((victimCount
* 3) > channel
->members
.used
) && !IsOper(user
))
3082 reply("CSMSG_LAME_MASK", argv
[1]);
3087 if((action
== ACTION_KICK
) && (victimCount
== 0))
3089 reply("CSMSG_NO_MATCHING_USERS", channel
->name
, argv
[1]);
3093 name
= ban
= strdup(argv
[1]);
3096 /* Truncate the ban in place if necessary; we must ensure
3097 that 'ban' is a valid ban mask before sanitizing it. */
3098 sanitize_ircmask(ban
);
3100 if(action
& ACTION_ADD_LAMER
)
3102 struct banData
*bData
, *next
;
3104 if(channel
->channel_info
->banCount
>= chanserv_conf
.max_chan_bans
) /* ..lamers.. */
3106 reply("CSMSG_MAXIMUM_LAMERS", chanserv_conf
.max_chan_bans
); /* ..lamers.. */
3111 if(action
& ACTION_ADD_TIMED_LAMER
)
3113 duration
= ParseInterval(argv
[2]);
3117 reply("CSMSG_DURATION_TOO_LOW");
3121 else if(duration
> (86400 * 365 * 2))
3123 reply("CSMSG_DURATION_TOO_HIGH");
3130 for(bData
= channel
->channel_info
->bans
; bData
; bData
= next
)
3132 if(match_ircglobs(bData
->mask
, ban
))
3134 int exact
= !irccasecmp(bData
->mask
, ban
);
3136 /* The ban is redundant; there is already a ban
3137 with the same effect in place. */
3141 free(bData
->reason
);
3142 bData
->reason
= strdup(reason
);
3143 safestrncpy(bData
->owner
, (user
->handle_info
? user
->handle_info
->handle
: user
->nick
), sizeof(bData
->owner
));
3145 reply("CSMSG_REASON_CHANGE", ban
);
3149 if(exact
&& bData
->expires
)
3153 /* If the ban matches an existing one exactly,
3154 extend the expiration time if the provided
3155 duration is longer. */
3156 if(duration
&& ((time_t)(now
+ duration
) > bData
->expires
))
3158 bData
->expires
= now
+ duration
;
3169 /* Delete the expiration timeq entry and
3170 requeue if necessary. */
3171 timeq_del(0, expire_ban
, bData
, TIMEQ_IGNORE_WHEN
);
3174 timeq_add(bData
->expires
, expire_ban
, bData
);
3178 /* automated kickban */
3181 reply("CSMSG_LAMER_EXTENDED", ban
, intervalString(interval
, duration
, user
->handle_info
));
3183 reply("CSMSG_LAMER_ADDED", name
, channel
->name
);
3189 reply("CSMSG_REDUNDANT_LAMER", name
, channel
->name
);
3196 if(match_ircglobs(ban
, bData
->mask
))
3198 /* The ban we are adding makes previously existing
3199 bans redundant; silently remove them. */
3200 del_channel_ban(bData
);
3204 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
);
3206 name
= ban
= strdup(bData
->mask
);
3210 /* WHAT DOES THIS DO?? -Rubin */
3211 for(n
= 0; n
< chanserv_conf
.old_ban_names
->used
; ++n
)
3213 extern const char *hidden_host_suffix
;
3214 const char *old_name
= chanserv_conf
.old_ban_names
->list
[n
];
3216 unsigned int l1
, l2
;
3219 l2
= strlen(old_name
);
3222 if(irccasecmp(ban
+ l1
- l2
, old_name
))
3224 new_mask
= malloc(MAXLEN
);
3225 sprintf(new_mask
, "%.*s%s", (int)(l1
-l2
), ban
, hidden_host_suffix
);
3227 name
= ban
= new_mask
;
3232 if(action
& ACTION_BAN
)
3234 unsigned int exists
;
3235 struct mod_chanmode
*change
;
3237 if(channel
->banlist
.used
>= MAXBANS
)
3240 reply("CSMSG_BANLIST_FULL", channel
->name
);
3245 exists
= ChannelBanExists(channel
, ban
);
3246 change
= mod_chanmode_alloc(victimCount
+ 1);
3247 for(n
= 0; n
< victimCount
; ++n
)
3249 change
->args
[n
].mode
= MODE_REMOVE
|MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
;
3250 change
->args
[n
].u
.member
= victims
[n
];
3254 change
->args
[n
].mode
= MODE_BAN
;
3255 change
->args
[n
++].u
.hostmask
= ban
;
3259 modcmd_chanmode_announce(change
);
3261 mod_chanmode_announce(chanserv
, channel
, change
);
3262 mod_chanmode_free(change
);
3264 if(exists
&& (action
== ACTION_BAN
))
3267 reply("CSMSG_REDUNDANT_BAN", name
, channel
->name
);
3273 if(action
& ACTION_KICK
)
3275 char kick_reason
[MAXLEN
];
3276 sprintf(kick_reason
, "(%s) %s", user
->nick
, reason
);
3278 for(n
= 0; n
< victimCount
; n
++)
3279 KickChannelUser(victims
[n
]->user
, channel
, chanserv
, kick_reason
);
3284 /* No response, since it was automated. */
3286 else if(action
& ACTION_ADD_LAMER
)
3289 reply("CSMSG_TIMED_LAMER_ADDED", name
, channel
->name
, intervalString(interval
, duration
, user
->handle_info
));
3291 reply("CSMSG_LAMER_ADDED", name
, channel
->name
);
3293 else if((action
& (ACTION_BAN
| ACTION_KICK
)) == (ACTION_BAN
| ACTION_KICK
))
3294 reply("CSMSG_KICK_BAN_DONE", name
, channel
->name
);
3295 else if(action
& ACTION_BAN
)
3296 reply("CSMSG_BAN_DONE", name
, channel
->name
);
3297 else if(action
& ACTION_KICK
&& victimCount
)
3298 reply("CSMSG_KICK_DONE", name
, channel
->name
);
3304 static CHANSERV_FUNC(cmd_kickban
)
3306 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
);
3309 static CHANSERV_FUNC(cmd_kick
)
3311 return eject_user(CSFUNC_ARGS
, ACTION_KICK
);
3314 static CHANSERV_FUNC(cmd_ban
)
3316 return eject_user(CSFUNC_ARGS
, ACTION_BAN
);
3319 static CHANSERV_FUNC(cmd_addlamer
)
3321 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
| ACTION_ADD_LAMER
);
3324 static CHANSERV_FUNC(cmd_addtimedlamer
)
3326 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
);
3329 static struct mod_chanmode
*
3330 find_matching_bans(struct banList
*bans
, struct userNode
*actee
, const char *mask
)
3332 struct mod_chanmode
*change
;
3333 unsigned char *match
;
3334 unsigned int ii
, count
;
3336 match
= alloca(bans
->used
);
3339 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3341 match
[ii
] = user_matches_glob(actee
, bans
->list
[ii
]->ban
, 1);
3348 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3350 match
[ii
] = match_ircglobs(mask
, bans
->list
[ii
]->ban
);
3357 change
= mod_chanmode_alloc(count
);
3358 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3362 change
->args
[count
].mode
= MODE_REMOVE
| MODE_BAN
;
3363 change
->args
[count
++].u
.hostmask
= strdup(bans
->list
[ii
]->ban
);
3365 assert(count
== change
->argc
);
3370 unban_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
)
3372 struct userNode
*actee
;
3378 /* may want to allow a comma delimited list of users... */
3379 if(!(actee
= GetUserH(argv
[1])))
3381 if(!is_ircmask(argv
[1]))
3383 reply("MSG_NICK_UNKNOWN", argv
[1]);
3387 mask
= strdup(argv
[1]);
3390 /* We don't sanitize the mask here because ircu
3392 if(action
& ACTION_UNBAN
)
3394 struct mod_chanmode
*change
;
3395 change
= find_matching_bans(&channel
->banlist
, actee
, mask
);
3400 modcmd_chanmode_announce(change
);
3401 for(ii
= 0; ii
< change
->argc
; ++ii
)
3402 free((char*)change
->args
[ii
].u
.hostmask
);
3403 mod_chanmode_free(change
);
3408 if(action
& ACTION_DEL_LAMER
)
3410 struct banData
*ban
, *next
;
3412 ban
= channel
->channel_info
->bans
; /* lamers */
3416 for( ; ban
&& !user_matches_glob(actee
, ban
->mask
, 1);
3419 for( ; ban
&& !match_ircglobs(mask
, ban
->mask
);
3424 del_channel_ban(ban
);
3431 reply("CSMSG_BAN_NOT_FOUND", actee
? actee
->nick
: mask
);
3433 reply("CSMSG_BAN_REMOVED", actee
? actee
->nick
: mask
);
3439 static CHANSERV_FUNC(cmd_unban
)
3441 return unban_user(CSFUNC_ARGS
, ACTION_UNBAN
);
3444 static CHANSERV_FUNC(cmd_dellamer
)
3446 /* it doesn't necessarily have to remove the channel ban - may want
3447 to make that an option. */
3448 return unban_user(CSFUNC_ARGS
, ACTION_UNBAN
| ACTION_DEL_LAMER
);
3451 static CHANSERV_FUNC(cmd_unbanme
)
3453 struct userData
*uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
3454 long flags
= ACTION_UNBAN
;
3456 /* remove permanent bans if the user has the proper access. */
3457 if(uData
->access
>= UL_MANAGER
)
3458 flags
|= ACTION_DEL_LAMER
;
3460 argv
[1] = user
->nick
;
3461 return unban_user(user
, channel
, 2, argv
, cmd
, flags
);
3464 static CHANSERV_FUNC(cmd_unbanall
)
3466 struct mod_chanmode
*change
;
3469 if(!channel
->banlist
.used
)
3471 reply("CSMSG_NO_BANS", channel
->name
);
3475 change
= mod_chanmode_alloc(channel
->banlist
.used
);
3476 for(ii
=0; ii
<channel
->banlist
.used
; ii
++)
3478 change
->args
[ii
].mode
= MODE_REMOVE
| MODE_BAN
;
3479 change
->args
[ii
].u
.hostmask
= strdup(channel
->banlist
.list
[ii
]->ban
);
3481 modcmd_chanmode_announce(change
);
3482 for(ii
= 0; ii
< change
->argc
; ++ii
)
3483 free((char*)change
->args
[ii
].u
.hostmask
);
3484 mod_chanmode_free(change
);
3485 reply("CSMSG_BANS_REMOVED", channel
->name
);
3489 static CHANSERV_FUNC(cmd_open
)
3491 struct mod_chanmode
*change
;
3494 change
= find_matching_bans(&channel
->banlist
, user
, NULL
);
3496 change
= mod_chanmode_alloc(0);
3497 change
->modes_clear
|= MODE_INVITEONLY
| MODE_LIMIT
| MODE_KEY
;
3498 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
3499 && channel
->channel_info
->modes
.modes_set
)
3500 change
->modes_clear
&= ~channel
->channel_info
->modes
.modes_set
;
3501 modcmd_chanmode_announce(change
);
3502 reply("CSMSG_CHANNEL_OPENED", channel
->name
);
3503 for(ii
= 0; ii
< change
->argc
; ++ii
)
3504 free((char*)change
->args
[ii
].u
.hostmask
);
3505 mod_chanmode_free(change
);
3509 static CHANSERV_FUNC(cmd_myaccess
)
3511 static struct string_buffer sbuf
;
3512 struct handle_info
*target_handle
;
3513 struct userData
*uData
;
3516 target_handle
= user
->handle_info
;
3517 else if(!IsHelping(user
))
3519 reply("CSMSG_MYACCESS_SELF_ONLY", argv
[0]);
3522 else if(!(target_handle
= modcmd_get_handle_info(user
, argv
[1])))
3525 if(!target_handle
->channels
)
3527 reply("CSMSG_SQUAT_ACCESS", target_handle
->handle
);
3531 reply("CSMSG_INFOLINE_LIST", target_handle
->handle
);
3532 for(uData
= target_handle
->channels
; uData
; uData
= uData
->u_next
)
3534 struct chanData
*cData
= uData
->channel
;
3536 if(uData
->access
> UL_OWNER
)
3538 if(IsProtected(cData
)
3539 && (target_handle
!= user
->handle_info
)
3540 && !GetTrueChannelAccess(cData
, user
->handle_info
))
3543 string_buffer_append_printf(&sbuf
, "[%s (%d", cData
->channel
->name
, uData
->access
);
3544 if(uData
->flags
== USER_AUTO_OP
)
3545 string_buffer_append(&sbuf
, ',');
3546 if(IsUserSuspended(uData
))
3547 string_buffer_append(&sbuf
, 's');
3548 if(IsUserAutoOp(uData
))
3550 if(uData
->access
>= UL_OP
/*cData->lvlOpts[lvlGiveOps]*/)
3551 string_buffer_append(&sbuf
, 'o');
3552 else if(uData
->access
>= UL_HALFOP
/*cData->lvlOpts[lvlGiveHalfOps]*/)
3553 string_buffer_append(&sbuf
, 'h');
3554 else if(uData
->access
>= UL_PEON
/*cData->lvlOpts[lvlGiveVoice]*/)
3555 string_buffer_append(&sbuf
, 'v');
3557 if(IsUserAutoInvite(uData
) && (uData
->access
>= cData
->lvlOpts
[lvlInviteMe
]))
3558 string_buffer_append(&sbuf
, 'i');
3560 string_buffer_append_printf(&sbuf
, ")] %s", uData
->info
);
3562 string_buffer_append_string(&sbuf
, ")]");
3563 string_buffer_append(&sbuf
, '\0');
3564 send_message_type(4, user
, cmd
->parent
->bot
, "%s", sbuf
.list
);
3570 static CHANSERV_FUNC(cmd_access
)
3572 struct userNode
*target
;
3573 struct handle_info
*target_handle
;
3574 struct userData
*uData
;
3576 char prefix
[MAXLEN
];
3581 target_handle
= target
->handle_info
;
3583 else if((target
= GetUserH(argv
[1])))
3585 target_handle
= target
->handle_info
;
3587 else if(argv
[1][0] == '*')
3589 if(!(target_handle
= get_handle_info(argv
[1]+1)))
3591 reply("MSG_HANDLE_UNKNOWN", argv
[1]+1);
3597 reply("MSG_NICK_UNKNOWN", argv
[1]);
3601 assert(target
|| target_handle
);
3603 if(target
== chanserv
)
3605 reply("CSMSG_IS_CHANSERV");
3613 reply("CSMSG_LAZY_SMURF_TARGET", target
->nick
, chanserv_conf
.irc_operator_epithet
);
3618 reply("MSG_USER_AUTHENTICATE", target
->nick
);
3621 reply("MSG_AUTHENTICATE");
3627 const char *epithet
= NULL
, *type
= NULL
;
3630 epithet
= chanserv_conf
.irc_operator_epithet
;
3633 else if(IsNetworkHelper(target
))
3635 epithet
= chanserv_conf
.network_helper_epithet
;
3636 type
= "network helper";
3638 else if(IsSupportHelper(target
))
3640 epithet
= chanserv_conf
.support_helper_epithet
;
3641 type
= "support helper";
3645 if(target_handle
->epithet
)
3646 reply("CSMSG_SMURF_TARGET", target
->nick
, target_handle
->epithet
, type
);
3648 reply("CSMSG_SMURF_TARGET", target
->nick
, epithet
, type
);
3650 sprintf(prefix
, "%s (%s)", target
->nick
, target_handle
->handle
);
3654 sprintf(prefix
, "%s", target_handle
->handle
);
3657 if(!channel
->channel_info
)
3659 reply("CSMSG_NOT_REGISTERED", channel
->name
);
3663 helping
= HANDLE_FLAGGED(target_handle
, HELPING
)
3664 && ((target_handle
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
->channel_info
));
3665 if((uData
= GetTrueChannelAccess(channel
->channel_info
, target_handle
)))
3667 reply((helping
? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix
, user_level_name_from_level(uData
->access
), uData
->access
, channel
->name
);
3668 /* To prevent possible information leaks, only show infolines
3669 * if the requestor is in the channel or it's their own
3671 if(uData
->info
&& (GetUserMode(channel
, user
) || (target_handle
== user
->handle_info
)))
3673 send_message_type(4, user
, cmd
->parent
->bot
, "[%s] %s", (target
? target
->nick
: target_handle
->handle
), uData
->info
);
3675 /* Likewise, only say it's suspended if the user has active
3676 * access in that channel or it's their own entry. */
3677 if(IsUserSuspended(uData
)
3678 && (GetChannelUser(channel
->channel_info
, user
->handle_info
)
3679 || (user
->handle_info
== uData
->handle
)))
3681 reply("CSMSG_USER_SUSPENDED", (target
? target
->nick
: target_handle
->handle
), channel
->name
);
3686 reply((helping
? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix
, channel
->name
);
3692 /* This is never used...
3694 zoot_list(struct listData *list)
3696 struct userData *uData;
3697 unsigned int start, curr, highest, lowest;
3698 struct helpfile_table tmp_table;
3699 const char **temp, *msg;
3701 if(list->table.length == 1)
3704 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, user_level_name_from_level(list->lowest), user_level_name_from_level(list->highest), list->search);
3706 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, user_level_name_from_level(list->lowest), user_level_name_from_level(list->highest));
3707 msg = user_find_message(list->user, "MSG_NONE");
3708 send_message_type(4, list->user, list->bot, " %s", msg);
3710 tmp_table.width = list->table.width;
3711 tmp_table.flags = list->table.flags;
3712 list->table.contents[0][0] = " ";
3713 highest = list->highest;
3714 if(list->lowest != 0)
3715 lowest = list->lowest;
3716 else if(highest < 100)
3719 lowest = highest - 100;
3720 for(start = curr = 1; curr < list->table.length; )
3722 uData = list->users[curr-1];
3723 list->table.contents[curr++][0] = " ";
3724 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
3727 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, user_level_name_from_level(lowest), user_level_name_from_level(highest), list->search);
3729 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, user_level_name_from_level(lowest), user_level_name_from_level(highest));
3730 temp = list->table.contents[--start];
3731 list->table.contents[start] = list->table.contents[0];
3732 tmp_table.contents = list->table.contents + start;
3733 tmp_table.length = curr - start;
3734 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
3735 list->table.contents[start] = temp;
3737 highest = lowest - 1;
3738 lowest = (highest < 100) ? 0 : (highest - 99);
3745 def_list(struct listData
*list
)
3749 send_message(list
->user
, list
->bot
, "CSMSG_ACCESS_SEARCH_HEADER", list
->channel
->name
, user_level_name_from_level(list
->lowest
), user_level_name_from_level(list
->highest
), list
->search
);
3751 send_message(list
->user
, list
->bot
, "CSMSG_ACCESS_ALL_HEADER", list
->channel
->name
, user_level_name_from_level(list
->lowest
), user_level_name_from_level(list
->highest
));
3752 if(list
->table
.length
== 1)
3754 msg
= user_find_message(list
->user
, "MSG_NONE");
3755 send_message_type(4, list
->user
, list
->bot
, " %s", msg
);
3758 table_send(list
->bot
, list
->user
->nick
, 0, NULL
, list
->table
);
3762 userData_access_comp(const void *arg_a
, const void *arg_b
)
3764 const struct userData
*a
= *(struct userData
**)arg_a
;
3765 const struct userData
*b
= *(struct userData
**)arg_b
;
3767 if(a
->access
!= b
->access
)
3768 res
= b
->access
- a
->access
;
3770 res
= irccasecmp(a
->handle
->handle
, b
->handle
->handle
);
3775 cmd_list_users(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, unsigned short lowest
, unsigned short highest
)
3777 void (*send_list
)(struct listData
*);
3778 struct userData
*uData
;
3779 struct listData lData
;
3780 unsigned int matches
;
3784 lData
.bot
= cmd
->parent
->bot
;
3785 lData
.channel
= channel
;
3786 lData
.lowest
= lowest
;
3787 lData
.highest
= highest
;
3788 lData
.search
= (argc
> 1) ? argv
[1] : NULL
;
3789 send_list
= def_list
;
3790 /* What does the following line do exactly?? */
3791 /*(void)zoot_list; ** since it doesn't show user levels */
3793 /* this does nothing!! -rubin
3794 if(user->handle_info)
3796 switch(user->handle_info->userlist_style)
3798 case HI_STYLE_DEF: send_list = def_list; break;
3799 case HI_STYLE_ZOOT: send_list = def_list; break;
3804 lData
.users
= alloca(channel
->channel_info
->userCount
* sizeof(struct userData
*));
3806 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
3808 if((uData
->access
< lowest
)
3809 || (uData
->access
> highest
)
3810 || (lData
.search
&& !match_ircglob(uData
->handle
->handle
, lData
.search
)))
3812 lData
.users
[matches
++] = uData
;
3814 qsort(lData
.users
, matches
, sizeof(lData
.users
[0]), userData_access_comp
);
3816 lData
.table
.length
= matches
+1;
3817 lData
.table
.width
= 5;
3818 lData
.table
.flags
= TABLE_NO_FREE
;
3819 lData
.table
.contents
= malloc(lData
.table
.length
*sizeof(*lData
.table
.contents
));
3820 ary
= malloc(lData
.table
.width
*sizeof(**lData
.table
.contents
));
3821 lData
.table
.contents
[0] = ary
;
3825 ary
[3] = "Last Seen";
3827 for(matches
= 1; matches
< lData
.table
.length
; ++matches
)
3829 struct userData
*uData
= lData
.users
[matches
-1];
3830 char seen
[INTERVALLEN
];
3832 ary
= malloc(lData
.table
.width
*sizeof(**lData
.table
.contents
));
3833 lData
.table
.contents
[matches
] = ary
;
3834 /* ary[0] = strtab(uData->access);*/
3835 ary
[0] = user_level_name_from_level(uData
->access
);
3836 ary
[1] = strtab(uData
->access
);
3837 ary
[2] = uData
->handle
->handle
;
3840 else if(!uData
->seen
)
3843 ary
[3] = intervalString(seen
, now
- uData
->seen
, user
->handle_info
);
3844 ary
[3] = strdup(ary
[3]);
3845 if(IsUserSuspended(uData
))
3846 ary
[4] = "Suspended";
3847 else if(HANDLE_FLAGGED(uData
->handle
, FROZEN
))
3848 ary
[4] = "Vacation";
3853 for(matches
= 1; matches
< lData
.table
.length
; ++matches
)
3855 free((char*)lData
.table
.contents
[matches
][3]);
3856 free(lData
.table
.contents
[matches
]);
3858 free(lData
.table
.contents
[0]);
3859 free(lData
.table
.contents
);
3863 /* Remove this now that debugging is over? or improve it for
3864 * users? Would it be better tied into USERS somehow? -Rubin */
3865 static CHANSERV_FUNC(cmd_pending
)
3867 struct adduserPending
*ap
;
3868 reply("CSMSG_ADDUSER_PENDING_HEADER");
3870 for(ap
= adduser_pendings
;ap
;ap
= ap
->next
)
3871 reply("CSMSG_ADDUSER_PENDING_LIST", ap
->channel
->name
, ap
->user
->nick
);
3872 reply("CSMSG_ADDUSER_PENDING_FOOTER");
3876 static CHANSERV_FUNC(cmd_users
)
3878 return cmd_list_users(CSFUNC_ARGS
, 1, UL_OWNER
);
3881 static CHANSERV_FUNC(cmd_wlist
)
3883 return cmd_list_users(CSFUNC_ARGS
, UL_OWNER
, UL_OWNER
);
3886 static CHANSERV_FUNC(cmd_clist
)
3888 return cmd_list_users(CSFUNC_ARGS
, UL_COOWNER
, UL_OWNER
-1);
3891 static CHANSERV_FUNC(cmd_mlist
)
3893 return cmd_list_users(CSFUNC_ARGS
, UL_MANAGER
, UL_COOWNER
-1);
3896 static CHANSERV_FUNC(cmd_olist
)
3898 return cmd_list_users(CSFUNC_ARGS
, UL_OP
, UL_MANAGER
-1);
3901 static CHANSERV_FUNC(cmd_hlist
)
3903 return cmd_list_users(CSFUNC_ARGS
, UL_HALFOP
, UL_OP
-1);
3906 static CHANSERV_FUNC(cmd_plist
)
3908 return cmd_list_users(CSFUNC_ARGS
, 1, UL_HALFOP
-1);
3911 static CHANSERV_FUNC(cmd_lamers
)
3913 struct helpfile_table tbl
;
3914 unsigned int matches
= 0, timed
= 0, ii
;
3915 char t_buffer
[INTERVALLEN
], e_buffer
[INTERVALLEN
], *search
;
3916 const char *msg_never
, *triggered
, *expires
;
3917 struct banData
*ban
, **bans
; /* lamers */
3924 reply("CSMSG_LAMERS_HEADER", channel
->name
);
3925 bans
= alloca(channel
->channel_info
->banCount
* sizeof(struct banData
*)); /* lamers */
3928 for(ban
= channel
->channel_info
->bans
; ban
; ban
= ban
->next
)
3930 if(search
&& !match_ircglobs(search
, ban
->mask
))
3932 bans
[matches
++] = ban
;
3937 tbl
.length
= matches
+ 1;
3938 tbl
.width
= 4 + timed
;
3940 tbl
.flags
= TABLE_NO_FREE
;
3941 tbl
.contents
= malloc(tbl
.length
* sizeof(tbl
.contents
[0]));
3942 tbl
.contents
[0] = malloc(tbl
.width
* sizeof(tbl
.contents
[0][0]));
3943 tbl
.contents
[0][0] = "Mask";
3944 tbl
.contents
[0][1] = "Set By";
3945 tbl
.contents
[0][2] = "Triggered";
3948 tbl
.contents
[0][3] = "Expires";
3949 tbl
.contents
[0][4] = "Reason";
3952 tbl
.contents
[0][3] = "Reason";
3955 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
3957 free(tbl
.contents
[0]);
3962 msg_never
= user_find_message(user
, "MSG_NEVER");
3963 for(ii
= 0; ii
< matches
; )
3969 else if(ban
->expires
)
3970 expires
= intervalString(e_buffer
, ban
->expires
- now
, user
->handle_info
);
3972 expires
= msg_never
;
3975 triggered
= intervalString(t_buffer
, now
- ban
->triggered
, user
->handle_info
);
3977 triggered
= msg_never
;
3979 tbl
.contents
[++ii
] = malloc(tbl
.width
* sizeof(tbl
.contents
[0][0]));
3980 tbl
.contents
[ii
][0] = ban
->mask
;
3981 tbl
.contents
[ii
][1] = ban
->owner
;
3982 tbl
.contents
[ii
][2] = strdup(triggered
);
3985 tbl
.contents
[ii
][3] = strdup(expires
);
3986 tbl
.contents
[ii
][4] = ban
->reason
;
3989 tbl
.contents
[ii
][3] = ban
->reason
;
3991 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
3992 /* reply("MSG_MATCH_COUNT", matches); */
3993 for(ii
= 1; ii
< tbl
.length
; ++ii
)
3995 free((char*)tbl
.contents
[ii
][2]);
3997 free((char*)tbl
.contents
[ii
][3]);
3998 free(tbl
.contents
[ii
]);
4000 free(tbl
.contents
[0]);
4007 * return + if the user does NOT have the right to set the topic, and
4008 * the topic is changed.
4011 bad_topic(struct chanNode
*channel
, struct userNode
*user
, const char *new_topic
)
4013 struct chanData
*cData
= channel
->channel_info
;
4014 if(check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
4016 else if(cData
->topic
)
4017 return irccasecmp(new_topic
, cData
->topic
);
4024 * Makes a givin topic fit into a givin topic mask and returns
4027 * topic_mask - the mask to conform to
4028 * topic - the topic to make conform
4029 * new_topic - the pre-allocated char* to put the new topic into
4031 * modifies: new_topic
4034 conform_topic(char* topic_mask
, char* topic
, char *new_topic
)
4036 //char *topic_mask = cData->topic_mask;
4038 int pos
=0, starpos
=-1, dpos
=0, len
;
4040 while((tchar
= topic_mask
[pos
++]) && (dpos
<= TOPICLEN
))
4047 strcpy(new_topic
, "");
4050 len
= strlen(topic
);
4051 if((dpos
+ len
) > TOPICLEN
)
4052 len
= TOPICLEN
+ 1 - dpos
;
4053 memcpy(new_topic
+dpos
, topic
, len
);
4057 case '\\': tchar
= topic_mask
[pos
++]; /* and fall through */
4058 default: new_topic
[dpos
++] = tchar
; break;
4061 if((dpos
> TOPICLEN
) || tchar
)
4063 strcpy(new_topic
, "");
4066 new_topic
[dpos
] = 0;
4070 static CHANSERV_FUNC(cmd_topic
)
4072 struct chanData
*cData
;
4075 cData
= channel
->channel_info
;
4080 SetChannelTopic(channel
, chanserv
, cData
->topic
, 1);
4081 reply("CSMSG_TOPIC_SET", cData
->topic
);
4085 reply("CSMSG_NO_TOPIC", channel
->name
);
4089 topic
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
4090 /* If they say "!topic *", use an empty topic. */
4091 if((topic
[0] == '*') && (topic
[1] == 0))
4094 if(bad_topic(channel
, user
, topic
))
4096 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
4101 /* If there is a topicmask set, and the new topic doesnt match, make it */
4102 if(cData
->topic_mask
&& !match_ircglob(topic
, cData
->topic_mask
))
4104 char *topic_mask
= cData
->topic_mask
;
4105 char new_topic
[TOPICLEN
+1];
4107 /* make a new topic fitting mask */
4108 conform_topic(topic_mask
, topic
, new_topic
);
4111 /* Topic couldnt fit into mask, was too long */
4112 reply("CSMSG_TOPICMASK_CONFLICT1", channel
->name
, topic_mask
);
4113 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN
);
4116 SetChannelTopic(channel
, chanserv
, new_topic
, 1);
4118 else /* No mask set, just set the topic */
4119 SetChannelTopic(channel
, chanserv
, topic
, 1);
4122 if(check_user_level(channel
, user
, lvlTopicSnarf
, 1, 0))
4124 /* Grab the topic and save it as the default topic. */
4126 cData
->topic
= strdup(channel
->topic
);
4132 static CHANSERV_FUNC(cmd_mode
)
4134 struct mod_chanmode
*change
;
4138 change
= &channel
->channel_info
->modes
;
4139 if(change
->modes_set
|| change
->modes_clear
) {
4140 modcmd_chanmode_announce(change
);
4141 reply("CSMSG_DEFAULTED_MODES", channel
->name
);
4143 reply("CSMSG_NO_MODES", channel
->name
);
4147 change
= mod_chanmode_parse(channel
, argv
+1, argc
-1, MCP_KEY_FREE
|MCP_REGISTERED
);
4150 reply("MSG_INVALID_MODES", unsplit_string(argv
+1, argc
-1, NULL
));
4154 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
4155 && mode_lock_violated(&channel
->channel_info
->modes
, change
))
4158 mod_chanmode_format(&channel
->channel_info
->modes
, modes
);
4159 reply("CSMSG_MODE_LOCKED", modes
, channel
->name
);
4163 modcmd_chanmode_announce(change
);
4164 mod_chanmode_free(change
);
4165 reply("CSMSG_MODES_SET", unsplit_string(argv
+1, argc
-1, NULL
));
4169 static CHANSERV_FUNC(cmd_invite
)
4171 struct userData
*uData
;
4172 struct userNode
*invite
;
4174 uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
4178 if(!(invite
= GetUserH(argv
[1])))
4180 reply("MSG_NICK_UNKNOWN", argv
[1]);
4187 if(GetUserMode(channel
, invite
))
4189 reply("CSMSG_ALREADY_PRESENT", invite
->nick
, channel
->name
);
4197 char *reason
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
4198 send_message(invite
, chanserv
, "CSMSG_INVITING_YOU_REASON", user
->nick
, channel
->name
, reason
);
4201 send_message(invite
, chanserv
, "CSMSG_INVITING_YOU", user
->nick
, channel
->name
);
4203 irc_invite(chanserv
, invite
, channel
);
4205 reply("CSMSG_INVITED_USER", argv
[1], channel
->name
);
4210 static CHANSERV_FUNC(cmd_inviteme
)
4212 if(GetUserMode(channel
, user
))
4214 reply("CSMSG_YOU_ALREADY_PRESENT", channel
->name
);
4217 if(channel
->channel_info
4218 && !check_user_level(channel
, user
, lvlInviteMe
, 1, 0))
4220 reply("CSMSG_LOW_CHANNEL_ACCESS", channel
->name
);
4223 irc_invite(cmd
->parent
->bot
, user
, channel
);
4228 show_suspension_info(struct svccmd
*cmd
, struct userNode
*user
, struct suspended
*suspended
)
4231 char buf1
[INTERVALLEN
], buf2
[INTERVALLEN
];
4233 /* We display things based on two dimensions:
4234 * - Issue time: present or absent
4235 * - Expiration: revoked, expired, expires in future, or indefinite expiration
4236 * (in order of precedence, so something both expired and revoked
4237 * only counts as revoked)
4239 combo
= (suspended
->issued
? 4 : 0)
4240 + (suspended
->revoked
? 3 : suspended
->expires
? ((suspended
->expires
< now
) ? 2 : 1) : 0);
4242 case 0: /* no issue time, indefinite expiration */
4243 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended
->suspender
, suspended
->reason
);
4245 case 1: /* no issue time, expires in future */
4246 intervalString(buf1
, suspended
->expires
-now
, user
->handle_info
);
4247 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended
->suspender
, buf1
, suspended
->reason
);
4249 case 2: /* no issue time, expired */
4250 intervalString(buf1
, now
-suspended
->expires
, user
->handle_info
);
4251 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended
->suspender
, buf1
, suspended
->reason
);
4253 case 3: /* no issue time, revoked */
4254 intervalString(buf1
, now
-suspended
->revoked
, user
->handle_info
);
4255 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended
->suspender
, buf1
, suspended
->reason
);
4257 case 4: /* issue time set, indefinite expiration */
4258 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4259 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1
, suspended
->suspender
, suspended
->reason
);
4261 case 5: /* issue time set, expires in future */
4262 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4263 intervalString(buf2
, suspended
->expires
-now
, user
->handle_info
);
4264 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4266 case 6: /* issue time set, expired */
4267 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4268 intervalString(buf2
, now
-suspended
->expires
, user
->handle_info
);
4269 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4271 case 7: /* issue time set, revoked */
4272 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4273 intervalString(buf2
, now
-suspended
->revoked
, user
->handle_info
);
4274 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4277 log_module(CS_LOG
, LOG_ERROR
, "Invalid combo value %d in show_suspension_info()", combo
);
4282 static CHANSERV_FUNC(cmd_info
)
4284 char modes
[MAXLEN
], buffer
[INTERVALLEN
];
4285 struct userData
*uData
, *owner
;
4286 struct chanData
*cData
;
4287 struct do_not_register
*dnr
;
4292 cData
= channel
->channel_info
;
4293 reply("CSMSG_CHANNEL_INFO", channel
->name
);
4296 uData
= GetChannelUser(cData
, user
->handle_info
);
4297 if(uData
&& (uData
->access
>= UL_OP
/*cData->lvlOpts[lvlGiveOps]*/))
4299 mod_chanmode_format(&cData
->modes
, modes
);
4300 reply("CSMSG_CHANNEL_TOPIC", cData
->topic
);
4301 reply("CSMSG_CHANNEL_MODES", modes
[0] ? modes
: user_find_message(user
, "MSG_NONE"));
4304 for(it
= dict_first(cData
->notes
); it
; it
= iter_next(it
))
4308 note
= iter_data(it
);
4309 if(!note_type_visible_to_user(cData
, note
->type
, user
))
4312 padding
= PADLEN
- 1 - strlen(iter_key(it
));
4313 reply("CSMSG_CHANNEL_NOTE", iter_key(it
), padding
> 0 ? padding
: 1, "", note
->note
);
4316 reply("CSMSG_CHANNEL_MAX", cData
->max
);
4317 for(owner
= cData
->users
; owner
; owner
= owner
->next
)
4318 if(owner
->access
== UL_OWNER
)
4319 reply("CSMSG_CHANNEL_OWNER", owner
->handle
->handle
);
4320 reply("CSMSG_CHANNEL_USERS", cData
->userCount
);
4321 reply("CSMSG_CHANNEL_LAMERS", cData
->banCount
);
4322 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer
, now
- cData
->visited
, user
->handle_info
));
4323 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer
, now
- cData
->registered
, user
->handle_info
));
4325 privileged
= IsStaff(user
);
4326 if(((uData
&& uData
->access
>= UL_COOWNER
) || privileged
) && cData
->registrar
)
4327 reply("CSMSG_CHANNEL_REGISTRAR", cData
->registrar
);
4329 if(privileged
&& (dnr
= chanserv_is_dnr(channel
->name
, NULL
)))
4330 chanserv_show_dnrs(user
, cmd
, channel
->name
, NULL
);
4332 if(cData
->suspended
&& ((uData
&& (uData
->access
>= UL_COOWNER
)) || IsHelping(user
)))
4334 struct suspended
*suspended
;
4335 reply((IsSuspended(cData
) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel
->name
);
4336 for(suspended
= cData
->suspended
; suspended
; suspended
= suspended
->previous
)
4337 show_suspension_info(cmd
, user
, suspended
);
4339 else if(IsSuspended(cData
))
4341 reply("CSMSG_CHANNEL_SUSPENDED", channel
->name
);
4342 show_suspension_info(cmd
, user
, cData
->suspended
);
4344 reply("CSMSG_CHANNEL_END");
4348 static CHANSERV_FUNC(cmd_netinfo
)
4350 extern time_t boot_time
;
4351 extern unsigned long burst_length
;
4352 char interval
[INTERVALLEN
];
4354 reply("CSMSG_NETWORK_INFO");
4355 reply("CSMSG_NETWORK_SERVERS", dict_size(servers
));
4356 reply("CSMSG_NETWORK_USERS", dict_size(clients
));
4357 reply("CSMSG_NETWORK_OPERS", curr_opers
.used
);
4358 reply("CSMSG_NETWORK_CHANNELS", registered_channels
);
4359 reply("CSMSG_NETWORK_LAMERS", banCount
);
4360 reply("CSMSG_NETWORK_CHANUSERS", userCount
);
4361 reply("CSMSG_SERVICES_UPTIME", intervalString(interval
, time(NULL
) - boot_time
, user
->handle_info
));
4362 reply("CSMSG_BURST_LENGTH", intervalString(interval
, burst_length
, user
->handle_info
));
4367 send_staff_list(struct userNode
*to
, struct userList
*list
, int skip_flags
)
4369 struct helpfile_table table
;
4371 struct userNode
*user
;
4376 table
.flags
= TABLE_REPEAT_ROWS
| TABLE_NO_FREE
| TABLE_NO_HEADERS
;
4377 table
.contents
= alloca(list
->used
*sizeof(*table
.contents
));
4378 for(nn
=0; nn
<list
->used
; nn
++)
4380 user
= list
->list
[nn
];
4381 if(user
->modes
& skip_flags
)
4385 table
.contents
[table
.length
] = alloca(table
.width
*sizeof(**table
.contents
));
4388 nick
= alloca(strlen(user
->nick
)+3);
4389 sprintf(nick
, "(%s)", user
->nick
);
4393 table
.contents
[table
.length
][0] = nick
;
4396 table_send(chanserv
, to
->nick
, 0, NULL
, table
);
4399 static CHANSERV_FUNC(cmd_ircops
)
4401 reply("CSMSG_STAFF_OPERS");
4402 send_staff_list(user
, &curr_opers
, FLAGS_SERVICE
);
4406 static CHANSERV_FUNC(cmd_helpers
)
4408 reply("CSMSG_STAFF_HELPERS");
4409 send_staff_list(user
, &curr_helpers
, FLAGS_OPER
);
4413 static CHANSERV_FUNC(cmd_staff
)
4415 reply("CSMSG_NETWORK_STAFF");
4416 cmd_ircops(CSFUNC_ARGS
);
4417 cmd_helpers(CSFUNC_ARGS
);
4421 static CHANSERV_FUNC(cmd_peek
)
4423 struct modeNode
*mn
;
4424 char modes
[MODELEN
];
4426 struct helpfile_table table
;
4428 irc_make_chanmode(channel
, modes
);
4430 reply("CSMSG_PEEK_INFO", channel
->name
);
4432 reply("CSMSG_PEEK_TOPIC", channel
->topic
);
4433 reply("CSMSG_PEEK_MODES", modes
);
4434 reply("CSMSG_PEEK_USERS", channel
->members
.used
);
4438 table
.flags
= TABLE_REPEAT_ROWS
| TABLE_NO_FREE
| TABLE_NO_HEADERS
;
4439 table
.contents
= alloca(channel
->members
.used
*sizeof(*table
.contents
));
4440 for(n
= 0; n
< channel
->members
.used
; n
++)
4442 mn
= channel
->members
.list
[n
];
4443 if(!(mn
->modes
& MODE_CHANOP
) || IsLocal(mn
->user
))
4445 table
.contents
[table
.length
] = alloca(sizeof(**table
.contents
));
4446 table
.contents
[table
.length
][0] = mn
->user
->nick
;
4451 reply("CSMSG_PEEK_OPS");
4452 table_send(chanserv
, user
->nick
, 0, NULL
, table
);
4455 reply("CSMSG_PEEK_NO_OPS");
4456 reply("CSMSG_PEEK_END");
4460 static MODCMD_FUNC(cmd_wipeinfo
)
4462 struct handle_info
*victim
;
4463 struct userData
*ud
, *actor
;
4466 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
4467 if(!(victim
= modcmd_get_handle_info(user
, argv
[1])))
4469 if(!(ud
= GetTrueChannelAccess(channel
->channel_info
, victim
)))
4471 reply("CSMSG_NO_CHAN_USER", argv
[1], channel
->name
);
4474 if((ud
->access
>= actor
->access
) && (ud
!= actor
))
4476 reply("MSG_USER_OUTRANKED", victim
->handle
);
4482 reply("CSMSG_WIPED_INFO_LINE", argv
[1], channel
->name
);
4486 static CHANSERV_FUNC(cmd_resync
)
4488 struct mod_chanmode
*changes
;
4489 struct chanData
*cData
= channel
->channel_info
;
4490 unsigned int ii
, used
;
4492 changes
= mod_chanmode_alloc(channel
->members
.used
* 2);
4493 for(ii
= used
= 0; ii
< channel
->members
.used
; ++ii
)
4495 struct modeNode
*mn
= channel
->members
.list
[ii
];
4496 struct userData
*uData
;
4498 if(IsService(mn
->user
))
4501 uData
= GetChannelAccess(cData
, mn
->user
->handle_info
);
4502 if(uData
&& uData
->access
>= UL_OP
/* cData->lvlOpts[lvlGiveOps]*/)
4504 if(!(mn
->modes
& MODE_CHANOP
))
4506 changes
->args
[used
].mode
= MODE_CHANOP
;
4507 changes
->args
[used
++].u
.member
= mn
;
4510 else if(uData
&& uData
->access
>= UL_HALFOP
/*cData->lvlOpts[lvlGiveHalfOps]*/)
4512 if(mn
->modes
& MODE_CHANOP
)
4514 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_CHANOP
;
4515 changes
->args
[used
++].u
.member
= mn
;
4517 if(!(mn
->modes
& MODE_HALFOP
))
4519 changes
->args
[used
].mode
= MODE_HALFOP
;
4520 changes
->args
[used
++].u
.member
= mn
;
4522 /* why cant halfops keep voice
4523 if(mn->modes & MODE_VOICE)
4525 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4526 changes->args[used++].u.member = mn;
4530 else if(uData
&& uData
->access
>= UL_PEON
/* cData->lvlOpts[lvlGiveVoice]*/)
4532 if(mn
->modes
& MODE_CHANOP
)
4534 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_CHANOP
;
4535 changes
->args
[used
++].u
.member
= mn
;
4537 if(mn
->modes
& MODE_HALFOP
)
4539 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_HALFOP
;
4540 changes
->args
[used
++].u
.member
= mn
;
4542 if(!(mn
->modes
& MODE_VOICE
))
4544 changes
->args
[used
].mode
= MODE_VOICE
;
4545 changes
->args
[used
++].u
.member
= mn
;
4552 changes
->args
[used
].mode
= MODE_REMOVE
| mn
->modes
;
4553 changes
->args
[used
++].u
.member
= mn
;
4557 changes
->argc
= used
;
4558 modcmd_chanmode_announce(changes
);
4559 mod_chanmode_free(changes
);
4560 reply("CSMSG_RESYNCED_USERS", channel
->name
);
4564 static CHANSERV_FUNC(cmd_seen
)
4566 struct userData
*uData
;
4567 struct handle_info
*handle
;
4568 char seen
[INTERVALLEN
];
4572 if(!irccasecmp(argv
[1], chanserv
->nick
))
4574 reply("CSMSG_IS_CHANSERV");
4578 if(!(handle
= get_handle_info(argv
[1])))
4580 reply("MSG_HANDLE_UNKNOWN", argv
[1]);
4584 if(!(uData
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
4586 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
4591 reply("CSMSG_USER_PRESENT", handle
->handle
);
4592 else if(uData
->seen
)
4593 reply("CSMSG_USER_SEEN", handle
->handle
, channel
->name
, intervalString(seen
, now
- uData
->seen
, user
->handle_info
));
4595 reply("CSMSG_NEVER_SEEN", handle
->handle
, channel
->name
);
4597 if(!uData
->present
&& HANDLE_FLAGGED(handle
, FROZEN
))
4598 reply("CSMSG_USER_VACATION", handle
->handle
);
4603 static MODCMD_FUNC(cmd_names
)
4605 struct userNode
*targ
;
4606 struct userData
*targData
;
4607 unsigned int ii
, pos
;
4610 for(ii
=pos
=0; ii
<channel
->members
.used
; ++ii
)
4612 targ
= channel
->members
.list
[ii
]->user
;
4613 targData
= GetTrueChannelAccess(channel
->channel_info
, targ
->handle_info
);
4616 if(pos
+ strlen(targ
->nick
) + strlen(targ
->handle_info
->handle
) + 8 > sizeof(buf
))
4619 reply("CSMSG_CHANNEL_NAMES", channel
->name
, buf
);
4623 if(IsUserSuspended(targData
))
4625 pos
+= sprintf(buf
+pos
, "%d:%s(%s)", targData
->access
, targ
->nick
, targ
->handle_info
->handle
);
4628 reply("CSMSG_CHANNEL_NAMES", channel
->name
, buf
);
4629 reply("CSMSG_END_NAMES", channel
->name
);
4634 note_type_visible_to_user(struct chanData
*channel
, struct note_type
*ntype
, struct userNode
*user
)
4636 switch(ntype
->visible_type
)
4638 case NOTE_VIS_ALL
: return 1;
4639 case NOTE_VIS_CHANNEL_USERS
: return !channel
|| !user
|| (user
->handle_info
&& GetChannelUser(channel
, user
->handle_info
));
4640 case NOTE_VIS_PRIVILEGED
: default: return user
&& (IsOper(user
) || IsSupportHelper(user
) || IsNetworkHelper(user
));
4645 note_type_settable_by_user(struct chanNode
*channel
, struct note_type
*ntype
, struct userNode
*user
)
4647 struct userData
*uData
;
4649 switch(ntype
->set_access_type
)
4651 case NOTE_SET_CHANNEL_ACCESS
:
4652 if(!user
->handle_info
)
4654 if(!(uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
)))
4656 return uData
->access
>= ntype
->set_access
.min_ulevel
;
4657 case NOTE_SET_CHANNEL_SETTER
:
4658 return check_user_level(channel
, user
, lvlSetters
, 1, 0);
4659 case NOTE_SET_PRIVILEGED
: default:
4660 return IsHelping(user
) && (user
->handle_info
->opserv_level
>= ntype
->set_access
.min_opserv
);
4664 static CHANSERV_FUNC(cmd_note
)
4666 struct chanData
*cData
;
4668 struct note_type
*ntype
;
4670 cData
= channel
->channel_info
;
4673 reply("CSMSG_NOT_REGISTERED", channel
->name
);
4677 /* If no arguments, show all visible notes for the channel. */
4683 for(count
=0, it
=dict_first(cData
->notes
); it
; it
=iter_next(it
))
4685 note
= iter_data(it
);
4686 if(!note_type_visible_to_user(cData
, note
->type
, user
))
4689 reply("CSMSG_NOTELIST_HEADER", channel
->name
);
4690 reply("CSMSG_NOTE_FORMAT", iter_key(it
), note
->setter
, note
->note
);
4693 reply("CSMSG_NOTELIST_END", channel
->name
);
4695 reply("CSMSG_NOTELIST_EMPTY", channel
->name
);
4697 /* If one argument, show the named note. */
4700 if((note
= dict_find(cData
->notes
, argv
[1], NULL
))
4701 && note_type_visible_to_user(cData
, note
->type
, user
))
4703 reply("CSMSG_NOTE_FORMAT", note
->type
->name
, note
->setter
, note
->note
);
4705 else if((ntype
= dict_find(note_types
, argv
[1], NULL
))
4706 && note_type_visible_to_user(NULL
, ntype
, user
))
4708 reply("CSMSG_NO_SUCH_NOTE", channel
->name
, ntype
->name
);
4713 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
4717 /* Assume they're trying to set a note. */
4721 ntype
= dict_find(note_types
, argv
[1], NULL
);
4724 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
4727 else if(note_type_settable_by_user(channel
, ntype
, user
))
4729 note_text
= unsplit_string(argv
+2, argc
-2, NULL
);
4730 if((note
= dict_find(cData
->notes
, argv
[1], NULL
)))
4731 reply("CSMSG_REPLACED_NOTE", ntype
->name
, channel
->name
, note
->setter
, note
->note
);
4732 chanserv_add_channel_note(cData
, ntype
, user
->handle_info
->handle
, note_text
);
4733 reply("CSMSG_NOTE_SET", ntype
->name
, channel
->name
);
4735 if(ntype
->visible_type
== NOTE_VIS_PRIVILEGED
)
4737 /* The note is viewable to staff only, so return 0
4738 to keep the invocation from getting logged (or
4739 regular users can see it in !events). */
4745 reply("CSMSG_NO_ACCESS");
4752 static CHANSERV_FUNC(cmd_delnote
)
4757 if(!(note
= dict_find(channel
->channel_info
->notes
, argv
[1], NULL
))
4758 || !note_type_settable_by_user(channel
, note
->type
, user
))
4760 reply("CSMSG_NO_SUCH_NOTE", channel
->name
, argv
[1]);
4763 dict_remove(channel
->channel_info
->notes
, note
->type
->name
);
4764 reply("CSMSG_NOTE_REMOVED", argv
[1], channel
->name
);
4768 static CHANSERV_FUNC(cmd_last
)
4774 numoflines
= (argc
> 1) ? atoi(argv
[1]) : 10;
4776 if(numoflines
< 1 || numoflines
> 200)
4778 reply("CSMSG_LAST_INVALID");
4781 ShowLog(user
, channel
, "*", "*", "*", "*", numoflines
);
4785 static CHANSERV_FUNC(cmd_events
)
4787 struct logSearch discrim
;
4788 struct logReport report
;
4789 unsigned int matches
, limit
;
4791 limit
= (argc
> 1) ? atoi(argv
[1]) : 10;
4792 if(limit
< 1 || limit
> 200)
4795 memset(&discrim
, 0, sizeof(discrim
));
4796 discrim
.masks
.bot
= chanserv
;
4797 discrim
.masks
.channel_name
= channel
->name
;
4799 discrim
.masks
.command
= argv
[2];
4800 discrim
.limit
= limit
;
4801 discrim
.max_time
= INT_MAX
;
4802 discrim
.severities
= 1 << LOG_COMMAND
;
4803 report
.reporter
= chanserv
;
4805 reply("CSMSG_EVENT_SEARCH_RESULTS", channel
->name
);
4807 matches
= log_entry_search(&discrim
, log_report_entry
, &report
);
4809 reply("MSG_MATCH_COUNT", matches
);
4811 reply("MSG_NO_MATCHES");
4815 static CHANSERV_FUNC(cmd_say
)
4821 msg
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
4822 send_channel_message(channel
, cmd
->parent
->bot
, "%s", msg
);
4824 else if(GetUserH(argv
[1]))
4827 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
4828 send_target_message(5, argv
[1], cmd
->parent
->bot
, "%s", msg
);
4832 reply("MSG_NOT_TARGET_NAME");
4838 static CHANSERV_FUNC(cmd_emote
)
4844 /* CTCP is so annoying. */
4845 msg
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
4846 send_channel_message(channel
, cmd
->parent
->bot
, "\001ACTION %s\001", msg
);
4848 else if(GetUserH(argv
[1]))
4850 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
4851 send_target_message(5, argv
[1], cmd
->parent
->bot
, "\001ACTION %s\001", msg
);
4855 reply("MSG_NOT_TARGET_NAME");
4861 struct channelList
*
4862 chanserv_support_channels(void)
4864 return &chanserv_conf
.support_channels
;
4867 static CHANSERV_FUNC(cmd_expire
)
4869 int channel_count
= registered_channels
;
4870 expire_channels(NULL
);
4871 reply("CSMSG_CHANNELS_EXPIRED", channel_count
- registered_channels
);
4876 chanserv_expire_suspension(void *data
)
4878 struct suspended
*suspended
= data
;
4879 struct chanNode
*channel
;
4881 if(!suspended
->expires
|| (now
< suspended
->expires
))
4882 suspended
->revoked
= now
;
4883 channel
= suspended
->cData
->channel
;
4884 suspended
->cData
->channel
= channel
;
4885 suspended
->cData
->flags
&= ~CHANNEL_SUSPENDED
;
4886 if(!IsOffChannel(suspended
->cData
))
4888 struct mod_chanmode change
;
4889 mod_chanmode_init(&change
);
4891 change
.args
[0].mode
= MODE_CHANOP
;
4892 change
.args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
4893 mod_chanmode_announce(chanserv
, channel
, &change
);
4897 static CHANSERV_FUNC(cmd_csuspend
)
4899 struct suspended
*suspended
;
4900 char reason
[MAXLEN
];
4901 time_t expiry
, duration
;
4902 struct userData
*uData
;
4906 if(IsProtected(channel
->channel_info
))
4908 reply("CSMSG_SUSPEND_NODELETE", channel
->name
);
4912 if(argv
[1][0] == '!')
4914 else if(IsSuspended(channel
->channel_info
))
4916 reply("CSMSG_ALREADY_SUSPENDED", channel
->name
);
4917 show_suspension_info(cmd
, user
, channel
->channel_info
->suspended
);
4921 if(!strcmp(argv
[1], "0"))
4923 else if((duration
= ParseInterval(argv
[1])))
4924 expiry
= now
+ duration
;
4927 reply("MSG_INVALID_DURATION", argv
[1]);
4931 unsplit_string(argv
+ 2, argc
- 2, reason
);
4933 suspended
= calloc(1, sizeof(*suspended
));
4934 suspended
->revoked
= 0;
4935 suspended
->issued
= now
;
4936 suspended
->suspender
= strdup(user
->handle_info
->handle
);
4937 suspended
->expires
= expiry
;
4938 suspended
->reason
= strdup(reason
);
4939 suspended
->cData
= channel
->channel_info
;
4940 suspended
->previous
= suspended
->cData
->suspended
;
4941 suspended
->cData
->suspended
= suspended
;
4943 if(suspended
->expires
)
4944 timeq_add(suspended
->expires
, chanserv_expire_suspension
, suspended
);
4946 if(IsSuspended(channel
->channel_info
))
4948 suspended
->previous
->revoked
= now
;
4949 if(suspended
->previous
->expires
)
4950 timeq_del(suspended
->previous
->expires
, chanserv_expire_suspension
, suspended
->previous
, 0);
4951 sprintf(reason
, "%s suspension modified by %s.", channel
->name
, suspended
->suspender
);
4952 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
4956 /* Mark all users in channel as absent. */
4957 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
4966 /* Mark the channel as suspended, then part. */
4967 channel
->channel_info
->flags
|= CHANNEL_SUSPENDED
;
4968 DelChannelUser(chanserv
, channel
, suspended
->reason
, 0);
4969 reply("CSMSG_SUSPENDED", channel
->name
);
4970 sprintf(reason
, "%s suspended by %s.", channel
->name
, suspended
->suspender
);
4971 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
4976 static CHANSERV_FUNC(cmd_cunsuspend
)
4978 struct suspended
*suspended
;
4979 char message
[MAXLEN
];
4981 if(!IsSuspended(channel
->channel_info
))
4983 reply("CSMSG_NOT_SUSPENDED", channel
->name
);
4987 suspended
= channel
->channel_info
->suspended
;
4989 /* Expire the suspension and join ChanServ to the channel. */
4990 timeq_del(suspended
->expires
, chanserv_expire_suspension
, suspended
, 0);
4991 chanserv_expire_suspension(suspended
);
4992 reply("CSMSG_UNSUSPENDED", channel
->name
);
4993 sprintf(message
, "%s unsuspended by %s.", channel
->name
, user
->handle_info
->handle
);
4994 global_message(MESSAGE_RECIPIENT_OPERS
|MESSAGE_RECIPIENT_HELPERS
, message
);
4998 typedef struct chanservSearch
5006 unsigned long flags
;
5010 typedef void (*channel_search_func
)(struct chanData
*channel
, void *data
);
5013 chanserv_search_create(struct userNode
*user
, unsigned int argc
, char *argv
[])
5018 search
= malloc(sizeof(struct chanservSearch
));
5019 memset(search
, 0, sizeof(*search
));
5022 for(i
= 0; i
< argc
; i
++)
5024 /* Assume all criteria require arguments. */
5027 send_message(user
, chanserv
, "MSG_MISSING_PARAMS", argv
[i
]);
5031 if(!irccasecmp(argv
[i
], "name"))
5032 search
->name
= argv
[++i
];
5033 else if(!irccasecmp(argv
[i
], "registrar"))
5034 search
->registrar
= argv
[++i
];
5035 else if(!irccasecmp(argv
[i
], "unvisited"))
5036 search
->unvisited
= ParseInterval(argv
[++i
]);
5037 else if(!irccasecmp(argv
[i
], "registered"))
5038 search
->registered
= ParseInterval(argv
[++i
]);
5039 else if(!irccasecmp(argv
[i
], "flags"))
5042 if(!irccasecmp(argv
[i
], "nodelete"))
5043 search
->flags
|= CHANNEL_NODELETE
;
5044 else if(!irccasecmp(argv
[i
], "suspended"))
5045 search
->flags
|= CHANNEL_SUSPENDED
;
5048 send_message(user
, chanserv
, "CSMSG_INVALID_CFLAG", argv
[i
]);
5052 else if(!irccasecmp(argv
[i
], "limit"))
5053 search
->limit
= strtoul(argv
[++i
], NULL
, 10);
5056 send_message(user
, chanserv
, "MSG_INVALID_CRITERIA", argv
[i
]);
5061 if(search
->name
&& !strcmp(search
->name
, "*"))
5063 if(search
->registrar
&& !strcmp(search
->registrar
, "*"))
5064 search
->registrar
= 0;
5073 chanserv_channel_match(struct chanData
*channel
, search_t search
)
5075 const char *name
= channel
->channel
->name
;
5076 if((search
->name
&& !match_ircglob(name
, search
->name
)) ||
5077 (search
->registrar
&& !channel
->registrar
) ||
5078 (search
->registrar
&& !match_ircglob(channel
->registrar
, search
->registrar
)) ||
5079 (search
->unvisited
&& (now
- channel
->visited
) < search
->unvisited
) ||
5080 (search
->registered
&& (now
- channel
->registered
) > search
->registered
) ||
5081 (search
->flags
&& ((search
->flags
& channel
->flags
) != search
->flags
)))
5088 chanserv_channel_search(search_t search
, channel_search_func smf
, void *data
)
5090 struct chanData
*channel
;
5091 unsigned int matches
= 0;
5093 for(channel
= channelList
; channel
&& matches
< search
->limit
; channel
= channel
->next
)
5095 if(!chanserv_channel_match(channel
, search
))
5105 search_count(UNUSED_ARG(struct chanData
*channel
), UNUSED_ARG(void *data
))
5110 search_print(struct chanData
*channel
, void *data
)
5112 send_message_type(4, data
, chanserv
, "%s", channel
->channel
->name
);
5115 static CHANSERV_FUNC(cmd_search
)
5118 unsigned int matches
;
5119 channel_search_func action
;
5123 if(!irccasecmp(argv
[1], "count"))
5124 action
= search_count
;
5125 else if(!irccasecmp(argv
[1], "print"))
5126 action
= search_print
;
5129 reply("CSMSG_ACTION_INVALID", argv
[1]);
5133 search
= chanserv_search_create(user
, argc
- 2, argv
+ 2);
5137 if(action
== search_count
)
5138 search
->limit
= INT_MAX
;
5140 if(action
== search_print
)
5142 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
5146 matches
= chanserv_channel_search(search
, action
, user
);
5149 reply("MSG_MATCH_COUNT", matches
);
5151 reply("MSG_NO_MATCHES");
5157 static CHANSERV_FUNC(cmd_unvisited
)
5159 struct chanData
*cData
;
5160 time_t interval
= chanserv_conf
.channel_expire_delay
;
5161 char buffer
[INTERVALLEN
];
5162 unsigned int limit
= 25, matches
= 0;
5166 interval
= ParseInterval(argv
[1]);
5168 limit
= atoi(argv
[2]);
5171 intervalString(buffer
, interval
, user
->handle_info
);
5172 reply("CSMSG_UNVISITED_HEADER", limit
, buffer
);
5174 for(cData
= channelList
; cData
&& matches
< limit
; cData
= cData
->next
)
5176 if((now
- cData
->visited
) < interval
)
5179 intervalString(buffer
, now
- cData
->visited
, user
->handle_info
);
5180 reply("CSMSG_UNVISITED_DATA", cData
->channel
->name
, buffer
);
5187 static MODCMD_FUNC(chan_opt_defaulttopic
)
5193 if(!check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
5195 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
5199 topic
= unsplit_string(argv
+1, argc
-1, NULL
);
5201 free(channel
->channel_info
->topic
);
5202 if(topic
[0] == '*' && topic
[1] == 0)
5204 topic
= channel
->channel_info
->topic
= NULL
;
5208 topic
= channel
->channel_info
->topic
= strdup(topic
);
5209 if(channel
->channel_info
->topic_mask
5210 && !match_ircglob(channel
->channel_info
->topic
, channel
->channel_info
->topic_mask
))
5211 reply("CSMSG_TOPIC_MISMATCH", channel
->name
);
5213 SetChannelTopic(channel
, chanserv
, topic
? topic
: "", 1);
5216 if(channel
->channel_info
->topic
)
5217 reply("CSMSG_SET_DEFAULT_TOPIC", channel
->channel_info
->topic
);
5219 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user
, "MSG_NONE"));
5223 static MODCMD_FUNC(chan_opt_topicmask
)
5227 struct chanData
*cData
= channel
->channel_info
;
5230 if(!check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
5232 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
5236 mask
= unsplit_string(argv
+1, argc
-1, NULL
);
5238 if(cData
->topic_mask
)
5239 free(cData
->topic_mask
);
5240 if(mask
[0] == '*' && mask
[1] == 0)
5242 cData
->topic_mask
= 0;
5246 cData
->topic_mask
= strdup(mask
);
5248 reply("CSMSG_MASK_BUT_NO_TOPIC", channel
->name
);
5249 else if(!match_ircglob(cData
->topic
, cData
->topic_mask
))
5250 reply("CSMSG_TOPIC_MISMATCH", channel
->name
);
5254 if(channel
->channel_info
->topic_mask
)
5255 reply("CSMSG_SET_TOPICMASK", channel
->channel_info
->topic_mask
);
5257 reply("CSMSG_SET_TOPICMASK", user_find_message(user
, "MSG_NONE"));
5261 int opt_greeting_common(struct userNode
*user
, struct svccmd
*cmd
, int argc
, char *argv
[], char *name
, char **data
)
5265 char *greeting
= unsplit_string(argv
+1, argc
-1, NULL
);
5269 if(greeting
[0] == '*' && greeting
[1] == 0)
5273 unsigned int length
= strlen(greeting
);
5274 if(length
> chanserv_conf
.greeting_length
)
5276 reply("CSMSG_GREETING_TOO_LONG", length
, chanserv_conf
.greeting_length
);
5279 *data
= strdup(greeting
);
5288 reply(name
, user_find_message(user
, "MSG_NONE"));
5292 static MODCMD_FUNC(chan_opt_greeting
)
5294 return opt_greeting_common(user
, cmd
, argc
, argv
, "CSMSG_SET_GREETING", &channel
->channel_info
->greeting
);
5297 static MODCMD_FUNC(chan_opt_usergreeting
)
5299 return opt_greeting_common(user
, cmd
, argc
, argv
, "CSMSG_SET_USERGREETING", &channel
->channel_info
->user_greeting
);
5302 static MODCMD_FUNC(chan_opt_modes
)
5304 struct mod_chanmode
*new_modes
;
5305 char modes
[MODELEN
];
5309 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0))
5311 reply("CSMSG_NO_ACCESS");
5314 if(argv
[1][0] == '*' && argv
[1][1] == 0)
5316 memset(&channel
->channel_info
->modes
, 0, sizeof(channel
->channel_info
->modes
));
5318 else if(!(new_modes
= mod_chanmode_parse(channel
, argv
+1, argc
-1, MCP_KEY_FREE
|MCP_REGISTERED
)))
5320 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv
+1, argc
-1, NULL
));
5323 else if(new_modes
->argc
> 1)
5325 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv
+1, argc
-1, NULL
));
5326 mod_chanmode_free(new_modes
);
5331 channel
->channel_info
->modes
= *new_modes
;
5332 modcmd_chanmode_announce(new_modes
);
5333 mod_chanmode_free(new_modes
);
5337 mod_chanmode_format(&channel
->channel_info
->modes
, modes
);
5339 reply("CSMSG_SET_MODES", modes
);
5341 reply("CSMSG_SET_MODES", user_find_message(user
, "MSG_NONE"));
5345 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5347 channel_binary_option(char *name
, unsigned long mask
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5349 struct chanData
*cData
= channel
->channel_info
;
5354 /* Set flag according to value. */
5355 if(enabled_string(argv
[1]))
5357 cData
->flags
|= mask
;
5360 else if(disabled_string(argv
[1]))
5362 cData
->flags
&= ~mask
;
5367 reply("MSG_INVALID_BINARY", argv
[1]);
5373 /* Find current option value. */
5374 value
= (cData
->flags
& mask
) ? 1 : 0;
5378 reply(name
, user_find_message(user
, "MSG_ON"));
5380 reply(name
, user_find_message(user
, "MSG_OFF"));
5384 static MODCMD_FUNC(chan_opt_nodelete
)
5386 if((argc
> 1) && (!IsOper(user
) || !user
->handle_info
|| (user
->handle_info
->opserv_level
< chanserv_conf
.nodelete_level
)))
5388 reply("MSG_SETTING_PRIVILEGED", argv
[0]);
5392 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE
);
5395 static MODCMD_FUNC(chan_opt_dynlimit
)
5397 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT
);
5400 static MODCMD_FUNC(chan_opt_offchannel
)
5402 struct chanData
*cData
= channel
->channel_info
;
5407 /* Set flag according to value. */
5408 if(enabled_string(argv
[1]))
5410 if(!IsOffChannel(cData
))
5411 DelChannelUser(chanserv
, channel
, "Going off-channel.", 0);
5412 cData
->flags
|= CHANNEL_OFFCHANNEL
;
5415 else if(disabled_string(argv
[1]))
5417 if(IsOffChannel(cData
))
5419 struct mod_chanmode change
;
5420 mod_chanmode_init(&change
);
5422 change
.args
[0].mode
= MODE_CHANOP
;
5423 change
.args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
5424 mod_chanmode_announce(chanserv
, channel
, &change
);
5426 cData
->flags
&= ~CHANNEL_OFFCHANNEL
;
5431 reply("MSG_INVALID_BINARY", argv
[1]);
5437 /* Find current option value. */
5438 value
= (cData
->flags
& CHANNEL_OFFCHANNEL
) ? 1 : 0;
5442 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user
, "MSG_ON"));
5444 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user
, "MSG_OFF"));
5448 static MODCMD_FUNC(chan_opt_defaults
)
5450 struct userData
*uData
;
5451 struct chanData
*cData
;
5452 const char *confirm
;
5453 enum levelOption lvlOpt
;
5454 enum charOption chOpt
;
5456 cData
= channel
->channel_info
;
5457 uData
= GetChannelUser(cData
, user
->handle_info
);
5458 if(!uData
|| (uData
->access
< UL_OWNER
))
5460 reply("CSMSG_OWNER_DEFAULTS", channel
->name
);
5463 confirm
= make_confirmation_string(uData
);
5464 if((argc
< 2) || strcmp(argv
[1], confirm
))
5466 reply("CSMSG_CONFIRM_DEFAULTS", channel
->name
, confirm
);
5469 cData
->flags
= CHANNEL_DEFAULT_FLAGS
;
5470 cData
->modes
= chanserv_conf
.default_modes
;
5471 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
5472 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
5473 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
5474 cData
->chOpts
[chOpt
] = charOptions
[chOpt
].default_value
;
5475 reply("CSMSG_SETTINGS_DEFAULTED", channel
->name
);
5480 channel_level_option(enum levelOption option
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5482 struct chanData
*cData
= channel
->channel_info
;
5483 struct userData
*uData
;
5484 unsigned short value
;
5488 if(!check_user_level(channel
, user
, option
, 1, 1))
5490 reply("CSMSG_CANNOT_SET");
5493 value
= user_level_from_name(argv
[1], UL_OWNER
+1);
5494 if(!value
&& strcmp(argv
[1], "0"))
5496 reply("CSMSG_INVALID_ACCESS", argv
[1]);
5499 uData
= GetChannelUser(cData
, user
->handle_info
);
5500 if(!uData
|| ((uData
->access
< UL_OWNER
) && (value
> uData
->access
)))
5502 reply("CSMSG_BAD_SETLEVEL");
5507 /* removing these level sets..
5509 if(value > cData->lvlOpts[lvlGiveOps])
5511 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
5515 case lvlGiveHalfOps:
5516 if(value < cData->lvlOpts[lvlGiveVoice])
5518 reply("CSMSG_BAD_GIVEHOPS", cData->lvlOpts[lvlGiveHalfOps]);
5523 if(value < cData->lvlOpts[lvlGiveVoice])
5525 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
5531 /* This test only applies to owners, since non-owners
5532 * trying to set an option to above their level get caught
5533 * by the CSMSG_BAD_SETLEVEL test above.
5535 if(value
> uData
->access
)
5537 reply("CSMSG_BAD_SETTERS");
5544 cData
->lvlOpts
[option
] = value
;
5546 reply(levelOptions
[option
].format_name
, cData
->lvlOpts
[option
]);
5550 static MODCMD_FUNC(chan_opt_enfops
)
5552 return channel_level_option(lvlEnfOps
, CSFUNC_ARGS
);
5555 static MODCMD_FUNC(chan_opt_enfhalfops
)
5557 return channel_level_option(lvlEnfHalfOps
, CSFUNC_ARGS
);
5560 static MODCMD_FUNC(chan_opt_giveops)
5562 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
5565 static MODCMD_FUNC(chan_opt_givehalfops)
5567 return channel_level_option(lvlGiveHalfOps, CSFUNC_ARGS);
5570 static MODCMD_FUNC(chan_opt_enfmodes
)
5572 return channel_level_option(lvlEnfModes
, CSFUNC_ARGS
);
5575 static MODCMD_FUNC(chan_opt_enftopic
)
5577 return channel_level_option(lvlEnfTopic
, CSFUNC_ARGS
);
5580 static MODCMD_FUNC(chan_opt_pubcmd
)
5582 return channel_level_option(lvlPubCmd
, CSFUNC_ARGS
);
5585 static MODCMD_FUNC(chan_opt_setters
)
5587 return channel_level_option(lvlSetters
, CSFUNC_ARGS
);
5590 static MODCMD_FUNC(chan_opt_ctcpusers
)
5592 return channel_level_option(lvlCTCPUsers
, CSFUNC_ARGS
);
5595 static MODCMD_FUNC(chan_opt_userinfo
)
5597 return channel_level_option(lvlUserInfo
, CSFUNC_ARGS
);
5601 static MODCMD_FUNC(chan_opt_givevoice)
5603 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
5607 static MODCMD_FUNC(chan_opt_topicsnarf
)
5609 return channel_level_option(lvlTopicSnarf
, CSFUNC_ARGS
);
5612 static MODCMD_FUNC(chan_opt_inviteme
)
5614 return channel_level_option(lvlInviteMe
, CSFUNC_ARGS
);
5618 channel_multiple_option(enum charOption option
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5620 struct chanData
*cData
= channel
->channel_info
;
5621 int count
= charOptions
[option
].count
, index
;
5625 index
= atoi(argv
[1]);
5627 if(!isdigit(argv
[1][0]) || (index
< 0) || (index
>= count
))
5629 reply("CSMSG_INVALID_NUMERIC", index
);
5630 /* Show possible values. */
5631 for(index
= 0; index
< count
; index
++)
5632 reply(charOptions
[option
].format_name
, index
, user_find_message(user
, charOptions
[option
].values
[index
].format_name
));
5636 cData
->chOpts
[option
] = charOptions
[option
].values
[index
].value
;
5640 /* Find current option value. */
5643 (index
< count
) && (cData
->chOpts
[option
] != charOptions
[option
].values
[index
].value
);
5647 /* Somehow, the option value is corrupt; reset it to the default. */
5648 cData
->chOpts
[option
] = charOptions
[option
].default_value
;
5653 reply(charOptions
[option
].format_name
, index
, user_find_message(user
, charOptions
[option
].values
[index
].format_name
));
5657 static MODCMD_FUNC(chan_opt_voice
)
5659 return channel_multiple_option(chVoice
, CSFUNC_ARGS
);
5662 static MODCMD_FUNC(chan_opt_protect
)
5664 return channel_multiple_option(chProtect
, CSFUNC_ARGS
);
5667 static MODCMD_FUNC(chan_opt_toys
)
5669 return channel_multiple_option(chToys
, CSFUNC_ARGS
);
5672 static MODCMD_FUNC(chan_opt_ctcpreaction
)
5674 return channel_multiple_option(chCTCPReaction
, CSFUNC_ARGS
);
5677 static MODCMD_FUNC(chan_opt_topicrefresh
)
5679 return channel_multiple_option(chTopicRefresh
, CSFUNC_ARGS
);
5682 static struct svccmd_list set_shows_list
;
5685 handle_svccmd_unbind(struct svccmd
*target
) {
5687 for(ii
=0; ii
<set_shows_list
.used
; ++ii
)
5688 if(target
== set_shows_list
.list
[ii
])
5689 set_shows_list
.used
= 0;
5692 static CHANSERV_FUNC(cmd_set
)
5694 struct svccmd
*subcmd
;
5698 /* Check if we need to (re-)initialize set_shows_list. */
5699 if(!set_shows_list
.used
)
5701 if(!set_shows_list
.size
)
5703 set_shows_list
.size
= chanserv_conf
.set_shows
->used
;
5704 set_shows_list
.list
= calloc(set_shows_list
.size
, sizeof(set_shows_list
.list
[0]));
5706 for(ii
= 0; ii
< chanserv_conf
.set_shows
->used
; ii
++)
5708 const char *name
= chanserv_conf
.set_shows
->list
[ii
];
5709 sprintf(buf
, "%s %s", argv
[0], name
);
5710 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
5713 log_module(CS_LOG
, LOG_ERROR
, "Unable to find set option \"%s\".", name
);
5716 svccmd_list_append(&set_shows_list
, subcmd
);
5722 reply("CSMSG_CHANNEL_OPTIONS", channel
->name
);
5724 for(ii
= 0; ii
< set_shows_list
.used
; ii
++)
5726 subcmd
= set_shows_list
.list
[ii
];
5727 subcmd
->command
->func(user
, channel
, 1, argv
+1, subcmd
);
5729 reply("CSMSG_CHANNEL_OPTIONS_END");
5733 sprintf(buf
, "%s %s", argv
[0], argv
[1]);
5734 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
5737 reply("CSMSG_INVALID_OPTION", argv
[1], argv
[0]);
5740 if((argc
> 2) && !check_user_level(channel
, user
, lvlSetters
, 1, 0))
5742 reply("CSMSG_NO_ACCESS");
5746 return subcmd
->command
->func(user
, channel
, argc
- 1, argv
+ 1, subcmd
);
5750 user_binary_option(char *name
, unsigned long mask
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5752 struct userData
*uData
;
5754 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
5757 reply("CSMSG_NOT_USER", channel
->name
);
5763 /* Just show current option value. */
5765 else if(enabled_string(argv
[1]))
5767 uData
->flags
|= mask
;
5769 else if(disabled_string(argv
[1]))
5771 uData
->flags
&= ~mask
;
5775 reply("MSG_INVALID_BINARY", argv
[1]);
5779 reply(name
, user_find_message(user
, (uData
->flags
& mask
) ? "MSG_ON" : "MSG_OFF"));
5783 static MODCMD_FUNC(user_opt_autoop
)
5785 struct userData
*uData
;
5787 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
5790 reply("CSMSG_NOT_USER", channel
->name
);
5793 if(uData
->access
< UL_OP
/*channel->channel_info->lvlOpts[lvlGiveOps]*/)
5794 return user_binary_option("CSMSG_USET_AUTOVOICE", USER_AUTO_OP
, CSFUNC_ARGS
);
5796 return user_binary_option("CSMSG_USET_AUTOOP", USER_AUTO_OP
, CSFUNC_ARGS
);
5797 /* TODO: add halfops error message? or is the op one generic enough? */
5800 static MODCMD_FUNC(user_opt_autoinvite
)
5802 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE
, CSFUNC_ARGS
);
5805 static MODCMD_FUNC(user_opt_info
)
5807 struct userData
*uData
;
5810 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
5814 /* If they got past the command restrictions (which require access)
5815 * but fail this test, we have some fool with security override on.
5817 reply("CSMSG_NOT_USER", channel
->name
);
5824 infoline
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
5825 if(strlen(infoline
) > chanserv_conf
.max_userinfo_length
)
5827 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf
.max_userinfo_length
);
5830 bp
= strcspn(infoline
, "\001");
5833 reply("CSMSG_BAD_INFOLINE", infoline
[bp
]);
5838 if(infoline
[0] == '*' && infoline
[1] == 0)
5841 uData
->info
= strdup(infoline
);
5844 reply("CSMSG_USET_INFO", uData
->info
);
5846 reply("CSMSG_USET_INFO", user_find_message(user
, "MSG_NONE"));
5850 struct svccmd_list uset_shows_list
;
5852 static CHANSERV_FUNC(cmd_uset
)
5854 struct svccmd
*subcmd
;
5858 /* Check if we need to (re-)initialize uset_shows_list. */
5859 if(!uset_shows_list
.used
)
5863 "AutoOp", "AutoInvite", "Info"
5866 if(!uset_shows_list
.size
)
5868 uset_shows_list
.size
= ArrayLength(options
);
5869 uset_shows_list
.list
= calloc(uset_shows_list
.size
, sizeof(uset_shows_list
.list
[0]));
5871 for(ii
= 0; ii
< ArrayLength(options
); ii
++)
5873 const char *name
= options
[ii
];
5874 sprintf(buf
, "%s %s", argv
[0], name
);
5875 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
5878 log_module(CS_LOG
, LOG_ERROR
, "Unable to find uset option %s.", name
);
5881 svccmd_list_append(&uset_shows_list
, subcmd
);
5887 /* Do this so options are presented in a consistent order. */
5888 reply("CSMSG_USER_OPTIONS");
5889 for(ii
= 0; ii
< uset_shows_list
.used
; ii
++)
5890 uset_shows_list
.list
[ii
]->command
->func(user
, channel
, 1, argv
+1, uset_shows_list
.list
[ii
]);
5894 sprintf(buf
, "%s %s", argv
[0], argv
[1]);
5895 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
5898 reply("CSMSG_INVALID_OPTION", argv
[1], argv
[0]);
5902 return subcmd
->command
->func(user
, channel
, argc
- 1, argv
+ 1, subcmd
);
5905 static CHANSERV_FUNC(cmd_giveownership
)
5907 struct handle_info
*new_owner_hi
;
5908 struct userData
*new_owner
, *curr_user
;
5909 struct chanData
*cData
= channel
->channel_info
;
5910 struct do_not_register
*dnr
;
5912 unsigned short co_access
;
5913 char reason
[MAXLEN
];
5916 curr_user
= GetChannelAccess(cData
, user
->handle_info
);
5917 force
= IsHelping(user
) && (argc
> 2) && !irccasecmp(argv
[2], "force");
5918 if(!curr_user
|| (curr_user
->access
!= UL_OWNER
))
5920 struct userData
*owner
= NULL
;
5921 for(curr_user
= channel
->channel_info
->users
;
5923 curr_user
= curr_user
->next
)
5925 if(curr_user
->access
!= UL_OWNER
)
5929 reply("CSMSG_MULTIPLE_OWNERS", channel
->name
);
5936 else if (!force
&& (now
< (time_t)(cData
->ownerTransfer
+ chanserv_conf
.giveownership_period
)))
5938 char delay
[INTERVALLEN
];
5939 intervalString(delay
, cData
->ownerTransfer
+ chanserv_conf
.giveownership_period
- now
, user
->handle_info
);
5940 reply("CSMSG_TRANSFER_WAIT", delay
, channel
->name
);
5943 if(!(new_owner_hi
= modcmd_get_handle_info(user
, argv
[1])))
5945 if(new_owner_hi
== user
->handle_info
)
5947 reply("CSMSG_NO_TRANSFER_SELF");
5950 new_owner
= GetChannelAccess(cData
, new_owner_hi
);
5955 new_owner
= add_channel_user(cData
, new_owner_hi
, UL_COOWNER
, 0, NULL
);
5959 reply("CSMSG_NO_CHAN_USER", new_owner_hi
->handle
, channel
->name
);
5963 if((chanserv_get_owned_count(new_owner_hi
) >= chanserv_conf
.max_owned
) && !force
)
5965 reply("CSMSG_OWN_TOO_MANY", new_owner_hi
->handle
, chanserv_conf
.max_owned
);
5968 if((dnr
= chanserv_is_dnr(NULL
, new_owner_hi
)) && !force
) {
5969 if(!IsHelping(user
))
5970 reply("CSMSG_DNR_ACCOUNT", new_owner_hi
->handle
);
5972 chanserv_show_dnrs(user
, cmd
, NULL
, new_owner_hi
->handle
);
5975 if(new_owner
->access
>= UL_COOWNER
)
5976 co_access
= new_owner
->access
;
5978 co_access
= UL_COOWNER
;
5979 new_owner
->access
= UL_OWNER
;
5981 curr_user
->access
= co_access
;
5982 cData
->ownerTransfer
= now
;
5983 reply("CSMSG_OWNERSHIP_GIVEN", channel
->name
, new_owner_hi
->handle
);
5984 sprintf(reason
, "%s ownership transferred to %s by %s.", channel
->name
, new_owner_hi
->handle
, user
->handle_info
->handle
);
5985 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
5989 static CHANSERV_FUNC(cmd_suspend
)
5991 struct handle_info
*hi
;
5992 struct userData
*self
, *target
;
5995 if(!(hi
= modcmd_get_handle_info(user
, argv
[1]))) return 0;
5996 self
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
5997 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
5999 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
6002 if(target
->access
>= self
->access
)
6004 reply("MSG_USER_OUTRANKED", hi
->handle
);
6007 if(target
->flags
& USER_SUSPENDED
)
6009 reply("CSMSG_ALREADY_SUSPENDED", hi
->handle
);
6014 target
->present
= 0;
6017 target
->flags
|= USER_SUSPENDED
;
6018 reply("CSMSG_USER_SUSPENDED", hi
->handle
, channel
->name
);
6022 static CHANSERV_FUNC(cmd_unsuspend
)
6024 struct handle_info
*hi
;
6025 struct userData
*self
, *target
;
6028 if(!(hi
= modcmd_get_handle_info(user
, argv
[1]))) return 0;
6029 self
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
6030 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
6032 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
6035 if(target
->access
>= self
->access
)
6037 reply("MSG_USER_OUTRANKED", hi
->handle
);
6040 if(!(target
->flags
& USER_SUSPENDED
))
6042 reply("CSMSG_NOT_SUSPENDED", hi
->handle
);
6045 target
->flags
&= ~USER_SUSPENDED
;
6046 scan_user_presence(target
, NULL
);
6047 reply("CSMSG_USER_UNSUSPENDED", hi
->handle
, channel
->name
);
6051 static MODCMD_FUNC(cmd_deleteme
)
6053 struct handle_info
*hi
;
6054 struct userData
*target
;
6055 const char *confirm_string
;
6056 unsigned short access
;
6059 hi
= user
->handle_info
;
6060 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
6062 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
6065 if(target
->access
== UL_OWNER
)
6067 reply("CSMSG_NO_OWNER_DELETEME", channel
->name
);
6070 confirm_string
= make_confirmation_string(target
);
6071 if((argc
< 2) || strcmp(argv
[1], confirm_string
))
6073 reply("CSMSG_CONFIRM_DELETEME", confirm_string
);
6076 access
= target
->access
;
6077 channel_name
= strdup(channel
->name
);
6078 del_channel_user(target
, 1);
6079 reply("CSMSG_DELETED_YOU", access
, channel_name
);
6085 chanserv_refresh_topics(UNUSED_ARG(void *data
))
6087 unsigned int refresh_num
= (now
- self
->link
) / chanserv_conf
.refresh_period
;
6088 struct chanData
*cData
;
6091 for(cData
= channelList
; cData
; cData
= cData
->next
)
6093 if(IsSuspended(cData
))
6095 opt
= cData
->chOpts
[chTopicRefresh
];
6098 if((refresh_num
- cData
->last_refresh
) < (unsigned int)(1 << (opt
- '1')))
6101 SetChannelTopic(cData
->channel
, chanserv
, cData
->topic
, 1);
6102 cData
->last_refresh
= refresh_num
;
6104 timeq_add(now
+ chanserv_conf
.refresh_period
, chanserv_refresh_topics
, NULL
);
6107 static CHANSERV_FUNC(cmd_unf
)
6111 char response
[MAXLEN
];
6112 const char *fmt
= user_find_message(user
, "CSMSG_UNF_RESPONSE");
6113 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
6114 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6117 reply("CSMSG_UNF_RESPONSE");
6121 static CHANSERV_FUNC(cmd_ping
)
6125 char response
[MAXLEN
];
6126 const char *fmt
= user_find_message(user
, "CSMSG_PING_RESPONSE");
6127 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
6128 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6131 reply("CSMSG_PING_RESPONSE");
6135 static CHANSERV_FUNC(cmd_wut
)
6139 char response
[MAXLEN
];
6140 const char *fmt
= user_find_message(user
, "CSMSG_WUT_RESPONSE");
6141 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
6142 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6145 reply("CSMSG_WUT_RESPONSE");
6149 static CHANSERV_FUNC(cmd_8ball
)
6151 unsigned int i
, j
, accum
;
6156 for(i
=1; i
<argc
; i
++)
6157 for(j
=0; argv
[i
][j
]; j
++)
6158 accum
= (accum
<< 5) - accum
+ toupper(argv
[i
][j
]);
6159 resp
= chanserv_conf
.eightball
->list
[accum
% chanserv_conf
.eightball
->used
];
6162 char response
[MAXLEN
];
6163 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, resp
);
6164 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6167 send_message_type(4, user
, cmd
->parent
->bot
, "%s", resp
);
6171 static CHANSERV_FUNC(cmd_d
)
6173 unsigned long sides
, count
, modifier
, ii
, total
;
6174 char response
[MAXLEN
], *sep
;
6178 if((count
= strtoul(argv
[1], &sep
, 10)) < 1)
6188 else if(((sep
[0] == 'd') || (sep
[0] == 'D')) && isdigit(sep
[1])
6189 && (sides
= strtoul(sep
+1, &sep
, 10)) > 1)
6193 else if((sep
[0] == '-') && isdigit(sep
[1]))
6194 modifier
= strtoul(sep
, NULL
, 10);
6195 else if((sep
[0] == '+') && isdigit(sep
[1]))
6196 modifier
= strtoul(sep
+1, NULL
, 10);
6203 reply("CSMSG_BAD_DIE_FORMAT", argv
[1]);
6208 reply("CSMSG_BAD_DICE_COUNT", count
, 10);
6211 for(total
= ii
= 0; ii
< count
; ++ii
)
6212 total
+= (rand() % sides
) + 1;
6215 if((count
> 1) || modifier
)
6217 fmt
= user_find_message(user
, "CSMSG_DICE_ROLL");
6218 sprintf(response
, fmt
, total
, count
, sides
, modifier
);
6222 fmt
= user_find_message(user
, "CSMSG_DIE_ROLL");
6223 sprintf(response
, fmt
, total
, sides
);
6226 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, response
);
6228 send_message_type(4, user
, cmd
->parent
->bot
, "%s", response
);
6232 static CHANSERV_FUNC(cmd_huggle
)
6234 /* CTCP must be via PRIVMSG, never notice */
6236 send_target_message(1, channel
->name
, cmd
->parent
->bot
, "CSMSG_HUGGLES_HIM", user
->nick
);
6238 send_target_message(1, user
->nick
, cmd
->parent
->bot
, "CSMSG_HUGGLES_YOU");
6242 static CHANSERV_FUNC(cmd_calc
)
6244 char response
[MAXLEN
];
6247 do_math(response
, unsplit_string(argv
+ 1, argc
- 1, NULL
));
6250 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, response
);
6252 send_message_type(4, user
, cmd
->parent
->bot
, "%s", response
);
6257 chanserv_adjust_limit(void *data
)
6259 struct mod_chanmode change
;
6260 struct chanData
*cData
= data
;
6261 struct chanNode
*channel
= cData
->channel
;
6264 if(IsSuspended(cData
))
6267 cData
->limitAdjusted
= now
;
6268 limit
= channel
->members
.used
+ chanserv_conf
.adjust_threshold
+ 5;
6269 if(cData
->modes
.modes_set
& MODE_LIMIT
)
6271 if(limit
> cData
->modes
.new_limit
)
6272 limit
= cData
->modes
.new_limit
;
6273 else if(limit
== cData
->modes
.new_limit
)
6277 mod_chanmode_init(&change
);
6278 change
.modes_set
= MODE_LIMIT
;
6279 change
.new_limit
= limit
;
6280 mod_chanmode_announce(chanserv
, channel
, &change
);
6284 handle_new_channel(struct chanNode
*channel
)
6286 struct chanData
*cData
;
6288 if(!(cData
= channel
->channel_info
))
6291 if(cData
->modes
.modes_set
|| cData
->modes
.modes_clear
)
6292 mod_chanmode_announce(chanserv
, cData
->channel
, &cData
->modes
);
6294 if(self
->uplink
&& !self
->uplink
->burst
&& channel
->channel_info
->topic
)
6295 SetChannelTopic(channel
, chanserv
, channel
->channel_info
->topic
, 1);
6298 /* Welcome to my worst nightmare. Warning: Read (or modify)
6299 the code below at your own risk. */
6301 handle_join(struct modeNode
*mNode
)
6303 struct mod_chanmode change
;
6304 struct userNode
*user
= mNode
->user
;
6305 struct chanNode
*channel
= mNode
->channel
;
6306 struct chanData
*cData
;
6307 struct userData
*uData
= NULL
;
6308 struct banData
*bData
;
6309 struct handle_info
*handle
;
6310 unsigned int modes
= 0, info
= 0;
6313 if(IsLocal(user
) || !channel
->channel_info
|| IsSuspended(channel
->channel_info
))
6316 cData
= channel
->channel_info
;
6317 if(channel
->members
.used
> cData
->max
)
6318 cData
->max
= channel
->members
.used
;
6320 /* Check for bans. If they're joining through a ban, one of two
6322 * 1: Join during a netburst, by riding the break. Kick them
6323 * unless they have ops or voice in the channel.
6324 * 2: They're allowed to join through the ban (an invite in
6325 * ircu2.10, or a +e on Hybrid, or something).
6326 * If they're not joining through a ban, and the banlist is not
6327 * full, see if they're on the banlist for the channel. If so,
6330 /* This is really, really stupid. not all banned people are kicked.
6331 * sometimes we like to leave them unkicked.
6332 * I tried to explain this to the srvx developers and
6333 * got insulted.. hence one reason for this fork.
6335 if(user->uplink->burst && !mNode->modes)
6338 for(ii = 0; ii < channel->banlist.used; ii++)
6340 if(user_matches_glob(user, channel->banlist.list[ii]->ban, 1))
6342 ** Riding a netburst. Naughty. **
6343 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
6350 mod_chanmode_init(&change
);
6352 if(channel
->banlist
.used
< MAXBANS
)
6354 /* Not joining through a ban. */
6355 for(bData
= cData
->bans
;
6356 bData
&& !user_matches_glob(user
, bData
->mask
, 1);
6357 bData
= bData
->next
);
6361 char kick_reason
[MAXLEN
];
6362 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
6364 bData
->triggered
= now
;
6365 if(bData
!= cData
->bans
)
6367 /* Shuffle the ban to the head of the list. */
6369 bData
->next
->prev
= bData
->prev
;
6371 bData
->prev
->next
= bData
->next
;
6374 bData
->next
= cData
->bans
;
6377 cData
->bans
->prev
= bData
;
6378 cData
->bans
= bData
;
6381 change
.args
[0].mode
= MODE_BAN
;
6382 change
.args
[0].u
.hostmask
= bData
->mask
;
6383 mod_chanmode_announce(chanserv
, channel
, &change
);
6384 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
6389 /* ChanServ will not modify the limits in join-flooded channels.
6390 It will also skip DynLimit processing when the user (or srvx)
6391 is bursting in, because there are likely more incoming. */
6392 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
)
6393 && !user
->uplink
->burst
6394 && !channel
->join_flooded
6395 && (channel
->limit
- channel
->members
.used
) < chanserv_conf
.adjust_threshold
)
6397 /* The user count has begun "bumping" into the channel limit,
6398 so set a timer to raise the limit a bit. Any previous
6399 timers are removed so three incoming users within the delay
6400 results in one limit change, not three. */
6402 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
6403 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
6406 if(channel
->join_flooded
)
6408 /* don't automatically give non users ops or voice during a join flood */
6410 /* EVERYONE is to get voice */
6411 else if(cData
->chOpts
[chVoice
] == 'a')
6412 modes
|= MODE_VOICE
;
6414 greeting
= cData
->greeting
;
6415 if(user
->handle_info
)
6417 handle
= user
->handle_info
;
6419 if(IsHelper(user
) && !IsHelping(user
))
6422 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
6424 if(channel
== chanserv_conf
.support_channels
.list
[ii
])
6426 HANDLE_SET_FLAG(user
->handle_info
, HELPING
);
6432 uData
= GetTrueChannelAccess(cData
, handle
);
6433 if(uData
&& !IsUserSuspended(uData
))
6435 /* non users getting voice are handled above. */
6436 if(IsUserAutoOp(uData
))
6438 if(uData
->access
>= UL_OP
/*cData->lvlOpts[lvlGiveOps]*/)
6439 modes
|= MODE_CHANOP
;
6440 if(uData
->access
>= UL_HALFOP
/*cData->lvlOpts[lvlGiveHalfOps]*/)
6441 modes
|= MODE_HALFOP
;
6442 else if(uData
->access
>= UL_PEON
&& cData
->chOpts
[chVoice
] == 'p')
6443 modes
|= MODE_VOICE
;
6445 if(uData
->access
>= UL_PRESENT
)
6446 cData
->visited
= now
;
6447 if(cData
->user_greeting
)
6448 greeting
= cData
->user_greeting
;
6450 && (uData
->access
>= cData
->lvlOpts
[lvlUserInfo
])
6451 && ((now
- uData
->seen
) >= chanserv_conf
.info_delay
)
6458 if(!user
->uplink
->burst
)
6462 if(modes
& MODE_CHANOP
) {
6463 modes
&= ~MODE_HALFOP
;
6464 modes
&= ~MODE_VOICE
;
6466 change
.args
[0].mode
= modes
;
6467 change
.args
[0].u
.member
= mNode
;
6468 mod_chanmode_announce(chanserv
, channel
, &change
);
6470 if(greeting
&& !user
->uplink
->burst
)
6471 send_message_type(4, user
, chanserv
, "(%s) %s", channel
->name
, greeting
);
6473 send_target_message(5, channel
->name
, chanserv
, "[%s] %s", user
->nick
, uData
->info
);
6479 handle_auth(struct userNode
*user
, UNUSED_ARG(struct handle_info
*old_handle
))
6481 struct mod_chanmode change
;
6482 struct userData
*channel
;
6483 unsigned int ii
, jj
;
6485 if(!user
->handle_info
)
6488 mod_chanmode_init(&change
);
6490 for(channel
= user
->handle_info
->channels
; channel
; channel
= channel
->u_next
)
6492 struct chanNode
*cn
;
6493 struct modeNode
*mn
;
6494 if(IsUserSuspended(channel
)
6495 || IsSuspended(channel
->channel
)
6496 || !(cn
= channel
->channel
->channel
))
6499 mn
= GetUserMode(cn
, user
);
6502 if(!IsUserSuspended(channel
)
6503 && IsUserAutoInvite(channel
)
6504 && (channel
->access
>= channel
->channel
->lvlOpts
[lvlInviteMe
])
6506 && !user
->uplink
->burst
)
6507 irc_invite(chanserv
, user
, cn
);
6511 if(channel
->access
>= UL_PRESENT
)
6512 channel
->channel
->visited
= now
;
6514 if(IsUserAutoOp(channel
))
6516 if(channel
->access
>= UL_OP
/* cn->channel_info->lvlOpts[lvlGiveOps] */)
6517 change
.args
[0].mode
= MODE_CHANOP
;
6518 else if(channel
->access
>= UL_HALFOP
/* cn->channel_info->lvlOpts[lvlGiveHalfOps]*/)
6519 change
.args
[0].mode
= MODE_HALFOP
;
6520 else if(channel
->access
>= UL_PEON
/* cn->channel_info->lvlOpts[lvlGiveVoice]*/)
6521 change
.args
[0].mode
= MODE_VOICE
;
6523 change
.args
[0].mode
= 0;
6524 change
.args
[0].u
.member
= mn
;
6525 if(change
.args
[0].mode
)
6526 mod_chanmode_announce(chanserv
, cn
, &change
);
6529 channel
->seen
= now
;
6530 channel
->present
= 1;
6533 for(ii
= 0; ii
< user
->channels
.used
; ++ii
)
6535 struct chanNode
*channel
= user
->channels
.list
[ii
]->channel
;
6536 struct banData
*ban
;
6538 if((user
->channels
.list
[ii
]->modes
& (MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
))
6539 || !channel
->channel_info
6540 || IsSuspended(channel
->channel_info
))
6542 for(jj
= 0; jj
< channel
->banlist
.used
; ++jj
)
6543 if(user_matches_glob(user
, channel
->banlist
.list
[jj
]->ban
, 1))
6545 if(jj
< channel
->banlist
.used
)
6547 for(ban
= channel
->channel_info
->bans
; ban
; ban
= ban
->next
)
6549 char kick_reason
[MAXLEN
];
6550 if(!user_matches_glob(user
, ban
->mask
, 1))
6552 change
.args
[0].mode
= MODE_BAN
;
6553 change
.args
[0].u
.hostmask
= ban
->mask
;
6554 mod_chanmode_announce(chanserv
, channel
, &change
);
6555 sprintf(kick_reason
, "(%s) %s", ban
->owner
, ban
->reason
);
6556 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
6557 ban
->triggered
= now
;
6562 if(IsSupportHelper(user
))
6564 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
6566 if(GetUserMode(chanserv_conf
.support_channels
.list
[ii
], user
))
6568 HANDLE_SET_FLAG(user
->handle_info
, HELPING
);
6576 handle_part(struct modeNode
*mn
, UNUSED_ARG(const char *reason
))
6578 struct chanData
*cData
;
6579 struct userData
*uData
;
6581 cData
= mn
->channel
->channel_info
;
6582 if(!cData
|| IsSuspended(cData
) || IsLocal(mn
->user
))
6585 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
) && !mn
->channel
->join_flooded
)
6587 /* Allow for a bit of padding so that the limit doesn't
6588 track the user count exactly, which could get annoying. */
6589 if((mn
->channel
->limit
- mn
->channel
->members
.used
) > chanserv_conf
.adjust_threshold
+ 5)
6591 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
6592 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
6596 if((uData
= GetTrueChannelAccess(cData
, mn
->user
->handle_info
)))
6598 scan_user_presence(uData
, mn
->user
);
6602 if(IsHelping(mn
->user
) && IsSupportHelper(mn
->user
))
6604 unsigned int ii
, jj
;
6605 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
6607 for(jj
= 0; jj
< mn
->user
->channels
.used
; ++jj
)
6608 if(mn
->user
->channels
.list
[jj
]->channel
== chanserv_conf
.support_channels
.list
[ii
])
6610 if(jj
< mn
->user
->channels
.used
)
6613 if(ii
== chanserv_conf
.support_channels
.used
)
6614 HANDLE_CLEAR_FLAG(mn
->user
->handle_info
, HELPING
);
6619 handle_kick(struct userNode
*kicker
, struct userNode
*victim
, struct chanNode
*channel
)
6621 struct userData
*uData
;
6623 if(!channel
->channel_info
|| !kicker
|| IsService(kicker
)
6624 || (kicker
== victim
) || IsSuspended(channel
->channel_info
)
6625 || (kicker
->handle_info
&& kicker
->handle_info
== victim
->handle_info
))
6628 if(protect_user(victim
, kicker
, channel
->channel_info
))
6630 const char *reason
= user_find_message(kicker
, "CSMSG_USER_PROTECTED");
6631 KickChannelUser(kicker
, channel
, chanserv
, reason
);
6634 if((uData
= GetTrueChannelAccess(channel
->channel_info
, victim
->handle_info
)))
6639 handle_topic(struct userNode
*user
, struct chanNode
*channel
, const char *old_topic
)
6641 struct chanData
*cData
;
6643 if(!channel
->channel_info
|| !user
|| IsSuspended(channel
->channel_info
) || IsService(user
))
6646 cData
= channel
->channel_info
;
6647 if(bad_topic(channel
, user
, channel
->topic
))
6648 { /* User doesnt have privs to set topics. Undo it */
6649 send_message(user
, chanserv
, "CSMSG_TOPIC_LOCKED", channel
->name
);
6650 SetChannelTopic(channel
, chanserv
, old_topic
, 1);
6653 /* If there is a topic mask set, and the new topic doesnt match,
6654 * set the topic to mask + new_topic */
6655 if(cData
->topic_mask
&& !match_ircglob(channel
->topic
, cData
->topic_mask
))
6657 char new_topic
[TOPICLEN
+1];
6658 conform_topic(cData
->topic_mask
, channel
->topic
, new_topic
);
6661 SetChannelTopic(channel
, chanserv
, new_topic
, 1);
6662 /* and fall through to topicsnarf code below.. */
6664 else /* Topic couldnt fit into mask, was too long */
6666 SetChannelTopic(channel
, chanserv
, old_topic
, 1);
6667 send_message(user
, chanserv
, "CSMSG_TOPICMASK_CONFLICT1", channel
->name
, cData
->topic_mask
);
6668 send_message(user
, chanserv
, "CSMSG_TOPICMASK_CONFLICT2", TOPICLEN
);
6672 /* With topicsnarf, grab the topic and save it as the default topic. */
6673 if(check_user_level(channel
, user
, lvlTopicSnarf
, 0, 0))
6676 cData
->topic
= strdup(channel
->topic
);
6682 handle_mode(struct chanNode
*channel
, struct userNode
*user
, const struct mod_chanmode
*change
)
6684 struct mod_chanmode
*bounce
= NULL
;
6685 unsigned int bnc
, ii
;
6688 if(!channel
->channel_info
|| IsLocal(user
) || IsSuspended(channel
->channel_info
) || IsService(user
))
6691 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
6692 && mode_lock_violated(&channel
->channel_info
->modes
, change
))
6694 char correct
[MAXLEN
];
6695 bounce
= mod_chanmode_dup(&channel
->channel_info
->modes
, change
->argc
+ 1);
6696 mod_chanmode_format(&channel
->channel_info
->modes
, correct
);
6697 send_message(user
, chanserv
, "CSMSG_MODE_LOCKED", correct
, channel
->name
);
6699 for(ii
= bnc
= 0; ii
< change
->argc
; ++ii
)
6701 if((change
->args
[ii
].mode
& (MODE_REMOVE
|MODE_CHANOP
)) == (MODE_REMOVE
|MODE_CHANOP
))
6703 const struct userNode
*victim
= change
->args
[ii
].u
.member
->user
;
6704 if(!protect_user(victim
, user
, channel
->channel_info
))
6707 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
6710 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_CHANOP
;
6711 bounce
->args
[bnc
].u
.member
= GetUserMode(channel
, user
);
6712 if(bounce
->args
[bnc
].u
.member
)
6716 bounce
->args
[bnc
].mode
= MODE_CHANOP
;
6717 bounce
->args
[bnc
].u
.member
= change
->args
[ii
].u
.member
;
6719 send_message(user
, chanserv
, "CSMSG_USER_PROTECTED", victim
->nick
);
6721 else if(change
->args
[ii
].mode
& MODE_CHANOP
)
6723 const struct userNode
*victim
= change
->args
[ii
].u
.member
->user
;
6724 if(IsService(victim
) || validate_op(user
, channel
, (struct userNode
*)victim
))
6727 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
6728 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_CHANOP
;
6729 bounce
->args
[bnc
].u
.member
= change
->args
[ii
].u
.member
;
6732 else if((change
->args
[ii
].mode
& (MODE_REMOVE
| MODE_BAN
)) == MODE_BAN
)
6734 const char *ban
= change
->args
[ii
].u
.hostmask
;
6735 if(!bad_channel_ban(channel
, user
, ban
, NULL
, NULL
))
6738 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
6739 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_BAN
;
6740 bounce
->args
[bnc
].u
.hostmask
= strdup(ban
);
6742 send_message(user
, chanserv
, "CSMSG_MASK_PROTECTED", ban
);
6747 if((bounce
->argc
= bnc
) || bounce
->modes_set
|| bounce
->modes_clear
)
6748 mod_chanmode_announce(chanserv
, channel
, bounce
);
6749 for(ii
= 0; ii
< change
->argc
; ++ii
)
6750 if(bounce
->args
[ii
].mode
== (MODE_REMOVE
| MODE_BAN
))
6751 free((char*)bounce
->args
[ii
].u
.hostmask
);
6752 mod_chanmode_free(bounce
);
6757 handle_nick_change(struct userNode
*user
, UNUSED_ARG(const char *old_nick
))
6759 struct chanNode
*channel
;
6760 struct banData
*bData
;
6761 struct mod_chanmode change
;
6762 unsigned int ii
, jj
;
6763 char kick_reason
[MAXLEN
];
6765 mod_chanmode_init(&change
);
6767 change
.args
[0].mode
= MODE_BAN
;
6768 for(ii
= 0; ii
< user
->channels
.used
; ++ii
)
6770 channel
= user
->channels
.list
[ii
]->channel
;
6771 /* Need not check for bans if they're opped or voiced. */
6772 if(user
->channels
.list
[ii
]->modes
& (MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
))
6774 /* Need not check for bans unless channel registration is active. */
6775 if(!channel
->channel_info
|| IsSuspended(channel
->channel_info
))
6777 /* Look for a matching ban already on the channel. */
6778 for(jj
= 0; jj
< channel
->banlist
.used
; ++jj
)
6779 if(user_matches_glob(user
, channel
->banlist
.list
[jj
]->ban
, 1))
6781 /* Need not act if we found one. */
6782 if(jj
< channel
->banlist
.used
)
6784 /* Look for a matching ban in this channel. */
6785 for(bData
= channel
->channel_info
->bans
; bData
; bData
= bData
->next
)
6787 if(!user_matches_glob(user
, bData
->mask
, 1))
6789 change
.args
[0].u
.hostmask
= bData
->mask
;
6790 mod_chanmode_announce(chanserv
, channel
, &change
);
6791 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
6792 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
6793 bData
->triggered
= now
;
6794 break; /* we don't need to check any more bans in the channel */
6799 static void handle_rename(struct handle_info
*handle
, const char *old_handle
)
6801 struct do_not_register
*dnr
= dict_find(handle_dnrs
, old_handle
, NULL
);
6805 dict_remove2(handle_dnrs
, old_handle
, 1);
6806 safestrncpy(dnr
->chan_name
+ 1, handle
->handle
, sizeof(dnr
->chan_name
) - 1);
6807 dict_insert(handle_dnrs
, dnr
->chan_name
+ 1, dnr
);
6812 handle_unreg(UNUSED_ARG(struct userNode
*user
), struct handle_info
*handle
)
6814 struct userNode
*h_user
;
6816 if(handle
->channels
)
6818 for(h_user
= handle
->users
; h_user
; h_user
= h_user
->next_authed
)
6819 send_message(h_user
, chanserv
, "CSMSG_HANDLE_UNREGISTERED");
6821 while(handle
->channels
)
6822 del_channel_user(handle
->channels
, 1);
6827 handle_server_link(UNUSED_ARG(struct server
*server
))
6829 struct chanData
*cData
;
6831 for(cData
= channelList
; cData
; cData
= cData
->next
)
6833 if(!IsSuspended(cData
))
6834 cData
->may_opchan
= 1;
6835 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
)
6836 && !cData
->channel
->join_flooded
6837 && ((cData
->channel
->limit
- cData
->channel
->members
.used
)
6838 < chanserv_conf
.adjust_threshold
))
6840 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
6841 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
6847 chanserv_conf_read(void)
6851 char mode_line
[MAXLEN
], *modes
[MAXNUMPARAMS
];
6852 struct mod_chanmode
*change
;
6853 struct string_list
*strlist
;
6854 struct chanNode
*chan
;
6857 if(!(conf_node
= conf_get_data(CHANSERV_CONF_NAME
, RECDB_OBJECT
)))
6859 log_module(CS_LOG
, LOG_ERROR
, "Invalid config node `%s'.", CHANSERV_CONF_NAME
);
6862 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
6863 UnlockChannel(chanserv_conf
.support_channels
.list
[ii
]);
6864 chanserv_conf
.support_channels
.used
= 0;
6865 if((strlist
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL
, RECDB_STRING_LIST
)))
6867 for(ii
= 0; ii
< strlist
->used
; ++ii
)
6869 const char *str2
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL_MODES
, RECDB_QSTRING
);
6872 chan
= AddChannel(strlist
->list
[ii
], now
, str2
, NULL
, NULL
);
6874 channelList_append(&chanserv_conf
.support_channels
, chan
);
6877 else if((str
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL
, RECDB_QSTRING
)))
6880 str2
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL_MODES
, RECDB_QSTRING
);
6883 chan
= AddChannel(str
, now
, str2
, NULL
, NULL
);
6885 channelList_append(&chanserv_conf
.support_channels
, chan
);
6887 str
= database_get_data(conf_node
, KEY_DB_BACKUP_FREQ
, RECDB_QSTRING
);
6888 chanserv_conf
.db_backup_frequency
= str
? ParseInterval(str
) : 7200;
6889 str
= database_get_data(conf_node
, KEY_INFO_DELAY
, RECDB_QSTRING
);
6890 chanserv_conf
.info_delay
= str
? ParseInterval(str
) : 180;
6891 str
= database_get_data(conf_node
, KEY_MAX_GREETLEN
, RECDB_QSTRING
);
6892 chanserv_conf
.greeting_length
= str
? atoi(str
) : 200;
6893 str
= database_get_data(conf_node
, KEY_ADJUST_THRESHOLD
, RECDB_QSTRING
);
6894 chanserv_conf
.adjust_threshold
= str
? atoi(str
) : 15;
6895 str
= database_get_data(conf_node
, KEY_ADJUST_DELAY
, RECDB_QSTRING
);
6896 chanserv_conf
.adjust_delay
= str
? ParseInterval(str
) : 30;
6897 str
= database_get_data(conf_node
, KEY_CHAN_EXPIRE_FREQ
, RECDB_QSTRING
);
6898 chanserv_conf
.channel_expire_frequency
= str
? ParseInterval(str
) : 86400;
6899 str
= database_get_data(conf_node
, KEY_CHAN_EXPIRE_DELAY
, RECDB_QSTRING
);
6900 chanserv_conf
.channel_expire_delay
= str
? ParseInterval(str
) : 86400*30;
6901 str
= database_get_data(conf_node
, KEY_NODELETE_LEVEL
, RECDB_QSTRING
);
6902 chanserv_conf
.nodelete_level
= str
? atoi(str
) : 1;
6903 str
= database_get_data(conf_node
, KEY_MAX_CHAN_USERS
, RECDB_QSTRING
);
6904 chanserv_conf
.max_chan_users
= str
? atoi(str
) : 512;
6905 str
= database_get_data(conf_node
, KEY_MAX_CHAN_BANS
, RECDB_QSTRING
);
6906 chanserv_conf
.max_chan_bans
= str
? atoi(str
) : 512;
6907 str
= database_get_data(conf_node
, KEY_MAX_USERINFO_LENGTH
, RECDB_QSTRING
);
6908 chanserv_conf
.max_userinfo_length
= str
? atoi(str
) : 400;
6909 str
= database_get_data(conf_node
, KEY_NICK
, RECDB_QSTRING
);
6911 NickChange(chanserv
, str
, 0);
6912 str
= database_get_data(conf_node
, KEY_REFRESH_PERIOD
, RECDB_QSTRING
);
6913 chanserv_conf
.refresh_period
= str
? ParseInterval(str
) : 3*60*60;
6914 str
= database_get_data(conf_node
, KEY_GIVEOWNERSHIP_PERIOD
, RECDB_QSTRING
);
6915 chanserv_conf
.giveownership_period
= str
? ParseInterval(str
) : 0;
6916 str
= database_get_data(conf_node
, KEY_CTCP_SHORT_BAN_DURATION
, RECDB_QSTRING
);
6917 chanserv_conf
.ctcp_short_ban_duration
= str
? str
: "3m";
6918 str
= database_get_data(conf_node
, KEY_CTCP_LONG_BAN_DURATION
, RECDB_QSTRING
);
6919 chanserv_conf
.ctcp_long_ban_duration
= str
? str
: "1h";
6920 str
= database_get_data(conf_node
, KEY_MAX_OWNED
, RECDB_QSTRING
);
6921 chanserv_conf
.max_owned
= str
? atoi(str
) : 5;
6922 str
= database_get_data(conf_node
, KEY_IRC_OPERATOR_EPITHET
, RECDB_QSTRING
);
6923 chanserv_conf
.irc_operator_epithet
= str
? str
: "a megalomaniacal power hungry tyrant";
6924 str
= database_get_data(conf_node
, KEY_NETWORK_HELPER_EPITHET
, RECDB_QSTRING
);
6925 chanserv_conf
.network_helper_epithet
= str
? str
: "a wannabe tyrant";
6926 str
= database_get_data(conf_node
, KEY_SUPPORT_HELPER_EPITHET
, RECDB_QSTRING
);
6927 chanserv_conf
.support_helper_epithet
= str
? str
: "a wannabe tyrant";
6928 str
= database_get_data(conf_node
, "default_modes", RECDB_QSTRING
);
6931 safestrncpy(mode_line
, str
, sizeof(mode_line
));
6932 ii
= split_line(mode_line
, 0, ArrayLength(modes
), modes
);
6933 if((change
= mod_chanmode_parse(NULL
, modes
, ii
, MCP_KEY_FREE
)) && (change
->argc
< 2))
6935 chanserv_conf
.default_modes
= *change
;
6936 mod_chanmode_free(change
);
6938 free_string_list(chanserv_conf
.set_shows
);
6939 strlist
= database_get_data(conf_node
, "set_shows", RECDB_STRING_LIST
);
6941 strlist
= string_list_copy(strlist
);
6944 static const char *list
[] = {
6945 /* free form text */
6946 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
6947 /* options based on user level */
6948 "PubCmd", "InviteMe", "UserInfo",/* "GiveVoice", "GiveHalfOps", "GiveOps", */ "EnfOps",
6949 "EnfHalfOps", "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
6950 /* multiple choice options */
6951 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
6952 /* binary options */
6953 "DynLimit", "NoDelete",
6958 strlist
= alloc_string_list(ArrayLength(list
)-1);
6959 for(ii
=0; list
[ii
]; ii
++)
6960 string_list_append(strlist
, strdup(list
[ii
]));
6962 chanserv_conf
.set_shows
= strlist
;
6963 /* We don't look things up now, in case the list refers to options
6964 * defined by modules initialized after this point. Just mark the
6965 * function list as invalid, so it will be initialized.
6967 set_shows_list
.used
= 0;
6968 free_string_list(chanserv_conf
.eightball
);
6969 strlist
= database_get_data(conf_node
, KEY_8BALL_RESPONSES
, RECDB_STRING_LIST
);
6972 strlist
= string_list_copy(strlist
);
6976 strlist
= alloc_string_list(4);
6977 string_list_append(strlist
, strdup("Yes."));
6978 string_list_append(strlist
, strdup("No."));
6979 string_list_append(strlist
, strdup("Maybe so."));
6981 chanserv_conf
.eightball
= strlist
;
6982 free_string_list(chanserv_conf
.old_ban_names
);
6983 strlist
= database_get_data(conf_node
, KEY_OLD_BAN_NAMES
, RECDB_STRING_LIST
);
6985 strlist
= string_list_copy(strlist
);
6987 strlist
= alloc_string_list(2);
6988 chanserv_conf
.old_ban_names
= strlist
;
6989 str
= database_get_data(conf_node
, "off_channel", RECDB_QSTRING
);
6990 off_channel
= str
? atoi(str
) : 0;
6994 chanserv_note_type_read(const char *key
, struct record_data
*rd
)
6997 struct note_type
*ntype
;
7000 if(!(obj
= GET_RECORD_OBJECT(rd
)))
7002 log_module(CS_LOG
, LOG_ERROR
, "Invalid note type %s.", key
);
7005 if(!(ntype
= chanserv_create_note_type(key
)))
7007 log_module(CS_LOG
, LOG_ERROR
, "Memory allocation failed for note %s.", key
);
7011 /* Figure out set access */
7012 if((str
= database_get_data(obj
, KEY_NOTE_OPSERV_ACCESS
, RECDB_QSTRING
)))
7014 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
7015 ntype
->set_access
.min_opserv
= strtoul(str
, NULL
, 0);
7017 else if((str
= database_get_data(obj
, KEY_NOTE_CHANNEL_ACCESS
, RECDB_QSTRING
)))
7019 ntype
->set_access_type
= NOTE_SET_CHANNEL_ACCESS
;
7020 ntype
->set_access
.min_ulevel
= strtoul(str
, NULL
, 0);
7022 else if((str
= database_get_data(obj
, KEY_NOTE_SETTER_ACCESS
, RECDB_QSTRING
)))
7024 ntype
->set_access_type
= NOTE_SET_CHANNEL_SETTER
;
7028 log_module(CS_LOG
, LOG_ERROR
, "Could not find access type for note %s; defaulting to OpServ access level 0.", key
);
7029 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
7030 ntype
->set_access
.min_opserv
= 0;
7033 /* Figure out visibility */
7034 if(!(str
= database_get_data(obj
, KEY_NOTE_VISIBILITY
, RECDB_QSTRING
)))
7035 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
7036 else if(!irccasecmp(str
, KEY_NOTE_VIS_PRIVILEGED
))
7037 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
7038 else if(!irccasecmp(str
, KEY_NOTE_VIS_CHANNEL_USERS
))
7039 ntype
->visible_type
= NOTE_VIS_CHANNEL_USERS
;
7040 else if(!irccasecmp(str
, KEY_NOTE_VIS_ALL
))
7041 ntype
->visible_type
= NOTE_VIS_ALL
;
7043 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
7045 str
= database_get_data(obj
, KEY_NOTE_MAX_LENGTH
, RECDB_QSTRING
);
7046 ntype
->max_length
= str
? strtoul(str
, NULL
, 0) : 400;
7050 user_read_helper(const char *key
, struct record_data
*rd
, struct chanData
*chan
)
7052 struct handle_info
*handle
;
7053 struct userData
*uData
;
7054 char *seen
, *inf
, *flags
;
7056 unsigned short access
;
7058 if(rd
->type
!= RECDB_OBJECT
|| !dict_size(rd
->d
.object
))
7060 log_module(CS_LOG
, LOG_ERROR
, "Invalid user in %s.", chan
->channel
->name
);
7064 access
= atoi(database_get_data(rd
->d
.object
, KEY_LEVEL
, RECDB_QSTRING
));
7065 if(access
> UL_OWNER
)
7067 log_module(CS_LOG
, LOG_ERROR
, "Invalid access level for %s in %s.", key
, chan
->channel
->name
);
7071 inf
= database_get_data(rd
->d
.object
, KEY_INFO
, RECDB_QSTRING
);
7072 seen
= database_get_data(rd
->d
.object
, KEY_SEEN
, RECDB_QSTRING
);
7073 last_seen
= seen
? (signed)strtoul(seen
, NULL
, 0) : now
;
7074 flags
= database_get_data(rd
->d
.object
, KEY_FLAGS
, RECDB_QSTRING
);
7075 handle
= get_handle_info(key
);
7078 log_module(CS_LOG
, LOG_ERROR
, "Nonexistent account %s in %s.", key
, chan
->channel
->name
);
7082 uData
= add_channel_user(chan
, handle
, access
, last_seen
, inf
);
7083 uData
->flags
= flags
? strtoul(flags
, NULL
, 0) : 0;
7085 /* Upgrade: set autoop to the inverse of noautoop */
7086 if(chanserv_read_version
< 2)
7088 /* if noautoop is true, set autoop false, and vice versa */
7089 if(uData
->flags
& USER_NOAUTO_OP
)
7090 uData
->flags
= uData
->flags
& ~USER_AUTO_OP
;
7092 uData
->flags
= uData
->flags
| USER_AUTO_OP
;
7093 log_module(CS_LOG
, LOG_INFO
, "UPGRADE: to db version 2 from %u. Changing flag to %d for %s in %s.", chanserv_read_version
, uData
->flags
, key
, chan
->channel
->name
);
7099 ban_read_helper(const char *key
, struct record_data
*rd
, struct chanData
*chan
)
7101 struct banData
*bData
;
7102 char *set
, *triggered
, *s_duration
, *s_expires
, *reason
, *owner
;
7103 time_t set_time
, triggered_time
, expires_time
;
7105 if(rd
->type
!= RECDB_OBJECT
|| !dict_size(rd
->d
.object
))
7107 log_module(CS_LOG
, LOG_ERROR
, "Invalid ban in %s.", chan
->channel
->name
);
7111 set
= database_get_data(rd
->d
.object
, KEY_SET
, RECDB_QSTRING
);
7112 triggered
= database_get_data(rd
->d
.object
, KEY_TRIGGERED
, RECDB_QSTRING
);
7113 s_duration
= database_get_data(rd
->d
.object
, KEY_DURATION
, RECDB_QSTRING
);
7114 s_expires
= database_get_data(rd
->d
.object
, KEY_EXPIRES
, RECDB_QSTRING
);
7115 owner
= database_get_data(rd
->d
.object
, KEY_OWNER
, RECDB_QSTRING
);
7116 reason
= database_get_data(rd
->d
.object
, KEY_REASON
, RECDB_QSTRING
);
7117 if (!reason
|| !owner
)
7120 set_time
= set
? (time_t)strtoul(set
, NULL
, 0) : now
;
7121 triggered_time
= triggered
? (time_t)strtoul(triggered
, NULL
, 0) : 0;
7123 expires_time
= (time_t)strtoul(s_expires
, NULL
, 0);
7125 expires_time
= set_time
+ atoi(s_duration
);
7129 if(!reason
|| (expires_time
&& (expires_time
< now
)))
7132 bData
= add_channel_ban(chan
, key
, owner
, set_time
, triggered_time
, expires_time
, reason
);
7135 static struct suspended
*
7136 chanserv_read_suspended(dict_t obj
)
7138 struct suspended
*suspended
= calloc(1, sizeof(*suspended
));
7142 str
= database_get_data(obj
, KEY_EXPIRES
, RECDB_QSTRING
);
7143 suspended
->expires
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7144 str
= database_get_data(obj
, KEY_REVOKED
, RECDB_QSTRING
);
7145 suspended
->revoked
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7146 str
= database_get_data(obj
, KEY_ISSUED
, RECDB_QSTRING
);
7147 suspended
->issued
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7148 suspended
->suspender
= strdup(database_get_data(obj
, KEY_SUSPENDER
, RECDB_QSTRING
));
7149 suspended
->reason
= strdup(database_get_data(obj
, KEY_REASON
, RECDB_QSTRING
));
7150 previous
= database_get_data(obj
, KEY_PREVIOUS
, RECDB_OBJECT
);
7151 suspended
->previous
= previous
? chanserv_read_suspended(previous
) : NULL
;
7156 chanserv_channel_read(const char *key
, struct record_data
*hir
)
7158 struct suspended
*suspended
;
7159 struct mod_chanmode
*modes
;
7160 struct chanNode
*cNode
;
7161 struct chanData
*cData
;
7162 struct dict
*channel
, *obj
;
7163 char *str
, *argv
[10];
7167 channel
= hir
->d
.object
;
7169 str
= database_get_data(channel
, KEY_REGISTRAR
, RECDB_QSTRING
);
7172 cNode
= AddChannel(key
, now
, NULL
, NULL
, NULL
);
7175 log_module(CS_LOG
, LOG_ERROR
, "Unable to create registered channel %s.", key
);
7178 cData
= register_channel(cNode
, str
);
7181 log_module(CS_LOG
, LOG_ERROR
, "Unable to register channel %s from database.", key
);
7185 if((obj
= database_get_data(channel
, KEY_OPTIONS
, RECDB_OBJECT
)))
7187 enum levelOption lvlOpt
;
7188 enum charOption chOpt
;
7190 if((str
= database_get_data(obj
, KEY_FLAGS
, RECDB_QSTRING
)))
7191 cData
->flags
= atoi(str
);
7193 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
7195 str
= database_get_data(obj
, levelOptions
[lvlOpt
].db_name
, RECDB_QSTRING
);
7197 cData
->lvlOpts
[lvlOpt
] = user_level_from_name(str
, UL_OWNER
+1);
7198 else if(levelOptions
[lvlOpt
].old_flag
)
7200 if(cData
->flags
& levelOptions
[lvlOpt
].old_flag
)
7201 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].flag_value
;
7203 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
7207 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
7209 if(!(str
= database_get_data(obj
, charOptions
[chOpt
].db_name
, RECDB_QSTRING
)))
7211 cData
->chOpts
[chOpt
] = str
[0];
7214 else if((str
= database_get_data(channel
, KEY_FLAGS
, RECDB_QSTRING
)))
7216 enum levelOption lvlOpt
;
7217 enum charOption chOpt
;
7220 cData
->flags
= base64toint(str
, 5);
7221 count
= strlen(str
+= 5);
7222 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
7225 if(levelOptions
[lvlOpt
].old_flag
)
7227 if(cData
->flags
& levelOptions
[lvlOpt
].old_flag
)
7228 lvl
= levelOptions
[lvlOpt
].flag_value
;
7230 lvl
= levelOptions
[lvlOpt
].default_value
;
7232 else switch(((count
<= levelOptions
[lvlOpt
].old_idx
) ? str
: CHANNEL_DEFAULT_OPTIONS
)[levelOptions
[lvlOpt
].old_idx
])
7234 case 'c': lvl
= UL_COOWNER
; break;
7235 case 'm': lvl
= UL_MANAGER
; break;
7236 case 'n': lvl
= UL_OWNER
+1; break;
7237 case 'o': lvl
= UL_OP
; break;
7238 case 'p': lvl
= UL_PEON
; break;
7239 case 'h': lvl
= UL_HALFOP
; break;
7240 case 'w': lvl
= UL_OWNER
; break;
7241 default: lvl
= 0; break;
7243 cData
->lvlOpts
[lvlOpt
] = lvl
;
7245 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
7246 cData
->chOpts
[chOpt
] = ((count
<= charOptions
[chOpt
].old_idx
) ? str
: CHANNEL_DEFAULT_OPTIONS
)[charOptions
[chOpt
].old_idx
];
7249 if((obj
= database_get_data(hir
->d
.object
, KEY_SUSPENDED
, RECDB_OBJECT
)))
7251 suspended
= chanserv_read_suspended(obj
);
7252 cData
->suspended
= suspended
;
7253 suspended
->cData
= cData
;
7254 /* We could use suspended->expires and suspended->revoked to
7255 * set the CHANNEL_SUSPENDED flag, but we don't. */
7257 else if(IsSuspended(cData
) && (str
= database_get_data(hir
->d
.object
, KEY_SUSPENDER
, RECDB_QSTRING
)))
7259 suspended
= calloc(1, sizeof(*suspended
));
7260 suspended
->issued
= 0;
7261 suspended
->revoked
= 0;
7262 suspended
->suspender
= strdup(str
);
7263 str
= database_get_data(hir
->d
.object
, KEY_SUSPEND_EXPIRES
, RECDB_QSTRING
);
7264 suspended
->expires
= str
? atoi(str
) : 0;
7265 str
= database_get_data(hir
->d
.object
, KEY_SUSPEND_REASON
, RECDB_QSTRING
);
7266 suspended
->reason
= strdup(str
? str
: "No reason");
7267 suspended
->previous
= NULL
;
7268 cData
->suspended
= suspended
;
7269 suspended
->cData
= cData
;
7273 cData
->flags
&= ~CHANNEL_SUSPENDED
;
7274 suspended
= NULL
; /* to squelch a warning */
7277 if(IsSuspended(cData
)) {
7278 if(suspended
->expires
> now
)
7279 timeq_add(suspended
->expires
, chanserv_expire_suspension
, suspended
);
7280 else if(suspended
->expires
)
7281 cData
->flags
&= ~CHANNEL_SUSPENDED
;
7284 if((!off_channel
|| !IsOffChannel(cData
)) && !IsSuspended(cData
)) {
7285 struct mod_chanmode change
;
7286 mod_chanmode_init(&change
);
7288 change
.args
[0].mode
= MODE_CHANOP
;
7289 change
.args
[0].u
.member
= AddChannelUser(chanserv
, cNode
);
7290 mod_chanmode_announce(chanserv
, cNode
, &change
);
7293 str
= database_get_data(channel
, KEY_REGISTERED
, RECDB_QSTRING
);
7294 cData
->registered
= str
? (time_t)strtoul(str
, NULL
, 0) : now
;
7295 str
= database_get_data(channel
, KEY_VISITED
, RECDB_QSTRING
);
7296 cData
->visited
= str
? (time_t)strtoul(str
, NULL
, 0) : now
;
7297 str
= database_get_data(channel
, KEY_OWNER_TRANSFER
, RECDB_QSTRING
);
7298 cData
->ownerTransfer
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7299 str
= database_get_data(channel
, KEY_MAX
, RECDB_QSTRING
);
7300 cData
->max
= str
? atoi(str
) : 0;
7301 str
= database_get_data(channel
, KEY_GREETING
, RECDB_QSTRING
);
7302 cData
->greeting
= str
? strdup(str
) : NULL
;
7303 str
= database_get_data(channel
, KEY_USER_GREETING
, RECDB_QSTRING
);
7304 cData
->user_greeting
= str
? strdup(str
) : NULL
;
7305 str
= database_get_data(channel
, KEY_TOPIC_MASK
, RECDB_QSTRING
);
7306 cData
->topic_mask
= str
? strdup(str
) : NULL
;
7307 str
= database_get_data(channel
, KEY_TOPIC
, RECDB_QSTRING
);
7308 cData
->topic
= str
? strdup(str
) : NULL
;
7310 if(!IsSuspended(cData
)
7311 && (str
= database_get_data(channel
, KEY_MODES
, RECDB_QSTRING
))
7312 && (argc
= split_line(str
, 0, ArrayLength(argv
), argv
))
7313 && (modes
= mod_chanmode_parse(cNode
, argv
, argc
, MCP_KEY_FREE
))) {
7314 cData
->modes
= *modes
;
7316 cData
->modes
.modes_set
|= MODE_REGISTERED
;
7317 if(cData
->modes
.argc
> 1)
7318 cData
->modes
.argc
= 1;
7319 mod_chanmode_announce(chanserv
, cNode
, &cData
->modes
);
7320 mod_chanmode_free(modes
);
7323 obj
= database_get_data(channel
, KEY_USERS
, RECDB_OBJECT
);
7324 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
7325 user_read_helper(iter_key(it
), iter_data(it
), cData
);
7327 if(!cData
->users
&& !IsProtected(cData
))
7329 log_module(CS_LOG
, LOG_ERROR
, "Channel %s had no users in database, unregistering it.", key
);
7330 unregister_channel(cData
, "has empty user list.");
7334 obj
= database_get_data(channel
, KEY_BANS
, RECDB_OBJECT
);
7335 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
7336 ban_read_helper(iter_key(it
), iter_data(it
), cData
);
7338 obj
= database_get_data(channel
, KEY_NOTES
, RECDB_OBJECT
);
7339 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
7341 struct note_type
*ntype
= dict_find(note_types
, iter_key(it
), NULL
);
7342 struct record_data
*rd
= iter_data(it
);
7343 const char *note
, *setter
;
7345 if(rd
->type
!= RECDB_OBJECT
)
7347 log_module(CS_LOG
, LOG_ERROR
, "Bad record type for note %s in channel %s.", iter_key(it
), key
);
7351 log_module(CS_LOG
, LOG_ERROR
, "Bad note type name %s in channel %s.", iter_key(it
), key
);
7353 else if(!(note
= database_get_data(rd
->d
.object
, KEY_NOTE_NOTE
, RECDB_QSTRING
)))
7355 log_module(CS_LOG
, LOG_ERROR
, "Missing note text for note %s in channel %s.", iter_key(it
), key
);
7359 setter
= database_get_data(rd
->d
.object
, KEY_NOTE_SETTER
, RECDB_QSTRING
);
7360 if(!setter
) setter
= "<unknown>";
7361 chanserv_add_channel_note(cData
, ntype
, setter
, note
);
7369 chanserv_dnr_read(const char *key
, struct record_data
*hir
)
7371 const char *setter
, *reason
, *str
;
7372 struct do_not_register
*dnr
;
7374 setter
= database_get_data(hir
->d
.object
, KEY_DNR_SETTER
, RECDB_QSTRING
);
7377 log_module(CS_LOG
, LOG_ERROR
, "Missing setter for DNR %s.", key
);
7380 reason
= database_get_data(hir
->d
.object
, KEY_DNR_REASON
, RECDB_QSTRING
);
7383 log_module(CS_LOG
, LOG_ERROR
, "Missing reason for DNR %s.", key
);
7386 dnr
= chanserv_add_dnr(key
, setter
, reason
);
7389 str
= database_get_data(hir
->d
.object
, KEY_DNR_SET
, RECDB_QSTRING
);
7391 dnr
->set
= atoi(str
);
7397 chanserv_version_read(struct dict
*section
)
7401 str
= database_get_data(section
, KEY_VERSION_NUMBER
, RECDB_QSTRING
);
7403 chanserv_read_version
= atoi(str
);
7404 log_module(CS_LOG
, LOG_DEBUG
, "Chanserv db version is %d.", chanserv_read_version
);
7408 chanserv_saxdb_read(struct dict
*database
)
7410 struct dict
*section
;
7413 if((section
= database_get_data(database
, KEY_VERSION_CONTROL
, RECDB_OBJECT
)))
7414 chanserv_version_read(section
);
7416 if((section
= database_get_data(database
, KEY_NOTE_TYPES
, RECDB_OBJECT
)))
7417 for(it
= dict_first(section
); it
; it
= iter_next(it
))
7418 chanserv_note_type_read(iter_key(it
), iter_data(it
));
7420 if((section
= database_get_data(database
, KEY_CHANNELS
, RECDB_OBJECT
)))
7421 for(it
= dict_first(section
); it
; it
= iter_next(it
))
7422 chanserv_channel_read(iter_key(it
), iter_data(it
));
7424 if((section
= database_get_data(database
, KEY_DNR
, RECDB_OBJECT
)))
7425 for(it
= dict_first(section
); it
; it
= iter_next(it
))
7426 chanserv_dnr_read(iter_key(it
), iter_data(it
));
7432 chanserv_write_users(struct saxdb_context
*ctx
, struct userData
*uData
)
7434 int high_present
= 0;
7435 saxdb_start_record(ctx
, KEY_USERS
, 1);
7436 for(; uData
; uData
= uData
->next
)
7438 if((uData
->access
>= UL_PRESENT
) && uData
->present
)
7440 saxdb_start_record(ctx
, uData
->handle
->handle
, 0);
7441 saxdb_write_int(ctx
, KEY_LEVEL
, uData
->access
);
7442 saxdb_write_int(ctx
, KEY_SEEN
, uData
->seen
);
7444 saxdb_write_int(ctx
, KEY_FLAGS
, uData
->flags
);
7446 saxdb_write_string(ctx
, KEY_INFO
, uData
->info
);
7447 saxdb_end_record(ctx
);
7449 saxdb_end_record(ctx
);
7450 return high_present
;
7454 chanserv_write_bans(struct saxdb_context
*ctx
, struct banData
*bData
)
7458 saxdb_start_record(ctx
, KEY_BANS
, 1);
7459 for(; bData
; bData
= bData
->next
)
7461 saxdb_start_record(ctx
, bData
->mask
, 0);
7462 saxdb_write_int(ctx
, KEY_SET
, bData
->set
);
7463 if(bData
->triggered
)
7464 saxdb_write_int(ctx
, KEY_TRIGGERED
, bData
->triggered
);
7466 saxdb_write_int(ctx
, KEY_EXPIRES
, bData
->expires
);
7468 saxdb_write_string(ctx
, KEY_OWNER
, bData
->owner
);
7470 saxdb_write_string(ctx
, KEY_REASON
, bData
->reason
);
7471 saxdb_end_record(ctx
);
7473 saxdb_end_record(ctx
);
7477 chanserv_write_suspended(struct saxdb_context
*ctx
, const char *name
, struct suspended
*susp
)
7479 saxdb_start_record(ctx
, name
, 0);
7480 saxdb_write_string(ctx
, KEY_SUSPENDER
, susp
->suspender
);
7481 saxdb_write_string(ctx
, KEY_REASON
, susp
->reason
);
7483 saxdb_write_int(ctx
, KEY_ISSUED
, susp
->issued
);
7485 saxdb_write_int(ctx
, KEY_EXPIRES
, susp
->expires
);
7487 saxdb_write_int(ctx
, KEY_REVOKED
, susp
->revoked
);
7489 chanserv_write_suspended(ctx
, KEY_PREVIOUS
, susp
->previous
);
7490 saxdb_end_record(ctx
);
7494 chanserv_write_channel(struct saxdb_context
*ctx
, struct chanData
*channel
)
7498 enum levelOption lvlOpt
;
7499 enum charOption chOpt
;
7501 saxdb_start_record(ctx
, channel
->channel
->name
, 1);
7503 saxdb_write_int(ctx
, KEY_REGISTERED
, channel
->registered
);
7504 saxdb_write_int(ctx
, KEY_MAX
, channel
->max
);
7506 saxdb_write_string(ctx
, KEY_TOPIC
, channel
->topic
);
7507 if(channel
->registrar
)
7508 saxdb_write_string(ctx
, KEY_REGISTRAR
, channel
->registrar
);
7509 if(channel
->greeting
)
7510 saxdb_write_string(ctx
, KEY_GREETING
, channel
->greeting
);
7511 if(channel
->user_greeting
)
7512 saxdb_write_string(ctx
, KEY_USER_GREETING
, channel
->user_greeting
);
7513 if(channel
->topic_mask
)
7514 saxdb_write_string(ctx
, KEY_TOPIC_MASK
, channel
->topic_mask
);
7515 if(channel
->suspended
)
7516 chanserv_write_suspended(ctx
, "suspended", channel
->suspended
);
7518 saxdb_start_record(ctx
, KEY_OPTIONS
, 0);
7519 saxdb_write_int(ctx
, KEY_FLAGS
, channel
->flags
);
7520 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
7521 saxdb_write_int(ctx
, levelOptions
[lvlOpt
].db_name
, channel
->lvlOpts
[lvlOpt
]);
7522 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
7524 buf
[0] = channel
->chOpts
[chOpt
];
7526 saxdb_write_string(ctx
, charOptions
[chOpt
].db_name
, buf
);
7528 saxdb_end_record(ctx
);
7530 if(channel
->modes
.modes_set
|| channel
->modes
.modes_clear
)
7532 mod_chanmode_format(&channel
->modes
, buf
);
7533 saxdb_write_string(ctx
, KEY_MODES
, buf
);
7536 high_present
= chanserv_write_users(ctx
, channel
->users
);
7537 chanserv_write_bans(ctx
, channel
->bans
);
7539 if(dict_size(channel
->notes
))
7543 saxdb_start_record(ctx
, KEY_NOTES
, 1);
7544 for(it
= dict_first(channel
->notes
); it
; it
= iter_next(it
))
7546 struct note
*note
= iter_data(it
);
7547 saxdb_start_record(ctx
, iter_key(it
), 0);
7548 saxdb_write_string(ctx
, KEY_NOTE_SETTER
, note
->setter
);
7549 saxdb_write_string(ctx
, KEY_NOTE_NOTE
, note
->note
);
7550 saxdb_end_record(ctx
);
7552 saxdb_end_record(ctx
);
7555 if(channel
->ownerTransfer
)
7556 saxdb_write_int(ctx
, KEY_OWNER_TRANSFER
, channel
->ownerTransfer
);
7557 saxdb_write_int(ctx
, KEY_VISITED
, high_present
? now
: channel
->visited
);
7558 saxdb_end_record(ctx
);
7562 chanserv_write_note_type(struct saxdb_context
*ctx
, struct note_type
*ntype
)
7566 saxdb_start_record(ctx
, ntype
->name
, 0);
7567 switch(ntype
->set_access_type
)
7569 case NOTE_SET_CHANNEL_ACCESS
:
7570 saxdb_write_int(ctx
, KEY_NOTE_CHANNEL_ACCESS
, ntype
->set_access
.min_ulevel
);
7572 case NOTE_SET_CHANNEL_SETTER
:
7573 saxdb_write_int(ctx
, KEY_NOTE_SETTER_ACCESS
, 1);
7575 case NOTE_SET_PRIVILEGED
: default:
7576 saxdb_write_int(ctx
, KEY_NOTE_OPSERV_ACCESS
, ntype
->set_access
.min_opserv
);
7579 switch(ntype
->visible_type
)
7581 case NOTE_VIS_ALL
: str
= KEY_NOTE_VIS_ALL
; break;
7582 case NOTE_VIS_CHANNEL_USERS
: str
= KEY_NOTE_VIS_CHANNEL_USERS
; break;
7583 case NOTE_VIS_PRIVILEGED
: default: str
= KEY_NOTE_VIS_PRIVILEGED
; break;
7585 saxdb_write_string(ctx
, KEY_NOTE_VISIBILITY
, str
);
7586 saxdb_write_int(ctx
, KEY_NOTE_MAX_LENGTH
, ntype
->max_length
);
7587 saxdb_end_record(ctx
);
7591 write_dnrs_helper(struct saxdb_context
*ctx
, struct dict
*dnrs
)
7593 struct do_not_register
*dnr
;
7596 for(it
= dict_first(dnrs
); it
; it
= iter_next(it
))
7598 dnr
= iter_data(it
);
7599 saxdb_start_record(ctx
, dnr
->chan_name
, 0);
7601 saxdb_write_int(ctx
, KEY_DNR_SET
, dnr
->set
);
7602 saxdb_write_string(ctx
, KEY_DNR_SETTER
, dnr
->setter
);
7603 saxdb_write_string(ctx
, KEY_DNR_REASON
, dnr
->reason
);
7604 saxdb_end_record(ctx
);
7609 chanserv_saxdb_write(struct saxdb_context
*ctx
)
7612 struct chanData
*channel
;
7614 /* Version Control*/
7615 saxdb_start_record(ctx
, KEY_VERSION_CONTROL
, 1);
7616 saxdb_write_int(ctx
, KEY_VERSION_NUMBER
, CHANSERV_DB_VERSION
);
7617 saxdb_end_record(ctx
);
7620 saxdb_start_record(ctx
, KEY_NOTE_TYPES
, 1);
7621 for(it
= dict_first(note_types
); it
; it
= iter_next(it
))
7622 chanserv_write_note_type(ctx
, iter_data(it
));
7623 saxdb_end_record(ctx
);
7626 saxdb_start_record(ctx
, KEY_DNR
, 1);
7627 write_dnrs_helper(ctx
, handle_dnrs
);
7628 write_dnrs_helper(ctx
, plain_dnrs
);
7629 write_dnrs_helper(ctx
, mask_dnrs
);
7630 saxdb_end_record(ctx
);
7633 saxdb_start_record(ctx
, KEY_CHANNELS
, 1);
7634 for(channel
= channelList
; channel
; channel
= channel
->next
)
7635 chanserv_write_channel(ctx
, channel
);
7636 saxdb_end_record(ctx
);
7642 chanserv_db_cleanup(void) {
7644 unreg_part_func(handle_part
);
7646 unregister_channel(channelList
, "terminating.");
7647 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
7648 UnlockChannel(chanserv_conf
.support_channels
.list
[ii
]);
7649 free(chanserv_conf
.support_channels
.list
);
7650 dict_delete(handle_dnrs
);
7651 dict_delete(plain_dnrs
);
7652 dict_delete(mask_dnrs
);
7653 dict_delete(note_types
);
7654 free_string_list(chanserv_conf
.eightball
);
7655 free_string_list(chanserv_conf
.old_ban_names
);
7656 free_string_list(chanserv_conf
.set_shows
);
7657 free(set_shows_list
.list
);
7658 free(uset_shows_list
.list
);
7661 struct userData
*helper
= helperList
;
7662 helperList
= helperList
->next
;
7667 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
7668 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
7669 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
7672 init_chanserv(const char *nick
)
7674 CS_LOG
= log_register_type("ChanServ", "file:chanserv.log");
7675 conf_register_reload(chanserv_conf_read
);
7677 reg_server_link_func(handle_server_link
);
7679 reg_new_channel_func(handle_new_channel
);
7680 reg_join_func(handle_join
);
7681 reg_part_func(handle_part
);
7682 reg_kick_func(handle_kick
);
7683 reg_topic_func(handle_topic
);
7684 reg_mode_change_func(handle_mode
);
7685 reg_nick_change_func(handle_nick_change
);
7687 reg_auth_func(handle_auth
);
7688 reg_handle_rename_func(handle_rename
);
7689 reg_unreg_func(handle_unreg
);
7691 handle_dnrs
= dict_new();
7692 dict_set_free_data(handle_dnrs
, free
);
7693 plain_dnrs
= dict_new();
7694 dict_set_free_data(plain_dnrs
, free
);
7695 mask_dnrs
= dict_new();
7696 dict_set_free_data(mask_dnrs
, free
);
7698 reg_svccmd_unbind_func(handle_svccmd_unbind
);
7699 chanserv_module
= module_register("ChanServ", CS_LOG
, "chanserv.help", chanserv_expand_variable
);
7700 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+acceptchan,+helping", NULL
);
7701 DEFINE_COMMAND(noregister
, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+helping", NULL
);
7702 DEFINE_COMMAND(allowregister
, 2, 0, "template", "noregister", NULL
);
7703 DEFINE_COMMAND(move
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "template", "register", NULL
);
7704 DEFINE_COMMAND(csuspend
, 2, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+helping", NULL
);
7705 DEFINE_COMMAND(cunsuspend
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+helping", NULL
);
7706 DEFINE_COMMAND(createnote
, 5, 0, "level", "800", NULL
);
7707 DEFINE_COMMAND(removenote
, 2, 0, "level", "800", NULL
);
7709 DEFINE_COMMAND(pending
, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+helping", NULL
);
7711 DEFINE_COMMAND(unregister
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+loghostmask", NULL
);
7712 DEFINE_COMMAND(merge
, 2, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "access", "owner", NULL
);
7714 DEFINE_COMMAND(adduser
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7715 DEFINE_COMMAND(deluser
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7716 DEFINE_COMMAND(suspend
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7717 DEFINE_COMMAND(unsuspend
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7718 DEFINE_COMMAND(deleteme
, 1, MODCMD_REQUIRE_CHANUSER
, NULL
);
7720 DEFINE_COMMAND(mdelowner
, 2, MODCMD_REQUIRE_CHANUSER
, "flags", "+helping", NULL
);
7721 DEFINE_COMMAND(mdelcoowner
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "owner", NULL
);
7722 DEFINE_COMMAND(mdelmanager
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "coowner", NULL
);
7723 DEFINE_COMMAND(mdelop
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7724 DEFINE_COMMAND(mdelpeon
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7725 DEFINE_COMMAND(mdelhalfop
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7727 DEFINE_COMMAND(trim
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7728 DEFINE_COMMAND(opchan
, 1, MODCMD_REQUIRE_REGCHAN
|MODCMD_NEVER_CSUSPEND
, "access", "1", NULL
);
7729 DEFINE_COMMAND(clvl
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7730 DEFINE_COMMAND(giveownership
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "owner", "flags", "+loghostmask", NULL
);
7732 DEFINE_COMMAND(up
, 1, MODCMD_REQUIRE_CHANUSER
, NULL
);
7733 DEFINE_COMMAND(down
, 1, MODCMD_REQUIRE_REGCHAN
, NULL
);
7734 DEFINE_COMMAND(upall
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
7735 DEFINE_COMMAND(downall
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
7736 DEFINE_COMMAND(hop
, 2, MODCMD_REQUIRE_CHANNEL
, "access", "op", NULL
);
7737 DEFINE_COMMAND(op
, 2, MODCMD_REQUIRE_CHANNEL
, "access", "op", NULL
);
7738 DEFINE_COMMAND(deop
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
7739 DEFINE_COMMAND(dehop
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
7740 DEFINE_COMMAND(voice
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
7741 DEFINE_COMMAND(devoice
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
7743 DEFINE_COMMAND(kickban
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
7744 DEFINE_COMMAND(kick
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
7745 DEFINE_COMMAND(ban
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
7746 DEFINE_COMMAND(unban
, 2, 0, "template", "hop", NULL
);
7747 DEFINE_COMMAND(unbanall
, 1, 0, "template", "hop", NULL
);
7748 DEFINE_COMMAND(unbanme
, 1, MODCMD_REQUIRE_CHANUSER
, "template", "hop", NULL
);
7749 DEFINE_COMMAND(open
, 1, MODCMD_REQUIRE_CHANUSER
, "template", "op", NULL
);
7750 DEFINE_COMMAND(topic
, 1, MODCMD_REQUIRE_REGCHAN
, "template", "hop", "flags", "+never_csuspend", NULL
);
7751 DEFINE_COMMAND(mode
, 1, MODCMD_REQUIRE_REGCHAN
, "template", "op", NULL
);
7752 DEFINE_COMMAND(inviteme
, 1, MODCMD_REQUIRE_CHANNEL
, "access", "1", NULL
);
7753 DEFINE_COMMAND(invite
, 1, MODCMD_REQUIRE_CHANNEL
, "access", "manager", NULL
);
7754 DEFINE_COMMAND(set
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "op", NULL
);
7755 DEFINE_COMMAND(wipeinfo
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7756 DEFINE_COMMAND(resync
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7758 DEFINE_COMMAND(events
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog", "access", "manager", NULL
);
7759 DEFINE_COMMAND(last
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog", "access", "manager", NULL
);
7760 DEFINE_COMMAND(addlamer
, 2, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
7761 DEFINE_COMMAND(addtimedlamer
, 3, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
7763 /* if you change dellamer access, see also places
7764 * like unbanme which have manager hardcoded. */
7765 DEFINE_COMMAND(dellamer
, 2, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
7766 DEFINE_COMMAND(uset
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "1", NULL
);
7768 DEFINE_COMMAND(lamers
, 1, MODCMD_REQUIRE_REGCHAN
, "access", "1", "flags", "+nolog", NULL
);
7770 DEFINE_COMMAND(peek
, 1, MODCMD_REQUIRE_REGCHAN
, "access", "op", "flags", "+nolog", NULL
);
7772 DEFINE_COMMAND(myaccess
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
7773 DEFINE_COMMAND(access
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7774 DEFINE_COMMAND(users
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7775 DEFINE_COMMAND(wlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7776 DEFINE_COMMAND(clist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7777 DEFINE_COMMAND(mlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7778 DEFINE_COMMAND(olist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7779 DEFINE_COMMAND(hlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7780 DEFINE_COMMAND(plist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7781 DEFINE_COMMAND(info
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7782 DEFINE_COMMAND(seen
, 2, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7783 DEFINE_COMMAND(names
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7785 DEFINE_COMMAND(note
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+joinable,+acceptchan", NULL
);
7786 DEFINE_COMMAND(delnote
, 2, MODCMD_REQUIRE_CHANUSER
, NULL
);
7788 DEFINE_COMMAND(netinfo
, 1, 0, "flags", "+nolog", NULL
);
7789 DEFINE_COMMAND(ircops
, 1, 0, "flags", "+nolog", NULL
);
7790 DEFINE_COMMAND(helpers
, 1, 0, "flags", "+nolog", NULL
);
7791 DEFINE_COMMAND(staff
, 1, 0, "flags", "+nolog", NULL
);
7793 DEFINE_COMMAND(say
, 2, 0, "flags", "+oper,+acceptchan", NULL
);
7794 DEFINE_COMMAND(emote
, 2, 0, "flags", "+oper,+acceptchan", NULL
);
7795 DEFINE_COMMAND(expire
, 1, 0, "flags", "+oper", NULL
);
7796 DEFINE_COMMAND(search
, 3, 0, "flags", "+nolog,+helping", NULL
);
7797 DEFINE_COMMAND(unvisited
, 1, 0, "flags", "+nolog,+helping", NULL
);
7799 DEFINE_COMMAND(unf
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
7800 DEFINE_COMMAND(ping
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
7801 DEFINE_COMMAND(wut
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
7802 DEFINE_COMMAND(8ball
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
7803 DEFINE_COMMAND(d
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
7804 DEFINE_COMMAND(huggle
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
7805 DEFINE_COMMAND(calc
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
7807 /* Channel options */
7808 DEFINE_CHANNEL_OPTION(defaulttopic
);
7809 DEFINE_CHANNEL_OPTION(topicmask
);
7810 DEFINE_CHANNEL_OPTION(greeting
);
7811 DEFINE_CHANNEL_OPTION(usergreeting
);
7812 DEFINE_CHANNEL_OPTION(modes
);
7813 DEFINE_CHANNEL_OPTION(enfops
);
7814 DEFINE_CHANNEL_OPTION(enfhalfops
);
7815 /*DEFINE_CHANNEL_OPTION(giveops);
7816 DEFINE_CHANNEL_OPTION(givehalfops);
7818 DEFINE_CHANNEL_OPTION(voice
);
7819 DEFINE_CHANNEL_OPTION(protect
);
7820 DEFINE_CHANNEL_OPTION(enfmodes
);
7821 DEFINE_CHANNEL_OPTION(enftopic
);
7822 DEFINE_CHANNEL_OPTION(pubcmd
);
7823 /*DEFINE_CHANNEL_OPTION(givevoice);
7825 DEFINE_CHANNEL_OPTION(userinfo
);
7826 DEFINE_CHANNEL_OPTION(dynlimit
);
7827 DEFINE_CHANNEL_OPTION(topicsnarf
);
7828 DEFINE_CHANNEL_OPTION(nodelete
);
7829 DEFINE_CHANNEL_OPTION(toys
);
7830 DEFINE_CHANNEL_OPTION(setters
);
7831 DEFINE_CHANNEL_OPTION(topicrefresh
);
7832 DEFINE_CHANNEL_OPTION(ctcpusers
);
7833 DEFINE_CHANNEL_OPTION(ctcpreaction
);
7834 DEFINE_CHANNEL_OPTION(inviteme
);
7836 DEFINE_CHANNEL_OPTION(offchannel
);
7837 modcmd_register(chanserv_module
, "set defaults", chan_opt_defaults
, 1, 0, "access", "owner", NULL
);
7839 /* Alias set topic to set defaulttopic for compatibility. */
7840 modcmd_register(chanserv_module
, "set topic", chan_opt_defaulttopic
, 1, 0, NULL
);
7843 DEFINE_USER_OPTION(autoinvite
);
7844 DEFINE_USER_OPTION(info
);
7845 DEFINE_USER_OPTION(autoop
);
7847 /* Alias uset autovoice to uset autoop. */
7848 modcmd_register(chanserv_module
, "uset autovoice", user_opt_autoop
, 1, 0, NULL
);
7850 note_types
= dict_new();
7851 dict_set_free_data(note_types
, chanserv_deref_note_type
);
7854 const char *modes
= conf_get_data("services/chanserv/modes", RECDB_QSTRING
);
7855 chanserv
= AddService(nick
, modes
? modes
: NULL
, "Channel Services", NULL
);
7856 service_register(chanserv
)->trigger
= '!';
7857 reg_chanmsg_func('\001', chanserv
, chanserv_ctcp_check
);
7860 saxdb_register("ChanServ", chanserv_saxdb_read
, chanserv_saxdb_write
);
7862 if(chanserv_conf
.channel_expire_frequency
)
7863 timeq_add(now
+ chanserv_conf
.channel_expire_frequency
, expire_channels
, NULL
);
7865 if(chanserv_conf
.refresh_period
)
7867 time_t next_refresh
;
7868 next_refresh
= (now
+ chanserv_conf
.refresh_period
- 1) / chanserv_conf
.refresh_period
* chanserv_conf
.refresh_period
;
7869 timeq_add(next_refresh
, chanserv_refresh_topics
, NULL
);
7872 reg_exit_func(chanserv_db_cleanup
);
7873 message_register_table(msgtab
);