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 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 enough channels (at least %d); use FORCE to override." },
136 /* Do-not-register channels */
137 { "CSMSG_NOT_DNR", "$b%s$b is not a valid channel name or *account." },
138 { "CSMSG_DNR_SEARCH_RESULTS", "$bDo-Not-Register Channels$b" },
139 { "CSMSG_DNR_INFO", "$b%s$b (set by $b%s$b): %s" },
140 { "CSMSG_DNR_INFO_SET", "$b%s$b (set %s ago by $b%s$b): %s" },
141 { "CSMSG_MORE_DNRS", "%d more do-not-register entries skipped." },
142 { "CSMSG_DNR_CHANNEL", "Only network staff may register $b%s$b." },
143 { "CSMSG_DNR_CHANNEL_MOVE", "Only network staff may move $b%s$b." },
144 { "CSMSG_DNR_ACCOUNT", "Only network staff may register channels to $b%s$b." },
145 { "CSMSG_NOREGISTER_CHANNEL", "$b%s$b has been added to the do-not-register list." },
146 { "CSMSG_NO_SUCH_DNR", "$b%s$b is not in the do-not-register list." },
147 { "CSMSG_DNR_REMOVED", "$b%s$b has been removed from the do-not-register list." },
149 /* Channel unregistration */
150 { "CSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
151 { "CSMSG_UNREG_NODELETE", "$b%s$b is protected from unregistration." },
152 { "CSMSG_CHAN_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended (%s)." },
153 { "CSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must use 'unregister %s'." },
156 { "CSMSG_MOVE_SUCCESS", "Channel registration has been moved to $b%s$b." },
157 { "CSMSG_MOVE_NODELETE", "$b%s$b is protected from unregistration, and cannot be moved." },
159 /* Channel merging */
160 { "CSMSG_MERGE_SUCCESS", "Channel successfully merged into $b%s$b." },
161 { "CSMSG_MERGE_SELF", "Merging cannot be performed if the source and target channels are the same." },
162 { "CSMSG_MERGE_NODELETE", "You may not merge a channel that is marked NoDelete." },
163 { "CSMSG_MERGE_SUSPENDED", "Merging cannot be performed if the source or target channel is suspended." },
164 { "CSMSG_MERGE_NOT_OWNER", "You must be the owner of the target channel (or a helper) to merge into the channel." },
166 /* Handle unregistration */
167 { "CSMSG_HANDLE_UNREGISTERED", "As a result of your account unregistration, you have been deleted from all of your channels' userlists." },
170 { "CSMSG_NOT_USER", "You lack access to $b%s$b." },
171 { "CSMSG_NO_CHAN_USER", "%s lacks access to $b%s$b." },
172 { "CSMSG_NO_ACCESS", "You lack sufficient access to use this command." },
173 { "CSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$C$b." },
174 { "CSMSG_MAXIMUM_LAMERS", "This channel has reached the lamer count limit of $b%d$b." },
175 { "CSMSG_MAXIMUM_USERS", "This channel has reached the user count limit of $b%d$b." },
176 { "CSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel, and cannot be registered." },
177 { "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." },
178 { "CSMSG_ALREADY_OPPED", "You are already opped in $b%s$b." },
179 { "CSMSG_ALREADY_HALFOPPED", "You are already halfopped in $b%s$b." },
180 { "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." },
181 { "CSMSG_ALREADY_DOWN", "You are not opped, halfopped, or voiced in $b%s$b." },
182 { "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." },
183 { "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." },
185 /* Removing yourself from a channel. */
186 { "CSMSG_NO_OWNER_DELETEME", "You cannot delete your owner access in $b%s$b." },
187 { "CSMSG_CONFIRM_DELETEME", "To really remove yourself, you must use 'deleteme %s'." },
188 { "CSMSG_DELETED_YOU", "Your $b%d$b access has been deleted from $b%s$b." },
190 /* User management */
191 { "CSMSG_ADDED_USER", "Added %s to the %s user list with access %s (%d)." },
192 { "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." },
193 { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be greater than maximum (%d)." },
194 { "CSMSG_DELETED_USERS", "Deleted accounts matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list." },
195 { "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." },
196 { "CSMSG_INCORRECT_ACCESS", "%s has access $b%s$b, not %s." },
197 { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with %s access)." },
198 { "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." },
199 { "CSMSG_ADDUSER_PENDING_ALREADY", "He or she is already pending addition to %s once he/she auths with $b$N$b." },
200 { "CSMSG_ADDUSER_PENDING_HEADER", "Users to add to channels pending logins:" }, /* Remove after testing? */
201 { "CSMSG_ADDUSER_PENDING_LIST", "Channel %s user %s" }, /* Remove after testing? */
202 { "CSMSG_ADDUSER_PENDING_HEADER", "--------- End of pending list ----------" }, /* Remove after testing? */
203 /*{ "CSMSG_ADDUSER_PENDING_NOTINCHAN", "That user is not in %s, and is not auth'd." }, */
204 { "CSMSG_ADDUSER_PENDING_TARGET", "Channel Services bot here, %s would like to add you to my userlist in channel %s, but you are not auth'd to $b$N$b. Please auth now, and you will be added. If you do not have an accont, type /msg $N help register" },
205 { "CSMSG_CANNOT_TRIM", "You must include a minimum inactivity duration of at least 60 seconds to trim." },
207 { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." },
208 { "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." },
209 { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
210 { "CSMSG_TRANSFER_WAIT", "You must wait %s before you can give ownership of $b%s$b to someone else." },
211 { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
212 { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
215 { "CSMSG_LAMER_ADDED", "Added $b%s$b to %s LAMERs." },
216 { "CSMSG_TIMED_LAMER_ADDED", "LAMERed $b%s$b on %s for %s." },
217 { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
218 { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
219 { "CSMSG_REASON_CHANGE", "Reason for LAMER $b%s$b changed." },
220 { "CSMSG_LAMER_EXTENDED", "Extended LAMER for $b%s$b, now expires in %s." },
221 { "CSMSG_BAN_REMOVED", "Matching ban(s) and LAMER(s) in $b%s$b were removed." },
222 { "CSMSG_TRIMMED_LAMERS", "Trimmed $b%d LAMERs$b from the %s LAMER list that were inactive for at least %s." },
223 { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
224 { "CSMSG_REDUNDANT_LAMER", "$b%s$b is already LAMER'd in %s." },
225 { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
226 { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
227 { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
228 { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
229 { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
230 { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban or LAMER found: $b%s$b." },
231 { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
233 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
235 /* Channel management */
236 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
237 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
238 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
240 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
241 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
242 { "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" },
243 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic is at most %d characters and matches the topic mask pattern." },
244 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
245 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
246 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
248 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
249 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
250 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
251 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
252 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
253 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
254 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you must use 'set defaults %s'." },
255 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
256 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
258 { "CSMSG_BAD_GIVEVOICE", "You cannot change GiveVoice to above GiveHalfOps (%d)." },
259 { "CSMSG_BAD_GIVEHOPS", "You cannot change GiveHalfOps to below GiveOps (%d)." },
260 { "CSMSG_BAD_GIVEOPS", "You cannot change GiveOps to below GiveVoice (%d)." },
262 { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
263 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
264 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
265 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
266 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
267 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
268 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
269 { "CSMSG_SET_MODES", "$bModes $b %s" },
270 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
271 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s" },
272 { "CSMSG_SET_OFFCHANNEL", "$bOffChannel $b %s" },
273 { "CSMSG_SET_USERINFO", "$bUserInfo $b %d" },
275 { "CSMSG_SET_GIVE_VOICE", "$bGiveVoice $b %d" },
276 { "CSMSG_SET_GIVE_HALFOPS", "$bGiveHalfOps $b %d" },
278 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" },
279 { "CSMSG_SET_INVITEME", "$bInviteMe $b %d" },
280 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d" },
281 { "CSMSG_SET_ENFHALFOPS", "$bEnfHalfOps $b %d" },
283 { "CSMSG_SET_GIVE_OPS", "$bGiveOps $b %d" },
285 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d" },
286 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d" },
287 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d" },
288 { "CSMSG_SET_SETTERS", "$bSetters $b %d" },
289 { "CSMSG_SET_CTCPUSERS", "$bCTCPUsers $b %d" },
290 { "CSMSG_SET_VOICE", "$bvoice $b %d - %s" },
291 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
292 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
293 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
294 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
295 { "CSMSG_USET_AUTOOP", "$bAutoOp $b %s" },
296 { "CSMSG_USET_AUTOVOICE", "$bAutoVoice $b %s" },
297 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
298 { "CSMSG_USET_INFO", "$bInfo $b %s" },
300 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
301 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
302 { "CSMSG_HOPBY_LOCKED", "You may not halfop users who lack halfop or greater access." },
303 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
304 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
305 { "CSMSG_HALFOPPED_USERS", "Halfopped users in $b%s$b." },
306 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
307 { "CSMSG_DEHALFOPPED_USERS", "DeHalfopped users in $b%s$b." },
308 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
309 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
310 { "CSMSG_VOICE_NONE", "Noone will be auto-voiced" },
311 { "CSMSG_VOICE_PEON", "PEONs will be auto-voiced" },
312 { "CSMSG_VOICE_ALL", "Everyone will be auto-voiced" },
313 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
314 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
315 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
316 { "CSMSG_PROTECT_NONE", "No users will be protected." },
317 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
318 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
319 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
320 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
321 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
322 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
323 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
324 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
325 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
326 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
327 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
328 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
330 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
331 { "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" },
332 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s." },
333 { "CSMSG_ALREADY_PRESENT", "%s is already in $b%s$b." },
334 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
335 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s to use this command." },
336 { "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
337 { "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
339 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
340 { "CSMSG_NO_BANS", "No bans found on $b%s$b." },
341 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
343 /* Channel userlist */
344 { "CSMSG_ACCESS_ALL_HEADER", "$b%s Users From Level %s To %s$b" },
345 { "CSMSG_ACCESS_SEARCH_HEADER", "$b%s Users From Level %s To %s Matching %s$b" },
346 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
347 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%s$b (%u) in %s." },
348 { "CSMSG_LAMERS_HEADER", "$bLamers in %s$b" },
350 /* Channel note list */
351 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
352 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
353 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
354 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
355 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
356 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
357 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
358 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
359 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
360 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
361 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
362 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
363 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
364 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
365 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
367 /* Channel [un]suspension */
368 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
369 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
370 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
371 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
372 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
373 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
374 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
376 /* Access information */
377 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
378 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
379 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
380 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
381 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
382 { "CSMSG_USER_HAS_ACCESS", "%s has $b%s$b access (%d) in %s." },
383 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
384 { "CSMSG_HELPER_HAS_ACCESS", "%s has $b%s$b access (%d) in %s and has $bsecurity override$b enabled." },
385 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
386 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
387 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
389 /* Seen information */
390 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
391 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
392 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
393 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
395 /* Names information */
396 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
397 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
399 /* Channel information */
400 { "CSMSG_CHANNEL_INFO", "$bInformation About %s$b" },
401 { "CSMSG_BAR", "----------------------------------------"},
402 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
403 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
404 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
405 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
406 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
407 { "CSMSG_CHANNEL_LAMERS", "$bLamer Count: $b%i" },
408 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
409 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
410 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
411 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
412 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
413 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
414 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
415 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
416 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
417 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
418 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
419 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
420 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
421 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
422 { "CSMSG_CHANNEL_END", "---------------End of Info--------------"},
424 { "CSMSG_PEEK_INFO", "$bStatus of %s$b" },
425 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
426 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
427 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d" },
428 { "CSMSG_PEEK_OPS", "$bOps:$b" },
429 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
430 { "CSMSG_PEEK_END", "-------------End of Status--------------" },
432 /* Network information */
433 { "CSMSG_NETWORK_INFO", "Network Information:" },
434 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
435 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
436 { "CSMSG_NETWORK_LAMERS", "$bTotal Lamer Count: $b%i" },
437 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
438 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
439 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
440 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
441 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
444 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
445 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
446 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
448 /* Channel searches */
449 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
450 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
451 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
452 { "CSMSG_CHANNEL_SEARCH_RESULTS", "$bChannels Found Matching Search$b" },
454 /* Channel configuration */
455 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
456 { "CSMSG_CHANNEL_OPTIONS", "$bChannel Options for %s$b" },
457 { "CSMSG_CHANNEL_OPTIONS_END", "-------------End of Options-------------" },
458 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
461 { "CSMSG_USER_OPTIONS", "User Options:" },
462 { "CSMSG_USER_PROTECTED", "That user is protected." },
465 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
466 { "CSMSG_PING_RESPONSE", "Pong!" },
467 { "CSMSG_WUT_RESPONSE", "wut" },
468 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
469 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
470 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
471 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
472 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
473 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
474 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
477 { "CSMSG_EVENT_SEARCH_RESULTS", "$bChannel Events for %s$b" },
481 /* eject_user and unban_user flags */
482 #define ACTION_KICK 0x0001
483 #define ACTION_BAN 0x0002
484 #define ACTION_ADD_LAMER 0x0004
485 #define ACTION_ADD_TIMED_LAMER 0x0008
486 #define ACTION_UNBAN 0x0010
487 #define ACTION_DEL_LAMER 0x0020
489 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
490 #define MODELEN 40 + KEYLEN
494 #define CSFUNC_ARGS user, channel, argc, argv, cmd
496 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
497 #define CHANSERV_SYNTAX() svccmd_send_help_brief(user, chanserv, cmd)
498 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
499 reply("MSG_MISSING_PARAMS", argv[0]); \
503 DECLARE_LIST(dnrList
, struct do_not_register
*);
504 DEFINE_LIST(dnrList
, struct do_not_register
*);
506 static int eject_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
);
508 struct userNode
*chanserv
;
511 static dict_t plain_dnrs
, mask_dnrs
, handle_dnrs
;
512 static struct log_type
*CS_LOG
;
513 struct adduserPending
* adduser_pendings
= NULL
;
514 unsigned int adduser_pendings_count
= 0;
518 struct channelList support_channels
;
519 struct mod_chanmode default_modes
;
521 unsigned long db_backup_frequency
;
522 unsigned long channel_expire_frequency
;
525 unsigned int adjust_delay
;
526 long channel_expire_delay
;
527 unsigned int nodelete_level
;
529 unsigned int adjust_threshold
;
530 int join_flood_threshold
;
532 unsigned int greeting_length
;
533 unsigned int refresh_period
;
534 unsigned int giveownership_period
;
536 unsigned int max_owned
;
537 unsigned int max_chan_users
;
538 unsigned int max_chan_bans
; /* lamers */
539 unsigned int max_userinfo_length
;
541 struct string_list
*set_shows
;
542 struct string_list
*eightball
;
543 struct string_list
*old_ban_names
;
545 const char *ctcp_short_ban_duration
;
546 const char *ctcp_long_ban_duration
;
548 const char *irc_operator_epithet
;
549 const char *network_helper_epithet
;
550 const char *support_helper_epithet
;
555 struct userNode
*user
;
556 struct userNode
*bot
;
557 struct chanNode
*channel
;
559 unsigned short lowest
;
560 unsigned short highest
;
561 struct userData
**users
;
562 struct helpfile_table table
;
565 enum note_access_type
567 NOTE_SET_CHANNEL_ACCESS
,
568 NOTE_SET_CHANNEL_SETTER
,
572 enum note_visible_type
575 NOTE_VIS_CHANNEL_USERS
,
581 enum note_access_type set_access_type
;
583 unsigned int min_opserv
;
584 unsigned short min_ulevel
;
586 enum note_visible_type visible_type
;
587 unsigned int max_length
;
594 struct note_type
*type
;
595 char setter
[NICKSERV_HANDLE_LEN
+1];
599 static unsigned int registered_channels
;
600 static unsigned int banCount
;
602 static const struct {
605 unsigned short level
;
607 } accessLevels
[] = { /* MUST be orderd less to most! */
608 { "peon", "Peon", UL_PEON
, '+' },
609 { "halfop", "HalfOp", UL_HALFOP
, '%' },
610 { "op", "Op", UL_OP
, '@' },
611 { "manager", "Manager", UL_MANAGER
, '%' },
612 { "coowner", "Coowner", UL_COOWNER
, '*' },
613 { "owner", "Owner", UL_OWNER
, '!' },
614 { "helper", "BUG:", UL_HELPER
, 'X' }
617 static const struct {
620 unsigned short default_value
;
621 unsigned int old_idx
;
622 unsigned int old_flag
;
623 unsigned short flag_value
;
625 { "CSMSG_SET_GIVE_VOICE", "givevoice", 100, ~0, CHANNEL_VOICE_ALL
, 0 },
626 { "CSMSG_SET_GIVE_HALFOPS", "givehalfops", 150, ~0, CHANNEL_HOP_ALL
, 0 },
627 { "CSMSG_SET_GIVE_OPS", "giveops", 200, 2, 0, 0 },
628 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
629 { "CSMSG_SET_ENFHALFOPS", "enfhalfops", 300, 1, 0, 0 },
630 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
631 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
632 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
633 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
634 { "CSMSG_SET_CTCPUSERS", "ctcpusers", 0, 9, 0, 0 },
635 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES
, 1 },
636 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE
, 200 },
637 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF
, 1 }
640 struct charOptionValues
{
644 { 'n', "CSMSG_VOICE_NONE" },
645 { 'p', "CSMSG_VOICE_PEON" },
646 { 'a', "CSMSG_VOICE_ALL" }
647 }, protectValues
[] = {
648 { 'a', "CSMSG_PROTECT_ALL" },
649 { 'e', "CSMSG_PROTECT_EQUAL" },
650 { 'l', "CSMSG_PROTECT_LOWER" },
651 { 'n', "CSMSG_PROTECT_NONE" }
653 { 'd', "CSMSG_TOYS_DISABLED" },
654 { 'n', "CSMSG_TOYS_PRIVATE" },
655 { 'p', "CSMSG_TOYS_PUBLIC" }
656 }, topicRefreshValues
[] = {
657 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
658 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
659 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
660 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
661 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
662 }, ctcpReactionValues
[] = {
663 { 'k', "CSMSG_CTCPREACTION_KICK" },
664 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
665 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
666 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
669 static const struct {
673 unsigned int old_idx
;
675 struct charOptionValues
*values
;
677 { "CSMSG_SET_VOICE", "voice", 'p', 99, ArrayLength(voiceValues
), voiceValues
},
678 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues
), protectValues
},
679 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues
), toysValues
},
680 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues
), topicRefreshValues
},
681 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 't', 10, ArrayLength(ctcpReactionValues
), ctcpReactionValues
}
684 struct userData
*helperList
;
685 struct chanData
*channelList
;
686 static struct module *chanserv_module
;
687 static unsigned int userCount
;
688 unsigned int chanserv_read_version
= 0; /* db version control */
690 #define CHANSERV_DB_VERSION 2
692 #define GetChannelUser(channel, handle) _GetChannelUser(channel, handle, 1, 0)
693 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
694 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
697 user_level_from_name(const char *name
, unsigned short clamp_level
)
699 unsigned int level
= 0, ii
;
701 level
= strtoul(name
, NULL
, 10);
702 else for(ii
= 0; (ii
< ArrayLength(accessLevels
)) && !level
; ++ii
)
703 if(!irccasecmp(name
, accessLevels
[ii
].name
))
704 level
= accessLevels
[ii
].level
;
705 if(level
> clamp_level
)
711 user_level_name_from_level(int level
)
719 for(ii
= 0; (ii
< ArrayLength(accessLevels
)); ii
++)
720 if(level
>= accessLevels
[ii
].level
)
721 highest
= accessLevels
[ii
].title
;
727 parse_level_range(unsigned short *minl
, unsigned short *maxl
, const char *arg
)
730 *minl
= strtoul(arg
, &sep
, 10);
738 *maxl
= strtoul(sep
+1, &sep
, 10);
746 _GetChannelUser(struct chanData
*channel
, struct handle_info
*handle
, int override
, int allow_suspended
)
748 struct userData
*uData
, **head
;
750 if(!channel
|| !handle
)
753 if(override
&& HANDLE_FLAGGED(handle
, HELPING
)
754 && ((handle
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
)))
756 for(uData
= helperList
;
757 uData
&& uData
->handle
!= handle
;
758 uData
= uData
->next
);
762 uData
= calloc(1, sizeof(struct userData
));
763 uData
->handle
= handle
;
765 uData
->access
= UL_HELPER
;
771 uData
->next
= helperList
;
773 helperList
->prev
= uData
;
781 for(uData
= channel
->users
; uData
; uData
= uData
->next
)
782 if((uData
->handle
== handle
) && (allow_suspended
|| !IsUserSuspended(uData
)))
785 head
= &(channel
->users
);
788 if(uData
&& (uData
!= *head
))
790 /* Shuffle the user to the head of whatever list he was in. */
792 uData
->next
->prev
= uData
->prev
;
794 uData
->prev
->next
= uData
->next
;
800 (**head
).prev
= uData
;
807 /* Returns non-zero if user has at least the minimum access.
808 * exempt_owner is set when handling !set, so the owner can set things
811 int check_user_level(struct chanNode
*channel
, struct userNode
*user
, enum levelOption opt
, int allow_override
, int exempt_owner
)
813 struct userData
*uData
;
814 struct chanData
*cData
= channel
->channel_info
;
815 unsigned short minimum
= cData
->lvlOpts
[opt
];
818 uData
= _GetChannelUser(cData
, user
->handle_info
, allow_override
, 0);
821 if(minimum
<= uData
->access
)
823 if((minimum
> UL_OWNER
) && (uData
->access
== UL_OWNER
) && exempt_owner
)
828 /* Scan for other users authenticated to the same handle
829 still in the channel. If so, keep them listed as present.
831 user is optional, if not null, it skips checking that userNode
832 (for the handle_part function) */
834 scan_user_presence(struct userData
*uData
, struct userNode
*user
)
838 if(IsSuspended(uData
->channel
)
839 || IsUserSuspended(uData
)
840 || !(mn
= find_handle_in_channel(uData
->channel
->channel
, uData
->handle
, user
)))
852 chanserv_ctcp_check(struct userNode
*user
, struct chanNode
*channel
, char *text
, UNUSED_ARG(struct userNode
*bot
))
854 unsigned int eflags
, argc
;
856 static char *bad_ctcp_reason
= "CTCPs to this channel are forbidden.";
858 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
859 if(!channel
->channel_info
860 || IsSuspended(channel
->channel_info
)
862 || !ircncasecmp(text
, "ACTION ", 7))
864 /* Figure out the minimum level needed to CTCP the channel */
865 if(check_user_level(channel
, user
, lvlCTCPUsers
, 1, 0))
867 /* We need to enforce against them; do so. */
870 argv
[1] = user
->nick
;
872 if(GetUserMode(channel
, user
))
873 eflags
|= ACTION_KICK
;
874 switch(channel
->channel_info
->chOpts
[chCTCPReaction
]) {
875 default: case 'k': /* just do the kick */ break;
877 eflags
|= ACTION_BAN
;
880 eflags
|= ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
;
881 argv
[argc
++] = (char*)chanserv_conf
.ctcp_short_ban_duration
;
884 eflags
|= ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
;
885 argv
[argc
++] = (char*)chanserv_conf
.ctcp_long_ban_duration
;
888 argv
[argc
++] = bad_ctcp_reason
;
889 eject_user(chanserv
, channel
, argc
, argv
, NULL
, eflags
);
893 chanserv_create_note_type(const char *name
)
895 struct note_type
*ntype
= calloc(1, sizeof(*ntype
) + strlen(name
));
896 strcpy(ntype
->name
, name
);
898 dict_insert(note_types
, ntype
->name
, ntype
);
903 chanserv_deref_note_type(void *data
)
905 struct note_type
*ntype
= data
;
907 if(--ntype
->refs
> 0)
913 chanserv_flush_note_type(struct note_type
*ntype
)
915 struct chanData
*cData
;
916 for(cData
= channelList
; cData
; cData
= cData
->next
)
917 dict_remove(cData
->notes
, ntype
->name
);
921 chanserv_truncate_notes(struct note_type
*ntype
)
923 struct chanData
*cData
;
925 unsigned int size
= sizeof(*note
) + ntype
->max_length
;
927 for(cData
= channelList
; cData
; cData
= cData
->next
) {
928 note
= dict_find(cData
->notes
, ntype
->name
, NULL
);
931 if(strlen(note
->note
) <= ntype
->max_length
)
933 dict_remove2(cData
->notes
, ntype
->name
, 1);
934 note
= realloc(note
, size
);
935 note
->note
[ntype
->max_length
] = 0;
936 dict_insert(cData
->notes
, ntype
->name
, note
);
940 static int note_type_visible_to_user(struct chanData
*channel
, struct note_type
*ntype
, struct userNode
*user
);
943 chanserv_add_channel_note(struct chanData
*channel
, struct note_type
*type
, const char *setter
, const char *text
)
946 unsigned int len
= strlen(text
);
948 if(len
> type
->max_length
) len
= type
->max_length
;
949 note
= calloc(1, sizeof(*note
) + len
);
951 strncpy(note
->setter
, setter
, sizeof(note
->setter
)-1);
952 memcpy(note
->note
, text
, len
);
954 dict_insert(channel
->notes
, type
->name
, note
);
960 chanserv_free_note(void *data
)
962 struct note
*note
= data
;
964 chanserv_deref_note_type(note
->type
);
965 assert(note
->type
->refs
> 0); /* must use delnote to remove the type */
969 static MODCMD_FUNC(cmd_createnote
) {
970 struct note_type
*ntype
;
971 unsigned int arg
= 1, existed
= 0, max_length
;
973 if((ntype
= dict_find(note_types
, argv
[1], NULL
)))
976 ntype
= chanserv_create_note_type(argv
[arg
]);
977 if(!irccasecmp(argv
[++arg
], "privileged"))
980 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
981 ntype
->set_access
.min_opserv
= strtoul(argv
[arg
], NULL
, 0);
983 else if(!irccasecmp(argv
[arg
], "channel"))
985 unsigned short ulvl
= user_level_from_name(argv
[++arg
], UL_OWNER
);
988 reply("CSMSG_INVALID_ACCESS", argv
[arg
]);
991 ntype
->set_access_type
= NOTE_SET_CHANNEL_ACCESS
;
992 ntype
->set_access
.min_ulevel
= ulvl
;
994 else if(!irccasecmp(argv
[arg
], "setter"))
996 ntype
->set_access_type
= NOTE_SET_CHANNEL_SETTER
;
1000 reply("CSMSG_BAD_NOTE_ACCESS", argv
[arg
]);
1004 if(!irccasecmp(argv
[++arg
], "privileged"))
1005 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
1006 else if(!irccasecmp(argv
[arg
], "channel_users"))
1007 ntype
->visible_type
= NOTE_VIS_CHANNEL_USERS
;
1008 else if(!irccasecmp(argv
[arg
], "all"))
1009 ntype
->visible_type
= NOTE_VIS_ALL
;
1011 reply("CSMSG_BAD_NOTE_ACCESS", argv
[arg
]);
1015 if((arg
+1) >= argc
) {
1016 reply("MSG_MISSING_PARAMS", argv
[0]);
1019 max_length
= strtoul(argv
[++arg
], NULL
, 0);
1020 if(max_length
< 20 || max_length
> 450)
1022 reply("CSMSG_BAD_MAX_LENGTH", argv
[arg
]);
1025 if(existed
&& (max_length
< ntype
->max_length
))
1027 ntype
->max_length
= max_length
;
1028 chanserv_truncate_notes(ntype
);
1030 ntype
->max_length
= max_length
;
1033 reply("CSMSG_NOTE_MODIFIED", ntype
->name
);
1035 reply("CSMSG_NOTE_CREATED", ntype
->name
);
1040 dict_remove(note_types
, ntype
->name
);
1044 static MODCMD_FUNC(cmd_removenote
) {
1045 struct note_type
*ntype
;
1048 ntype
= dict_find(note_types
, argv
[1], NULL
);
1049 force
= (argc
> 2) && !irccasecmp(argv
[2], "force");
1052 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
1059 reply("CSMSG_NOTE_TYPE_USED", ntype
->name
);
1062 chanserv_flush_note_type(ntype
);
1064 dict_remove(note_types
, argv
[1]);
1065 reply("CSMSG_NOTE_DELETED", argv
[1]);
1070 mode_lock_violated(const struct mod_chanmode
*orig
, const struct mod_chanmode
*change
)
1074 if(orig
->modes_set
& change
->modes_clear
)
1076 if(orig
->modes_clear
& change
->modes_set
)
1078 if((orig
->modes_set
& MODE_KEY
) && (change
->modes_set
& MODE_KEY
)
1079 && strcmp(orig
->new_key
, change
->new_key
))
1081 if((orig
->modes_set
& MODE_LIMIT
) && (change
->modes_set
& MODE_LIMIT
)
1082 && (orig
->new_limit
!= change
->new_limit
))
1087 static char max_length_text
[MAXLEN
+1][16];
1089 static struct helpfile_expansion
1090 chanserv_expand_variable(const char *variable
)
1092 struct helpfile_expansion exp
;
1094 if(!irccasecmp(variable
, "notes"))
1097 exp
.type
= HF_TABLE
;
1098 exp
.value
.table
.length
= 1;
1099 exp
.value
.table
.width
= 3;
1100 exp
.value
.table
.flags
= 0;
1101 exp
.value
.table
.contents
= calloc(dict_size(note_types
)+1, sizeof(char**));
1102 exp
.value
.table
.contents
[0] = calloc(exp
.value
.table
.width
, sizeof(char*));
1103 exp
.value
.table
.contents
[0][0] = "Note Type";
1104 exp
.value
.table
.contents
[0][1] = "Visibility";
1105 exp
.value
.table
.contents
[0][2] = "Max Length";
1106 for(it
=dict_first(note_types
); it
; it
=iter_next(it
))
1108 struct note_type
*ntype
= iter_data(it
);
1111 if(!note_type_visible_to_user(NULL
, ntype
, message_dest
)) continue;
1112 row
= exp
.value
.table
.length
++;
1113 exp
.value
.table
.contents
[row
] = calloc(exp
.value
.table
.width
, sizeof(char*));
1114 exp
.value
.table
.contents
[row
][0] = ntype
->name
;
1115 exp
.value
.table
.contents
[row
][1] = (ntype
->visible_type
== NOTE_VIS_ALL
) ? "all" :
1116 (ntype
->visible_type
== NOTE_VIS_CHANNEL_USERS
) ? "chan users" :
1118 if(!max_length_text
[ntype
->max_length
][0])
1119 snprintf(max_length_text
[ntype
->max_length
], sizeof(max_length_text
[ntype
->max_length
]), "%u", ntype
->max_length
);
1120 exp
.value
.table
.contents
[row
][2] = max_length_text
[ntype
->max_length
];
1125 exp
.type
= HF_STRING
;
1126 exp
.value
.str
= NULL
;
1130 static struct chanData
*
1131 register_channel(struct chanNode
*cNode
, char *registrar
)
1133 struct chanData
*channel
;
1134 enum levelOption lvlOpt
;
1135 enum charOption chOpt
;
1137 channel
= calloc(1, sizeof(struct chanData
));
1139 channel
->notes
= dict_new();
1140 dict_set_free_data(channel
->notes
, chanserv_free_note
);
1142 channel
->registrar
= strdup(registrar
);
1143 channel
->registered
= now
;
1144 channel
->visited
= now
;
1145 channel
->limitAdjusted
= now
;
1146 channel
->ownerTransfer
= now
;
1147 channel
->flags
= CHANNEL_DEFAULT_FLAGS
;
1148 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
1149 channel
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
1150 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
1151 channel
->chOpts
[chOpt
] = charOptions
[chOpt
].default_value
;
1153 channel
->prev
= NULL
;
1154 channel
->next
= channelList
;
1157 channelList
->prev
= channel
;
1158 channelList
= channel
;
1159 registered_channels
++;
1161 channel
->channel
= cNode
;
1163 cNode
->channel_info
= channel
;
1168 static struct userData
*
1169 add_channel_user(struct chanData
*channel
, struct handle_info
*handle
, unsigned short access
, time_t seen
, const char *info
)
1171 struct userData
*ud
;
1173 if(access
> UL_OWNER
)
1176 ud
= calloc(1, sizeof(*ud
));
1177 ud
->channel
= channel
;
1178 ud
->handle
= handle
;
1180 ud
->access
= access
;
1181 ud
->info
= info
? strdup(info
) : NULL
;
1184 ud
->next
= channel
->users
;
1186 channel
->users
->prev
= ud
;
1187 channel
->users
= ud
;
1189 channel
->userCount
++;
1193 ud
->u_next
= ud
->handle
->channels
;
1195 ud
->u_next
->u_prev
= ud
;
1196 ud
->handle
->channels
= ud
;
1198 ud
->flags
= USER_FLAGS_DEFAULT
;
1202 static void unregister_channel(struct chanData
*channel
, const char *reason
);
1205 del_channel_user(struct userData
*user
, int do_gc
)
1207 struct chanData
*channel
= user
->channel
;
1209 channel
->userCount
--;
1213 user
->prev
->next
= user
->next
;
1215 channel
->users
= user
->next
;
1217 user
->next
->prev
= user
->prev
;
1220 user
->u_prev
->u_next
= user
->u_next
;
1222 user
->handle
->channels
= user
->u_next
;
1224 user
->u_next
->u_prev
= user
->u_prev
;
1228 if(do_gc
&& !channel
->users
&& !IsProtected(channel
))
1229 unregister_channel(channel
, "lost all users.");
1232 static struct adduserPending
*
1233 add_adduser_pending(struct chanNode
*channel
, struct userNode
*user
, int level
)
1235 struct adduserPending
*ap
;
1236 ap
= calloc(1,sizeof(struct adduserPending
));
1237 ap
->channel
= channel
;
1240 ap
->created
= time(NULL
);
1242 /* ap->prev defaults to NULL already.. */
1243 ap
->next
= adduser_pendings
;
1244 if(adduser_pendings
)
1245 adduser_pendings
->prev
= ap
;
1246 adduser_pendings
= ap
;
1247 adduser_pendings_count
++;
1252 del_adduser_pending(struct adduserPending
*ap
)
1255 ap
->prev
->next
= ap
->next
;
1257 adduser_pendings
= ap
->next
;
1260 ap
->next
->prev
= ap
->prev
;
1264 static void expire_adduser_pending();
1266 /* find_adduser_pending(channel, user) will find an arbitrary record
1267 * from user, channel, or user and channel.
1268 * if user or channel are NULL, they will match any records.
1270 static struct adduserPending
*
1271 find_adduser_pending(struct chanNode
*channel
, struct userNode
*user
)
1273 struct adduserPending
*ap
;
1275 expire_adduser_pending(); /* why not here.. */
1277 if(!channel
&& !user
) /* 2 nulls matches all */
1278 return(adduser_pendings
);
1279 for(ap
= adduser_pendings
;ap
;ap
= ap
->next
)
1281 if((channel
== ap
->channel
&& (user
== NULL
|| user
== ap
->user
)) || (user
==ap
->user
&& channel
==NULL
))
1288 /* Remove all pendings for a user or channel
1290 * called in nickserv.c DelUser() and proto-* unregister_channel()
1293 wipe_adduser_pending(struct chanNode
*channel
, struct userNode
*user
)
1295 struct adduserPending
*ap
;
1297 /* So this is a bit wastefull, i hate dealing with linked lists.
1298 * if its a problem we'll rewrite it right */
1299 while((ap
= find_adduser_pending(channel
, user
))) {
1300 del_adduser_pending(ap
);
1304 /* Called from nickserv.c cmd_auth after someone auths */
1306 process_adduser_pending(struct userNode
*user
)
1308 struct adduserPending
*ap
;
1309 if(!user
->handle_info
)
1310 return; /* not associated with an account */
1311 while((ap
= find_adduser_pending(NULL
, user
)))
1313 struct userData
*actee
;
1314 if(GetTrueChannelAccess(ap
->channel
->channel_info
, ap
->user
->handle_info
))
1316 /* Already on the userlist. do nothing*/
1320 actee
= add_channel_user(ap
->channel
->channel_info
, ap
->user
->handle_info
, ap
->level
, 0, NULL
);
1321 scan_user_presence(actee
, NULL
);
1323 del_adduser_pending(ap
);
1328 expire_adduser_pending()
1330 struct adduserPending
*ap
, *ap_next
;
1331 ap
= adduser_pendings
;
1334 if((ap
->created
+ ADDUSER_PENDING_EXPIRE
) < time(NULL
))
1336 ap_next
= ap
->next
; /* save next */
1337 del_adduser_pending(ap
); /* free and relink */
1338 ap
= ap_next
; /* advance */
1345 static void expire_ban(void *data
);
1347 static struct banData
*
1348 add_channel_ban(struct chanData
*channel
, const char *mask
, char *owner
, time_t set
, time_t triggered
, time_t expires
, char *reason
)
1351 unsigned int ii
, l1
, l2
;
1356 bd
= malloc(sizeof(struct banData
));
1358 bd
->channel
= channel
;
1360 bd
->triggered
= triggered
;
1361 bd
->expires
= expires
;
1363 for(ii
= 0; ii
< chanserv_conf
.old_ban_names
->used
; ++ii
)
1365 extern const char *hidden_host_suffix
;
1366 const char *old_name
= chanserv_conf
.old_ban_names
->list
[ii
];
1370 l2
= strlen(old_name
);
1373 if(irccasecmp(mask
+ l1
- l2
, old_name
))
1375 new_mask
= alloca(MAXLEN
);
1376 sprintf(new_mask
, "%.*s%s", (int)(l1
-l2
), mask
, hidden_host_suffix
);
1379 safestrncpy(bd
->mask
, mask
, sizeof(bd
->mask
));
1381 safestrncpy(bd
->owner
, owner
, sizeof(bd
->owner
));
1382 bd
->reason
= strdup(reason
);
1385 timeq_add(expires
, expire_ban
, bd
);
1388 bd
->next
= channel
->bans
; /* lamers */
1390 channel
->bans
->prev
= bd
;
1392 channel
->banCount
++;
1399 del_channel_ban(struct banData
*ban
)
1401 ban
->channel
->banCount
--;
1405 ban
->prev
->next
= ban
->next
;
1407 ban
->channel
->bans
= ban
->next
;
1410 ban
->next
->prev
= ban
->prev
;
1413 timeq_del(0, expire_ban
, ban
, TIMEQ_IGNORE_WHEN
);
1422 expire_ban(void *data
) /* lamer.. */
1424 struct banData
*bd
= data
;
1425 if(!IsSuspended(bd
->channel
))
1427 struct banList bans
;
1428 struct mod_chanmode change
;
1430 bans
= bd
->channel
->channel
->banlist
;
1431 mod_chanmode_init(&change
);
1432 for(ii
=0; ii
<bans
.used
; ii
++)
1434 if(!strcmp(bans
.list
[ii
]->ban
, bd
->mask
))
1437 change
.args
[0].mode
= MODE_REMOVE
|MODE_BAN
;
1438 change
.args
[0].u
.hostmask
= bd
->mask
;
1439 mod_chanmode_announce(chanserv
, bd
->channel
->channel
, &change
);
1445 del_channel_ban(bd
);
1448 static void chanserv_expire_suspension(void *data
);
1451 unregister_channel(struct chanData
*channel
, const char *reason
)
1453 struct mod_chanmode change
;
1454 char msgbuf
[MAXLEN
];
1456 /* After channel unregistration, the following must be cleaned
1458 - Channel information.
1460 - Channel bans. (lamers)
1461 - Channel suspension data.
1462 - adduser_pending data.
1463 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1469 timeq_del(0, NULL
, channel
, TIMEQ_IGNORE_FUNC
| TIMEQ_IGNORE_WHEN
);
1473 mod_chanmode_init(&change
);
1474 change
.modes_clear
|= MODE_REGISTERED
;
1475 mod_chanmode_announce(chanserv
, channel
->channel
, &change
);
1478 wipe_adduser_pending(channel
->channel
, NULL
);
1480 while(channel
->users
)
1481 del_channel_user(channel
->users
, 0);
1483 while(channel
->bans
)
1484 del_channel_ban(channel
->bans
);
1486 free(channel
->topic
);
1487 free(channel
->registrar
);
1488 free(channel
->greeting
);
1489 free(channel
->user_greeting
);
1490 free(channel
->topic_mask
);
1493 channel
->prev
->next
= channel
->next
;
1495 channelList
= channel
->next
;
1498 channel
->next
->prev
= channel
->prev
;
1500 if(channel
->suspended
)
1502 struct chanNode
*cNode
= channel
->channel
;
1503 struct suspended
*suspended
, *next_suspended
;
1505 for(suspended
= channel
->suspended
; suspended
; suspended
= next_suspended
)
1507 next_suspended
= suspended
->previous
;
1508 free(suspended
->suspender
);
1509 free(suspended
->reason
);
1510 if(suspended
->expires
)
1511 timeq_del(suspended
->expires
, chanserv_expire_suspension
, suspended
, 0);
1516 cNode
->channel_info
= NULL
;
1518 channel
->channel
->channel_info
= NULL
;
1520 dict_delete(channel
->notes
);
1521 sprintf(msgbuf
, "%s %s", channel
->channel
->name
, reason
);
1522 if(!IsSuspended(channel
))
1523 DelChannelUser(chanserv
, channel
->channel
, msgbuf
, 0);
1524 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, msgbuf
);
1525 UnlockChannel(channel
->channel
);
1527 registered_channels
--;
1531 expire_channels(UNUSED_ARG(void *data
))
1533 struct chanData
*channel
, *next
;
1534 struct userData
*user
;
1535 char delay
[INTERVALLEN
], reason
[INTERVALLEN
+ 64];
1537 intervalString(delay
, chanserv_conf
.channel_expire_delay
, NULL
);
1538 sprintf(reason
, "Channel registration automatically expired after %s of disuse.", delay
);
1540 for(channel
= channelList
; channel
; channel
= next
)
1542 next
= channel
->next
;
1544 /* See if the channel can be expired. */
1545 if(((now
- channel
->visited
) <= chanserv_conf
.channel_expire_delay
)
1546 || IsProtected(channel
))
1549 /* Make sure there are no high-ranking users still in the channel. */
1550 for(user
=channel
->users
; user
; user
=user
->next
)
1551 if(user
->present
&& (user
->access
>= UL_PRESENT
))
1556 /* Unregister the channel */
1557 log_module(CS_LOG
, LOG_INFO
, "(%s) Channel registration expired.", channel
->channel
->name
);
1558 unregister_channel(channel
, "registration expired.");
1561 if(chanserv_conf
.channel_expire_frequency
)
1562 timeq_add(now
+ chanserv_conf
.channel_expire_frequency
, expire_channels
, NULL
);
1566 protect_user(const struct userNode
*victim
, const struct userNode
*aggressor
, struct chanData
*channel
)
1568 char protect
= channel
->chOpts
[chProtect
];
1569 struct userData
*cs_victim
, *cs_aggressor
;
1571 /* Don't protect if no one is to be protected, someone is attacking
1572 himself, or if the aggressor is an IRC Operator. */
1573 if(protect
== 'n' || victim
== aggressor
/* Opers dont get special treatment :/ || IsOper(aggressor) */)
1576 /* Don't protect if the victim isn't authenticated (because they
1577 can't be a channel user), unless we are to protect non-users
1579 cs_victim
= GetChannelAccess(channel
, victim
->handle_info
);
1580 if(protect
!= 'a' && !cs_victim
)
1583 /* Protect if the aggressor isn't a user because at this point,
1584 the aggressor can only be less than or equal to the victim. */
1585 cs_aggressor
= GetChannelAccess(channel
, aggressor
->handle_info
);
1589 /* If the aggressor was a user, then the victim can't be helped. */
1596 if(cs_victim
->access
> cs_aggressor
->access
)
1601 if(cs_victim
->access
>= cs_aggressor
->access
)
1610 validate_op(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1612 struct chanData
*cData
= channel
->channel_info
;
1613 struct userData
*cs_victim
;
1615 if((!(cs_victim
= GetChannelUser(cData
, victim
->handle_info
))
1616 || (cs_victim
->access
< UL_OP
/* cData->lvlOpts[lvlGiveOps]*/))
1617 && !check_user_level(channel
, user
, lvlEnfOps
, 0, 0))
1619 send_message(user
, chanserv
, "CSMSG_OPBY_LOCKED");
1627 validate_halfop(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1629 struct chanData
*cData
= channel
->channel_info
;
1630 struct userData
*cs_victim
;
1632 if((!(cs_victim
= GetChannelUser(cData
, victim
->handle_info
))
1633 || (cs_victim
->access
< UL_HALFOP
/* cData->lvlOpts[lvlGiveHalfOps] */))
1634 && !check_user_level(channel
, user
, lvlEnfHalfOps
, 0, 0))
1636 send_message(user
, chanserv
, "CSMSG_HOPBY_LOCKED");
1645 validate_deop(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1647 if(IsService(victim
))
1649 send_message(user
, chanserv
, "MSG_SERVICE_IMMUNE", victim
->nick
);
1653 if(protect_user(victim
, user
, channel
->channel_info
))
1655 send_message(user
, chanserv
, "CSMSG_USER_PROTECTED", victim
->nick
);
1663 validate_dehop(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1665 if(IsService(victim
))
1667 send_message(user
, chanserv
, "MSG_SERVICE_IMMUNE", victim
->nick
);
1671 if(protect_user(victim
, user
, channel
->channel_info
))
1673 send_message(user
, chanserv
, "CSMSG_USER_PROTECTED", victim
->nick
);
1680 static struct do_not_register
*
1681 chanserv_add_dnr(const char *chan_name
, const char *setter
, const char *reason
)
1683 struct do_not_register
*dnr
= calloc(1, sizeof(*dnr
)+strlen(reason
));
1684 safestrncpy(dnr
->chan_name
, chan_name
, sizeof(dnr
->chan_name
));
1685 safestrncpy(dnr
->setter
, setter
, sizeof(dnr
->setter
));
1686 strcpy(dnr
->reason
, reason
);
1688 if(dnr
->chan_name
[0] == '*')
1689 dict_insert(handle_dnrs
, dnr
->chan_name
+1, dnr
);
1690 else if(strpbrk(dnr
->chan_name
, "*?"))
1691 dict_insert(mask_dnrs
, dnr
->chan_name
, dnr
);
1693 dict_insert(plain_dnrs
, dnr
->chan_name
, dnr
);
1697 static struct dnrList
1698 chanserv_find_dnrs(const char *chan_name
, const char *handle
)
1700 struct dnrList list
;
1702 struct do_not_register
*dnr
;
1704 dnrList_init(&list
);
1705 if(handle
&& (dnr
= dict_find(handle_dnrs
, handle
, NULL
)))
1706 dnrList_append(&list
, dnr
);
1707 if(chan_name
&& (dnr
= dict_find(plain_dnrs
, chan_name
, NULL
)))
1708 dnrList_append(&list
, dnr
);
1710 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1711 if(match_ircglob(chan_name
, iter_key(it
)))
1712 dnrList_append(&list
, iter_data(it
));
1717 chanserv_show_dnrs(struct userNode
*user
, struct svccmd
*cmd
, const char *chan_name
, const char *handle
)
1719 struct dnrList list
;
1720 struct do_not_register
*dnr
;
1722 char buf
[INTERVALLEN
];
1724 list
= chanserv_find_dnrs(chan_name
, handle
);
1725 for(ii
= 0; (ii
< list
.used
) && (ii
< 10); ++ii
)
1727 dnr
= list
.list
[ii
];
1730 strftime(buf
, sizeof(buf
), "%Y %b %d", localtime(&dnr
->set
));
1731 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, buf
, dnr
->setter
, dnr
->reason
);
1734 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1737 reply("CSMSG_MORE_DNRS", list
.used
- ii
);
1742 struct do_not_register
*
1743 chanserv_is_dnr(const char *chan_name
, struct handle_info
*handle
)
1745 struct do_not_register
*dnr
;
1748 if(handle
&& (dnr
= dict_find(handle_dnrs
, handle
->handle
, NULL
)))
1752 if((dnr
= dict_find(plain_dnrs
, chan_name
, NULL
)))
1754 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1755 if(match_ircglob(chan_name
, iter_key(it
)))
1756 return iter_data(it
);
1761 static CHANSERV_FUNC(cmd_noregister
)
1764 struct do_not_register
*dnr
;
1765 char buf
[INTERVALLEN
];
1766 unsigned int matches
;
1772 reply("CSMSG_DNR_SEARCH_RESULTS");
1775 for(it
= dict_first(handle_dnrs
); it
; it
= iter_next(it
))
1777 dnr
= iter_data(it
);
1779 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
1781 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1784 for(it
= dict_first(plain_dnrs
); it
; it
= iter_next(it
))
1786 dnr
= iter_data(it
);
1788 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
1790 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1793 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1795 dnr
= iter_data(it
);
1797 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
1799 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1804 reply("MSG_MATCH_COUNT", matches
);
1806 reply("MSG_NO_MATCHES");
1812 if(!IsChannelName(target
) && (*target
!= '*'))
1814 reply("CSMSG_NOT_DNR", target
);
1820 const char *reason
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
1821 if((*target
== '*') && !get_handle_info(target
+ 1))
1823 reply("MSG_HANDLE_UNKNOWN", target
+ 1);
1826 chanserv_add_dnr(target
, user
->handle_info
->handle
, reason
);
1827 reply("CSMSG_NOREGISTER_CHANNEL", target
);
1831 reply("CSMSG_DNR_SEARCH_RESULTS");
1834 matches
= chanserv_show_dnrs(user
, cmd
, NULL
, target
+ 1);
1836 matches
= chanserv_show_dnrs(user
, cmd
, target
, NULL
);
1838 reply("MSG_NO_MATCHES");
1842 static CHANSERV_FUNC(cmd_allowregister
)
1844 const char *chan_name
= argv
[1];
1846 if((chan_name
[0] == '*') && dict_find(handle_dnrs
, chan_name
+1, NULL
))
1848 dict_remove(handle_dnrs
, chan_name
+1);
1849 reply("CSMSG_DNR_REMOVED", chan_name
);
1851 else if(dict_find(plain_dnrs
, chan_name
, NULL
))
1853 dict_remove(plain_dnrs
, chan_name
);
1854 reply("CSMSG_DNR_REMOVED", chan_name
);
1856 else if(dict_find(mask_dnrs
, chan_name
, NULL
))
1858 dict_remove(mask_dnrs
, chan_name
);
1859 reply("CSMSG_DNR_REMOVED", chan_name
);
1863 reply("CSMSG_NO_SUCH_DNR", chan_name
);
1870 chanserv_get_owned_count(struct handle_info
*hi
)
1872 struct userData
*cList
;
1875 for(owned
=0, cList
=hi
->channels
; cList
; cList
=cList
->u_next
)
1876 if(cList
->access
== UL_OWNER
)
1881 static CHANSERV_FUNC(cmd_register
)
1883 struct handle_info
*handle
;
1884 struct chanData
*cData
;
1885 struct modeNode
*mn
;
1886 char reason
[MAXLEN
];
1888 unsigned int new_channel
, force
=0;
1889 struct do_not_register
*dnr
;
1893 if(channel
->channel_info
)
1895 reply("CSMSG_ALREADY_REGGED", channel
->name
);
1899 if(channel
->bad_channel
)
1901 reply("CSMSG_ILLEGAL_CHANNEL", channel
->name
);
1906 && (!(mn
= GetUserMode(channel
, user
)) || !(mn
->modes
& MODE_CHANOP
)))
1908 reply("CSMSG_MUST_BE_OPPED", channel
->name
);
1913 chan_name
= channel
->name
;
1919 reply("MSG_MISSING_PARAMS", cmd
->name
);
1920 svccmd_send_help_brief(user
, chanserv
, cmd
);
1923 if(!IsChannelName(argv
[1]))
1925 reply("MSG_NOT_CHANNEL_NAME");
1929 if(opserv_bad_channel(argv
[1]))
1931 reply("CSMSG_ILLEGAL_CHANNEL", argv
[1]);
1936 chan_name
= argv
[1];
1939 if(argc
>= (new_channel
+2))
1941 if(!IsHelping(user
))
1943 reply("CSMSG_PROXY_FORBIDDEN");
1947 if(!(handle
= modcmd_get_handle_info(user
, argv
[new_channel
+1])))
1949 force
= (argc
> (new_channel
+2)) && !irccasecmp(argv
[new_channel
+2], "force");
1950 dnr
= chanserv_is_dnr(chan_name
, handle
);
1954 handle
= user
->handle_info
;
1955 dnr
= chanserv_is_dnr(chan_name
, handle
);
1959 if(!IsHelping(user
))
1960 reply("CSMSG_DNR_CHANNEL", chan_name
);
1962 chanserv_show_dnrs(user
, cmd
, chan_name
, handle
->handle
);
1966 if((chanserv_get_owned_count(handle
) >= chanserv_conf
.max_owned
) && !force
)
1968 reply("CSMSG_OWN_TOO_MANY", handle
->handle
, chanserv_conf
.max_owned
);
1973 channel
= AddChannel(argv
[1], now
, NULL
, NULL
, NULL
);
1975 cData
= register_channel(channel
, user
->handle_info
->handle
);
1976 scan_user_presence(add_channel_user(cData
, handle
, UL_OWNER
, 0, NULL
), NULL
);
1977 cData
->modes
= chanserv_conf
.default_modes
;
1979 cData
->modes
.modes_set
|= MODE_REGISTERED
;
1980 if (IsOffChannel(cData
))
1982 mod_chanmode_announce(chanserv
, channel
, &cData
->modes
);
1986 struct mod_chanmode
*change
= mod_chanmode_dup(&cData
->modes
, 1);
1987 change
->args
[change
->argc
].mode
= MODE_CHANOP
;
1988 change
->args
[change
->argc
].u
.member
= AddChannelUser(chanserv
, channel
);
1990 mod_chanmode_announce(chanserv
, channel
, change
);
1991 mod_chanmode_free(change
);
1994 /* Initialize the channel's max user record. */
1995 cData
->max
= channel
->members
.used
;
1997 if(handle
!= user
->handle_info
)
1998 reply("CSMSG_PROXY_SUCCESS", handle
->handle
, channel
->name
);
2000 reply("CSMSG_REG_SUCCESS", channel
->name
);
2002 sprintf(reason
, "%s registered to %s by %s.", channel
->name
, handle
->handle
, user
->handle_info
->handle
);
2003 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
2008 make_confirmation_string(struct userData
*uData
)
2010 static char strbuf
[16];
2015 for(src
= uData
->handle
->handle
; *src
; )
2016 accum
= accum
* 31 + toupper(*src
++);
2018 for(src
= uData
->channel
->channel
->name
; *src
; )
2019 accum
= accum
* 31 + toupper(*src
++);
2020 sprintf(strbuf
, "%08x", accum
);
2024 static CHANSERV_FUNC(cmd_unregister
)
2027 char reason
[MAXLEN
];
2028 struct chanData
*cData
;
2029 struct userData
*uData
;
2031 cData
= channel
->channel_info
;
2034 reply("CSMSG_NOT_REGISTERED", channel
->name
);
2038 uData
= GetChannelUser(cData
, user
->handle_info
);
2039 if(!uData
|| (uData
->access
< UL_OWNER
))
2041 reply("CSMSG_NO_ACCESS");
2045 if(IsProtected(cData
))
2047 reply("CSMSG_UNREG_NODELETE", channel
->name
);
2051 if(!IsHelping(user
))
2053 const char *confirm_string
;
2054 if(IsSuspended(cData
))
2056 reply("CSMSG_CHAN_SUSPENDED", channel
->name
, cData
->suspended
->reason
);
2059 confirm_string
= make_confirmation_string(uData
);
2060 if((argc
< 2) || strcmp(argv
[1], confirm_string
))
2062 reply("CSMSG_CONFIRM_UNREG", confirm_string
);
2067 sprintf(reason
, "unregistered by %s.", user
->handle_info
->handle
);
2068 name
= strdup(channel
->name
);
2069 unregister_channel(cData
, reason
);
2070 reply("CSMSG_UNREG_SUCCESS", name
);
2075 static CHANSERV_FUNC(cmd_move
)
2077 struct mod_chanmode change
;
2078 struct chanNode
*target
;
2079 struct modeNode
*mn
;
2080 struct userData
*uData
;
2081 char reason
[MAXLEN
];
2082 struct do_not_register
*dnr
;
2086 if(IsProtected(channel
->channel_info
))
2088 reply("CSMSG_MOVE_NODELETE", channel
->name
);
2092 if(!IsChannelName(argv
[1]))
2094 reply("MSG_NOT_CHANNEL_NAME");
2098 if(opserv_bad_channel(argv
[1]))
2100 reply("CSMSG_ILLEGAL_CHANNEL", argv
[1]);
2104 if(!IsHelping(user
) || (argc
< 3) || irccasecmp(argv
[2], "force"))
2106 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
2108 if((uData
->access
== UL_OWNER
) && (dnr
= chanserv_is_dnr(argv
[1], uData
->handle
)))
2110 if(!IsHelping(user
))
2111 reply("CSMSG_DNR_CHANNEL_MOVE", argv
[1]);
2113 chanserv_show_dnrs(user
, cmd
, argv
[1], uData
->handle
->handle
);
2119 mod_chanmode_init(&change
);
2120 if(!(target
= GetChannel(argv
[1])))
2122 target
= AddChannel(argv
[1], now
, NULL
, NULL
, NULL
);
2123 if(!IsSuspended(channel
->channel_info
))
2124 AddChannelUser(chanserv
, target
);
2126 else if(target
->channel_info
)
2128 reply("CSMSG_ALREADY_REGGED", target
->name
);
2131 else if((!(mn
= GetUserMode(target
, user
)) || !(mn
->modes
&& MODE_CHANOP
))
2132 && !IsHelping(user
))
2134 reply("CSMSG_MUST_BE_OPPED", target
->name
);
2137 else if(!IsSuspended(channel
->channel_info
))
2140 change
.args
[0].mode
= MODE_CHANOP
;
2141 change
.args
[0].u
.member
= AddChannelUser(chanserv
, target
);
2142 mod_chanmode_announce(chanserv
, target
, &change
);
2147 /* Clear MODE_REGISTERED from old channel, add it to new. */
2149 change
.modes_clear
= MODE_REGISTERED
;
2150 mod_chanmode_announce(chanserv
, channel
, &change
);
2151 change
.modes_clear
= 0;
2152 change
.modes_set
= MODE_REGISTERED
;
2153 mod_chanmode_announce(chanserv
, target
, &change
);
2156 /* Move the channel_info to the target channel; it
2157 shouldn't be necessary to clear timeq callbacks
2158 for the old channel. */
2159 target
->channel_info
= channel
->channel_info
;
2160 target
->channel_info
->channel
= target
;
2161 channel
->channel_info
= NULL
;
2163 reply("CSMSG_MOVE_SUCCESS", target
->name
);
2165 sprintf(reason
, "%s moved to %s by %s.", channel
->name
, target
->name
, user
->handle_info
->handle
);
2166 if(!IsSuspended(target
->channel_info
))
2168 char reason2
[MAXLEN
];
2169 sprintf(reason2
, "Channel moved to %s by %s.", target
->name
, user
->handle_info
->handle
);
2170 DelChannelUser(chanserv
, channel
, reason2
, 0);
2172 UnlockChannel(channel
);
2173 LockChannel(target
);
2174 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
2179 merge_users(struct chanData
*source
, struct chanData
*target
)
2181 struct userData
*suData
, *tuData
, *next
;
2187 /* Insert the source's users into the scratch area. */
2188 for(suData
= source
->users
; suData
; suData
= suData
->next
)
2189 dict_insert(merge
, suData
->handle
->handle
, suData
);
2191 /* Iterate through the target's users, looking for
2192 users common to both channels. The lower access is
2193 removed from either the scratch area or target user
2195 for(tuData
= target
->users
; tuData
; tuData
= next
)
2197 struct userData
*choice
;
2199 next
= tuData
->next
;
2201 /* If a source user exists with the same handle as a target
2202 channel's user, resolve the conflict by removing one. */
2203 suData
= dict_find(merge
, tuData
->handle
->handle
, NULL
);
2207 /* Pick the data we want to keep. */
2208 /* If the access is the same, use the later seen time. */
2209 if(suData
->access
== tuData
->access
)
2210 choice
= (suData
->seen
> tuData
->seen
) ? suData
: tuData
;
2211 else /* Otherwise, keep the higher access level. */
2212 choice
= (suData
->access
> tuData
->access
) ? suData
: tuData
;
2214 /* Remove the user that wasn't picked. */
2215 if(choice
== tuData
)
2217 dict_remove(merge
, suData
->handle
->handle
);
2218 del_channel_user(suData
, 0);
2221 del_channel_user(tuData
, 0);
2224 /* Move the remaining users to the target channel. */
2225 for(it
= dict_first(merge
); it
; it
= iter_next(it
))
2227 suData
= iter_data(it
);
2229 /* Insert the user into the target channel's linked list. */
2230 suData
->prev
= NULL
;
2231 suData
->next
= target
->users
;
2232 suData
->channel
= target
;
2235 target
->users
->prev
= suData
;
2236 target
->users
= suData
;
2238 /* Update the user counts for the target channel; the
2239 source counts are left alone. */
2240 target
->userCount
++;
2243 /* Possible to assert (source->users == NULL) here. */
2244 source
->users
= NULL
;
2249 merge_bans(struct chanData
*source
, struct chanData
*target
)
2251 struct banData
*sbData
, *tbData
, *sNext
, *tNext
, *tFront
;
2253 /* Hold on to the original head of the target ban list
2254 to avoid comparing source bans with source bans. */
2255 tFront
= target
->bans
;
2257 /* Perform a totally expensive O(n*m) merge, ick. */
2258 for(sbData
= source
->bans
; sbData
; sbData
= sNext
)
2260 /* Flag to track whether the ban's been moved
2261 to the destination yet. */
2264 /* Possible to assert (sbData->prev == NULL) here. */
2265 sNext
= sbData
->next
;
2267 for(tbData
= tFront
; tbData
; tbData
= tNext
)
2269 tNext
= tbData
->next
;
2271 /* Perform two comparisons between each source
2272 and target ban, conflicts are resolved by
2273 keeping the broader ban and copying the later
2274 expiration and triggered time. */
2275 if(match_ircglobs(tbData
->mask
, sbData
->mask
))
2277 /* There is a broader ban in the target channel that
2278 overrides one in the source channel; remove the
2279 source ban and break. */
2280 if(sbData
->expires
> tbData
->expires
)
2281 tbData
->expires
= sbData
->expires
;
2282 if(sbData
->triggered
> tbData
->triggered
)
2283 tbData
->triggered
= sbData
->triggered
;
2284 del_channel_ban(sbData
);
2287 else if(match_ircglobs(sbData
->mask
, tbData
->mask
))
2289 /* There is a broader ban in the source channel that
2290 overrides one in the target channel; remove the
2291 target ban, fall through and move the source over. */
2292 if(tbData
->expires
> sbData
->expires
)
2293 sbData
->expires
= tbData
->expires
;
2294 if(tbData
->triggered
> sbData
->triggered
)
2295 sbData
->triggered
= tbData
->triggered
;
2296 if(tbData
== tFront
)
2298 del_channel_ban(tbData
);
2301 /* Source bans can override multiple target bans, so
2302 we allow a source to run through this loop multiple
2303 times, but we can only move it once. */
2308 /* Remove the source ban from the source ban list. */
2310 sbData
->next
->prev
= sbData
->prev
;
2312 /* Modify the source ban's associated channel. */
2313 sbData
->channel
= target
;
2315 /* Insert the ban into the target channel's linked list. */
2316 sbData
->prev
= NULL
;
2317 sbData
->next
= target
->bans
;
2320 target
->bans
->prev
= sbData
;
2321 target
->bans
= sbData
;
2323 /* Update the user counts for the target channel. */
2328 /* Possible to assert (source->bans == NULL) here. */
2329 source
->bans
= NULL
;
2333 merge_data(struct chanData
*source
, struct chanData
*target
)
2335 if(source
->visited
> target
->visited
)
2336 target
->visited
= source
->visited
;
2340 merge_channel(struct chanData
*source
, struct chanData
*target
)
2342 merge_users(source
, target
);
2343 merge_bans(source
, target
);
2344 merge_data(source
, target
);
2347 static CHANSERV_FUNC(cmd_merge
)
2349 struct userData
*target_user
;
2350 struct chanNode
*target
;
2351 char reason
[MAXLEN
];
2355 /* Make sure the target channel exists and is registered to the user
2356 performing the command. */
2357 if(!(target
= GetChannel(argv
[1])))
2359 reply("MSG_INVALID_CHANNEL");
2363 if(!target
->channel_info
)
2365 reply("CSMSG_NOT_REGISTERED", target
->name
);
2369 if(IsProtected(channel
->channel_info
))
2371 reply("CSMSG_MERGE_NODELETE");
2375 if(IsSuspended(target
->channel_info
))
2377 reply("CSMSG_MERGE_SUSPENDED");
2381 if(channel
== target
)
2383 reply("CSMSG_MERGE_SELF");
2387 target_user
= GetChannelUser(target
->channel_info
, user
->handle_info
);
2388 if(!target_user
|| (target_user
->access
< UL_OWNER
))
2390 reply("CSMSG_MERGE_NOT_OWNER");
2394 /* Merge the channel structures and associated data. */
2395 merge_channel(channel
->channel_info
, target
->channel_info
);
2396 sprintf(reason
, "merged into %s by %s.", target
->name
, user
->handle_info
->handle
);
2397 unregister_channel(channel
->channel_info
, reason
);
2398 reply("CSMSG_MERGE_SUCCESS", target
->name
);
2402 static CHANSERV_FUNC(cmd_opchan
)
2404 struct mod_chanmode change
;
2405 if(!IsHelping(user
) && !channel
->channel_info
->may_opchan
)
2407 reply("CSMSG_ALREADY_OPCHANNED", channel
->name
);
2410 channel
->channel_info
->may_opchan
= 0;
2411 mod_chanmode_init(&change
);
2413 change
.args
[0].mode
= MODE_CHANOP
;
2414 change
.args
[0].u
.member
= GetUserMode(channel
, chanserv
);
2415 mod_chanmode_announce(chanserv
, channel
, &change
);
2416 reply("CSMSG_OPCHAN_DONE", channel
->name
);
2420 static CHANSERV_FUNC(cmd_adduser
)
2422 struct userData
*actee
;
2423 struct userData
*actor
;
2424 struct handle_info
*handle
;
2425 unsigned short access
;
2429 if(channel
->channel_info
->userCount
>= chanserv_conf
.max_chan_users
)
2431 reply("CSMSG_MAXIMUM_USERS", chanserv_conf
.max_chan_users
);
2435 access
= user_level_from_name(argv
[2], UL_OWNER
);
2438 reply("CSMSG_INVALID_ACCESS", argv
[2]);
2442 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2443 if(actor
->access
<= access
)
2445 reply("CSMSG_NO_BUMP_ACCESS");
2449 if(!(handle
= modcmd_get_handle_info(user
, argv
[1])))
2451 // 'kevin must first authenticate with AuthServ.' is sent to user
2452 struct userNode
*unode
;
2453 unode
= GetUserH(argv
[1]); /* find user struct by nick */
2456 if(find_adduser_pending(channel
, unode
)) {
2457 reply("CSMSG_ADDUSER_PENDING_ALREADY", channel
->name
);
2460 if(IsInChannel(channel
, unode
)) {
2461 reply("CSMSG_ADDUSER_PENDING");
2462 add_adduser_pending(channel
, unode
, access
);
2463 send_message_type(1,unode
, chanserv
, "CSMSG_ADDUSER_PENDING_TARGET", user
->nick
, channel
->name
);
2465 /* this results in user must auth AND not in chan errors. too confusing..
2467 reply("CSMSG_ADDUSER_PENDING_NOTINCHAN", channel->name);
2475 if((actee
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2477 reply("CSMSG_USER_EXISTS", handle
->handle
, channel
->name
, user_level_name_from_level(actee
->access
));
2481 actee
= add_channel_user(channel
->channel_info
, handle
, access
, 0, NULL
);
2482 scan_user_presence(actee
, NULL
);
2483 reply("CSMSG_ADDED_USER", handle
->handle
, channel
->name
, user_level_name_from_level(access
), access
);
2487 static CHANSERV_FUNC(cmd_clvl
)
2489 struct handle_info
*handle
;
2490 struct userData
*victim
;
2491 struct userData
*actor
;
2492 unsigned short new_access
;
2493 int privileged
= IsHelping(user
) && ((user
->handle_info
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
->channel_info
));
2497 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2499 if(!(handle
= modcmd_get_handle_info(user
, argv
[1])))
2502 if(handle
== user
->handle_info
&& !privileged
)
2504 reply("CSMSG_NO_SELF_CLVL");
2508 if(!(victim
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2510 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
2514 if(actor
->access
<= victim
->access
&& !privileged
)
2516 reply("MSG_USER_OUTRANKED", handle
->handle
);
2520 new_access
= user_level_from_name(argv
[2], UL_OWNER
);
2524 reply("CSMSG_INVALID_ACCESS", argv
[2]);
2528 if(new_access
>= actor
->access
&& !privileged
)
2530 reply("CSMSG_NO_BUMP_ACCESS");
2534 victim
->access
= new_access
;
2535 reply("CSMSG_CHANGED_ACCESS", handle
->handle
, user_level_name_from_level(new_access
), new_access
, channel
->name
);
2539 static CHANSERV_FUNC(cmd_deluser
)
2541 struct handle_info
*handle
;
2542 struct userData
*victim
;
2543 struct userData
*actor
;
2544 unsigned short access
;
2549 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2551 if(!(handle
= modcmd_get_handle_info(user
, argv
[argc
-1])))
2554 if(!(victim
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2556 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
2562 access
= user_level_from_name(argv
[1], UL_OWNER
);
2565 reply("CSMSG_INVALID_ACCESS", argv
[1]);
2568 if(access
!= victim
->access
)
2570 reply("CSMSG_INCORRECT_ACCESS", handle
->handle
, user_level_name_from_level(victim
->access
), argv
[1]);
2576 access
= victim
->access
;
2579 if((actor
->access
<= victim
->access
) && !IsHelping(user
))
2581 reply("MSG_USER_OUTRANKED", victim
->handle
->handle
);
2585 chan_name
= strdup(channel
->name
);
2586 del_channel_user(victim
, 1);
2587 reply("CSMSG_DELETED_USER", handle
->handle
, access
, chan_name
);
2593 cmd_mdel_user(struct userNode
*user
, struct chanNode
*channel
, unsigned short min_access
, unsigned short max_access
, char *mask
, struct svccmd
*cmd
)
2595 struct userData
*actor
, *uData
, *next
;
2597 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2599 if(min_access
> max_access
)
2601 reply("CSMSG_BAD_RANGE", min_access
, max_access
);
2605 if((actor
->access
<= max_access
) && !IsHelping(user
))
2607 reply("CSMSG_NO_ACCESS");
2611 for(uData
= channel
->channel_info
->users
; uData
; uData
= next
)
2615 if((uData
->access
>= min_access
)
2616 && (uData
->access
<= max_access
)
2617 && match_ircglob(uData
->handle
->handle
, mask
))
2618 del_channel_user(uData
, 1);
2621 reply("CSMSG_DELETED_USERS", mask
, min_access
, max_access
, channel
->name
);
2625 static CHANSERV_FUNC(cmd_mdelowner
)
2627 return cmd_mdel_user(user
, channel
, UL_OWNER
, UL_OWNER
, argv
[1], cmd
);
2630 static CHANSERV_FUNC(cmd_mdelcoowner
)
2632 return cmd_mdel_user(user
, channel
, UL_COOWNER
, UL_COOWNER
, argv
[1], cmd
);
2635 static CHANSERV_FUNC(cmd_mdelmanager
)
2637 return cmd_mdel_user(user
, channel
, UL_MANAGER
, UL_MANAGER
, argv
[1], cmd
);
2640 static CHANSERV_FUNC(cmd_mdelop
)
2642 return cmd_mdel_user(user
, channel
, UL_OP
, UL_OP
, argv
[1], cmd
);
2645 static CHANSERV_FUNC(cmd_mdelpeon
)
2647 return cmd_mdel_user(user
, channel
, UL_PEON
, UL_PEON
, argv
[1], cmd
);
2650 static CHANSERV_FUNC(cmd_mdelhalfop
)
2652 return cmd_mdel_user(user
, channel
, UL_HALFOP
, UL_HALFOP
, argv
[1], cmd
);
2658 cmd_trim_bans(struct userNode
*user
, struct chanNode
*channel
, unsigned long duration
)
2660 struct banData
*bData
, *next
;
2661 char interval
[INTERVALLEN
];
2666 limit
= now
- duration
;
2667 for(bData
= channel
->channel_info
->bans
; bData
; bData
= next
)
2671 if((bData
->triggered
&& bData
->triggered
>= limit
) || (bData
->set
&& bData
->set
>= limit
))
2674 del_channel_ban(bData
);
2678 intervalString(interval
, duration
, user
->handle_info
);
2679 send_message(user
, chanserv
, "CSMSG_TRIMMED_LAMERS", count
, channel
->name
, interval
);
2684 cmd_trim_users(struct userNode
*user
, struct chanNode
*channel
, unsigned short min_access
, unsigned short max_access
, unsigned long duration
)
2686 struct userData
*actor
, *uData
, *next
;
2687 char interval
[INTERVALLEN
];
2691 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2692 if(min_access
> max_access
)
2694 send_message(user
, chanserv
, "CSMSG_BAD_RANGE", min_access
, max_access
);
2698 if((actor
->access
<= max_access
) && !IsHelping(user
))
2700 send_message(user
, chanserv
, "CSMSG_NO_ACCESS");
2705 limit
= now
- duration
;
2706 for(uData
= channel
->channel_info
->users
; uData
; uData
= next
)
2710 if((uData
->seen
> limit
) || uData
->present
)
2713 if(((uData
->access
>= min_access
) && (uData
->access
<= max_access
))
2714 || (!max_access
&& (uData
->access
< actor
->access
)))
2716 del_channel_user(uData
, 1);
2724 max_access
= (actor
->access
> UL_OWNER
) ? UL_OWNER
: (actor
->access
- 1);
2726 send_message(user
, chanserv
, "CSMSG_TRIMMED_USERS", count
, min_access
, max_access
, channel
->name
, intervalString(interval
, duration
, user
->handle_info
));
2730 static CHANSERV_FUNC(cmd_trim
)
2732 unsigned long duration
;
2733 unsigned short min_level
, max_level
;
2737 duration
= ParseInterval(argv
[2]);
2740 reply("CSMSG_CANNOT_TRIM");
2744 if(!irccasecmp(argv
[1], "lamers"))
2746 cmd_trim_bans(user
, channel
, duration
); /* trim_lamers.. */
2749 else if(!irccasecmp(argv
[1], "users"))
2751 cmd_trim_users(user
, channel
, 0, 0, duration
);
2754 else if(parse_level_range(&min_level
, &max_level
, argv
[1]))
2756 cmd_trim_users(user
, channel
, min_level
, max_level
, duration
);
2759 else if((min_level
= user_level_from_name(argv
[1], UL_OWNER
)))
2761 cmd_trim_users(user
, channel
, min_level
, min_level
, duration
);
2766 reply("CSMSG_INVALID_TRIM", argv
[1]);
2771 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
2772 to the user. cmd_all takes advantage of this. */
2773 static CHANSERV_FUNC(cmd_up
)
2775 struct mod_chanmode change
;
2776 struct userData
*uData
;
2779 mod_chanmode_init(&change
);
2781 change
.args
[0].u
.member
= GetUserMode(channel
, user
);
2782 if(!change
.args
[0].u
.member
)
2785 reply("MSG_CHANNEL_ABSENT", channel
->name
);
2789 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
2793 reply("CSMSG_GODMODE_UP", argv
[0]);
2796 else if(uData
->access
>= UL_OP
/*channel->channel_info->lvlOpts[lvlGiveOps]*/)
2798 change
.args
[0].mode
= MODE_CHANOP
;
2799 errmsg
= "CSMSG_ALREADY_OPPED";
2801 else if(uData
->access
>= UL_HALFOP
/*channel->channel_info->lvlOpts[lvlGiveHalfOps]*/)
2803 change
.args
[0].mode
= MODE_HALFOP
;
2804 errmsg
= "CSMSG_ALREADY_HALFOPPED";
2806 else if(uData
->access
>= UL_PEON
&& (channel
->channel_info
->chOpts
[chVoice
] == 'p' || channel
->channel_info
->chOpts
[chVoice
] == 'a'))
2808 change
.args
[0].mode
= MODE_VOICE
;
2809 errmsg
= "CSMSG_ALREADY_VOICED";
2814 reply("CSMSG_NO_ACCESS");
2817 change
.args
[0].mode
&= ~change
.args
[0].u
.member
->modes
;
2818 if(!change
.args
[0].mode
)
2821 reply(errmsg
, channel
->name
);
2824 modcmd_chanmode_announce(&change
);
2828 static CHANSERV_FUNC(cmd_down
)
2830 struct mod_chanmode change
;
2832 mod_chanmode_init(&change
);
2834 change
.args
[0].u
.member
= GetUserMode(channel
, user
);
2835 if(!change
.args
[0].u
.member
)
2838 reply("MSG_CHANNEL_ABSENT", channel
->name
);
2842 if(!change
.args
[0].u
.member
->modes
)
2845 reply("CSMSG_ALREADY_DOWN", channel
->name
);
2849 change
.args
[0].mode
= MODE_REMOVE
| change
.args
[0].u
.member
->modes
;
2850 modcmd_chanmode_announce(&change
);
2854 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
)
2856 struct userData
*cList
;
2858 for(cList
= user
->handle_info
->channels
; cList
; cList
= cList
->u_next
)
2860 if(IsSuspended(cList
->channel
)
2861 || IsUserSuspended(cList
)
2862 || !GetUserMode(cList
->channel
->channel
, user
))
2865 mcmd(user
, cList
->channel
->channel
, 0, NULL
, cmd
);
2871 static CHANSERV_FUNC(cmd_upall
)
2873 return cmd_all(CSFUNC_ARGS
, cmd_up
);
2876 static CHANSERV_FUNC(cmd_downall
)
2878 return cmd_all(CSFUNC_ARGS
, cmd_down
);
2881 typedef int validate_func_t(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
);
2882 typedef void process_func_t(unsigned int num
, struct userNode
**newops
, struct chanNode
*channel
, struct userNode
*who
, int announce
);
2885 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
)
2887 unsigned int ii
, valid
;
2888 struct userNode
*victim
;
2889 struct mod_chanmode
*change
;
2891 change
= mod_chanmode_alloc(argc
- 1);
2893 for(ii
=valid
=0; ++ii
< argc
; )
2895 if(!(victim
= GetUserH(argv
[ii
])))
2897 change
->args
[valid
].mode
= mode
;
2898 change
->args
[valid
].u
.member
= GetUserMode(channel
, victim
);
2899 if(!change
->args
[valid
].u
.member
)
2901 if(validate
&& !validate(user
, channel
, victim
))
2906 change
->argc
= valid
;
2907 if(valid
< (argc
-1))
2908 reply("CSMSG_PROCESS_FAILED");
2911 modcmd_chanmode_announce(change
);
2912 reply(action
, channel
->name
);
2914 mod_chanmode_free(change
);
2918 static CHANSERV_FUNC(cmd_op
)
2920 return modify_users(CSFUNC_ARGS
, validate_op
, MODE_CHANOP
, "CSMSG_OPPED_USERS");
2923 static CHANSERV_FUNC(cmd_hop
)
2925 return modify_users(CSFUNC_ARGS
, validate_halfop
, MODE_HALFOP
, "CSMSG_HALFOPPED_USERS");
2928 static CHANSERV_FUNC(cmd_deop
)
2930 return modify_users(CSFUNC_ARGS
, validate_deop
, MODE_REMOVE
|MODE_CHANOP
, "CSMSG_DEOPPED_USERS");
2933 static CHANSERV_FUNC(cmd_dehop
)
2935 return modify_users(CSFUNC_ARGS
, validate_dehop
, MODE_REMOVE
|MODE_HALFOP
, "CSMSG_DEHALFOPPED_USERS");
2938 static CHANSERV_FUNC(cmd_voice
)
2940 return modify_users(CSFUNC_ARGS
, NULL
, MODE_VOICE
, "CSMSG_VOICED_USERS");
2943 static CHANSERV_FUNC(cmd_devoice
)
2945 return modify_users(CSFUNC_ARGS
, NULL
, MODE_REMOVE
|MODE_VOICE
, "CSMSG_DEVOICED_USERS");
2949 bad_channel_ban(struct chanNode
*channel
, struct userNode
*user
, const char *ban
, int *victimCount
, struct modeNode
**victims
)
2955 for(ii
=0; ii
<channel
->members
.used
; ii
++)
2957 struct modeNode
*mn
= channel
->members
.list
[ii
];
2959 if(IsService(mn
->user
))
2962 if(!user_matches_glob(mn
->user
, ban
, 1))
2965 if(protect_user(mn
->user
, user
, channel
->channel_info
))
2969 victims
[(*victimCount
)++] = mn
;
2975 eject_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
)
2977 struct userNode
*victim
;
2978 struct modeNode
**victims
;
2979 unsigned int offset
, n
, victimCount
, duration
= 0;
2980 char *reason
= "Bye.", *ban
, *name
;
2981 char interval
[INTERVALLEN
];
2983 offset
= (action
& ACTION_ADD_TIMED_LAMER
) ? 3 : 2;
2984 REQUIRE_PARAMS(offset
);
2987 reason
= unsplit_string(argv
+ offset
, argc
- offset
, NULL
);
2988 if(strlen(reason
) > (TOPICLEN
- (NICKLEN
+ 3)))
2990 /* Truncate the reason to a length of TOPICLEN, as
2991 the ircd does; however, leave room for an ellipsis
2992 and the kicker's nick. */
2993 sprintf(reason
+ (TOPICLEN
- (NICKLEN
+ 6)), "...");
2997 if((victim
= GetUserH(argv
[1])))
2999 victims
= alloca(sizeof(victims
[0]));
3000 victims
[0] = GetUserMode(channel
, victim
);
3001 /* XXX: The comparison with ACTION_KICK is just because all
3002 * other actions can work on users outside the channel, and we
3003 * want to allow those (e.g. unbans) in that case. If we add
3004 * some other ejection action for in-channel users, change
3006 victimCount
= victims
[0] ? 1 : 0;
3008 if(IsService(victim
))
3010 reply("MSG_SERVICE_IMMUNE", victim
->nick
);
3014 if((action
== ACTION_KICK
) && !victimCount
)
3016 reply("MSG_CHANNEL_USER_ABSENT", victim
->nick
, channel
->name
);
3020 if(protect_user(victim
, user
, channel
->channel_info
))
3022 reply("CSMSG_USER_PROTECTED", victim
->nick
);
3026 ban
= generate_hostmask(victim
, GENMASK_STRICT_HOST
|GENMASK_ANY_IDENT
);
3027 name
= victim
->nick
;
3031 if(!is_ircmask(argv
[1]))
3033 reply("MSG_NICK_UNKNOWN", argv
[1]);
3037 victims
= alloca(sizeof(victims
[0]) * channel
->members
.used
);
3039 if(bad_channel_ban(channel
, user
, argv
[1], &victimCount
, victims
))
3041 reply("CSMSG_MASK_PROTECTED", argv
[1]);
3044 /* We dont actually want a victem count if were banning a mask manually, IMO -Rubin*/
3046 victimCount
= 0; /* Dont deop etc ppl who match this */
3048 #ifdef entropy_lameness
3049 if((victimCount
> 4) && ((victimCount
* 3) > channel
->members
.used
) && !IsOper(user
))
3051 reply("CSMSG_LAME_MASK", argv
[1]);
3056 if((action
== ACTION_KICK
) && (victimCount
== 0))
3058 reply("CSMSG_NO_MATCHING_USERS", channel
->name
, argv
[1]);
3062 name
= ban
= strdup(argv
[1]);
3065 /* Truncate the ban in place if necessary; we must ensure
3066 that 'ban' is a valid ban mask before sanitizing it. */
3067 sanitize_ircmask(ban
);
3069 if(action
& ACTION_ADD_LAMER
)
3071 struct banData
*bData
, *next
;
3073 if(channel
->channel_info
->banCount
>= chanserv_conf
.max_chan_bans
) /* ..lamers.. */
3075 reply("CSMSG_MAXIMUM_LAMERS", chanserv_conf
.max_chan_bans
); /* ..lamers.. */
3080 if(action
& ACTION_ADD_TIMED_LAMER
)
3082 duration
= ParseInterval(argv
[2]);
3086 reply("CSMSG_DURATION_TOO_LOW");
3090 else if(duration
> (86400 * 365 * 2))
3092 reply("CSMSG_DURATION_TOO_HIGH");
3099 for(bData
= channel
->channel_info
->bans
; bData
; bData
= next
)
3101 if(match_ircglobs(bData
->mask
, ban
))
3103 int exact
= !irccasecmp(bData
->mask
, ban
);
3105 /* The ban is redundant; there is already a ban
3106 with the same effect in place. */
3110 free(bData
->reason
);
3111 bData
->reason
= strdup(reason
);
3112 safestrncpy(bData
->owner
, (user
->handle_info
? user
->handle_info
->handle
: user
->nick
), sizeof(bData
->owner
));
3114 reply("CSMSG_REASON_CHANGE", ban
);
3118 if(exact
&& bData
->expires
)
3122 /* If the ban matches an existing one exactly,
3123 extend the expiration time if the provided
3124 duration is longer. */
3125 if(duration
&& ((time_t)(now
+ duration
) > bData
->expires
))
3127 bData
->expires
= now
+ duration
;
3138 /* Delete the expiration timeq entry and
3139 requeue if necessary. */
3140 timeq_del(0, expire_ban
, bData
, TIMEQ_IGNORE_WHEN
);
3143 timeq_add(bData
->expires
, expire_ban
, bData
);
3147 /* automated kickban */
3150 reply("CSMSG_LAMER_EXTENDED", ban
, intervalString(interval
, duration
, user
->handle_info
));
3152 reply("CSMSG_LAMER_ADDED", name
, channel
->name
);
3158 reply("CSMSG_REDUNDANT_LAMER", name
, channel
->name
);
3165 if(match_ircglobs(ban
, bData
->mask
))
3167 /* The ban we are adding makes previously existing
3168 bans redundant; silently remove them. */
3169 del_channel_ban(bData
);
3173 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
);
3175 name
= ban
= strdup(bData
->mask
);
3179 /* WHAT DOES THIS DO?? -Rubin */
3180 for(n
= 0; n
< chanserv_conf
.old_ban_names
->used
; ++n
)
3182 extern const char *hidden_host_suffix
;
3183 const char *old_name
= chanserv_conf
.old_ban_names
->list
[n
];
3185 unsigned int l1
, l2
;
3188 l2
= strlen(old_name
);
3191 if(irccasecmp(ban
+ l1
- l2
, old_name
))
3193 new_mask
= malloc(MAXLEN
);
3194 sprintf(new_mask
, "%.*s%s", (int)(l1
-l2
), ban
, hidden_host_suffix
);
3196 name
= ban
= new_mask
;
3201 if(action
& ACTION_BAN
)
3203 unsigned int exists
;
3204 struct mod_chanmode
*change
;
3206 if(channel
->banlist
.used
>= MAXBANS
)
3209 reply("CSMSG_BANLIST_FULL", channel
->name
);
3214 exists
= ChannelBanExists(channel
, ban
);
3215 change
= mod_chanmode_alloc(victimCount
+ 1);
3216 for(n
= 0; n
< victimCount
; ++n
)
3218 change
->args
[n
].mode
= MODE_REMOVE
|MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
;
3219 change
->args
[n
].u
.member
= victims
[n
];
3223 change
->args
[n
].mode
= MODE_BAN
;
3224 change
->args
[n
++].u
.hostmask
= ban
;
3228 modcmd_chanmode_announce(change
);
3230 mod_chanmode_announce(chanserv
, channel
, change
);
3231 mod_chanmode_free(change
);
3233 if(exists
&& (action
== ACTION_BAN
))
3236 reply("CSMSG_REDUNDANT_BAN", name
, channel
->name
);
3242 if(action
& ACTION_KICK
)
3244 char kick_reason
[MAXLEN
];
3245 sprintf(kick_reason
, "(%s) %s", user
->nick
, reason
);
3247 for(n
= 0; n
< victimCount
; n
++)
3248 KickChannelUser(victims
[n
]->user
, channel
, chanserv
, kick_reason
);
3253 /* No response, since it was automated. */
3255 else if(action
& ACTION_ADD_LAMER
)
3258 reply("CSMSG_TIMED_LAMER_ADDED", name
, channel
->name
, intervalString(interval
, duration
, user
->handle_info
));
3260 reply("CSMSG_LAMER_ADDED", name
, channel
->name
);
3262 else if((action
& (ACTION_BAN
| ACTION_KICK
)) == (ACTION_BAN
| ACTION_KICK
))
3263 reply("CSMSG_KICK_BAN_DONE", name
, channel
->name
);
3264 else if(action
& ACTION_BAN
)
3265 reply("CSMSG_BAN_DONE", name
, channel
->name
);
3266 else if(action
& ACTION_KICK
&& victimCount
)
3267 reply("CSMSG_KICK_DONE", name
, channel
->name
);
3273 static CHANSERV_FUNC(cmd_kickban
)
3275 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
);
3278 static CHANSERV_FUNC(cmd_kick
)
3280 return eject_user(CSFUNC_ARGS
, ACTION_KICK
);
3283 static CHANSERV_FUNC(cmd_ban
)
3285 return eject_user(CSFUNC_ARGS
, ACTION_BAN
);
3288 static CHANSERV_FUNC(cmd_addlamer
)
3290 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
| ACTION_ADD_LAMER
);
3293 static CHANSERV_FUNC(cmd_addtimedlamer
)
3295 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
);
3298 static struct mod_chanmode
*
3299 find_matching_bans(struct banList
*bans
, struct userNode
*actee
, const char *mask
)
3301 struct mod_chanmode
*change
;
3302 unsigned char *match
;
3303 unsigned int ii
, count
;
3305 match
= alloca(bans
->used
);
3308 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3310 match
[ii
] = user_matches_glob(actee
, bans
->list
[ii
]->ban
, 1);
3317 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3319 match
[ii
] = match_ircglobs(mask
, bans
->list
[ii
]->ban
);
3326 change
= mod_chanmode_alloc(count
);
3327 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3331 change
->args
[count
].mode
= MODE_REMOVE
| MODE_BAN
;
3332 change
->args
[count
++].u
.hostmask
= strdup(bans
->list
[ii
]->ban
);
3334 assert(count
== change
->argc
);
3339 unban_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
)
3341 struct userNode
*actee
;
3347 /* may want to allow a comma delimited list of users... */
3348 if(!(actee
= GetUserH(argv
[1])))
3350 if(!is_ircmask(argv
[1]))
3352 reply("MSG_NICK_UNKNOWN", argv
[1]);
3356 mask
= strdup(argv
[1]);
3359 /* We don't sanitize the mask here because ircu
3361 if(action
& ACTION_UNBAN
)
3363 struct mod_chanmode
*change
;
3364 change
= find_matching_bans(&channel
->banlist
, actee
, mask
);
3369 modcmd_chanmode_announce(change
);
3370 for(ii
= 0; ii
< change
->argc
; ++ii
)
3371 free((char*)change
->args
[ii
].u
.hostmask
);
3372 mod_chanmode_free(change
);
3377 if(action
& ACTION_DEL_LAMER
)
3379 struct banData
*ban
, *next
;
3381 ban
= channel
->channel_info
->bans
; /* lamers */
3385 for( ; ban
&& !user_matches_glob(actee
, ban
->mask
, 1);
3388 for( ; ban
&& !match_ircglobs(mask
, ban
->mask
);
3393 del_channel_ban(ban
);
3400 reply("CSMSG_BAN_NOT_FOUND", actee
? actee
->nick
: mask
);
3402 reply("CSMSG_BAN_REMOVED", actee
? actee
->nick
: mask
);
3408 static CHANSERV_FUNC(cmd_unban
)
3410 return unban_user(CSFUNC_ARGS
, ACTION_UNBAN
);
3413 static CHANSERV_FUNC(cmd_dellamer
)
3415 /* it doesn't necessarily have to remove the channel ban - may want
3416 to make that an option. */
3417 return unban_user(CSFUNC_ARGS
, ACTION_UNBAN
| ACTION_DEL_LAMER
);
3420 static CHANSERV_FUNC(cmd_unbanme
)
3422 struct userData
*uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
3423 long flags
= ACTION_UNBAN
;
3425 /* remove permanent bans if the user has the proper access. */
3426 if(uData
->access
>= UL_MANAGER
)
3427 flags
|= ACTION_DEL_LAMER
;
3429 argv
[1] = user
->nick
;
3430 return unban_user(user
, channel
, 2, argv
, cmd
, flags
);
3433 static CHANSERV_FUNC(cmd_unbanall
)
3435 struct mod_chanmode
*change
;
3438 if(!channel
->banlist
.used
)
3440 reply("CSMSG_NO_BANS", channel
->name
);
3444 change
= mod_chanmode_alloc(channel
->banlist
.used
);
3445 for(ii
=0; ii
<channel
->banlist
.used
; ii
++)
3447 change
->args
[ii
].mode
= MODE_REMOVE
| MODE_BAN
;
3448 change
->args
[ii
].u
.hostmask
= strdup(channel
->banlist
.list
[ii
]->ban
);
3450 modcmd_chanmode_announce(change
);
3451 for(ii
= 0; ii
< change
->argc
; ++ii
)
3452 free((char*)change
->args
[ii
].u
.hostmask
);
3453 mod_chanmode_free(change
);
3454 reply("CSMSG_BANS_REMOVED", channel
->name
);
3458 static CHANSERV_FUNC(cmd_open
)
3460 struct mod_chanmode
*change
;
3463 change
= find_matching_bans(&channel
->banlist
, user
, NULL
);
3465 change
= mod_chanmode_alloc(0);
3466 change
->modes_clear
|= MODE_INVITEONLY
| MODE_LIMIT
| MODE_KEY
;
3467 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
3468 && channel
->channel_info
->modes
.modes_set
)
3469 change
->modes_clear
&= ~channel
->channel_info
->modes
.modes_set
;
3470 modcmd_chanmode_announce(change
);
3471 reply("CSMSG_CHANNEL_OPENED", channel
->name
);
3472 for(ii
= 0; ii
< change
->argc
; ++ii
)
3473 free((char*)change
->args
[ii
].u
.hostmask
);
3474 mod_chanmode_free(change
);
3478 static CHANSERV_FUNC(cmd_myaccess
)
3480 static struct string_buffer sbuf
;
3481 struct handle_info
*target_handle
;
3482 struct userData
*uData
;
3485 target_handle
= user
->handle_info
;
3486 else if(!IsHelping(user
))
3488 reply("CSMSG_MYACCESS_SELF_ONLY", argv
[0]);
3491 else if(!(target_handle
= modcmd_get_handle_info(user
, argv
[1])))
3494 if(!target_handle
->channels
)
3496 reply("CSMSG_SQUAT_ACCESS", target_handle
->handle
);
3500 reply("CSMSG_INFOLINE_LIST", target_handle
->handle
);
3501 for(uData
= target_handle
->channels
; uData
; uData
= uData
->u_next
)
3503 struct chanData
*cData
= uData
->channel
;
3505 if(uData
->access
> UL_OWNER
)
3507 if(IsProtected(cData
)
3508 && (target_handle
!= user
->handle_info
)
3509 && !GetTrueChannelAccess(cData
, user
->handle_info
))
3512 string_buffer_append_printf(&sbuf
, "[%s (%d", cData
->channel
->name
, uData
->access
);
3513 if(uData
->flags
== USER_AUTO_OP
)
3514 string_buffer_append(&sbuf
, ',');
3515 if(IsUserSuspended(uData
))
3516 string_buffer_append(&sbuf
, 's');
3517 if(IsUserAutoOp(uData
))
3519 if(uData
->access
>= UL_OP
/*cData->lvlOpts[lvlGiveOps]*/)
3520 string_buffer_append(&sbuf
, 'o');
3521 else if(uData
->access
>= UL_HALFOP
/*cData->lvlOpts[lvlGiveHalfOps]*/)
3522 string_buffer_append(&sbuf
, 'h');
3523 else if(uData
->access
>= UL_PEON
/*cData->lvlOpts[lvlGiveVoice]*/)
3524 string_buffer_append(&sbuf
, 'v');
3526 if(IsUserAutoInvite(uData
) && (uData
->access
>= cData
->lvlOpts
[lvlInviteMe
]))
3527 string_buffer_append(&sbuf
, 'i');
3529 string_buffer_append_printf(&sbuf
, ")] %s", uData
->info
);
3531 string_buffer_append_string(&sbuf
, ")]");
3532 string_buffer_append(&sbuf
, '\0');
3533 send_message_type(4, user
, cmd
->parent
->bot
, "%s", sbuf
.list
);
3539 static CHANSERV_FUNC(cmd_access
)
3541 struct userNode
*target
;
3542 struct handle_info
*target_handle
;
3543 struct userData
*uData
;
3545 char prefix
[MAXLEN
];
3550 target_handle
= target
->handle_info
;
3552 else if((target
= GetUserH(argv
[1])))
3554 target_handle
= target
->handle_info
;
3556 else if(argv
[1][0] == '*')
3558 if(!(target_handle
= get_handle_info(argv
[1]+1)))
3560 reply("MSG_HANDLE_UNKNOWN", argv
[1]+1);
3566 reply("MSG_NICK_UNKNOWN", argv
[1]);
3570 assert(target
|| target_handle
);
3572 if(target
== chanserv
)
3574 reply("CSMSG_IS_CHANSERV");
3582 reply("CSMSG_LAZY_SMURF_TARGET", target
->nick
, chanserv_conf
.irc_operator_epithet
);
3587 reply("MSG_USER_AUTHENTICATE", target
->nick
);
3590 reply("MSG_AUTHENTICATE");
3596 const char *epithet
= NULL
, *type
= NULL
;
3599 epithet
= chanserv_conf
.irc_operator_epithet
;
3602 else if(IsNetworkHelper(target
))
3604 epithet
= chanserv_conf
.network_helper_epithet
;
3605 type
= "network helper";
3607 else if(IsSupportHelper(target
))
3609 epithet
= chanserv_conf
.support_helper_epithet
;
3610 type
= "support helper";
3614 if(target_handle
->epithet
)
3615 reply("CSMSG_SMURF_TARGET", target
->nick
, target_handle
->epithet
, type
);
3617 reply("CSMSG_SMURF_TARGET", target
->nick
, epithet
, type
);
3619 sprintf(prefix
, "%s (%s)", target
->nick
, target_handle
->handle
);
3623 sprintf(prefix
, "%s", target_handle
->handle
);
3626 if(!channel
->channel_info
)
3628 reply("CSMSG_NOT_REGISTERED", channel
->name
);
3632 helping
= HANDLE_FLAGGED(target_handle
, HELPING
)
3633 && ((target_handle
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
->channel_info
));
3634 if((uData
= GetTrueChannelAccess(channel
->channel_info
, target_handle
)))
3636 reply((helping
? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix
, user_level_name_from_level(uData
->access
), uData
->access
, channel
->name
);
3637 /* To prevent possible information leaks, only show infolines
3638 * if the requestor is in the channel or it's their own
3640 if(uData
->info
&& (GetUserMode(channel
, user
) || (target_handle
== user
->handle_info
)))
3642 send_message_type(4, user
, cmd
->parent
->bot
, "[%s] %s", (target
? target
->nick
: target_handle
->handle
), uData
->info
);
3644 /* Likewise, only say it's suspended if the user has active
3645 * access in that channel or it's their own entry. */
3646 if(IsUserSuspended(uData
)
3647 && (GetChannelUser(channel
->channel_info
, user
->handle_info
)
3648 || (user
->handle_info
== uData
->handle
)))
3650 reply("CSMSG_USER_SUSPENDED", (target
? target
->nick
: target_handle
->handle
), channel
->name
);
3655 reply((helping
? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix
, channel
->name
);
3661 /* This is never used... */
3663 zoot_list(struct listData
*list
)
3665 struct userData
*uData
;
3666 unsigned int start
, curr
, highest
, lowest
;
3667 struct helpfile_table tmp_table
;
3668 const char **temp
, *msg
;
3670 if(list
->table
.length
== 1)
3673 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
);
3675 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
));
3676 msg
= user_find_message(list
->user
, "MSG_NONE");
3677 send_message_type(4, list
->user
, list
->bot
, " %s", msg
);
3679 tmp_table
.width
= list
->table
.width
;
3680 tmp_table
.flags
= list
->table
.flags
;
3681 list
->table
.contents
[0][0] = " ";
3682 highest
= list
->highest
;
3683 if(list
->lowest
!= 0)
3684 lowest
= list
->lowest
;
3685 else if(highest
< 100)
3688 lowest
= highest
- 100;
3689 for(start
= curr
= 1; curr
< list
->table
.length
; )
3691 uData
= list
->users
[curr
-1];
3692 list
->table
.contents
[curr
++][0] = " ";
3693 if((curr
== list
->table
.length
) || (list
->users
[curr
-1]->access
< lowest
))
3696 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
);
3698 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
));
3699 temp
= list
->table
.contents
[--start
];
3700 list
->table
.contents
[start
] = list
->table
.contents
[0];
3701 tmp_table
.contents
= list
->table
.contents
+ start
;
3702 tmp_table
.length
= curr
- start
;
3703 table_send(list
->bot
, list
->user
->nick
, 0, NULL
, tmp_table
);
3704 list
->table
.contents
[start
] = temp
;
3706 highest
= lowest
- 1;
3707 lowest
= (highest
< 100) ? 0 : (highest
- 99);
3713 def_list(struct listData
*list
)
3717 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
);
3719 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
));
3720 table_send(list
->bot
, list
->user
->nick
, 0, NULL
, list
->table
);
3721 if(list
->table
.length
== 1)
3723 msg
= user_find_message(list
->user
, "MSG_NONE");
3724 send_message_type(4, list
->user
, list
->bot
, " %s", msg
);
3729 userData_access_comp(const void *arg_a
, const void *arg_b
)
3731 const struct userData
*a
= *(struct userData
**)arg_a
;
3732 const struct userData
*b
= *(struct userData
**)arg_b
;
3734 if(a
->access
!= b
->access
)
3735 res
= b
->access
- a
->access
;
3737 res
= irccasecmp(a
->handle
->handle
, b
->handle
->handle
);
3742 cmd_list_users(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, unsigned short lowest
, unsigned short highest
)
3744 void (*send_list
)(struct listData
*);
3745 struct userData
*uData
;
3746 struct listData lData
;
3747 unsigned int matches
;
3751 lData
.bot
= cmd
->parent
->bot
;
3752 lData
.channel
= channel
;
3753 lData
.lowest
= lowest
;
3754 lData
.highest
= highest
;
3755 lData
.search
= (argc
> 1) ? argv
[1] : NULL
;
3756 send_list
= def_list
;
3757 /* What does the following line do exactly?? */
3758 (void)zoot_list
; /* since it doesn't show user levels */
3760 /* this does nothing!! -rubin
3761 if(user->handle_info)
3763 switch(user->handle_info->userlist_style)
3765 case HI_STYLE_DEF: send_list = def_list; break;
3766 case HI_STYLE_ZOOT: send_list = def_list; break;
3771 lData
.users
= alloca(channel
->channel_info
->userCount
* sizeof(struct userData
*));
3773 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
3775 if((uData
->access
< lowest
)
3776 || (uData
->access
> highest
)
3777 || (lData
.search
&& !match_ircglob(uData
->handle
->handle
, lData
.search
)))
3779 lData
.users
[matches
++] = uData
;
3781 qsort(lData
.users
, matches
, sizeof(lData
.users
[0]), userData_access_comp
);
3783 lData
.table
.length
= matches
+1;
3784 lData
.table
.width
= 5;
3785 lData
.table
.flags
= TABLE_NO_FREE
;
3786 lData
.table
.contents
= malloc(lData
.table
.length
*sizeof(*lData
.table
.contents
));
3787 ary
= malloc(lData
.table
.width
*sizeof(**lData
.table
.contents
));
3788 lData
.table
.contents
[0] = ary
;
3792 ary
[3] = "Last Seen";
3794 for(matches
= 1; matches
< lData
.table
.length
; ++matches
)
3796 struct userData
*uData
= lData
.users
[matches
-1];
3797 char seen
[INTERVALLEN
];
3799 ary
= malloc(lData
.table
.width
*sizeof(**lData
.table
.contents
));
3800 lData
.table
.contents
[matches
] = ary
;
3801 /* ary[0] = strtab(uData->access);*/
3802 ary
[0] = user_level_name_from_level(uData
->access
);
3803 ary
[1] = strtab(uData
->access
);
3804 ary
[2] = uData
->handle
->handle
;
3807 else if(!uData
->seen
)
3810 ary
[3] = intervalString(seen
, now
- uData
->seen
, user
->handle_info
);
3811 ary
[3] = strdup(ary
[3]);
3812 if(IsUserSuspended(uData
))
3813 ary
[4] = "Suspended";
3814 else if(HANDLE_FLAGGED(uData
->handle
, FROZEN
))
3815 ary
[4] = "Vacation";
3820 for(matches
= 1; matches
< lData
.table
.length
; ++matches
)
3822 free((char*)lData
.table
.contents
[matches
][3]);
3823 free(lData
.table
.contents
[matches
]);
3825 free(lData
.table
.contents
[0]);
3826 free(lData
.table
.contents
);
3830 /* Remove this now that debugging is over? or improve it for
3831 * users? Would it be better tied into USERS somehow? -Rubin */
3832 static CHANSERV_FUNC(cmd_pending
)
3834 struct adduserPending
*ap
;
3835 reply("CSMSG_ADDUSER_PENDING_HEADER");
3837 for(ap
= adduser_pendings
;ap
;ap
= ap
->next
)
3838 reply("CSMSG_ADDUSER_PENDING_LIST", ap
->channel
->name
, ap
->user
->nick
);
3839 reply("CSMSG_ADDUSER_PENDING_FOOTER");
3843 static CHANSERV_FUNC(cmd_users
)
3845 return cmd_list_users(CSFUNC_ARGS
, 1, UL_OWNER
);
3848 static CHANSERV_FUNC(cmd_wlist
)
3850 return cmd_list_users(CSFUNC_ARGS
, UL_OWNER
, UL_OWNER
);
3853 static CHANSERV_FUNC(cmd_clist
)
3855 return cmd_list_users(CSFUNC_ARGS
, UL_COOWNER
, UL_OWNER
-1);
3858 static CHANSERV_FUNC(cmd_mlist
)
3860 return cmd_list_users(CSFUNC_ARGS
, UL_MANAGER
, UL_COOWNER
-1);
3863 static CHANSERV_FUNC(cmd_olist
)
3865 return cmd_list_users(CSFUNC_ARGS
, UL_OP
, UL_MANAGER
-1);
3868 static CHANSERV_FUNC(cmd_hlist
)
3870 return cmd_list_users(CSFUNC_ARGS
, UL_HALFOP
, UL_OP
-1);
3873 static CHANSERV_FUNC(cmd_plist
)
3875 return cmd_list_users(CSFUNC_ARGS
, 1, UL_HALFOP
-1);
3878 static CHANSERV_FUNC(cmd_lamers
)
3880 struct helpfile_table tbl
;
3881 unsigned int matches
= 0, timed
= 0, ii
;
3882 char t_buffer
[INTERVALLEN
], e_buffer
[INTERVALLEN
], *search
;
3883 const char *msg_never
, *triggered
, *expires
;
3884 struct banData
*ban
, **bans
; /* lamers */
3891 reply("CSMSG_LAMERS_HEADER", channel
->name
);
3892 bans
= alloca(channel
->channel_info
->banCount
* sizeof(struct banData
*)); /* lamers */
3895 for(ban
= channel
->channel_info
->bans
; ban
; ban
= ban
->next
)
3897 if(search
&& !match_ircglobs(search
, ban
->mask
))
3899 bans
[matches
++] = ban
;
3904 tbl
.length
= matches
+ 1;
3905 tbl
.width
= 4 + timed
;
3907 tbl
.flags
= TABLE_NO_FREE
;
3908 tbl
.contents
= malloc(tbl
.length
* sizeof(tbl
.contents
[0]));
3909 tbl
.contents
[0] = malloc(tbl
.width
* sizeof(tbl
.contents
[0][0]));
3910 tbl
.contents
[0][0] = "Mask";
3911 tbl
.contents
[0][1] = "Set By";
3912 tbl
.contents
[0][2] = "Triggered";
3915 tbl
.contents
[0][3] = "Expires";
3916 tbl
.contents
[0][4] = "Reason";
3919 tbl
.contents
[0][3] = "Reason";
3922 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
3924 free(tbl
.contents
[0]);
3929 msg_never
= user_find_message(user
, "MSG_NEVER");
3930 for(ii
= 0; ii
< matches
; )
3936 else if(ban
->expires
)
3937 expires
= intervalString(e_buffer
, ban
->expires
- now
, user
->handle_info
);
3939 expires
= msg_never
;
3942 triggered
= intervalString(t_buffer
, now
- ban
->triggered
, user
->handle_info
);
3944 triggered
= msg_never
;
3946 tbl
.contents
[++ii
] = malloc(tbl
.width
* sizeof(tbl
.contents
[0][0]));
3947 tbl
.contents
[ii
][0] = ban
->mask
;
3948 tbl
.contents
[ii
][1] = ban
->owner
;
3949 tbl
.contents
[ii
][2] = strdup(triggered
);
3952 tbl
.contents
[ii
][3] = strdup(expires
);
3953 tbl
.contents
[ii
][4] = ban
->reason
;
3956 tbl
.contents
[ii
][3] = ban
->reason
;
3958 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
3959 /* reply("MSG_MATCH_COUNT", matches); */
3960 for(ii
= 1; ii
< tbl
.length
; ++ii
)
3962 free((char*)tbl
.contents
[ii
][2]);
3964 free((char*)tbl
.contents
[ii
][3]);
3965 free(tbl
.contents
[ii
]);
3967 free(tbl
.contents
[0]);
3974 * return + if the user does NOT have the right to set the topic, and
3975 * the topic is changed.
3978 bad_topic(struct chanNode
*channel
, struct userNode
*user
, const char *new_topic
)
3980 struct chanData
*cData
= channel
->channel_info
;
3981 if(check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
3983 else if(cData
->topic
)
3984 return irccasecmp(new_topic
, cData
->topic
);
3991 * Makes a givin topic fit into a givin topic mask and returns
3994 * topic_mask - the mask to conform to
3995 * topic - the topic to make conform
3996 * new_topic - the pre-allocated char* to put the new topic into
3998 * modifies: new_topic
4001 conform_topic(char* topic_mask
, char* topic
, char *new_topic
)
4003 //char *topic_mask = cData->topic_mask;
4005 int pos
=0, starpos
=-1, dpos
=0, len
;
4007 while((tchar
= topic_mask
[pos
++]) && (dpos
<= TOPICLEN
))
4014 strcpy(new_topic
, "");
4017 len
= strlen(topic
);
4018 if((dpos
+ len
) > TOPICLEN
)
4019 len
= TOPICLEN
+ 1 - dpos
;
4020 memcpy(new_topic
+dpos
, topic
, len
);
4024 case '\\': tchar
= topic_mask
[pos
++]; /* and fall through */
4025 default: new_topic
[dpos
++] = tchar
; break;
4028 if((dpos
> TOPICLEN
) || tchar
)
4030 strcpy(new_topic
, "");
4033 new_topic
[dpos
] = 0;
4037 static CHANSERV_FUNC(cmd_topic
)
4039 struct chanData
*cData
;
4042 cData
= channel
->channel_info
;
4047 SetChannelTopic(channel
, chanserv
, cData
->topic
, 1);
4048 reply("CSMSG_TOPIC_SET", cData
->topic
);
4052 reply("CSMSG_NO_TOPIC", channel
->name
);
4056 topic
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
4057 /* If they say "!topic *", use an empty topic. */
4058 if((topic
[0] == '*') && (topic
[1] == 0))
4061 if(bad_topic(channel
, user
, topic
))
4063 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
4068 /* If there is a topicmask set, and the new topic doesnt match, make it */
4069 if(cData
->topic_mask
&& !match_ircglob(topic
, cData
->topic_mask
))
4071 char *topic_mask
= cData
->topic_mask
;
4072 char new_topic
[TOPICLEN
+1];
4074 /* make a new topic fitting mask */
4075 conform_topic(topic_mask
, topic
, new_topic
);
4078 /* Topic couldnt fit into mask, was too long */
4079 reply("CSMSG_TOPICMASK_CONFLICT1", channel
->name
, topic_mask
);
4080 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN
);
4083 SetChannelTopic(channel
, chanserv
, new_topic
, 1);
4085 else /* No mask set, just set the topic */
4086 SetChannelTopic(channel
, chanserv
, topic
, 1);
4089 if(check_user_level(channel
, user
, lvlTopicSnarf
, 1, 0))
4091 /* Grab the topic and save it as the default topic. */
4093 cData
->topic
= strdup(channel
->topic
);
4099 static CHANSERV_FUNC(cmd_mode
)
4101 struct mod_chanmode
*change
;
4105 change
= &channel
->channel_info
->modes
;
4106 if(change
->modes_set
|| change
->modes_clear
) {
4107 modcmd_chanmode_announce(change
);
4108 reply("CSMSG_DEFAULTED_MODES", channel
->name
);
4110 reply("CSMSG_NO_MODES", channel
->name
);
4114 change
= mod_chanmode_parse(channel
, argv
+1, argc
-1, MCP_KEY_FREE
|MCP_REGISTERED
);
4117 reply("MSG_INVALID_MODES", unsplit_string(argv
+1, argc
-1, NULL
));
4121 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
4122 && mode_lock_violated(&channel
->channel_info
->modes
, change
))
4125 mod_chanmode_format(&channel
->channel_info
->modes
, modes
);
4126 reply("CSMSG_MODE_LOCKED", modes
, channel
->name
);
4130 modcmd_chanmode_announce(change
);
4131 mod_chanmode_free(change
);
4132 reply("CSMSG_MODES_SET", unsplit_string(argv
+1, argc
-1, NULL
));
4136 static CHANSERV_FUNC(cmd_invite
)
4138 struct userData
*uData
;
4139 struct userNode
*invite
;
4141 uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
4145 if(!(invite
= GetUserH(argv
[1])))
4147 reply("MSG_NICK_UNKNOWN", argv
[1]);
4154 if(GetUserMode(channel
, invite
))
4156 reply("CSMSG_ALREADY_PRESENT", invite
->nick
, channel
->name
);
4164 char *reason
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
4165 send_message(invite
, chanserv
, "CSMSG_INVITING_YOU_REASON", user
->nick
, channel
->name
, reason
);
4168 send_message(invite
, chanserv
, "CSMSG_INVITING_YOU", user
->nick
, channel
->name
);
4170 irc_invite(chanserv
, invite
, channel
);
4172 reply("CSMSG_INVITED_USER", argv
[1], channel
->name
);
4177 static CHANSERV_FUNC(cmd_inviteme
)
4179 if(GetUserMode(channel
, user
))
4181 reply("CSMSG_YOU_ALREADY_PRESENT", channel
->name
);
4184 if(channel
->channel_info
4185 && !check_user_level(channel
, user
, lvlInviteMe
, 1, 0))
4187 reply("CSMSG_LOW_CHANNEL_ACCESS", channel
->name
);
4190 irc_invite(cmd
->parent
->bot
, user
, channel
);
4195 show_suspension_info(struct svccmd
*cmd
, struct userNode
*user
, struct suspended
*suspended
)
4198 char buf1
[INTERVALLEN
], buf2
[INTERVALLEN
];
4200 /* We display things based on two dimensions:
4201 * - Issue time: present or absent
4202 * - Expiration: revoked, expired, expires in future, or indefinite expiration
4203 * (in order of precedence, so something both expired and revoked
4204 * only counts as revoked)
4206 combo
= (suspended
->issued
? 4 : 0)
4207 + (suspended
->revoked
? 3 : suspended
->expires
? ((suspended
->expires
< now
) ? 2 : 1) : 0);
4209 case 0: /* no issue time, indefinite expiration */
4210 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended
->suspender
, suspended
->reason
);
4212 case 1: /* no issue time, expires in future */
4213 intervalString(buf1
, suspended
->expires
-now
, user
->handle_info
);
4214 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended
->suspender
, buf1
, suspended
->reason
);
4216 case 2: /* no issue time, expired */
4217 intervalString(buf1
, now
-suspended
->expires
, user
->handle_info
);
4218 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended
->suspender
, buf1
, suspended
->reason
);
4220 case 3: /* no issue time, revoked */
4221 intervalString(buf1
, now
-suspended
->revoked
, user
->handle_info
);
4222 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended
->suspender
, buf1
, suspended
->reason
);
4224 case 4: /* issue time set, indefinite expiration */
4225 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4226 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1
, suspended
->suspender
, suspended
->reason
);
4228 case 5: /* issue time set, expires in future */
4229 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4230 intervalString(buf2
, suspended
->expires
-now
, user
->handle_info
);
4231 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4233 case 6: /* issue time set, expired */
4234 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4235 intervalString(buf2
, now
-suspended
->expires
, user
->handle_info
);
4236 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4238 case 7: /* issue time set, revoked */
4239 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4240 intervalString(buf2
, now
-suspended
->revoked
, user
->handle_info
);
4241 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4244 log_module(CS_LOG
, LOG_ERROR
, "Invalid combo value %d in show_suspension_info()", combo
);
4249 static CHANSERV_FUNC(cmd_info
)
4251 char modes
[MAXLEN
], buffer
[INTERVALLEN
];
4252 struct userData
*uData
, *owner
;
4253 struct chanData
*cData
;
4254 struct do_not_register
*dnr
;
4259 cData
= channel
->channel_info
;
4260 reply("CSMSG_CHANNEL_INFO", channel
->name
);
4263 uData
= GetChannelUser(cData
, user
->handle_info
);
4264 if(uData
&& (uData
->access
>= UL_OP
/*cData->lvlOpts[lvlGiveOps]*/))
4266 mod_chanmode_format(&cData
->modes
, modes
);
4267 reply("CSMSG_CHANNEL_TOPIC", cData
->topic
);
4268 reply("CSMSG_CHANNEL_MODES", modes
[0] ? modes
: user_find_message(user
, "MSG_NONE"));
4271 for(it
= dict_first(cData
->notes
); it
; it
= iter_next(it
))
4275 note
= iter_data(it
);
4276 if(!note_type_visible_to_user(cData
, note
->type
, user
))
4279 padding
= PADLEN
- 1 - strlen(iter_key(it
));
4280 reply("CSMSG_CHANNEL_NOTE", iter_key(it
), padding
> 0 ? padding
: 1, "", note
->note
);
4283 reply("CSMSG_CHANNEL_MAX", cData
->max
);
4284 for(owner
= cData
->users
; owner
; owner
= owner
->next
)
4285 if(owner
->access
== UL_OWNER
)
4286 reply("CSMSG_CHANNEL_OWNER", owner
->handle
->handle
);
4287 reply("CSMSG_CHANNEL_USERS", cData
->userCount
);
4288 reply("CSMSG_CHANNEL_LAMERS", cData
->banCount
);
4289 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer
, now
- cData
->visited
, user
->handle_info
));
4290 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer
, now
- cData
->registered
, user
->handle_info
));
4292 privileged
= IsStaff(user
);
4293 if(((uData
&& uData
->access
>= UL_COOWNER
) || privileged
) && cData
->registrar
)
4294 reply("CSMSG_CHANNEL_REGISTRAR", cData
->registrar
);
4296 if(privileged
&& (dnr
= chanserv_is_dnr(channel
->name
, NULL
)))
4297 chanserv_show_dnrs(user
, cmd
, channel
->name
, NULL
);
4299 if(cData
->suspended
&& ((uData
&& (uData
->access
>= UL_COOWNER
)) || IsHelping(user
)))
4301 struct suspended
*suspended
;
4302 reply((IsSuspended(cData
) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel
->name
);
4303 for(suspended
= cData
->suspended
; suspended
; suspended
= suspended
->previous
)
4304 show_suspension_info(cmd
, user
, suspended
);
4306 else if(IsSuspended(cData
))
4308 reply("CSMSG_CHANNEL_SUSPENDED", channel
->name
);
4309 show_suspension_info(cmd
, user
, cData
->suspended
);
4311 reply("CSMSG_CHANNEL_END");
4315 static CHANSERV_FUNC(cmd_netinfo
)
4317 extern time_t boot_time
;
4318 extern unsigned long burst_length
;
4319 char interval
[INTERVALLEN
];
4321 reply("CSMSG_NETWORK_INFO");
4322 reply("CSMSG_NETWORK_SERVERS", dict_size(servers
));
4323 reply("CSMSG_NETWORK_USERS", dict_size(clients
));
4324 reply("CSMSG_NETWORK_OPERS", curr_opers
.used
);
4325 reply("CSMSG_NETWORK_CHANNELS", registered_channels
);
4326 reply("CSMSG_NETWORK_LAMERS", banCount
);
4327 reply("CSMSG_NETWORK_CHANUSERS", userCount
);
4328 reply("CSMSG_SERVICES_UPTIME", intervalString(interval
, time(NULL
) - boot_time
, user
->handle_info
));
4329 reply("CSMSG_BURST_LENGTH", intervalString(interval
, burst_length
, user
->handle_info
));
4334 send_staff_list(struct userNode
*to
, struct userList
*list
, int skip_flags
)
4336 struct helpfile_table table
;
4338 struct userNode
*user
;
4343 table
.flags
= TABLE_REPEAT_ROWS
| TABLE_NO_FREE
| TABLE_NO_HEADERS
;
4344 table
.contents
= alloca(list
->used
*sizeof(*table
.contents
));
4345 for(nn
=0; nn
<list
->used
; nn
++)
4347 user
= list
->list
[nn
];
4348 if(user
->modes
& skip_flags
)
4352 table
.contents
[table
.length
] = alloca(table
.width
*sizeof(**table
.contents
));
4355 nick
= alloca(strlen(user
->nick
)+3);
4356 sprintf(nick
, "(%s)", user
->nick
);
4360 table
.contents
[table
.length
][0] = nick
;
4363 table_send(chanserv
, to
->nick
, 0, NULL
, table
);
4366 static CHANSERV_FUNC(cmd_ircops
)
4368 reply("CSMSG_STAFF_OPERS");
4369 send_staff_list(user
, &curr_opers
, FLAGS_SERVICE
);
4373 static CHANSERV_FUNC(cmd_helpers
)
4375 reply("CSMSG_STAFF_HELPERS");
4376 send_staff_list(user
, &curr_helpers
, FLAGS_OPER
);
4380 static CHANSERV_FUNC(cmd_staff
)
4382 reply("CSMSG_NETWORK_STAFF");
4383 cmd_ircops(CSFUNC_ARGS
);
4384 cmd_helpers(CSFUNC_ARGS
);
4388 static CHANSERV_FUNC(cmd_peek
)
4390 struct modeNode
*mn
;
4391 char modes
[MODELEN
];
4393 struct helpfile_table table
;
4395 irc_make_chanmode(channel
, modes
);
4397 reply("CSMSG_PEEK_INFO", channel
->name
);
4399 reply("CSMSG_PEEK_TOPIC", channel
->topic
);
4400 reply("CSMSG_PEEK_MODES", modes
);
4401 reply("CSMSG_PEEK_USERS", channel
->members
.used
);
4405 table
.flags
= TABLE_REPEAT_ROWS
| TABLE_NO_FREE
| TABLE_NO_HEADERS
;
4406 table
.contents
= alloca(channel
->members
.used
*sizeof(*table
.contents
));
4407 for(n
= 0; n
< channel
->members
.used
; n
++)
4409 mn
= channel
->members
.list
[n
];
4410 if(!(mn
->modes
& MODE_CHANOP
) || IsLocal(mn
->user
))
4412 table
.contents
[table
.length
] = alloca(sizeof(**table
.contents
));
4413 table
.contents
[table
.length
][0] = mn
->user
->nick
;
4418 reply("CSMSG_PEEK_OPS");
4419 table_send(chanserv
, user
->nick
, 0, NULL
, table
);
4422 reply("CSMSG_PEEK_NO_OPS");
4423 reply("CSMSG_PEEK_END");
4427 static MODCMD_FUNC(cmd_wipeinfo
)
4429 struct handle_info
*victim
;
4430 struct userData
*ud
, *actor
;
4433 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
4434 if(!(victim
= modcmd_get_handle_info(user
, argv
[1])))
4436 if(!(ud
= GetTrueChannelAccess(channel
->channel_info
, victim
)))
4438 reply("CSMSG_NO_CHAN_USER", argv
[1], channel
->name
);
4441 if((ud
->access
>= actor
->access
) && (ud
!= actor
))
4443 reply("MSG_USER_OUTRANKED", victim
->handle
);
4449 reply("CSMSG_WIPED_INFO_LINE", argv
[1], channel
->name
);
4453 static CHANSERV_FUNC(cmd_resync
)
4455 struct mod_chanmode
*changes
;
4456 struct chanData
*cData
= channel
->channel_info
;
4457 unsigned int ii
, used
;
4459 changes
= mod_chanmode_alloc(channel
->members
.used
* 2);
4460 for(ii
= used
= 0; ii
< channel
->members
.used
; ++ii
)
4462 struct modeNode
*mn
= channel
->members
.list
[ii
];
4463 struct userData
*uData
;
4465 if(IsService(mn
->user
))
4468 uData
= GetChannelAccess(cData
, mn
->user
->handle_info
);
4469 if(uData
&& uData
->access
>= UL_OP
/* cData->lvlOpts[lvlGiveOps]*/)
4471 if(!(mn
->modes
& MODE_CHANOP
))
4473 changes
->args
[used
].mode
= MODE_CHANOP
;
4474 changes
->args
[used
++].u
.member
= mn
;
4477 else if(uData
&& uData
->access
>= UL_HALFOP
/*cData->lvlOpts[lvlGiveHalfOps]*/)
4479 if(!(mn
->modes
& MODE_HALFOP
))
4481 changes
->args
[used
].mode
= MODE_HALFOP
;
4482 changes
->args
[used
++].u
.member
= mn
;
4484 if(mn
->modes
& MODE_CHANOP
)
4486 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_CHANOP
);
4487 changes
->args
[used
++].u
.member
= mn
;
4489 if(mn
->modes
& MODE_VOICE
)
4491 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_VOICE
);
4492 changes
->args
[used
++].u
.member
= mn
;
4495 else if(uData
&& uData
->access
>= UL_PEON
/* cData->lvlOpts[lvlGiveVoice]*/)
4497 if(mn
->modes
& MODE_CHANOP
)
4499 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_VOICE
);
4500 changes
->args
[used
++].u
.member
= mn
;
4502 if(mn
->modes
& MODE_HALFOP
)
4504 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_VOICE
);
4505 changes
->args
[used
++].u
.member
= mn
;
4507 if(!(mn
->modes
& MODE_VOICE
))
4509 changes
->args
[used
].mode
= MODE_VOICE
;
4510 changes
->args
[used
++].u
.member
= mn
;
4517 changes
->args
[used
].mode
= MODE_REMOVE
| mn
->modes
;
4518 changes
->args
[used
++].u
.member
= mn
;
4522 changes
->argc
= used
;
4523 modcmd_chanmode_announce(changes
);
4524 mod_chanmode_free(changes
);
4525 reply("CSMSG_RESYNCED_USERS", channel
->name
);
4529 static CHANSERV_FUNC(cmd_seen
)
4531 struct userData
*uData
;
4532 struct handle_info
*handle
;
4533 char seen
[INTERVALLEN
];
4537 if(!irccasecmp(argv
[1], chanserv
->nick
))
4539 reply("CSMSG_IS_CHANSERV");
4543 if(!(handle
= get_handle_info(argv
[1])))
4545 reply("MSG_HANDLE_UNKNOWN", argv
[1]);
4549 if(!(uData
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
4551 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
4556 reply("CSMSG_USER_PRESENT", handle
->handle
);
4557 else if(uData
->seen
)
4558 reply("CSMSG_USER_SEEN", handle
->handle
, channel
->name
, intervalString(seen
, now
- uData
->seen
, user
->handle_info
));
4560 reply("CSMSG_NEVER_SEEN", handle
->handle
, channel
->name
);
4562 if(!uData
->present
&& HANDLE_FLAGGED(handle
, FROZEN
))
4563 reply("CSMSG_USER_VACATION", handle
->handle
);
4568 static MODCMD_FUNC(cmd_names
)
4570 struct userNode
*targ
;
4571 struct userData
*targData
;
4572 unsigned int ii
, pos
;
4575 for(ii
=pos
=0; ii
<channel
->members
.used
; ++ii
)
4577 targ
= channel
->members
.list
[ii
]->user
;
4578 targData
= GetTrueChannelAccess(channel
->channel_info
, targ
->handle_info
);
4581 if(pos
+ strlen(targ
->nick
) + strlen(targ
->handle_info
->handle
) + 8 > sizeof(buf
))
4584 reply("CSMSG_CHANNEL_NAMES", channel
->name
, buf
);
4588 if(IsUserSuspended(targData
))
4590 pos
+= sprintf(buf
+pos
, "%d:%s(%s)", targData
->access
, targ
->nick
, targ
->handle_info
->handle
);
4593 reply("CSMSG_CHANNEL_NAMES", channel
->name
, buf
);
4594 reply("CSMSG_END_NAMES", channel
->name
);
4599 note_type_visible_to_user(struct chanData
*channel
, struct note_type
*ntype
, struct userNode
*user
)
4601 switch(ntype
->visible_type
)
4603 case NOTE_VIS_ALL
: return 1;
4604 case NOTE_VIS_CHANNEL_USERS
: return !channel
|| !user
|| (user
->handle_info
&& GetChannelUser(channel
, user
->handle_info
));
4605 case NOTE_VIS_PRIVILEGED
: default: return user
&& (IsOper(user
) || IsSupportHelper(user
) || IsNetworkHelper(user
));
4610 note_type_settable_by_user(struct chanNode
*channel
, struct note_type
*ntype
, struct userNode
*user
)
4612 struct userData
*uData
;
4614 switch(ntype
->set_access_type
)
4616 case NOTE_SET_CHANNEL_ACCESS
:
4617 if(!user
->handle_info
)
4619 if(!(uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
)))
4621 return uData
->access
>= ntype
->set_access
.min_ulevel
;
4622 case NOTE_SET_CHANNEL_SETTER
:
4623 return check_user_level(channel
, user
, lvlSetters
, 1, 0);
4624 case NOTE_SET_PRIVILEGED
: default:
4625 return IsHelping(user
) && (user
->handle_info
->opserv_level
>= ntype
->set_access
.min_opserv
);
4629 static CHANSERV_FUNC(cmd_note
)
4631 struct chanData
*cData
;
4633 struct note_type
*ntype
;
4635 cData
= channel
->channel_info
;
4638 reply("CSMSG_NOT_REGISTERED", channel
->name
);
4642 /* If no arguments, show all visible notes for the channel. */
4648 for(count
=0, it
=dict_first(cData
->notes
); it
; it
=iter_next(it
))
4650 note
= iter_data(it
);
4651 if(!note_type_visible_to_user(cData
, note
->type
, user
))
4654 reply("CSMSG_NOTELIST_HEADER", channel
->name
);
4655 reply("CSMSG_NOTE_FORMAT", iter_key(it
), note
->setter
, note
->note
);
4658 reply("CSMSG_NOTELIST_END", channel
->name
);
4660 reply("CSMSG_NOTELIST_EMPTY", channel
->name
);
4662 /* If one argument, show the named note. */
4665 if((note
= dict_find(cData
->notes
, argv
[1], NULL
))
4666 && note_type_visible_to_user(cData
, note
->type
, user
))
4668 reply("CSMSG_NOTE_FORMAT", note
->type
->name
, note
->setter
, note
->note
);
4670 else if((ntype
= dict_find(note_types
, argv
[1], NULL
))
4671 && note_type_visible_to_user(NULL
, ntype
, user
))
4673 reply("CSMSG_NO_SUCH_NOTE", channel
->name
, ntype
->name
);
4678 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
4682 /* Assume they're trying to set a note. */
4686 ntype
= dict_find(note_types
, argv
[1], NULL
);
4689 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
4692 else if(note_type_settable_by_user(channel
, ntype
, user
))
4694 note_text
= unsplit_string(argv
+2, argc
-2, NULL
);
4695 if((note
= dict_find(cData
->notes
, argv
[1], NULL
)))
4696 reply("CSMSG_REPLACED_NOTE", ntype
->name
, channel
->name
, note
->setter
, note
->note
);
4697 chanserv_add_channel_note(cData
, ntype
, user
->handle_info
->handle
, note_text
);
4698 reply("CSMSG_NOTE_SET", ntype
->name
, channel
->name
);
4700 if(ntype
->visible_type
== NOTE_VIS_PRIVILEGED
)
4702 /* The note is viewable to staff only, so return 0
4703 to keep the invocation from getting logged (or
4704 regular users can see it in !events). */
4710 reply("CSMSG_NO_ACCESS");
4717 static CHANSERV_FUNC(cmd_delnote
)
4722 if(!(note
= dict_find(channel
->channel_info
->notes
, argv
[1], NULL
))
4723 || !note_type_settable_by_user(channel
, note
->type
, user
))
4725 reply("CSMSG_NO_SUCH_NOTE", channel
->name
, argv
[1]);
4728 dict_remove(channel
->channel_info
->notes
, note
->type
->name
);
4729 reply("CSMSG_NOTE_REMOVED", argv
[1], channel
->name
);
4733 static CHANSERV_FUNC(cmd_events
)
4735 struct logSearch discrim
;
4736 struct logReport report
;
4737 unsigned int matches
, limit
;
4739 limit
= (argc
> 1) ? atoi(argv
[1]) : 10;
4740 if(limit
< 1 || limit
> 200)
4743 memset(&discrim
, 0, sizeof(discrim
));
4744 discrim
.masks
.bot
= chanserv
;
4745 discrim
.masks
.channel_name
= channel
->name
;
4747 discrim
.masks
.command
= argv
[2];
4748 discrim
.limit
= limit
;
4749 discrim
.max_time
= INT_MAX
;
4750 discrim
.severities
= 1 << LOG_COMMAND
;
4751 report
.reporter
= chanserv
;
4753 reply("CSMSG_EVENT_SEARCH_RESULTS", channel
->name
);
4755 matches
= log_entry_search(&discrim
, log_report_entry
, &report
);
4757 reply("MSG_MATCH_COUNT", matches
);
4759 reply("MSG_NO_MATCHES");
4763 static CHANSERV_FUNC(cmd_say
)
4769 msg
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
4770 send_channel_message(channel
, cmd
->parent
->bot
, "%s", msg
);
4772 else if(GetUserH(argv
[1]))
4775 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
4776 send_target_message(5, argv
[1], cmd
->parent
->bot
, "%s", msg
);
4780 reply("MSG_NOT_TARGET_NAME");
4786 static CHANSERV_FUNC(cmd_emote
)
4792 /* CTCP is so annoying. */
4793 msg
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
4794 send_channel_message(channel
, cmd
->parent
->bot
, "\001ACTION %s\001", msg
);
4796 else if(GetUserH(argv
[1]))
4798 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
4799 send_target_message(5, argv
[1], cmd
->parent
->bot
, "\001ACTION %s\001", msg
);
4803 reply("MSG_NOT_TARGET_NAME");
4809 struct channelList
*
4810 chanserv_support_channels(void)
4812 return &chanserv_conf
.support_channels
;
4815 static CHANSERV_FUNC(cmd_expire
)
4817 int channel_count
= registered_channels
;
4818 expire_channels(NULL
);
4819 reply("CSMSG_CHANNELS_EXPIRED", channel_count
- registered_channels
);
4824 chanserv_expire_suspension(void *data
)
4826 struct suspended
*suspended
= data
;
4827 struct chanNode
*channel
;
4829 if(!suspended
->expires
|| (now
< suspended
->expires
))
4830 suspended
->revoked
= now
;
4831 channel
= suspended
->cData
->channel
;
4832 suspended
->cData
->channel
= channel
;
4833 suspended
->cData
->flags
&= ~CHANNEL_SUSPENDED
;
4834 if(!IsOffChannel(suspended
->cData
))
4836 struct mod_chanmode change
;
4837 mod_chanmode_init(&change
);
4839 change
.args
[0].mode
= MODE_CHANOP
;
4840 change
.args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
4841 mod_chanmode_announce(chanserv
, channel
, &change
);
4845 static CHANSERV_FUNC(cmd_csuspend
)
4847 struct suspended
*suspended
;
4848 char reason
[MAXLEN
];
4849 time_t expiry
, duration
;
4850 struct userData
*uData
;
4854 if(IsProtected(channel
->channel_info
))
4856 reply("CSMSG_SUSPEND_NODELETE", channel
->name
);
4860 if(argv
[1][0] == '!')
4862 else if(IsSuspended(channel
->channel_info
))
4864 reply("CSMSG_ALREADY_SUSPENDED", channel
->name
);
4865 show_suspension_info(cmd
, user
, channel
->channel_info
->suspended
);
4869 if(!strcmp(argv
[1], "0"))
4871 else if((duration
= ParseInterval(argv
[1])))
4872 expiry
= now
+ duration
;
4875 reply("MSG_INVALID_DURATION", argv
[1]);
4879 unsplit_string(argv
+ 2, argc
- 2, reason
);
4881 suspended
= calloc(1, sizeof(*suspended
));
4882 suspended
->revoked
= 0;
4883 suspended
->issued
= now
;
4884 suspended
->suspender
= strdup(user
->handle_info
->handle
);
4885 suspended
->expires
= expiry
;
4886 suspended
->reason
= strdup(reason
);
4887 suspended
->cData
= channel
->channel_info
;
4888 suspended
->previous
= suspended
->cData
->suspended
;
4889 suspended
->cData
->suspended
= suspended
;
4891 if(suspended
->expires
)
4892 timeq_add(suspended
->expires
, chanserv_expire_suspension
, suspended
);
4894 if(IsSuspended(channel
->channel_info
))
4896 suspended
->previous
->revoked
= now
;
4897 if(suspended
->previous
->expires
)
4898 timeq_del(suspended
->previous
->expires
, chanserv_expire_suspension
, suspended
->previous
, 0);
4899 sprintf(reason
, "%s suspension modified by %s.", channel
->name
, suspended
->suspender
);
4900 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
4904 /* Mark all users in channel as absent. */
4905 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
4914 /* Mark the channel as suspended, then part. */
4915 channel
->channel_info
->flags
|= CHANNEL_SUSPENDED
;
4916 DelChannelUser(chanserv
, channel
, suspended
->reason
, 0);
4917 reply("CSMSG_SUSPENDED", channel
->name
);
4918 sprintf(reason
, "%s suspended by %s.", channel
->name
, suspended
->suspender
);
4919 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
4924 static CHANSERV_FUNC(cmd_cunsuspend
)
4926 struct suspended
*suspended
;
4927 char message
[MAXLEN
];
4929 if(!IsSuspended(channel
->channel_info
))
4931 reply("CSMSG_NOT_SUSPENDED", channel
->name
);
4935 suspended
= channel
->channel_info
->suspended
;
4937 /* Expire the suspension and join ChanServ to the channel. */
4938 timeq_del(suspended
->expires
, chanserv_expire_suspension
, suspended
, 0);
4939 chanserv_expire_suspension(suspended
);
4940 reply("CSMSG_UNSUSPENDED", channel
->name
);
4941 sprintf(message
, "%s unsuspended by %s.", channel
->name
, user
->handle_info
->handle
);
4942 global_message(MESSAGE_RECIPIENT_OPERS
|MESSAGE_RECIPIENT_HELPERS
, message
);
4946 typedef struct chanservSearch
4954 unsigned long flags
;
4958 typedef void (*channel_search_func
)(struct chanData
*channel
, void *data
);
4961 chanserv_search_create(struct userNode
*user
, unsigned int argc
, char *argv
[])
4966 search
= malloc(sizeof(struct chanservSearch
));
4967 memset(search
, 0, sizeof(*search
));
4970 for(i
= 0; i
< argc
; i
++)
4972 /* Assume all criteria require arguments. */
4975 send_message(user
, chanserv
, "MSG_MISSING_PARAMS", argv
[i
]);
4979 if(!irccasecmp(argv
[i
], "name"))
4980 search
->name
= argv
[++i
];
4981 else if(!irccasecmp(argv
[i
], "registrar"))
4982 search
->registrar
= argv
[++i
];
4983 else if(!irccasecmp(argv
[i
], "unvisited"))
4984 search
->unvisited
= ParseInterval(argv
[++i
]);
4985 else if(!irccasecmp(argv
[i
], "registered"))
4986 search
->registered
= ParseInterval(argv
[++i
]);
4987 else if(!irccasecmp(argv
[i
], "flags"))
4990 if(!irccasecmp(argv
[i
], "nodelete"))
4991 search
->flags
|= CHANNEL_NODELETE
;
4992 else if(!irccasecmp(argv
[i
], "suspended"))
4993 search
->flags
|= CHANNEL_SUSPENDED
;
4996 send_message(user
, chanserv
, "CSMSG_INVALID_CFLAG", argv
[i
]);
5000 else if(!irccasecmp(argv
[i
], "limit"))
5001 search
->limit
= strtoul(argv
[++i
], NULL
, 10);
5004 send_message(user
, chanserv
, "MSG_INVALID_CRITERIA", argv
[i
]);
5009 if(search
->name
&& !strcmp(search
->name
, "*"))
5011 if(search
->registrar
&& !strcmp(search
->registrar
, "*"))
5012 search
->registrar
= 0;
5021 chanserv_channel_match(struct chanData
*channel
, search_t search
)
5023 const char *name
= channel
->channel
->name
;
5024 if((search
->name
&& !match_ircglob(name
, search
->name
)) ||
5025 (search
->registrar
&& !channel
->registrar
) ||
5026 (search
->registrar
&& !match_ircglob(channel
->registrar
, search
->registrar
)) ||
5027 (search
->unvisited
&& (now
- channel
->visited
) < search
->unvisited
) ||
5028 (search
->registered
&& (now
- channel
->registered
) > search
->registered
) ||
5029 (search
->flags
&& ((search
->flags
& channel
->flags
) != search
->flags
)))
5036 chanserv_channel_search(search_t search
, channel_search_func smf
, void *data
)
5038 struct chanData
*channel
;
5039 unsigned int matches
= 0;
5041 for(channel
= channelList
; channel
&& matches
< search
->limit
; channel
= channel
->next
)
5043 if(!chanserv_channel_match(channel
, search
))
5053 search_count(UNUSED_ARG(struct chanData
*channel
), UNUSED_ARG(void *data
))
5058 search_print(struct chanData
*channel
, void *data
)
5060 send_message_type(4, data
, chanserv
, "%s", channel
->channel
->name
);
5063 static CHANSERV_FUNC(cmd_search
)
5066 unsigned int matches
;
5067 channel_search_func action
;
5071 if(!irccasecmp(argv
[1], "count"))
5072 action
= search_count
;
5073 else if(!irccasecmp(argv
[1], "print"))
5074 action
= search_print
;
5077 reply("CSMSG_ACTION_INVALID", argv
[1]);
5081 search
= chanserv_search_create(user
, argc
- 2, argv
+ 2);
5085 if(action
== search_count
)
5086 search
->limit
= INT_MAX
;
5088 if(action
== search_print
)
5090 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
5094 matches
= chanserv_channel_search(search
, action
, user
);
5097 reply("MSG_MATCH_COUNT", matches
);
5099 reply("MSG_NO_MATCHES");
5105 static CHANSERV_FUNC(cmd_unvisited
)
5107 struct chanData
*cData
;
5108 time_t interval
= chanserv_conf
.channel_expire_delay
;
5109 char buffer
[INTERVALLEN
];
5110 unsigned int limit
= 25, matches
= 0;
5114 interval
= ParseInterval(argv
[1]);
5116 limit
= atoi(argv
[2]);
5119 intervalString(buffer
, interval
, user
->handle_info
);
5120 reply("CSMSG_UNVISITED_HEADER", limit
, buffer
);
5122 for(cData
= channelList
; cData
&& matches
< limit
; cData
= cData
->next
)
5124 if((now
- cData
->visited
) < interval
)
5127 intervalString(buffer
, now
- cData
->visited
, user
->handle_info
);
5128 reply("CSMSG_UNVISITED_DATA", cData
->channel
->name
, buffer
);
5135 static MODCMD_FUNC(chan_opt_defaulttopic
)
5141 if(!check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
5143 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
5147 topic
= unsplit_string(argv
+1, argc
-1, NULL
);
5149 free(channel
->channel_info
->topic
);
5150 if(topic
[0] == '*' && topic
[1] == 0)
5152 topic
= channel
->channel_info
->topic
= NULL
;
5156 topic
= channel
->channel_info
->topic
= strdup(topic
);
5157 if(channel
->channel_info
->topic_mask
5158 && !match_ircglob(channel
->channel_info
->topic
, channel
->channel_info
->topic_mask
))
5159 reply("CSMSG_TOPIC_MISMATCH", channel
->name
);
5161 SetChannelTopic(channel
, chanserv
, topic
? topic
: "", 1);
5164 if(channel
->channel_info
->topic
)
5165 reply("CSMSG_SET_DEFAULT_TOPIC", channel
->channel_info
->topic
);
5167 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user
, "MSG_NONE"));
5171 static MODCMD_FUNC(chan_opt_topicmask
)
5175 struct chanData
*cData
= channel
->channel_info
;
5178 if(!check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
5180 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
5184 mask
= unsplit_string(argv
+1, argc
-1, NULL
);
5186 if(cData
->topic_mask
)
5187 free(cData
->topic_mask
);
5188 if(mask
[0] == '*' && mask
[1] == 0)
5190 cData
->topic_mask
= 0;
5194 cData
->topic_mask
= strdup(mask
);
5196 reply("CSMSG_MASK_BUT_NO_TOPIC", channel
->name
);
5197 else if(!match_ircglob(cData
->topic
, cData
->topic_mask
))
5198 reply("CSMSG_TOPIC_MISMATCH", channel
->name
);
5202 if(channel
->channel_info
->topic_mask
)
5203 reply("CSMSG_SET_TOPICMASK", channel
->channel_info
->topic_mask
);
5205 reply("CSMSG_SET_TOPICMASK", user_find_message(user
, "MSG_NONE"));
5209 int opt_greeting_common(struct userNode
*user
, struct svccmd
*cmd
, int argc
, char *argv
[], char *name
, char **data
)
5213 char *greeting
= unsplit_string(argv
+1, argc
-1, NULL
);
5217 if(greeting
[0] == '*' && greeting
[1] == 0)
5221 unsigned int length
= strlen(greeting
);
5222 if(length
> chanserv_conf
.greeting_length
)
5224 reply("CSMSG_GREETING_TOO_LONG", length
, chanserv_conf
.greeting_length
);
5227 *data
= strdup(greeting
);
5236 reply(name
, user_find_message(user
, "MSG_NONE"));
5240 static MODCMD_FUNC(chan_opt_greeting
)
5242 return opt_greeting_common(user
, cmd
, argc
, argv
, "CSMSG_SET_GREETING", &channel
->channel_info
->greeting
);
5245 static MODCMD_FUNC(chan_opt_usergreeting
)
5247 return opt_greeting_common(user
, cmd
, argc
, argv
, "CSMSG_SET_USERGREETING", &channel
->channel_info
->user_greeting
);
5250 static MODCMD_FUNC(chan_opt_modes
)
5252 struct mod_chanmode
*new_modes
;
5253 char modes
[MODELEN
];
5257 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0))
5259 reply("CSMSG_NO_ACCESS");
5262 if(argv
[1][0] == '*' && argv
[1][1] == 0)
5264 memset(&channel
->channel_info
->modes
, 0, sizeof(channel
->channel_info
->modes
));
5266 else if(!(new_modes
= mod_chanmode_parse(channel
, argv
+1, argc
-1, MCP_KEY_FREE
|MCP_REGISTERED
)))
5268 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv
+1, argc
-1, NULL
));
5271 else if(new_modes
->argc
> 1)
5273 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv
+1, argc
-1, NULL
));
5274 mod_chanmode_free(new_modes
);
5279 channel
->channel_info
->modes
= *new_modes
;
5280 modcmd_chanmode_announce(new_modes
);
5281 mod_chanmode_free(new_modes
);
5285 mod_chanmode_format(&channel
->channel_info
->modes
, modes
);
5287 reply("CSMSG_SET_MODES", modes
);
5289 reply("CSMSG_SET_MODES", user_find_message(user
, "MSG_NONE"));
5293 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5295 channel_binary_option(char *name
, unsigned long mask
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5297 struct chanData
*cData
= channel
->channel_info
;
5302 /* Set flag according to value. */
5303 if(enabled_string(argv
[1]))
5305 cData
->flags
|= mask
;
5308 else if(disabled_string(argv
[1]))
5310 cData
->flags
&= ~mask
;
5315 reply("MSG_INVALID_BINARY", argv
[1]);
5321 /* Find current option value. */
5322 value
= (cData
->flags
& mask
) ? 1 : 0;
5326 reply(name
, user_find_message(user
, "MSG_ON"));
5328 reply(name
, user_find_message(user
, "MSG_OFF"));
5332 static MODCMD_FUNC(chan_opt_nodelete
)
5334 if((argc
> 1) && (!IsOper(user
) || !user
->handle_info
|| (user
->handle_info
->opserv_level
< chanserv_conf
.nodelete_level
)))
5336 reply("MSG_SETTING_PRIVILEGED", argv
[0]);
5340 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE
);
5343 static MODCMD_FUNC(chan_opt_dynlimit
)
5345 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT
);
5348 static MODCMD_FUNC(chan_opt_offchannel
)
5350 struct chanData
*cData
= channel
->channel_info
;
5355 /* Set flag according to value. */
5356 if(enabled_string(argv
[1]))
5358 if(!IsOffChannel(cData
))
5359 DelChannelUser(chanserv
, channel
, "Going off-channel.", 0);
5360 cData
->flags
|= CHANNEL_OFFCHANNEL
;
5363 else if(disabled_string(argv
[1]))
5365 if(IsOffChannel(cData
))
5367 struct mod_chanmode change
;
5368 mod_chanmode_init(&change
);
5370 change
.args
[0].mode
= MODE_CHANOP
;
5371 change
.args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
5372 mod_chanmode_announce(chanserv
, channel
, &change
);
5374 cData
->flags
&= ~CHANNEL_OFFCHANNEL
;
5379 reply("MSG_INVALID_BINARY", argv
[1]);
5385 /* Find current option value. */
5386 value
= (cData
->flags
& CHANNEL_OFFCHANNEL
) ? 1 : 0;
5390 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user
, "MSG_ON"));
5392 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user
, "MSG_OFF"));
5396 static MODCMD_FUNC(chan_opt_defaults
)
5398 struct userData
*uData
;
5399 struct chanData
*cData
;
5400 const char *confirm
;
5401 enum levelOption lvlOpt
;
5402 enum charOption chOpt
;
5404 cData
= channel
->channel_info
;
5405 uData
= GetChannelUser(cData
, user
->handle_info
);
5406 if(!uData
|| (uData
->access
< UL_OWNER
))
5408 reply("CSMSG_OWNER_DEFAULTS", channel
->name
);
5411 confirm
= make_confirmation_string(uData
);
5412 if((argc
< 2) || strcmp(argv
[1], confirm
))
5414 reply("CSMSG_CONFIRM_DEFAULTS", channel
->name
, confirm
);
5417 cData
->flags
= CHANNEL_DEFAULT_FLAGS
;
5418 cData
->modes
= chanserv_conf
.default_modes
;
5419 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
5420 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
5421 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
5422 cData
->chOpts
[chOpt
] = charOptions
[chOpt
].default_value
;
5423 reply("CSMSG_SETTINGS_DEFAULTED", channel
->name
);
5428 channel_level_option(enum levelOption option
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5430 struct chanData
*cData
= channel
->channel_info
;
5431 struct userData
*uData
;
5432 unsigned short value
;
5436 if(!check_user_level(channel
, user
, option
, 1, 1))
5438 reply("CSMSG_CANNOT_SET");
5441 value
= user_level_from_name(argv
[1], UL_OWNER
+1);
5442 if(!value
&& strcmp(argv
[1], "0"))
5444 reply("CSMSG_INVALID_ACCESS", argv
[1]);
5447 uData
= GetChannelUser(cData
, user
->handle_info
);
5448 if(!uData
|| ((uData
->access
< UL_OWNER
) && (value
> uData
->access
)))
5450 reply("CSMSG_BAD_SETLEVEL");
5455 /* removing these level sets..
5457 if(value > cData->lvlOpts[lvlGiveOps])
5459 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
5463 case lvlGiveHalfOps:
5464 if(value < cData->lvlOpts[lvlGiveVoice])
5466 reply("CSMSG_BAD_GIVEHOPS", cData->lvlOpts[lvlGiveHalfOps]);
5471 if(value < cData->lvlOpts[lvlGiveVoice])
5473 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
5479 /* This test only applies to owners, since non-owners
5480 * trying to set an option to above their level get caught
5481 * by the CSMSG_BAD_SETLEVEL test above.
5483 if(value
> uData
->access
)
5485 reply("CSMSG_BAD_SETTERS");
5492 cData
->lvlOpts
[option
] = value
;
5494 reply(levelOptions
[option
].format_name
, cData
->lvlOpts
[option
]);
5498 static MODCMD_FUNC(chan_opt_enfops
)
5500 return channel_level_option(lvlEnfOps
, CSFUNC_ARGS
);
5503 static MODCMD_FUNC(chan_opt_enfhalfops
)
5505 return channel_level_option(lvlEnfHalfOps
, CSFUNC_ARGS
);
5508 static MODCMD_FUNC(chan_opt_giveops)
5510 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
5513 static MODCMD_FUNC(chan_opt_givehalfops)
5515 return channel_level_option(lvlGiveHalfOps, CSFUNC_ARGS);
5518 static MODCMD_FUNC(chan_opt_enfmodes
)
5520 return channel_level_option(lvlEnfModes
, CSFUNC_ARGS
);
5523 static MODCMD_FUNC(chan_opt_enftopic
)
5525 return channel_level_option(lvlEnfTopic
, CSFUNC_ARGS
);
5528 static MODCMD_FUNC(chan_opt_pubcmd
)
5530 return channel_level_option(lvlPubCmd
, CSFUNC_ARGS
);
5533 static MODCMD_FUNC(chan_opt_setters
)
5535 return channel_level_option(lvlSetters
, CSFUNC_ARGS
);
5538 static MODCMD_FUNC(chan_opt_ctcpusers
)
5540 return channel_level_option(lvlCTCPUsers
, CSFUNC_ARGS
);
5543 static MODCMD_FUNC(chan_opt_userinfo
)
5545 return channel_level_option(lvlUserInfo
, CSFUNC_ARGS
);
5549 static MODCMD_FUNC(chan_opt_givevoice)
5551 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
5555 static MODCMD_FUNC(chan_opt_topicsnarf
)
5557 return channel_level_option(lvlTopicSnarf
, CSFUNC_ARGS
);
5560 static MODCMD_FUNC(chan_opt_inviteme
)
5562 return channel_level_option(lvlInviteMe
, CSFUNC_ARGS
);
5566 channel_multiple_option(enum charOption option
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5568 struct chanData
*cData
= channel
->channel_info
;
5569 int count
= charOptions
[option
].count
, index
;
5573 index
= atoi(argv
[1]);
5575 if(!isdigit(argv
[1][0]) || (index
< 0) || (index
>= count
))
5577 reply("CSMSG_INVALID_NUMERIC", index
);
5578 /* Show possible values. */
5579 for(index
= 0; index
< count
; index
++)
5580 reply(charOptions
[option
].format_name
, index
, user_find_message(user
, charOptions
[option
].values
[index
].format_name
));
5584 cData
->chOpts
[option
] = charOptions
[option
].values
[index
].value
;
5588 /* Find current option value. */
5591 (index
< count
) && (cData
->chOpts
[option
] != charOptions
[option
].values
[index
].value
);
5595 /* Somehow, the option value is corrupt; reset it to the default. */
5596 cData
->chOpts
[option
] = charOptions
[option
].default_value
;
5601 reply(charOptions
[option
].format_name
, index
, user_find_message(user
, charOptions
[option
].values
[index
].format_name
));
5605 static MODCMD_FUNC(chan_opt_voice
)
5607 return channel_multiple_option(chVoice
, CSFUNC_ARGS
);
5610 static MODCMD_FUNC(chan_opt_protect
)
5612 return channel_multiple_option(chProtect
, CSFUNC_ARGS
);
5615 static MODCMD_FUNC(chan_opt_toys
)
5617 return channel_multiple_option(chToys
, CSFUNC_ARGS
);
5620 static MODCMD_FUNC(chan_opt_ctcpreaction
)
5622 return channel_multiple_option(chCTCPReaction
, CSFUNC_ARGS
);
5625 static MODCMD_FUNC(chan_opt_topicrefresh
)
5627 return channel_multiple_option(chTopicRefresh
, CSFUNC_ARGS
);
5630 static struct svccmd_list set_shows_list
;
5633 handle_svccmd_unbind(struct svccmd
*target
) {
5635 for(ii
=0; ii
<set_shows_list
.used
; ++ii
)
5636 if(target
== set_shows_list
.list
[ii
])
5637 set_shows_list
.used
= 0;
5640 static CHANSERV_FUNC(cmd_set
)
5642 struct svccmd
*subcmd
;
5646 /* Check if we need to (re-)initialize set_shows_list. */
5647 if(!set_shows_list
.used
)
5649 if(!set_shows_list
.size
)
5651 set_shows_list
.size
= chanserv_conf
.set_shows
->used
;
5652 set_shows_list
.list
= calloc(set_shows_list
.size
, sizeof(set_shows_list
.list
[0]));
5654 for(ii
= 0; ii
< chanserv_conf
.set_shows
->used
; ii
++)
5656 const char *name
= chanserv_conf
.set_shows
->list
[ii
];
5657 sprintf(buf
, "%s %s", argv
[0], name
);
5658 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
5661 log_module(CS_LOG
, LOG_ERROR
, "Unable to find set option \"%s\".", name
);
5664 svccmd_list_append(&set_shows_list
, subcmd
);
5670 reply("CSMSG_CHANNEL_OPTIONS", channel
->name
);
5672 for(ii
= 0; ii
< set_shows_list
.used
; ii
++)
5674 subcmd
= set_shows_list
.list
[ii
];
5675 subcmd
->command
->func(user
, channel
, 1, argv
+1, subcmd
);
5677 reply("CSMSG_CHANNEL_OPTIONS_END");
5681 sprintf(buf
, "%s %s", argv
[0], argv
[1]);
5682 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
5685 reply("CSMSG_INVALID_OPTION", argv
[1], argv
[0]);
5688 if((argc
> 2) && !check_user_level(channel
, user
, lvlSetters
, 1, 0))
5690 reply("CSMSG_NO_ACCESS");
5694 return subcmd
->command
->func(user
, channel
, argc
- 1, argv
+ 1, subcmd
);
5698 user_binary_option(char *name
, unsigned long mask
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5700 struct userData
*uData
;
5702 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
5705 reply("CSMSG_NOT_USER", channel
->name
);
5711 /* Just show current option value. */
5713 else if(enabled_string(argv
[1]))
5715 uData
->flags
|= mask
;
5717 else if(disabled_string(argv
[1]))
5719 uData
->flags
&= ~mask
;
5723 reply("MSG_INVALID_BINARY", argv
[1]);
5727 reply(name
, user_find_message(user
, (uData
->flags
& mask
) ? "MSG_ON" : "MSG_OFF"));
5731 static MODCMD_FUNC(user_opt_autoop
)
5733 struct userData
*uData
;
5735 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
5738 reply("CSMSG_NOT_USER", channel
->name
);
5741 if(uData
->access
< UL_OP
/*channel->channel_info->lvlOpts[lvlGiveOps]*/)
5742 return user_binary_option("CSMSG_USET_AUTOVOICE", USER_AUTO_OP
, CSFUNC_ARGS
);
5744 return user_binary_option("CSMSG_USET_AUTOOP", USER_AUTO_OP
, CSFUNC_ARGS
);
5745 /* TODO: add halfops error message? or is the op one generic enough? */
5748 static MODCMD_FUNC(user_opt_autoinvite
)
5750 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE
, CSFUNC_ARGS
);
5753 static MODCMD_FUNC(user_opt_info
)
5755 struct userData
*uData
;
5758 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
5762 /* If they got past the command restrictions (which require access)
5763 * but fail this test, we have some fool with security override on.
5765 reply("CSMSG_NOT_USER", channel
->name
);
5772 infoline
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
5773 if(strlen(infoline
) > chanserv_conf
.max_userinfo_length
)
5775 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf
.max_userinfo_length
);
5778 bp
= strcspn(infoline
, "\001");
5781 reply("CSMSG_BAD_INFOLINE", infoline
[bp
]);
5786 if(infoline
[0] == '*' && infoline
[1] == 0)
5789 uData
->info
= strdup(infoline
);
5792 reply("CSMSG_USET_INFO", uData
->info
);
5794 reply("CSMSG_USET_INFO", user_find_message(user
, "MSG_NONE"));
5798 struct svccmd_list uset_shows_list
;
5800 static CHANSERV_FUNC(cmd_uset
)
5802 struct svccmd
*subcmd
;
5806 /* Check if we need to (re-)initialize uset_shows_list. */
5807 if(!uset_shows_list
.used
)
5811 "AutoOp", "AutoInvite", "Info"
5814 if(!uset_shows_list
.size
)
5816 uset_shows_list
.size
= ArrayLength(options
);
5817 uset_shows_list
.list
= calloc(uset_shows_list
.size
, sizeof(uset_shows_list
.list
[0]));
5819 for(ii
= 0; ii
< ArrayLength(options
); ii
++)
5821 const char *name
= options
[ii
];
5822 sprintf(buf
, "%s %s", argv
[0], name
);
5823 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
5826 log_module(CS_LOG
, LOG_ERROR
, "Unable to find uset option %s.", name
);
5829 svccmd_list_append(&uset_shows_list
, subcmd
);
5835 /* Do this so options are presented in a consistent order. */
5836 reply("CSMSG_USER_OPTIONS");
5837 for(ii
= 0; ii
< uset_shows_list
.used
; ii
++)
5838 uset_shows_list
.list
[ii
]->command
->func(user
, channel
, 1, argv
+1, uset_shows_list
.list
[ii
]);
5842 sprintf(buf
, "%s %s", argv
[0], argv
[1]);
5843 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
5846 reply("CSMSG_INVALID_OPTION", argv
[1], argv
[0]);
5850 return subcmd
->command
->func(user
, channel
, argc
- 1, argv
+ 1, subcmd
);
5853 static CHANSERV_FUNC(cmd_giveownership
)
5855 struct handle_info
*new_owner_hi
;
5856 struct userData
*new_owner
, *curr_user
;
5857 struct chanData
*cData
= channel
->channel_info
;
5858 struct do_not_register
*dnr
;
5860 unsigned short co_access
;
5861 char reason
[MAXLEN
];
5864 curr_user
= GetChannelAccess(cData
, user
->handle_info
);
5865 force
= IsHelping(user
) && (argc
> 2) && !irccasecmp(argv
[2], "force");
5866 if(!curr_user
|| (curr_user
->access
!= UL_OWNER
))
5868 struct userData
*owner
= NULL
;
5869 for(curr_user
= channel
->channel_info
->users
;
5871 curr_user
= curr_user
->next
)
5873 if(curr_user
->access
!= UL_OWNER
)
5877 reply("CSMSG_MULTIPLE_OWNERS", channel
->name
);
5884 else if (!force
&& (now
< (time_t)(cData
->ownerTransfer
+ chanserv_conf
.giveownership_period
)))
5886 char delay
[INTERVALLEN
];
5887 intervalString(delay
, cData
->ownerTransfer
+ chanserv_conf
.giveownership_period
- now
, user
->handle_info
);
5888 reply("CSMSG_TRANSFER_WAIT", delay
, channel
->name
);
5891 if(!(new_owner_hi
= modcmd_get_handle_info(user
, argv
[1])))
5893 if(new_owner_hi
== user
->handle_info
)
5895 reply("CSMSG_NO_TRANSFER_SELF");
5898 new_owner
= GetChannelAccess(cData
, new_owner_hi
);
5903 new_owner
= add_channel_user(cData
, new_owner_hi
, UL_COOWNER
, 0, NULL
);
5907 reply("CSMSG_NO_CHAN_USER", new_owner_hi
->handle
, channel
->name
);
5911 if((chanserv_get_owned_count(new_owner_hi
) >= chanserv_conf
.max_owned
) && !force
)
5913 reply("CSMSG_OWN_TOO_MANY", new_owner_hi
->handle
, chanserv_conf
.max_owned
);
5916 if((dnr
= chanserv_is_dnr(NULL
, new_owner_hi
)) && !force
) {
5917 if(!IsHelping(user
))
5918 reply("CSMSG_DNR_ACCOUNT", new_owner_hi
->handle
);
5920 chanserv_show_dnrs(user
, cmd
, NULL
, new_owner_hi
->handle
);
5923 if(new_owner
->access
>= UL_COOWNER
)
5924 co_access
= new_owner
->access
;
5926 co_access
= UL_COOWNER
;
5927 new_owner
->access
= UL_OWNER
;
5929 curr_user
->access
= co_access
;
5930 cData
->ownerTransfer
= now
;
5931 reply("CSMSG_OWNERSHIP_GIVEN", channel
->name
, new_owner_hi
->handle
);
5932 sprintf(reason
, "%s ownership transferred to %s by %s.", channel
->name
, new_owner_hi
->handle
, user
->handle_info
->handle
);
5933 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
5937 static CHANSERV_FUNC(cmd_suspend
)
5939 struct handle_info
*hi
;
5940 struct userData
*self
, *target
;
5943 if(!(hi
= modcmd_get_handle_info(user
, argv
[1]))) return 0;
5944 self
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
5945 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
5947 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
5950 if(target
->access
>= self
->access
)
5952 reply("MSG_USER_OUTRANKED", hi
->handle
);
5955 if(target
->flags
& USER_SUSPENDED
)
5957 reply("CSMSG_ALREADY_SUSPENDED", hi
->handle
);
5962 target
->present
= 0;
5965 target
->flags
|= USER_SUSPENDED
;
5966 reply("CSMSG_USER_SUSPENDED", hi
->handle
, channel
->name
);
5970 static CHANSERV_FUNC(cmd_unsuspend
)
5972 struct handle_info
*hi
;
5973 struct userData
*self
, *target
;
5976 if(!(hi
= modcmd_get_handle_info(user
, argv
[1]))) return 0;
5977 self
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
5978 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
5980 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
5983 if(target
->access
>= self
->access
)
5985 reply("MSG_USER_OUTRANKED", hi
->handle
);
5988 if(!(target
->flags
& USER_SUSPENDED
))
5990 reply("CSMSG_NOT_SUSPENDED", hi
->handle
);
5993 target
->flags
&= ~USER_SUSPENDED
;
5994 scan_user_presence(target
, NULL
);
5995 reply("CSMSG_USER_UNSUSPENDED", hi
->handle
, channel
->name
);
5999 static MODCMD_FUNC(cmd_deleteme
)
6001 struct handle_info
*hi
;
6002 struct userData
*target
;
6003 const char *confirm_string
;
6004 unsigned short access
;
6007 hi
= user
->handle_info
;
6008 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
6010 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
6013 if(target
->access
== UL_OWNER
)
6015 reply("CSMSG_NO_OWNER_DELETEME", channel
->name
);
6018 confirm_string
= make_confirmation_string(target
);
6019 if((argc
< 2) || strcmp(argv
[1], confirm_string
))
6021 reply("CSMSG_CONFIRM_DELETEME", confirm_string
);
6024 access
= target
->access
;
6025 channel_name
= strdup(channel
->name
);
6026 del_channel_user(target
, 1);
6027 reply("CSMSG_DELETED_YOU", access
, channel_name
);
6033 chanserv_refresh_topics(UNUSED_ARG(void *data
))
6035 unsigned int refresh_num
= (now
- self
->link
) / chanserv_conf
.refresh_period
;
6036 struct chanData
*cData
;
6039 for(cData
= channelList
; cData
; cData
= cData
->next
)
6041 if(IsSuspended(cData
))
6043 opt
= cData
->chOpts
[chTopicRefresh
];
6046 if((refresh_num
- cData
->last_refresh
) < (unsigned int)(1 << (opt
- '1')))
6049 SetChannelTopic(cData
->channel
, chanserv
, cData
->topic
, 1);
6050 cData
->last_refresh
= refresh_num
;
6052 timeq_add(now
+ chanserv_conf
.refresh_period
, chanserv_refresh_topics
, NULL
);
6055 static CHANSERV_FUNC(cmd_unf
)
6059 char response
[MAXLEN
];
6060 const char *fmt
= user_find_message(user
, "CSMSG_UNF_RESPONSE");
6061 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
6062 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6065 reply("CSMSG_UNF_RESPONSE");
6069 static CHANSERV_FUNC(cmd_ping
)
6073 char response
[MAXLEN
];
6074 const char *fmt
= user_find_message(user
, "CSMSG_PING_RESPONSE");
6075 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
6076 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6079 reply("CSMSG_PING_RESPONSE");
6083 static CHANSERV_FUNC(cmd_wut
)
6087 char response
[MAXLEN
];
6088 const char *fmt
= user_find_message(user
, "CSMSG_WUT_RESPONSE");
6089 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
6090 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6093 reply("CSMSG_WUT_RESPONSE");
6097 static CHANSERV_FUNC(cmd_8ball
)
6099 unsigned int i
, j
, accum
;
6104 for(i
=1; i
<argc
; i
++)
6105 for(j
=0; argv
[i
][j
]; j
++)
6106 accum
= (accum
<< 5) - accum
+ toupper(argv
[i
][j
]);
6107 resp
= chanserv_conf
.eightball
->list
[accum
% chanserv_conf
.eightball
->used
];
6110 char response
[MAXLEN
];
6111 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, resp
);
6112 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6115 send_message_type(4, user
, cmd
->parent
->bot
, "%s", resp
);
6119 static CHANSERV_FUNC(cmd_d
)
6121 unsigned long sides
, count
, modifier
, ii
, total
;
6122 char response
[MAXLEN
], *sep
;
6126 if((count
= strtoul(argv
[1], &sep
, 10)) < 1)
6136 else if(((sep
[0] == 'd') || (sep
[0] == 'D')) && isdigit(sep
[1])
6137 && (sides
= strtoul(sep
+1, &sep
, 10)) > 1)
6141 else if((sep
[0] == '-') && isdigit(sep
[1]))
6142 modifier
= strtoul(sep
, NULL
, 10);
6143 else if((sep
[0] == '+') && isdigit(sep
[1]))
6144 modifier
= strtoul(sep
+1, NULL
, 10);
6151 reply("CSMSG_BAD_DIE_FORMAT", argv
[1]);
6156 reply("CSMSG_BAD_DICE_COUNT", count
, 10);
6159 for(total
= ii
= 0; ii
< count
; ++ii
)
6160 total
+= (rand() % sides
) + 1;
6163 if((count
> 1) || modifier
)
6165 fmt
= user_find_message(user
, "CSMSG_DICE_ROLL");
6166 sprintf(response
, fmt
, total
, count
, sides
, modifier
);
6170 fmt
= user_find_message(user
, "CSMSG_DIE_ROLL");
6171 sprintf(response
, fmt
, total
, sides
);
6174 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, response
);
6176 send_message_type(4, user
, cmd
->parent
->bot
, "%s", response
);
6180 static CHANSERV_FUNC(cmd_huggle
)
6182 /* CTCP must be via PRIVMSG, never notice */
6184 send_target_message(1, channel
->name
, cmd
->parent
->bot
, "CSMSG_HUGGLES_HIM", user
->nick
);
6186 send_target_message(1, user
->nick
, cmd
->parent
->bot
, "CSMSG_HUGGLES_YOU");
6190 static CHANSERV_FUNC(cmd_calc
)
6192 char response
[MAXLEN
];
6195 do_math(response
, unsplit_string(argv
+ 1, argc
- 1, NULL
));
6198 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, response
);
6200 send_message_type(4, user
, cmd
->parent
->bot
, "%s", response
);
6205 chanserv_adjust_limit(void *data
)
6207 struct mod_chanmode change
;
6208 struct chanData
*cData
= data
;
6209 struct chanNode
*channel
= cData
->channel
;
6212 if(IsSuspended(cData
))
6215 cData
->limitAdjusted
= now
;
6216 limit
= channel
->members
.used
+ chanserv_conf
.adjust_threshold
+ 5;
6217 if(cData
->modes
.modes_set
& MODE_LIMIT
)
6219 if(limit
> cData
->modes
.new_limit
)
6220 limit
= cData
->modes
.new_limit
;
6221 else if(limit
== cData
->modes
.new_limit
)
6225 mod_chanmode_init(&change
);
6226 change
.modes_set
= MODE_LIMIT
;
6227 change
.new_limit
= limit
;
6228 mod_chanmode_announce(chanserv
, channel
, &change
);
6232 handle_new_channel(struct chanNode
*channel
)
6234 struct chanData
*cData
;
6236 if(!(cData
= channel
->channel_info
))
6239 if(cData
->modes
.modes_set
|| cData
->modes
.modes_clear
)
6240 mod_chanmode_announce(chanserv
, cData
->channel
, &cData
->modes
);
6242 if(self
->uplink
&& !self
->uplink
->burst
&& channel
->channel_info
->topic
)
6243 SetChannelTopic(channel
, chanserv
, channel
->channel_info
->topic
, 1);
6246 /* Welcome to my worst nightmare. Warning: Read (or modify)
6247 the code below at your own risk. */
6249 handle_join(struct modeNode
*mNode
)
6251 struct mod_chanmode change
;
6252 struct userNode
*user
= mNode
->user
;
6253 struct chanNode
*channel
= mNode
->channel
;
6254 struct chanData
*cData
;
6255 struct userData
*uData
= NULL
;
6256 struct banData
*bData
;
6257 struct handle_info
*handle
;
6258 unsigned int modes
= 0, info
= 0;
6261 if(IsLocal(user
) || !channel
->channel_info
|| IsSuspended(channel
->channel_info
))
6264 cData
= channel
->channel_info
;
6265 if(channel
->members
.used
> cData
->max
)
6266 cData
->max
= channel
->members
.used
;
6268 /* Check for bans. If they're joining through a ban, one of two
6270 * 1: Join during a netburst, by riding the break. Kick them
6271 * unless they have ops or voice in the channel.
6272 * 2: They're allowed to join through the ban (an invite in
6273 * ircu2.10, or a +e on Hybrid, or something).
6274 * If they're not joining through a ban, and the banlist is not
6275 * full, see if they're on the banlist for the channel. If so,
6278 /* This is really, really stupid. not all banned people are kicked.
6279 * sometimes we like to leave them unkicked.
6280 * I tried to explain this to the srvx developers and
6281 * got insulted.. hence one reason for this fork.
6283 if(user->uplink->burst && !mNode->modes)
6286 for(ii = 0; ii < channel->banlist.used; ii++)
6288 if(user_matches_glob(user, channel->banlist.list[ii]->ban, 1))
6290 ** Riding a netburst. Naughty. **
6291 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
6298 mod_chanmode_init(&change
);
6300 if(channel
->banlist
.used
< MAXBANS
)
6302 /* Not joining through a ban. */
6303 for(bData
= cData
->bans
;
6304 bData
&& !user_matches_glob(user
, bData
->mask
, 1);
6305 bData
= bData
->next
);
6309 char kick_reason
[MAXLEN
];
6310 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
6312 bData
->triggered
= now
;
6313 if(bData
!= cData
->bans
)
6315 /* Shuffle the ban to the head of the list. */
6317 bData
->next
->prev
= bData
->prev
;
6319 bData
->prev
->next
= bData
->next
;
6322 bData
->next
= cData
->bans
;
6325 cData
->bans
->prev
= bData
;
6326 cData
->bans
= bData
;
6329 change
.args
[0].mode
= MODE_BAN
;
6330 change
.args
[0].u
.hostmask
= bData
->mask
;
6331 mod_chanmode_announce(chanserv
, channel
, &change
);
6332 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
6337 /* ChanServ will not modify the limits in join-flooded channels.
6338 It will also skip DynLimit processing when the user (or srvx)
6339 is bursting in, because there are likely more incoming. */
6340 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
)
6341 && !user
->uplink
->burst
6342 && !channel
->join_flooded
6343 && (channel
->limit
- channel
->members
.used
) < chanserv_conf
.adjust_threshold
)
6345 /* The user count has begun "bumping" into the channel limit,
6346 so set a timer to raise the limit a bit. Any previous
6347 timers are removed so three incoming users within the delay
6348 results in one limit change, not three. */
6350 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
6351 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
6354 if(channel
->join_flooded
)
6356 /* don't automatically give non users ops or voice during a join flood */
6358 /* EVERYONE is to get voice */
6359 else if(cData
->chOpts
[chVoice
] == 'a')
6360 modes
|= MODE_VOICE
;
6362 greeting
= cData
->greeting
;
6363 if(user
->handle_info
)
6365 handle
= user
->handle_info
;
6367 if(IsHelper(user
) && !IsHelping(user
))
6370 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
6372 if(channel
== chanserv_conf
.support_channels
.list
[ii
])
6374 HANDLE_SET_FLAG(user
->handle_info
, HELPING
);
6380 uData
= GetTrueChannelAccess(cData
, handle
);
6381 if(uData
&& !IsUserSuspended(uData
))
6383 /* non users getting voice are handled above. */
6384 if(IsUserAutoOp(uData
))
6386 if(uData
->access
>= UL_OP
/*cData->lvlOpts[lvlGiveOps]*/)
6387 modes
|= MODE_CHANOP
;
6388 if(uData
->access
>= UL_HALFOP
/*cData->lvlOpts[lvlGiveHalfOps]*/)
6389 modes
|= MODE_HALFOP
;
6390 else if(uData
->access
>= UL_PEON
&& cData
->chOpts
[chVoice
] == 'p')
6391 modes
|= MODE_VOICE
;
6393 if(uData
->access
>= UL_PRESENT
)
6394 cData
->visited
= now
;
6395 if(cData
->user_greeting
)
6396 greeting
= cData
->user_greeting
;
6398 && (uData
->access
>= cData
->lvlOpts
[lvlUserInfo
])
6399 && ((now
- uData
->seen
) >= chanserv_conf
.info_delay
)
6406 if(!user
->uplink
->burst
)
6410 if(modes
& MODE_CHANOP
) {
6411 modes
&= ~MODE_HALFOP
;
6412 modes
&= ~MODE_VOICE
;
6414 change
.args
[0].mode
= modes
;
6415 change
.args
[0].u
.member
= mNode
;
6416 mod_chanmode_announce(chanserv
, channel
, &change
);
6418 if(greeting
&& !user
->uplink
->burst
)
6419 send_message_type(4, user
, chanserv
, "(%s) %s", channel
->name
, greeting
);
6421 send_target_message(5, channel
->name
, chanserv
, "[%s] %s", user
->nick
, uData
->info
);
6427 handle_auth(struct userNode
*user
, UNUSED_ARG(struct handle_info
*old_handle
))
6429 struct mod_chanmode change
;
6430 struct userData
*channel
;
6431 unsigned int ii
, jj
;
6433 if(!user
->handle_info
)
6436 mod_chanmode_init(&change
);
6438 for(channel
= user
->handle_info
->channels
; channel
; channel
= channel
->u_next
)
6440 struct chanNode
*cn
;
6441 struct modeNode
*mn
;
6442 if(IsUserSuspended(channel
)
6443 || IsSuspended(channel
->channel
)
6444 || !(cn
= channel
->channel
->channel
))
6447 mn
= GetUserMode(cn
, user
);
6450 if(!IsUserSuspended(channel
)
6451 && IsUserAutoInvite(channel
)
6452 && (channel
->access
>= channel
->channel
->lvlOpts
[lvlInviteMe
])
6454 && !user
->uplink
->burst
)
6455 irc_invite(chanserv
, user
, cn
);
6459 if(channel
->access
>= UL_PRESENT
)
6460 channel
->channel
->visited
= now
;
6462 if(IsUserAutoOp(channel
))
6464 if(channel
->access
>= UL_OP
/* cn->channel_info->lvlOpts[lvlGiveOps] */)
6465 change
.args
[0].mode
= MODE_CHANOP
;
6466 else if(channel
->access
>= UL_HALFOP
/* cn->channel_info->lvlOpts[lvlGiveHalfOps]*/)
6467 change
.args
[0].mode
= MODE_HALFOP
;
6468 else if(channel
->access
>= UL_PEON
/* cn->channel_info->lvlOpts[lvlGiveVoice]*/)
6469 change
.args
[0].mode
= MODE_VOICE
;
6471 change
.args
[0].mode
= 0;
6472 change
.args
[0].u
.member
= mn
;
6473 if(change
.args
[0].mode
)
6474 mod_chanmode_announce(chanserv
, cn
, &change
);
6477 channel
->seen
= now
;
6478 channel
->present
= 1;
6481 for(ii
= 0; ii
< user
->channels
.used
; ++ii
)
6483 struct chanNode
*channel
= user
->channels
.list
[ii
]->channel
;
6484 struct banData
*ban
;
6486 if((user
->channels
.list
[ii
]->modes
& (MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
))
6487 || !channel
->channel_info
6488 || IsSuspended(channel
->channel_info
))
6490 for(jj
= 0; jj
< channel
->banlist
.used
; ++jj
)
6491 if(user_matches_glob(user
, channel
->banlist
.list
[jj
]->ban
, 1))
6493 if(jj
< channel
->banlist
.used
)
6495 for(ban
= channel
->channel_info
->bans
; ban
; ban
= ban
->next
)
6497 char kick_reason
[MAXLEN
];
6498 if(!user_matches_glob(user
, ban
->mask
, 1))
6500 change
.args
[0].mode
= MODE_BAN
;
6501 change
.args
[0].u
.hostmask
= ban
->mask
;
6502 mod_chanmode_announce(chanserv
, channel
, &change
);
6503 sprintf(kick_reason
, "(%s) %s", ban
->owner
, ban
->reason
);
6504 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
6505 ban
->triggered
= now
;
6510 if(IsSupportHelper(user
))
6512 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
6514 if(GetUserMode(chanserv_conf
.support_channels
.list
[ii
], user
))
6516 HANDLE_SET_FLAG(user
->handle_info
, HELPING
);
6524 handle_part(struct modeNode
*mn
, UNUSED_ARG(const char *reason
))
6526 struct chanData
*cData
;
6527 struct userData
*uData
;
6529 cData
= mn
->channel
->channel_info
;
6530 if(!cData
|| IsSuspended(cData
) || IsLocal(mn
->user
))
6533 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
) && !mn
->channel
->join_flooded
)
6535 /* Allow for a bit of padding so that the limit doesn't
6536 track the user count exactly, which could get annoying. */
6537 if((mn
->channel
->limit
- mn
->channel
->members
.used
) > chanserv_conf
.adjust_threshold
+ 5)
6539 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
6540 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
6544 if((uData
= GetTrueChannelAccess(cData
, mn
->user
->handle_info
)))
6546 scan_user_presence(uData
, mn
->user
);
6550 if(IsHelping(mn
->user
) && IsSupportHelper(mn
->user
))
6552 unsigned int ii
, jj
;
6553 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
6555 for(jj
= 0; jj
< mn
->user
->channels
.used
; ++jj
)
6556 if(mn
->user
->channels
.list
[jj
]->channel
== chanserv_conf
.support_channels
.list
[ii
])
6558 if(jj
< mn
->user
->channels
.used
)
6561 if(ii
== chanserv_conf
.support_channels
.used
)
6562 HANDLE_CLEAR_FLAG(mn
->user
->handle_info
, HELPING
);
6567 handle_kick(struct userNode
*kicker
, struct userNode
*victim
, struct chanNode
*channel
)
6569 struct userData
*uData
;
6571 if(!channel
->channel_info
|| !kicker
|| IsService(kicker
)
6572 || (kicker
== victim
) || IsSuspended(channel
->channel_info
)
6573 || (kicker
->handle_info
&& kicker
->handle_info
== victim
->handle_info
))
6576 if(protect_user(victim
, kicker
, channel
->channel_info
))
6578 const char *reason
= user_find_message(kicker
, "CSMSG_USER_PROTECTED");
6579 KickChannelUser(kicker
, channel
, chanserv
, reason
);
6582 if((uData
= GetTrueChannelAccess(channel
->channel_info
, victim
->handle_info
)))
6587 handle_topic(struct userNode
*user
, struct chanNode
*channel
, const char *old_topic
)
6589 struct chanData
*cData
;
6591 if(!channel
->channel_info
|| !user
|| IsSuspended(channel
->channel_info
) || IsService(user
))
6594 cData
= channel
->channel_info
;
6595 if(bad_topic(channel
, user
, channel
->topic
))
6596 { /* User doesnt have privs to set topics. Undo it */
6597 send_message(user
, chanserv
, "CSMSG_TOPIC_LOCKED", channel
->name
);
6598 SetChannelTopic(channel
, chanserv
, old_topic
, 1);
6601 /* If there is a topic mask set, and the new topic doesnt match,
6602 * set the topic to mask + new_topic */
6603 if(cData
->topic_mask
&& !match_ircglob(channel
->topic
, cData
->topic_mask
))
6605 char new_topic
[TOPICLEN
+1];
6606 conform_topic(cData
->topic_mask
, channel
->topic
, new_topic
);
6609 SetChannelTopic(channel
, chanserv
, new_topic
, 1);
6610 /* and fall through to topicsnarf code below.. */
6612 else /* Topic couldnt fit into mask, was too long */
6614 SetChannelTopic(channel
, chanserv
, old_topic
, 1);
6615 send_message(user
, chanserv
, "CSMSG_TOPICMASK_CONFLICT1", channel
->name
, cData
->topic_mask
);
6616 send_message(user
, chanserv
, "CSMSG_TOPICMASK_CONFLICT2", TOPICLEN
);
6620 /* With topicsnarf, grab the topic and save it as the default topic. */
6621 if(check_user_level(channel
, user
, lvlTopicSnarf
, 0, 0))
6624 cData
->topic
= strdup(channel
->topic
);
6630 handle_mode(struct chanNode
*channel
, struct userNode
*user
, const struct mod_chanmode
*change
)
6632 struct mod_chanmode
*bounce
= NULL
;
6633 unsigned int bnc
, ii
;
6636 if(!channel
->channel_info
|| IsLocal(user
) || IsSuspended(channel
->channel_info
) || IsService(user
))
6639 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
6640 && mode_lock_violated(&channel
->channel_info
->modes
, change
))
6642 char correct
[MAXLEN
];
6643 bounce
= mod_chanmode_dup(&channel
->channel_info
->modes
, change
->argc
+ 1);
6644 mod_chanmode_format(&channel
->channel_info
->modes
, correct
);
6645 send_message(user
, chanserv
, "CSMSG_MODE_LOCKED", correct
, channel
->name
);
6647 for(ii
= bnc
= 0; ii
< change
->argc
; ++ii
)
6649 if((change
->args
[ii
].mode
& (MODE_REMOVE
|MODE_CHANOP
)) == (MODE_REMOVE
|MODE_CHANOP
))
6651 const struct userNode
*victim
= change
->args
[ii
].u
.member
->user
;
6652 if(!protect_user(victim
, user
, channel
->channel_info
))
6655 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
6658 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_CHANOP
;
6659 bounce
->args
[bnc
].u
.member
= GetUserMode(channel
, user
);
6660 if(bounce
->args
[bnc
].u
.member
)
6664 bounce
->args
[bnc
].mode
= MODE_CHANOP
;
6665 bounce
->args
[bnc
].u
.member
= change
->args
[ii
].u
.member
;
6667 send_message(user
, chanserv
, "CSMSG_USER_PROTECTED", victim
->nick
);
6669 else if(change
->args
[ii
].mode
& MODE_CHANOP
)
6671 const struct userNode
*victim
= change
->args
[ii
].u
.member
->user
;
6672 if(IsService(victim
) || validate_op(user
, channel
, (struct userNode
*)victim
))
6675 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
6676 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_CHANOP
;
6677 bounce
->args
[bnc
].u
.member
= change
->args
[ii
].u
.member
;
6680 else if((change
->args
[ii
].mode
& (MODE_REMOVE
| MODE_BAN
)) == MODE_BAN
)
6682 const char *ban
= change
->args
[ii
].u
.hostmask
;
6683 if(!bad_channel_ban(channel
, user
, ban
, NULL
, NULL
))
6686 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
6687 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_BAN
;
6688 bounce
->args
[bnc
].u
.hostmask
= strdup(ban
);
6690 send_message(user
, chanserv
, "CSMSG_MASK_PROTECTED", ban
);
6695 if((bounce
->argc
= bnc
) || bounce
->modes_set
|| bounce
->modes_clear
)
6696 mod_chanmode_announce(chanserv
, channel
, bounce
);
6697 for(ii
= 0; ii
< change
->argc
; ++ii
)
6698 if(bounce
->args
[ii
].mode
== (MODE_REMOVE
| MODE_BAN
))
6699 free((char*)bounce
->args
[ii
].u
.hostmask
);
6700 mod_chanmode_free(bounce
);
6705 handle_nick_change(struct userNode
*user
, UNUSED_ARG(const char *old_nick
))
6707 struct chanNode
*channel
;
6708 struct banData
*bData
;
6709 struct mod_chanmode change
;
6710 unsigned int ii
, jj
;
6711 char kick_reason
[MAXLEN
];
6713 mod_chanmode_init(&change
);
6715 change
.args
[0].mode
= MODE_BAN
;
6716 for(ii
= 0; ii
< user
->channels
.used
; ++ii
)
6718 channel
= user
->channels
.list
[ii
]->channel
;
6719 /* Need not check for bans if they're opped or voiced. */
6720 if(user
->channels
.list
[ii
]->modes
& (MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
))
6722 /* Need not check for bans unless channel registration is active. */
6723 if(!channel
->channel_info
|| IsSuspended(channel
->channel_info
))
6725 /* Look for a matching ban already on the channel. */
6726 for(jj
= 0; jj
< channel
->banlist
.used
; ++jj
)
6727 if(user_matches_glob(user
, channel
->banlist
.list
[jj
]->ban
, 1))
6729 /* Need not act if we found one. */
6730 if(jj
< channel
->banlist
.used
)
6732 /* Look for a matching ban in this channel. */
6733 for(bData
= channel
->channel_info
->bans
; bData
; bData
= bData
->next
)
6735 if(!user_matches_glob(user
, bData
->mask
, 1))
6737 change
.args
[0].u
.hostmask
= bData
->mask
;
6738 mod_chanmode_announce(chanserv
, channel
, &change
);
6739 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
6740 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
6741 bData
->triggered
= now
;
6742 break; /* we don't need to check any more bans in the channel */
6747 static void handle_rename(struct handle_info
*handle
, const char *old_handle
)
6749 struct do_not_register
*dnr
= dict_find(handle_dnrs
, old_handle
, NULL
);
6753 dict_remove2(handle_dnrs
, old_handle
, 1);
6754 safestrncpy(dnr
->chan_name
+ 1, handle
->handle
, sizeof(dnr
->chan_name
) - 1);
6755 dict_insert(handle_dnrs
, dnr
->chan_name
+ 1, dnr
);
6760 handle_unreg(UNUSED_ARG(struct userNode
*user
), struct handle_info
*handle
)
6762 struct userNode
*h_user
;
6764 if(handle
->channels
)
6766 for(h_user
= handle
->users
; h_user
; h_user
= h_user
->next_authed
)
6767 send_message(h_user
, chanserv
, "CSMSG_HANDLE_UNREGISTERED");
6769 while(handle
->channels
)
6770 del_channel_user(handle
->channels
, 1);
6775 handle_server_link(UNUSED_ARG(struct server
*server
))
6777 struct chanData
*cData
;
6779 for(cData
= channelList
; cData
; cData
= cData
->next
)
6781 if(!IsSuspended(cData
))
6782 cData
->may_opchan
= 1;
6783 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
)
6784 && !cData
->channel
->join_flooded
6785 && ((cData
->channel
->limit
- cData
->channel
->members
.used
)
6786 < chanserv_conf
.adjust_threshold
))
6788 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
6789 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
6795 chanserv_conf_read(void)
6799 char mode_line
[MAXLEN
], *modes
[MAXNUMPARAMS
];
6800 struct mod_chanmode
*change
;
6801 struct string_list
*strlist
;
6802 struct chanNode
*chan
;
6805 if(!(conf_node
= conf_get_data(CHANSERV_CONF_NAME
, RECDB_OBJECT
)))
6807 log_module(CS_LOG
, LOG_ERROR
, "Invalid config node `%s'.", CHANSERV_CONF_NAME
);
6810 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
6811 UnlockChannel(chanserv_conf
.support_channels
.list
[ii
]);
6812 chanserv_conf
.support_channels
.used
= 0;
6813 if((strlist
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL
, RECDB_STRING_LIST
)))
6815 for(ii
= 0; ii
< strlist
->used
; ++ii
)
6817 const char *str2
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL_MODES
, RECDB_QSTRING
);
6820 chan
= AddChannel(strlist
->list
[ii
], now
, str2
, NULL
, NULL
);
6822 channelList_append(&chanserv_conf
.support_channels
, chan
);
6825 else if((str
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL
, RECDB_QSTRING
)))
6828 str2
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL_MODES
, RECDB_QSTRING
);
6831 chan
= AddChannel(str
, now
, str2
, NULL
, NULL
);
6833 channelList_append(&chanserv_conf
.support_channels
, chan
);
6835 str
= database_get_data(conf_node
, KEY_DB_BACKUP_FREQ
, RECDB_QSTRING
);
6836 chanserv_conf
.db_backup_frequency
= str
? ParseInterval(str
) : 7200;
6837 str
= database_get_data(conf_node
, KEY_INFO_DELAY
, RECDB_QSTRING
);
6838 chanserv_conf
.info_delay
= str
? ParseInterval(str
) : 180;
6839 str
= database_get_data(conf_node
, KEY_MAX_GREETLEN
, RECDB_QSTRING
);
6840 chanserv_conf
.greeting_length
= str
? atoi(str
) : 200;
6841 str
= database_get_data(conf_node
, KEY_ADJUST_THRESHOLD
, RECDB_QSTRING
);
6842 chanserv_conf
.adjust_threshold
= str
? atoi(str
) : 15;
6843 str
= database_get_data(conf_node
, KEY_ADJUST_DELAY
, RECDB_QSTRING
);
6844 chanserv_conf
.adjust_delay
= str
? ParseInterval(str
) : 30;
6845 str
= database_get_data(conf_node
, KEY_CHAN_EXPIRE_FREQ
, RECDB_QSTRING
);
6846 chanserv_conf
.channel_expire_frequency
= str
? ParseInterval(str
) : 86400;
6847 str
= database_get_data(conf_node
, KEY_CHAN_EXPIRE_DELAY
, RECDB_QSTRING
);
6848 chanserv_conf
.channel_expire_delay
= str
? ParseInterval(str
) : 86400*30;
6849 str
= database_get_data(conf_node
, KEY_NODELETE_LEVEL
, RECDB_QSTRING
);
6850 chanserv_conf
.nodelete_level
= str
? atoi(str
) : 1;
6851 str
= database_get_data(conf_node
, KEY_MAX_CHAN_USERS
, RECDB_QSTRING
);
6852 chanserv_conf
.max_chan_users
= str
? atoi(str
) : 512;
6853 str
= database_get_data(conf_node
, KEY_MAX_CHAN_BANS
, RECDB_QSTRING
);
6854 chanserv_conf
.max_chan_bans
= str
? atoi(str
) : 512;
6855 str
= database_get_data(conf_node
, KEY_MAX_USERINFO_LENGTH
, RECDB_QSTRING
);
6856 chanserv_conf
.max_userinfo_length
= str
? atoi(str
) : 400;
6857 str
= database_get_data(conf_node
, KEY_NICK
, RECDB_QSTRING
);
6859 NickChange(chanserv
, str
, 0);
6860 str
= database_get_data(conf_node
, KEY_REFRESH_PERIOD
, RECDB_QSTRING
);
6861 chanserv_conf
.refresh_period
= str
? ParseInterval(str
) : 3*60*60;
6862 str
= database_get_data(conf_node
, KEY_GIVEOWNERSHIP_PERIOD
, RECDB_QSTRING
);
6863 chanserv_conf
.giveownership_period
= str
? ParseInterval(str
) : 0;
6864 str
= database_get_data(conf_node
, KEY_CTCP_SHORT_BAN_DURATION
, RECDB_QSTRING
);
6865 chanserv_conf
.ctcp_short_ban_duration
= str
? str
: "3m";
6866 str
= database_get_data(conf_node
, KEY_CTCP_LONG_BAN_DURATION
, RECDB_QSTRING
);
6867 chanserv_conf
.ctcp_long_ban_duration
= str
? str
: "1h";
6868 str
= database_get_data(conf_node
, KEY_MAX_OWNED
, RECDB_QSTRING
);
6869 chanserv_conf
.max_owned
= str
? atoi(str
) : 5;
6870 str
= database_get_data(conf_node
, KEY_IRC_OPERATOR_EPITHET
, RECDB_QSTRING
);
6871 chanserv_conf
.irc_operator_epithet
= str
? str
: "a megalomaniacal power hungry tyrant";
6872 str
= database_get_data(conf_node
, KEY_NETWORK_HELPER_EPITHET
, RECDB_QSTRING
);
6873 chanserv_conf
.network_helper_epithet
= str
? str
: "a wannabe tyrant";
6874 str
= database_get_data(conf_node
, KEY_SUPPORT_HELPER_EPITHET
, RECDB_QSTRING
);
6875 chanserv_conf
.support_helper_epithet
= str
? str
: "a wannabe tyrant";
6876 str
= database_get_data(conf_node
, "default_modes", RECDB_QSTRING
);
6879 safestrncpy(mode_line
, str
, sizeof(mode_line
));
6880 ii
= split_line(mode_line
, 0, ArrayLength(modes
), modes
);
6881 if((change
= mod_chanmode_parse(NULL
, modes
, ii
, MCP_KEY_FREE
)) && (change
->argc
< 2))
6883 chanserv_conf
.default_modes
= *change
;
6884 mod_chanmode_free(change
);
6886 free_string_list(chanserv_conf
.set_shows
);
6887 strlist
= database_get_data(conf_node
, "set_shows", RECDB_STRING_LIST
);
6889 strlist
= string_list_copy(strlist
);
6892 static const char *list
[] = {
6893 /* free form text */
6894 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
6895 /* options based on user level */
6896 "PubCmd", "InviteMe", "UserInfo",/* "GiveVoice", "GiveHalfOps", "GiveOps", */ "EnfOps",
6897 "EnfHalfOps", "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
6898 /* multiple choice options */
6899 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
6900 /* binary options */
6901 "DynLimit", "NoDelete",
6906 strlist
= alloc_string_list(ArrayLength(list
)-1);
6907 for(ii
=0; list
[ii
]; ii
++)
6908 string_list_append(strlist
, strdup(list
[ii
]));
6910 chanserv_conf
.set_shows
= strlist
;
6911 /* We don't look things up now, in case the list refers to options
6912 * defined by modules initialized after this point. Just mark the
6913 * function list as invalid, so it will be initialized.
6915 set_shows_list
.used
= 0;
6916 free_string_list(chanserv_conf
.eightball
);
6917 strlist
= database_get_data(conf_node
, KEY_8BALL_RESPONSES
, RECDB_STRING_LIST
);
6920 strlist
= string_list_copy(strlist
);
6924 strlist
= alloc_string_list(4);
6925 string_list_append(strlist
, strdup("Yes."));
6926 string_list_append(strlist
, strdup("No."));
6927 string_list_append(strlist
, strdup("Maybe so."));
6929 chanserv_conf
.eightball
= strlist
;
6930 free_string_list(chanserv_conf
.old_ban_names
);
6931 strlist
= database_get_data(conf_node
, KEY_OLD_BAN_NAMES
, RECDB_STRING_LIST
);
6933 strlist
= string_list_copy(strlist
);
6935 strlist
= alloc_string_list(2);
6936 chanserv_conf
.old_ban_names
= strlist
;
6937 str
= database_get_data(conf_node
, "off_channel", RECDB_QSTRING
);
6938 off_channel
= str
? atoi(str
) : 0;
6942 chanserv_note_type_read(const char *key
, struct record_data
*rd
)
6945 struct note_type
*ntype
;
6948 if(!(obj
= GET_RECORD_OBJECT(rd
)))
6950 log_module(CS_LOG
, LOG_ERROR
, "Invalid note type %s.", key
);
6953 if(!(ntype
= chanserv_create_note_type(key
)))
6955 log_module(CS_LOG
, LOG_ERROR
, "Memory allocation failed for note %s.", key
);
6959 /* Figure out set access */
6960 if((str
= database_get_data(obj
, KEY_NOTE_OPSERV_ACCESS
, RECDB_QSTRING
)))
6962 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
6963 ntype
->set_access
.min_opserv
= strtoul(str
, NULL
, 0);
6965 else if((str
= database_get_data(obj
, KEY_NOTE_CHANNEL_ACCESS
, RECDB_QSTRING
)))
6967 ntype
->set_access_type
= NOTE_SET_CHANNEL_ACCESS
;
6968 ntype
->set_access
.min_ulevel
= strtoul(str
, NULL
, 0);
6970 else if((str
= database_get_data(obj
, KEY_NOTE_SETTER_ACCESS
, RECDB_QSTRING
)))
6972 ntype
->set_access_type
= NOTE_SET_CHANNEL_SETTER
;
6976 log_module(CS_LOG
, LOG_ERROR
, "Could not find access type for note %s; defaulting to OpServ access level 0.", key
);
6977 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
6978 ntype
->set_access
.min_opserv
= 0;
6981 /* Figure out visibility */
6982 if(!(str
= database_get_data(obj
, KEY_NOTE_VISIBILITY
, RECDB_QSTRING
)))
6983 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
6984 else if(!irccasecmp(str
, KEY_NOTE_VIS_PRIVILEGED
))
6985 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
6986 else if(!irccasecmp(str
, KEY_NOTE_VIS_CHANNEL_USERS
))
6987 ntype
->visible_type
= NOTE_VIS_CHANNEL_USERS
;
6988 else if(!irccasecmp(str
, KEY_NOTE_VIS_ALL
))
6989 ntype
->visible_type
= NOTE_VIS_ALL
;
6991 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
6993 str
= database_get_data(obj
, KEY_NOTE_MAX_LENGTH
, RECDB_QSTRING
);
6994 ntype
->max_length
= str
? strtoul(str
, NULL
, 0) : 400;
6998 user_read_helper(const char *key
, struct record_data
*rd
, struct chanData
*chan
)
7000 struct handle_info
*handle
;
7001 struct userData
*uData
;
7002 char *seen
, *inf
, *flags
;
7004 unsigned short access
;
7006 if(rd
->type
!= RECDB_OBJECT
|| !dict_size(rd
->d
.object
))
7008 log_module(CS_LOG
, LOG_ERROR
, "Invalid user in %s.", chan
->channel
->name
);
7012 access
= atoi(database_get_data(rd
->d
.object
, KEY_LEVEL
, RECDB_QSTRING
));
7013 if(access
> UL_OWNER
)
7015 log_module(CS_LOG
, LOG_ERROR
, "Invalid access level for %s in %s.", key
, chan
->channel
->name
);
7019 inf
= database_get_data(rd
->d
.object
, KEY_INFO
, RECDB_QSTRING
);
7020 seen
= database_get_data(rd
->d
.object
, KEY_SEEN
, RECDB_QSTRING
);
7021 last_seen
= seen
? (signed)strtoul(seen
, NULL
, 0) : now
;
7022 flags
= database_get_data(rd
->d
.object
, KEY_FLAGS
, RECDB_QSTRING
);
7023 handle
= get_handle_info(key
);
7026 log_module(CS_LOG
, LOG_ERROR
, "Nonexistent account %s in %s.", key
, chan
->channel
->name
);
7030 uData
= add_channel_user(chan
, handle
, access
, last_seen
, inf
);
7031 uData
->flags
= flags
? strtoul(flags
, NULL
, 0) : 0;
7033 /* Upgrade: set autoop to the inverse of noautoop */
7034 if(chanserv_read_version
< 2)
7036 /* if noautoop is true, set autoop false, and vice versa */
7037 if(uData
->flags
& USER_NOAUTO_OP
)
7038 uData
->flags
= uData
->flags
& ~USER_AUTO_OP
;
7040 uData
->flags
= uData
->flags
| USER_AUTO_OP
;
7041 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
);
7047 ban_read_helper(const char *key
, struct record_data
*rd
, struct chanData
*chan
)
7049 struct banData
*bData
;
7050 char *set
, *triggered
, *s_duration
, *s_expires
, *reason
, *owner
;
7051 time_t set_time
, triggered_time
, expires_time
;
7053 if(rd
->type
!= RECDB_OBJECT
|| !dict_size(rd
->d
.object
))
7055 log_module(CS_LOG
, LOG_ERROR
, "Invalid ban in %s.", chan
->channel
->name
);
7059 set
= database_get_data(rd
->d
.object
, KEY_SET
, RECDB_QSTRING
);
7060 triggered
= database_get_data(rd
->d
.object
, KEY_TRIGGERED
, RECDB_QSTRING
);
7061 s_duration
= database_get_data(rd
->d
.object
, KEY_DURATION
, RECDB_QSTRING
);
7062 s_expires
= database_get_data(rd
->d
.object
, KEY_EXPIRES
, RECDB_QSTRING
);
7063 owner
= database_get_data(rd
->d
.object
, KEY_OWNER
, RECDB_QSTRING
);
7064 reason
= database_get_data(rd
->d
.object
, KEY_REASON
, RECDB_QSTRING
);
7065 if (!reason
|| !owner
)
7068 set_time
= set
? (time_t)strtoul(set
, NULL
, 0) : now
;
7069 triggered_time
= triggered
? (time_t)strtoul(triggered
, NULL
, 0) : 0;
7071 expires_time
= (time_t)strtoul(s_expires
, NULL
, 0);
7073 expires_time
= set_time
+ atoi(s_duration
);
7077 if(!reason
|| (expires_time
&& (expires_time
< now
)))
7080 bData
= add_channel_ban(chan
, key
, owner
, set_time
, triggered_time
, expires_time
, reason
);
7083 static struct suspended
*
7084 chanserv_read_suspended(dict_t obj
)
7086 struct suspended
*suspended
= calloc(1, sizeof(*suspended
));
7090 str
= database_get_data(obj
, KEY_EXPIRES
, RECDB_QSTRING
);
7091 suspended
->expires
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7092 str
= database_get_data(obj
, KEY_REVOKED
, RECDB_QSTRING
);
7093 suspended
->revoked
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7094 str
= database_get_data(obj
, KEY_ISSUED
, RECDB_QSTRING
);
7095 suspended
->issued
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7096 suspended
->suspender
= strdup(database_get_data(obj
, KEY_SUSPENDER
, RECDB_QSTRING
));
7097 suspended
->reason
= strdup(database_get_data(obj
, KEY_REASON
, RECDB_QSTRING
));
7098 previous
= database_get_data(obj
, KEY_PREVIOUS
, RECDB_OBJECT
);
7099 suspended
->previous
= previous
? chanserv_read_suspended(previous
) : NULL
;
7104 chanserv_channel_read(const char *key
, struct record_data
*hir
)
7106 struct suspended
*suspended
;
7107 struct mod_chanmode
*modes
;
7108 struct chanNode
*cNode
;
7109 struct chanData
*cData
;
7110 struct dict
*channel
, *obj
;
7111 char *str
, *argv
[10];
7115 channel
= hir
->d
.object
;
7117 str
= database_get_data(channel
, KEY_REGISTRAR
, RECDB_QSTRING
);
7120 cNode
= AddChannel(key
, now
, NULL
, NULL
, NULL
);
7123 log_module(CS_LOG
, LOG_ERROR
, "Unable to create registered channel %s.", key
);
7126 cData
= register_channel(cNode
, str
);
7129 log_module(CS_LOG
, LOG_ERROR
, "Unable to register channel %s from database.", key
);
7133 if((obj
= database_get_data(channel
, KEY_OPTIONS
, RECDB_OBJECT
)))
7135 enum levelOption lvlOpt
;
7136 enum charOption chOpt
;
7138 if((str
= database_get_data(obj
, KEY_FLAGS
, RECDB_QSTRING
)))
7139 cData
->flags
= atoi(str
);
7141 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
7143 str
= database_get_data(obj
, levelOptions
[lvlOpt
].db_name
, RECDB_QSTRING
);
7145 cData
->lvlOpts
[lvlOpt
] = user_level_from_name(str
, UL_OWNER
+1);
7146 else if(levelOptions
[lvlOpt
].old_flag
)
7148 if(cData
->flags
& levelOptions
[lvlOpt
].old_flag
)
7149 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].flag_value
;
7151 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
7155 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
7157 if(!(str
= database_get_data(obj
, charOptions
[chOpt
].db_name
, RECDB_QSTRING
)))
7159 cData
->chOpts
[chOpt
] = str
[0];
7162 else if((str
= database_get_data(channel
, KEY_FLAGS
, RECDB_QSTRING
)))
7164 enum levelOption lvlOpt
;
7165 enum charOption chOpt
;
7168 cData
->flags
= base64toint(str
, 5);
7169 count
= strlen(str
+= 5);
7170 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
7173 if(levelOptions
[lvlOpt
].old_flag
)
7175 if(cData
->flags
& levelOptions
[lvlOpt
].old_flag
)
7176 lvl
= levelOptions
[lvlOpt
].flag_value
;
7178 lvl
= levelOptions
[lvlOpt
].default_value
;
7180 else switch(((count
<= levelOptions
[lvlOpt
].old_idx
) ? str
: CHANNEL_DEFAULT_OPTIONS
)[levelOptions
[lvlOpt
].old_idx
])
7182 case 'c': lvl
= UL_COOWNER
; break;
7183 case 'm': lvl
= UL_MANAGER
; break;
7184 case 'n': lvl
= UL_OWNER
+1; break;
7185 case 'o': lvl
= UL_OP
; break;
7186 case 'p': lvl
= UL_PEON
; break;
7187 case 'h': lvl
= UL_HALFOP
; break;
7188 case 'w': lvl
= UL_OWNER
; break;
7189 default: lvl
= 0; break;
7191 cData
->lvlOpts
[lvlOpt
] = lvl
;
7193 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
7194 cData
->chOpts
[chOpt
] = ((count
<= charOptions
[chOpt
].old_idx
) ? str
: CHANNEL_DEFAULT_OPTIONS
)[charOptions
[chOpt
].old_idx
];
7197 if((obj
= database_get_data(hir
->d
.object
, KEY_SUSPENDED
, RECDB_OBJECT
)))
7199 suspended
= chanserv_read_suspended(obj
);
7200 cData
->suspended
= suspended
;
7201 suspended
->cData
= cData
;
7202 /* We could use suspended->expires and suspended->revoked to
7203 * set the CHANNEL_SUSPENDED flag, but we don't. */
7205 else if(IsSuspended(cData
) && (str
= database_get_data(hir
->d
.object
, KEY_SUSPENDER
, RECDB_QSTRING
)))
7207 suspended
= calloc(1, sizeof(*suspended
));
7208 suspended
->issued
= 0;
7209 suspended
->revoked
= 0;
7210 suspended
->suspender
= strdup(str
);
7211 str
= database_get_data(hir
->d
.object
, KEY_SUSPEND_EXPIRES
, RECDB_QSTRING
);
7212 suspended
->expires
= str
? atoi(str
) : 0;
7213 str
= database_get_data(hir
->d
.object
, KEY_SUSPEND_REASON
, RECDB_QSTRING
);
7214 suspended
->reason
= strdup(str
? str
: "No reason");
7215 suspended
->previous
= NULL
;
7216 cData
->suspended
= suspended
;
7217 suspended
->cData
= cData
;
7221 cData
->flags
&= ~CHANNEL_SUSPENDED
;
7222 suspended
= NULL
; /* to squelch a warning */
7225 if(IsSuspended(cData
)) {
7226 if(suspended
->expires
> now
)
7227 timeq_add(suspended
->expires
, chanserv_expire_suspension
, suspended
);
7228 else if(suspended
->expires
)
7229 cData
->flags
&= ~CHANNEL_SUSPENDED
;
7232 if((!off_channel
|| !IsOffChannel(cData
)) && !IsSuspended(cData
)) {
7233 struct mod_chanmode change
;
7234 mod_chanmode_init(&change
);
7236 change
.args
[0].mode
= MODE_CHANOP
;
7237 change
.args
[0].u
.member
= AddChannelUser(chanserv
, cNode
);
7238 mod_chanmode_announce(chanserv
, cNode
, &change
);
7241 str
= database_get_data(channel
, KEY_REGISTERED
, RECDB_QSTRING
);
7242 cData
->registered
= str
? (time_t)strtoul(str
, NULL
, 0) : now
;
7243 str
= database_get_data(channel
, KEY_VISITED
, RECDB_QSTRING
);
7244 cData
->visited
= str
? (time_t)strtoul(str
, NULL
, 0) : now
;
7245 str
= database_get_data(channel
, KEY_OWNER_TRANSFER
, RECDB_QSTRING
);
7246 cData
->ownerTransfer
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7247 str
= database_get_data(channel
, KEY_MAX
, RECDB_QSTRING
);
7248 cData
->max
= str
? atoi(str
) : 0;
7249 str
= database_get_data(channel
, KEY_GREETING
, RECDB_QSTRING
);
7250 cData
->greeting
= str
? strdup(str
) : NULL
;
7251 str
= database_get_data(channel
, KEY_USER_GREETING
, RECDB_QSTRING
);
7252 cData
->user_greeting
= str
? strdup(str
) : NULL
;
7253 str
= database_get_data(channel
, KEY_TOPIC_MASK
, RECDB_QSTRING
);
7254 cData
->topic_mask
= str
? strdup(str
) : NULL
;
7255 str
= database_get_data(channel
, KEY_TOPIC
, RECDB_QSTRING
);
7256 cData
->topic
= str
? strdup(str
) : NULL
;
7258 if(!IsSuspended(cData
)
7259 && (str
= database_get_data(channel
, KEY_MODES
, RECDB_QSTRING
))
7260 && (argc
= split_line(str
, 0, ArrayLength(argv
), argv
))
7261 && (modes
= mod_chanmode_parse(cNode
, argv
, argc
, MCP_KEY_FREE
))) {
7262 cData
->modes
= *modes
;
7264 cData
->modes
.modes_set
|= MODE_REGISTERED
;
7265 if(cData
->modes
.argc
> 1)
7266 cData
->modes
.argc
= 1;
7267 mod_chanmode_announce(chanserv
, cNode
, &cData
->modes
);
7268 mod_chanmode_free(modes
);
7271 obj
= database_get_data(channel
, KEY_USERS
, RECDB_OBJECT
);
7272 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
7273 user_read_helper(iter_key(it
), iter_data(it
), cData
);
7275 if(!cData
->users
&& !IsProtected(cData
))
7277 log_module(CS_LOG
, LOG_ERROR
, "Channel %s had no users in database, unregistering it.", key
);
7278 unregister_channel(cData
, "has empty user list.");
7282 obj
= database_get_data(channel
, KEY_BANS
, RECDB_OBJECT
);
7283 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
7284 ban_read_helper(iter_key(it
), iter_data(it
), cData
);
7286 obj
= database_get_data(channel
, KEY_NOTES
, RECDB_OBJECT
);
7287 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
7289 struct note_type
*ntype
= dict_find(note_types
, iter_key(it
), NULL
);
7290 struct record_data
*rd
= iter_data(it
);
7291 const char *note
, *setter
;
7293 if(rd
->type
!= RECDB_OBJECT
)
7295 log_module(CS_LOG
, LOG_ERROR
, "Bad record type for note %s in channel %s.", iter_key(it
), key
);
7299 log_module(CS_LOG
, LOG_ERROR
, "Bad note type name %s in channel %s.", iter_key(it
), key
);
7301 else if(!(note
= database_get_data(rd
->d
.object
, KEY_NOTE_NOTE
, RECDB_QSTRING
)))
7303 log_module(CS_LOG
, LOG_ERROR
, "Missing note text for note %s in channel %s.", iter_key(it
), key
);
7307 setter
= database_get_data(rd
->d
.object
, KEY_NOTE_SETTER
, RECDB_QSTRING
);
7308 if(!setter
) setter
= "<unknown>";
7309 chanserv_add_channel_note(cData
, ntype
, setter
, note
);
7317 chanserv_dnr_read(const char *key
, struct record_data
*hir
)
7319 const char *setter
, *reason
, *str
;
7320 struct do_not_register
*dnr
;
7322 setter
= database_get_data(hir
->d
.object
, KEY_DNR_SETTER
, RECDB_QSTRING
);
7325 log_module(CS_LOG
, LOG_ERROR
, "Missing setter for DNR %s.", key
);
7328 reason
= database_get_data(hir
->d
.object
, KEY_DNR_REASON
, RECDB_QSTRING
);
7331 log_module(CS_LOG
, LOG_ERROR
, "Missing reason for DNR %s.", key
);
7334 dnr
= chanserv_add_dnr(key
, setter
, reason
);
7337 str
= database_get_data(hir
->d
.object
, KEY_DNR_SET
, RECDB_QSTRING
);
7339 dnr
->set
= atoi(str
);
7345 chanserv_version_read(struct dict
*section
)
7349 str
= database_get_data(section
, KEY_VERSION_NUMBER
, RECDB_QSTRING
);
7351 chanserv_read_version
= atoi(str
);
7352 log_module(CS_LOG
, LOG_DEBUG
, "Chanserv db version is %d.", chanserv_read_version
);
7356 chanserv_saxdb_read(struct dict
*database
)
7358 struct dict
*section
;
7361 if((section
= database_get_data(database
, KEY_VERSION_CONTROL
, RECDB_OBJECT
)))
7362 chanserv_version_read(section
);
7364 if((section
= database_get_data(database
, KEY_NOTE_TYPES
, RECDB_OBJECT
)))
7365 for(it
= dict_first(section
); it
; it
= iter_next(it
))
7366 chanserv_note_type_read(iter_key(it
), iter_data(it
));
7368 if((section
= database_get_data(database
, KEY_CHANNELS
, RECDB_OBJECT
)))
7369 for(it
= dict_first(section
); it
; it
= iter_next(it
))
7370 chanserv_channel_read(iter_key(it
), iter_data(it
));
7372 if((section
= database_get_data(database
, KEY_DNR
, RECDB_OBJECT
)))
7373 for(it
= dict_first(section
); it
; it
= iter_next(it
))
7374 chanserv_dnr_read(iter_key(it
), iter_data(it
));
7380 chanserv_write_users(struct saxdb_context
*ctx
, struct userData
*uData
)
7382 int high_present
= 0;
7383 saxdb_start_record(ctx
, KEY_USERS
, 1);
7384 for(; uData
; uData
= uData
->next
)
7386 if((uData
->access
>= UL_PRESENT
) && uData
->present
)
7388 saxdb_start_record(ctx
, uData
->handle
->handle
, 0);
7389 saxdb_write_int(ctx
, KEY_LEVEL
, uData
->access
);
7390 saxdb_write_int(ctx
, KEY_SEEN
, uData
->seen
);
7392 saxdb_write_int(ctx
, KEY_FLAGS
, uData
->flags
);
7394 saxdb_write_string(ctx
, KEY_INFO
, uData
->info
);
7395 saxdb_end_record(ctx
);
7397 saxdb_end_record(ctx
);
7398 return high_present
;
7402 chanserv_write_bans(struct saxdb_context
*ctx
, struct banData
*bData
)
7406 saxdb_start_record(ctx
, KEY_BANS
, 1);
7407 for(; bData
; bData
= bData
->next
)
7409 saxdb_start_record(ctx
, bData
->mask
, 0);
7410 saxdb_write_int(ctx
, KEY_SET
, bData
->set
);
7411 if(bData
->triggered
)
7412 saxdb_write_int(ctx
, KEY_TRIGGERED
, bData
->triggered
);
7414 saxdb_write_int(ctx
, KEY_EXPIRES
, bData
->expires
);
7416 saxdb_write_string(ctx
, KEY_OWNER
, bData
->owner
);
7418 saxdb_write_string(ctx
, KEY_REASON
, bData
->reason
);
7419 saxdb_end_record(ctx
);
7421 saxdb_end_record(ctx
);
7425 chanserv_write_suspended(struct saxdb_context
*ctx
, const char *name
, struct suspended
*susp
)
7427 saxdb_start_record(ctx
, name
, 0);
7428 saxdb_write_string(ctx
, KEY_SUSPENDER
, susp
->suspender
);
7429 saxdb_write_string(ctx
, KEY_REASON
, susp
->reason
);
7431 saxdb_write_int(ctx
, KEY_ISSUED
, susp
->issued
);
7433 saxdb_write_int(ctx
, KEY_EXPIRES
, susp
->expires
);
7435 saxdb_write_int(ctx
, KEY_REVOKED
, susp
->revoked
);
7437 chanserv_write_suspended(ctx
, KEY_PREVIOUS
, susp
->previous
);
7438 saxdb_end_record(ctx
);
7442 chanserv_write_channel(struct saxdb_context
*ctx
, struct chanData
*channel
)
7446 enum levelOption lvlOpt
;
7447 enum charOption chOpt
;
7449 saxdb_start_record(ctx
, channel
->channel
->name
, 1);
7451 saxdb_write_int(ctx
, KEY_REGISTERED
, channel
->registered
);
7452 saxdb_write_int(ctx
, KEY_MAX
, channel
->max
);
7454 saxdb_write_string(ctx
, KEY_TOPIC
, channel
->topic
);
7455 if(channel
->registrar
)
7456 saxdb_write_string(ctx
, KEY_REGISTRAR
, channel
->registrar
);
7457 if(channel
->greeting
)
7458 saxdb_write_string(ctx
, KEY_GREETING
, channel
->greeting
);
7459 if(channel
->user_greeting
)
7460 saxdb_write_string(ctx
, KEY_USER_GREETING
, channel
->user_greeting
);
7461 if(channel
->topic_mask
)
7462 saxdb_write_string(ctx
, KEY_TOPIC_MASK
, channel
->topic_mask
);
7463 if(channel
->suspended
)
7464 chanserv_write_suspended(ctx
, "suspended", channel
->suspended
);
7466 saxdb_start_record(ctx
, KEY_OPTIONS
, 0);
7467 saxdb_write_int(ctx
, KEY_FLAGS
, channel
->flags
);
7468 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
7469 saxdb_write_int(ctx
, levelOptions
[lvlOpt
].db_name
, channel
->lvlOpts
[lvlOpt
]);
7470 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
7472 buf
[0] = channel
->chOpts
[chOpt
];
7474 saxdb_write_string(ctx
, charOptions
[chOpt
].db_name
, buf
);
7476 saxdb_end_record(ctx
);
7478 if(channel
->modes
.modes_set
|| channel
->modes
.modes_clear
)
7480 mod_chanmode_format(&channel
->modes
, buf
);
7481 saxdb_write_string(ctx
, KEY_MODES
, buf
);
7484 high_present
= chanserv_write_users(ctx
, channel
->users
);
7485 chanserv_write_bans(ctx
, channel
->bans
);
7487 if(dict_size(channel
->notes
))
7491 saxdb_start_record(ctx
, KEY_NOTES
, 1);
7492 for(it
= dict_first(channel
->notes
); it
; it
= iter_next(it
))
7494 struct note
*note
= iter_data(it
);
7495 saxdb_start_record(ctx
, iter_key(it
), 0);
7496 saxdb_write_string(ctx
, KEY_NOTE_SETTER
, note
->setter
);
7497 saxdb_write_string(ctx
, KEY_NOTE_NOTE
, note
->note
);
7498 saxdb_end_record(ctx
);
7500 saxdb_end_record(ctx
);
7503 if(channel
->ownerTransfer
)
7504 saxdb_write_int(ctx
, KEY_OWNER_TRANSFER
, channel
->ownerTransfer
);
7505 saxdb_write_int(ctx
, KEY_VISITED
, high_present
? now
: channel
->visited
);
7506 saxdb_end_record(ctx
);
7510 chanserv_write_note_type(struct saxdb_context
*ctx
, struct note_type
*ntype
)
7514 saxdb_start_record(ctx
, ntype
->name
, 0);
7515 switch(ntype
->set_access_type
)
7517 case NOTE_SET_CHANNEL_ACCESS
:
7518 saxdb_write_int(ctx
, KEY_NOTE_CHANNEL_ACCESS
, ntype
->set_access
.min_ulevel
);
7520 case NOTE_SET_CHANNEL_SETTER
:
7521 saxdb_write_int(ctx
, KEY_NOTE_SETTER_ACCESS
, 1);
7523 case NOTE_SET_PRIVILEGED
: default:
7524 saxdb_write_int(ctx
, KEY_NOTE_OPSERV_ACCESS
, ntype
->set_access
.min_opserv
);
7527 switch(ntype
->visible_type
)
7529 case NOTE_VIS_ALL
: str
= KEY_NOTE_VIS_ALL
; break;
7530 case NOTE_VIS_CHANNEL_USERS
: str
= KEY_NOTE_VIS_CHANNEL_USERS
; break;
7531 case NOTE_VIS_PRIVILEGED
: default: str
= KEY_NOTE_VIS_PRIVILEGED
; break;
7533 saxdb_write_string(ctx
, KEY_NOTE_VISIBILITY
, str
);
7534 saxdb_write_int(ctx
, KEY_NOTE_MAX_LENGTH
, ntype
->max_length
);
7535 saxdb_end_record(ctx
);
7539 write_dnrs_helper(struct saxdb_context
*ctx
, struct dict
*dnrs
)
7541 struct do_not_register
*dnr
;
7544 for(it
= dict_first(dnrs
); it
; it
= iter_next(it
))
7546 dnr
= iter_data(it
);
7547 saxdb_start_record(ctx
, dnr
->chan_name
, 0);
7549 saxdb_write_int(ctx
, KEY_DNR_SET
, dnr
->set
);
7550 saxdb_write_string(ctx
, KEY_DNR_SETTER
, dnr
->setter
);
7551 saxdb_write_string(ctx
, KEY_DNR_REASON
, dnr
->reason
);
7552 saxdb_end_record(ctx
);
7557 chanserv_saxdb_write(struct saxdb_context
*ctx
)
7560 struct chanData
*channel
;
7562 /* Version Control*/
7563 saxdb_start_record(ctx
, KEY_VERSION_CONTROL
, 1);
7564 saxdb_write_int(ctx
, KEY_VERSION_NUMBER
, CHANSERV_DB_VERSION
);
7565 saxdb_end_record(ctx
);
7568 saxdb_start_record(ctx
, KEY_NOTE_TYPES
, 1);
7569 for(it
= dict_first(note_types
); it
; it
= iter_next(it
))
7570 chanserv_write_note_type(ctx
, iter_data(it
));
7571 saxdb_end_record(ctx
);
7574 saxdb_start_record(ctx
, KEY_DNR
, 1);
7575 write_dnrs_helper(ctx
, handle_dnrs
);
7576 write_dnrs_helper(ctx
, plain_dnrs
);
7577 write_dnrs_helper(ctx
, mask_dnrs
);
7578 saxdb_end_record(ctx
);
7581 saxdb_start_record(ctx
, KEY_CHANNELS
, 1);
7582 for(channel
= channelList
; channel
; channel
= channel
->next
)
7583 chanserv_write_channel(ctx
, channel
);
7584 saxdb_end_record(ctx
);
7590 chanserv_db_cleanup(void) {
7592 unreg_part_func(handle_part
);
7594 unregister_channel(channelList
, "terminating.");
7595 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
7596 UnlockChannel(chanserv_conf
.support_channels
.list
[ii
]);
7597 free(chanserv_conf
.support_channels
.list
);
7598 dict_delete(handle_dnrs
);
7599 dict_delete(plain_dnrs
);
7600 dict_delete(mask_dnrs
);
7601 dict_delete(note_types
);
7602 free_string_list(chanserv_conf
.eightball
);
7603 free_string_list(chanserv_conf
.old_ban_names
);
7604 free_string_list(chanserv_conf
.set_shows
);
7605 free(set_shows_list
.list
);
7606 free(uset_shows_list
.list
);
7609 struct userData
*helper
= helperList
;
7610 helperList
= helperList
->next
;
7615 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
7616 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
7617 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
7620 init_chanserv(const char *nick
)
7622 CS_LOG
= log_register_type("ChanServ", "file:chanserv.log");
7623 conf_register_reload(chanserv_conf_read
);
7625 reg_server_link_func(handle_server_link
);
7627 reg_new_channel_func(handle_new_channel
);
7628 reg_join_func(handle_join
);
7629 reg_part_func(handle_part
);
7630 reg_kick_func(handle_kick
);
7631 reg_topic_func(handle_topic
);
7632 reg_mode_change_func(handle_mode
);
7633 reg_nick_change_func(handle_nick_change
);
7635 reg_auth_func(handle_auth
);
7636 reg_handle_rename_func(handle_rename
);
7637 reg_unreg_func(handle_unreg
);
7639 handle_dnrs
= dict_new();
7640 dict_set_free_data(handle_dnrs
, free
);
7641 plain_dnrs
= dict_new();
7642 dict_set_free_data(plain_dnrs
, free
);
7643 mask_dnrs
= dict_new();
7644 dict_set_free_data(mask_dnrs
, free
);
7646 reg_svccmd_unbind_func(handle_svccmd_unbind
);
7647 chanserv_module
= module_register("ChanServ", CS_LOG
, "chanserv.help", chanserv_expand_variable
);
7648 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+acceptchan,+helping", NULL
);
7649 DEFINE_COMMAND(noregister
, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+helping", NULL
);
7650 DEFINE_COMMAND(allowregister
, 2, 0, "template", "noregister", NULL
);
7651 DEFINE_COMMAND(move
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "template", "register", NULL
);
7652 DEFINE_COMMAND(csuspend
, 2, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+helping", NULL
);
7653 DEFINE_COMMAND(cunsuspend
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+helping", NULL
);
7654 DEFINE_COMMAND(createnote
, 5, 0, "level", "800", NULL
);
7655 DEFINE_COMMAND(removenote
, 2, 0, "level", "800", NULL
);
7657 DEFINE_COMMAND(pending
, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+helping", NULL
);
7659 DEFINE_COMMAND(unregister
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+loghostmask", NULL
);
7660 DEFINE_COMMAND(merge
, 2, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "access", "owner", NULL
);
7662 DEFINE_COMMAND(adduser
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7663 DEFINE_COMMAND(deluser
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7664 DEFINE_COMMAND(suspend
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7665 DEFINE_COMMAND(unsuspend
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7666 DEFINE_COMMAND(deleteme
, 1, MODCMD_REQUIRE_CHANUSER
, NULL
);
7668 DEFINE_COMMAND(mdelowner
, 2, MODCMD_REQUIRE_CHANUSER
, "flags", "+helping", NULL
);
7669 DEFINE_COMMAND(mdelcoowner
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "owner", NULL
);
7670 DEFINE_COMMAND(mdelmanager
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "coowner", NULL
);
7671 DEFINE_COMMAND(mdelop
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7672 DEFINE_COMMAND(mdelpeon
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7673 DEFINE_COMMAND(mdelhalfop
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7675 DEFINE_COMMAND(trim
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7676 DEFINE_COMMAND(opchan
, 1, MODCMD_REQUIRE_REGCHAN
|MODCMD_NEVER_CSUSPEND
, "access", "1", NULL
);
7677 DEFINE_COMMAND(clvl
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7678 DEFINE_COMMAND(giveownership
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "owner", "flags", "+loghostmask", NULL
);
7680 DEFINE_COMMAND(up
, 1, MODCMD_REQUIRE_CHANUSER
, NULL
);
7681 DEFINE_COMMAND(down
, 1, MODCMD_REQUIRE_REGCHAN
, NULL
);
7682 DEFINE_COMMAND(upall
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
7683 DEFINE_COMMAND(downall
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
7684 DEFINE_COMMAND(hop
, 2, MODCMD_REQUIRE_CHANNEL
, "access", "op", NULL
);
7685 DEFINE_COMMAND(op
, 2, MODCMD_REQUIRE_CHANNEL
, "access", "op", NULL
);
7686 DEFINE_COMMAND(deop
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
7687 DEFINE_COMMAND(dehop
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
7688 DEFINE_COMMAND(voice
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
7689 DEFINE_COMMAND(devoice
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
7691 DEFINE_COMMAND(kickban
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
7692 DEFINE_COMMAND(kick
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
7693 DEFINE_COMMAND(ban
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
7694 DEFINE_COMMAND(unban
, 2, 0, "template", "hop", NULL
);
7695 DEFINE_COMMAND(unbanall
, 1, 0, "template", "hop", NULL
);
7696 DEFINE_COMMAND(unbanme
, 1, MODCMD_REQUIRE_CHANUSER
, "template", "hop", NULL
);
7697 DEFINE_COMMAND(open
, 1, MODCMD_REQUIRE_CHANUSER
, "template", "op", NULL
);
7698 DEFINE_COMMAND(topic
, 1, MODCMD_REQUIRE_REGCHAN
, "template", "hop", "flags", "+never_csuspend", NULL
);
7699 DEFINE_COMMAND(mode
, 1, MODCMD_REQUIRE_REGCHAN
, "template", "op", NULL
);
7700 DEFINE_COMMAND(inviteme
, 1, MODCMD_REQUIRE_CHANNEL
, "access", "1", NULL
);
7701 DEFINE_COMMAND(invite
, 1, MODCMD_REQUIRE_CHANNEL
, "access", "manager", NULL
);
7702 DEFINE_COMMAND(set
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "op", NULL
);
7703 DEFINE_COMMAND(wipeinfo
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7704 DEFINE_COMMAND(resync
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7706 DEFINE_COMMAND(events
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog", "access", "manager", NULL
);
7707 DEFINE_COMMAND(addlamer
, 2, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
7708 DEFINE_COMMAND(addtimedlamer
, 3, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
7710 /* if you change dellamer access, see also places
7711 * like unbanme which have manager hardcoded. */
7712 DEFINE_COMMAND(dellamer
, 2, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
7713 DEFINE_COMMAND(uset
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "1", NULL
);
7715 DEFINE_COMMAND(lamers
, 1, MODCMD_REQUIRE_REGCHAN
, "access", "1", "flags", "+nolog", NULL
);
7717 DEFINE_COMMAND(peek
, 1, MODCMD_REQUIRE_REGCHAN
, "access", "op", "flags", "+nolog", NULL
);
7719 DEFINE_COMMAND(myaccess
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
7720 DEFINE_COMMAND(access
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7721 DEFINE_COMMAND(users
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7722 DEFINE_COMMAND(wlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7723 DEFINE_COMMAND(clist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7724 DEFINE_COMMAND(mlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7725 DEFINE_COMMAND(olist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7726 DEFINE_COMMAND(hlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7727 DEFINE_COMMAND(plist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7728 DEFINE_COMMAND(info
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7729 DEFINE_COMMAND(seen
, 2, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7730 DEFINE_COMMAND(names
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7732 DEFINE_COMMAND(note
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+joinable,+acceptchan", NULL
);
7733 DEFINE_COMMAND(delnote
, 2, MODCMD_REQUIRE_CHANUSER
, NULL
);
7735 DEFINE_COMMAND(netinfo
, 1, 0, "flags", "+nolog", NULL
);
7736 DEFINE_COMMAND(ircops
, 1, 0, "flags", "+nolog", NULL
);
7737 DEFINE_COMMAND(helpers
, 1, 0, "flags", "+nolog", NULL
);
7738 DEFINE_COMMAND(staff
, 1, 0, "flags", "+nolog", NULL
);
7740 DEFINE_COMMAND(say
, 2, 0, "flags", "+oper,+acceptchan", NULL
);
7741 DEFINE_COMMAND(emote
, 2, 0, "flags", "+oper,+acceptchan", NULL
);
7742 DEFINE_COMMAND(expire
, 1, 0, "flags", "+oper", NULL
);
7743 DEFINE_COMMAND(search
, 3, 0, "flags", "+nolog,+helping", NULL
);
7744 DEFINE_COMMAND(unvisited
, 1, 0, "flags", "+nolog,+helping", NULL
);
7746 DEFINE_COMMAND(unf
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
7747 DEFINE_COMMAND(ping
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
7748 DEFINE_COMMAND(wut
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
7749 DEFINE_COMMAND(8ball
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
7750 DEFINE_COMMAND(d
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
7751 DEFINE_COMMAND(huggle
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
7752 DEFINE_COMMAND(calc
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
7754 /* Channel options */
7755 DEFINE_CHANNEL_OPTION(defaulttopic
);
7756 DEFINE_CHANNEL_OPTION(topicmask
);
7757 DEFINE_CHANNEL_OPTION(greeting
);
7758 DEFINE_CHANNEL_OPTION(usergreeting
);
7759 DEFINE_CHANNEL_OPTION(modes
);
7760 DEFINE_CHANNEL_OPTION(enfops
);
7761 DEFINE_CHANNEL_OPTION(enfhalfops
);
7762 /*DEFINE_CHANNEL_OPTION(giveops);
7763 DEFINE_CHANNEL_OPTION(givehalfops);
7765 DEFINE_CHANNEL_OPTION(voice
);
7766 DEFINE_CHANNEL_OPTION(protect
);
7767 DEFINE_CHANNEL_OPTION(enfmodes
);
7768 DEFINE_CHANNEL_OPTION(enftopic
);
7769 DEFINE_CHANNEL_OPTION(pubcmd
);
7770 /*DEFINE_CHANNEL_OPTION(givevoice);
7772 DEFINE_CHANNEL_OPTION(userinfo
);
7773 DEFINE_CHANNEL_OPTION(dynlimit
);
7774 DEFINE_CHANNEL_OPTION(topicsnarf
);
7775 DEFINE_CHANNEL_OPTION(nodelete
);
7776 DEFINE_CHANNEL_OPTION(toys
);
7777 DEFINE_CHANNEL_OPTION(setters
);
7778 DEFINE_CHANNEL_OPTION(topicrefresh
);
7779 DEFINE_CHANNEL_OPTION(ctcpusers
);
7780 DEFINE_CHANNEL_OPTION(ctcpreaction
);
7781 DEFINE_CHANNEL_OPTION(inviteme
);
7783 DEFINE_CHANNEL_OPTION(offchannel
);
7784 modcmd_register(chanserv_module
, "set defaults", chan_opt_defaults
, 1, 0, "access", "owner", NULL
);
7786 /* Alias set topic to set defaulttopic for compatibility. */
7787 modcmd_register(chanserv_module
, "set topic", chan_opt_defaulttopic
, 1, 0, NULL
);
7790 DEFINE_USER_OPTION(autoinvite
);
7791 DEFINE_USER_OPTION(info
);
7792 DEFINE_USER_OPTION(autoop
);
7794 /* Alias uset autovoice to uset autoop. */
7795 modcmd_register(chanserv_module
, "uset autovoice", user_opt_autoop
, 1, 0, NULL
);
7797 note_types
= dict_new();
7798 dict_set_free_data(note_types
, chanserv_deref_note_type
);
7801 const char *modes
= conf_get_data("services/chanserv/modes", RECDB_QSTRING
);
7802 chanserv
= AddService(nick
, modes
? modes
: NULL
, "Channel Services", NULL
);
7803 service_register(chanserv
)->trigger
= '!';
7804 reg_chanmsg_func('\001', chanserv
, chanserv_ctcp_check
);
7807 saxdb_register("ChanServ", chanserv_saxdb_read
, chanserv_saxdb_write
);
7809 if(chanserv_conf
.channel_expire_frequency
)
7810 timeq_add(now
+ chanserv_conf
.channel_expire_frequency
, expire_channels
, NULL
);
7812 if(chanserv_conf
.refresh_period
)
7814 time_t next_refresh
;
7815 next_refresh
= (now
+ chanserv_conf
.refresh_period
- 1) / chanserv_conf
.refresh_period
* chanserv_conf
.refresh_period
;
7816 timeq_add(next_refresh
, chanserv_refresh_topics
, NULL
);
7819 reg_exit_func(chanserv_db_cleanup
);
7820 message_register_table(msgtab
);