1 /* chanserv.c - Channel service bot
2 * Copyright 2000-2004 srvx Development Team
4 * This file is part of x3.
6 * srvx is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with srvx; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
25 #include "opserv.h" /* for opserv_bad_channel() */
29 #define CHANSERV_CONF_NAME "services/chanserv"
31 /* ChanServ options */
32 #define KEY_SUPPORT_CHANNEL "support_channel"
33 #define KEY_SUPPORT_CHANNEL_MODES "support_channel_modes"
34 #define KEY_DB_BACKUP_FREQ "db_backup_freq"
35 #define KEY_INFO_DELAY "info_delay"
36 #define KEY_MAX_GREETLEN "max_greetlen"
37 #define KEY_ADJUST_THRESHOLD "adjust_threshold"
38 #define KEY_ADJUST_DELAY "adjust_delay"
39 #define KEY_CHAN_EXPIRE_FREQ "chan_expire_freq"
40 #define KEY_CHAN_EXPIRE_DELAY "chan_expire_delay"
41 #define KEY_MAX_CHAN_USERS "max_chan_users"
42 #define KEY_MAX_CHAN_BANS "max_chan_bans"
43 #define KEY_NICK "nick"
44 #define KEY_OLD_CHANSERV_NAME "old_chanserv_name"
45 #define KEY_8BALL_RESPONSES "8ball"
46 #define KEY_OLD_BAN_NAMES "old_ban_names"
47 #define KEY_REFRESH_PERIOD "refresh_period"
48 #define KEY_CTCP_SHORT_BAN_DURATION "ctcp_short_ban_duration"
49 #define KEY_CTCP_LONG_BAN_DURATION "ctcp_long_ban_duration"
50 #define KEY_MAX_OWNED "max_owned"
51 #define KEY_IRC_OPERATOR_EPITHET "irc_operator_epithet"
52 #define KEY_NETWORK_HELPER_EPITHET "network_helper_epithet"
53 #define KEY_SUPPORT_HELPER_EPITHET "support_helper_epithet"
54 #define KEY_NODELETE_LEVEL "nodelete_level"
55 #define KEY_MAX_USERINFO_LENGTH "max_userinfo_length"
56 #define KEY_GIVEOWNERSHIP_PERIOD "giveownership_timeout"
58 /* ChanServ database */
59 #define KEY_VERSION_CONTROL "version_control"
60 #define KEY_CHANNELS "channels"
61 #define KEY_NOTE_TYPES "note_types"
63 /* version control paramiter */
64 #define KEY_VERSION_NUMBER "version_number"
66 /* Note type parameters */
67 #define KEY_NOTE_OPSERV_ACCESS "opserv_access"
68 #define KEY_NOTE_CHANNEL_ACCESS "channel_access"
69 #define KEY_NOTE_SETTER_ACCESS "setter_access"
70 #define KEY_NOTE_VISIBILITY "visibility"
71 #define KEY_NOTE_VIS_PRIVILEGED "privileged"
72 #define KEY_NOTE_VIS_CHANNEL_USERS "channel_users"
73 #define KEY_NOTE_VIS_ALL "all"
74 #define KEY_NOTE_MAX_LENGTH "max_length"
75 #define KEY_NOTE_SETTER "setter"
76 #define KEY_NOTE_NOTE "note"
78 /* Do-not-register channels */
80 #define KEY_DNR_SET "set"
81 #define KEY_DNR_SETTER "setter"
82 #define KEY_DNR_REASON "reason"
85 #define KEY_REGISTERED "registered"
86 #define KEY_REGISTRAR "registrar"
87 #define KEY_SUSPENDED "suspended"
88 #define KEY_PREVIOUS "previous"
89 #define KEY_SUSPENDER "suspender"
90 #define KEY_ISSUED "issued"
91 #define KEY_REVOKED "revoked"
92 #define KEY_SUSPEND_EXPIRES "suspend_expires"
93 #define KEY_SUSPEND_REASON "suspend_reason"
94 #define KEY_VISITED "visited"
95 #define KEY_TOPIC "topic"
96 #define KEY_GREETING "greeting"
97 #define KEY_USER_GREETING "user_greeting"
98 #define KEY_MODES "modes"
99 #define KEY_FLAGS "flags"
100 #define KEY_OPTIONS "options"
101 #define KEY_USERS "users"
102 #define KEY_BANS "bans" /* for lamers */
103 #define KEY_MAX "max"
104 #define KEY_NOTES "notes"
105 #define KEY_TOPIC_MASK "topic_mask"
106 #define KEY_OWNER_TRANSFER "owner_transfer"
109 #define KEY_LEVEL "level"
110 #define KEY_INFO "info"
111 #define KEY_SEEN "seen"
114 #define KEY_OWNER "owner"
115 #define KEY_REASON "reason"
116 #define KEY_SET "set"
117 #define KEY_DURATION "duration"
118 #define KEY_EXPIRES "expires"
119 #define KEY_TRIGGERED "triggered"
121 #define CHANNEL_DEFAULT_FLAGS (CHANNEL_OFFCHANNEL)
122 #define CHANNEL_DEFAULT_OPTIONS "lmoooanpcnat"
124 /* Administrative messages */
125 static const struct message_entry msgtab
[] = {
126 { "CSMSG_CHANNELS_EXPIRED", "%i channels expired." },
128 /* Channel registration */
129 { "CSMSG_REG_SUCCESS", "You now have ownership of $b%s$b." },
130 { "CSMSG_PROXY_SUCCESS", "%s now has ownership of $b%s$b." },
131 { "CSMSG_ALREADY_REGGED", "$b%s$b is registered to someone else." },
132 { "CSMSG_MUST_BE_OPPED", "You must be a channel operator (+o) in $b%s$b to register it." },
133 { "CSMSG_PROXY_FORBIDDEN", "You may not register a channel for someone else." },
134 { "CSMSG_OWN_TOO_MANY", "%s already owns more than the limit of %d channels. Use FORCE to override." },
135 { "CSMSG_YOU_OWN_TOO_MANY", "You already own more than the limit of %d channels. Ask a staff member for help." },
136 { "CSMSG_ANOTHER_SERVICE", "Another service bot is in that channel already. Ask a staff member for help." },
138 /* Do-not-register channels */
139 { "CSMSG_NOT_DNR", "$b%s$b is not a valid channel name or *account." },
140 { "CSMSG_DNR_SEARCH_RESULTS", "$bDo-Not-Register Channels$b" },
141 { "CSMSG_DNR_INFO", "$b%s$b (set by $b%s$b): %s" },
142 { "CSMSG_DNR_INFO_SET", "$b%s$b (set %s ago by $b%s$b): %s" },
143 { "CSMSG_MORE_DNRS", "%d more do-not-register entries skipped." },
144 { "CSMSG_DNR_CHANNEL", "Only network staff may register $b%s$b." },
145 { "CSMSG_DNR_CHANNEL_MOVE", "Only network staff may move $b%s$b." },
146 { "CSMSG_DNR_ACCOUNT", "Only network staff may register channels to $b%s$b." },
147 { "CSMSG_NOREGISTER_CHANNEL", "$b%s$b has been added to the do-not-register list." },
148 { "CSMSG_NO_SUCH_DNR", "$b%s$b is not in the do-not-register list." },
149 { "CSMSG_DNR_REMOVED", "$b%s$b has been removed from the do-not-register list." },
151 /* Channel unregistration */
152 { "CSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
153 { "CSMSG_UNREG_NODELETE", "$b%s$b is protected from unregistration." },
154 { "CSMSG_CHAN_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended (%s)." },
155 { "CSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must use 'unregister %s %s'." },
158 { "CSMSG_MOVE_SUCCESS", "Channel registration has been moved to $b%s$b." },
159 { "CSMSG_MOVE_NODELETE", "$b%s$b is protected from unregistration, and cannot be moved." },
161 /* Channel merging */
162 { "CSMSG_MERGE_SUCCESS", "Channel successfully merged into $b%s$b." },
163 { "CSMSG_MERGE_SELF", "Merging cannot be performed if the source and target channels are the same." },
164 { "CSMSG_MERGE_NODELETE", "You may not merge a channel that is marked NoDelete." },
165 { "CSMSG_MERGE_SUSPENDED", "Merging cannot be performed if the source or target channel is suspended." },
166 { "CSMSG_MERGE_NOT_OWNER", "You must be the owner of the target channel (or a helper) to merge into the channel." },
168 /* Handle unregistration */
169 { "CSMSG_HANDLE_UNREGISTERED", "As a result of your account unregistration, you have been deleted from all of your channels' userlists." },
172 { "CSMSG_NOT_USER", "You lack access to $b%s$b." },
173 { "CSMSG_NO_CHAN_USER", "%s lacks access to $b%s$b." },
174 { "CSMSG_NO_ACCESS", "You lack sufficient access to use this command." },
175 { "CSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$C$b." },
176 { "CSMSG_MAXIMUM_LAMERS", "This channel has reached the lamer count limit of $b%d$b." },
177 { "CSMSG_MAXIMUM_USERS", "This channel has reached the user count limit of $b%d$b." },
178 { "CSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel, and cannot be registered." },
179 { "CSMSG_GODMODE_UP", "You may not use $b%s$b to op yourself unless you are on the user list. Use the $bop$b command instead." },
180 { "CSMSG_ALREADY_OPPED", "You are already opped in $b%s$b." },
181 { "CSMSG_ALREADY_HALFOPPED", "You are already halfopped in $b%s$b." },
182 { "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." },
183 { "CSMSG_ALREADY_DOWN", "You are not opped, halfopped, or voiced in $b%s$b." },
184 { "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." },
185 { "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." },
187 /* Removing yourself from a channel. */
188 { "CSMSG_NO_OWNER_DELETEME", "You cannot delete your owner access in $b%s$b." },
189 { "CSMSG_CONFIRM_DELETEME", "To really remove yourself, you must use 'deleteme %s'." },
190 { "CSMSG_DELETED_YOU", "Your $b%d$b access has been deleted from $b%s$b." },
192 /* User management */
193 { "CSMSG_ADDED_USER", "Added %s to the %s user list with access %s (%d)." },
194 { "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." },
195 { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be greater than maximum (%d)." },
196 { "CSMSG_DELETED_USERS", "Deleted accounts matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list." },
197 { "CSMSG_TRIMMED_USERS", "Trimmed $b%d users$b with access from %d to %d from the %s user list who were inactive for at least %s." },
198 { "CSMSG_INCORRECT_ACCESS", "%s has access $b%s$b, not %s." },
199 { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with %s access)." },
200 { "CSMSG_ADDUSER_PENDING", "I have sent him/her a message letting them know, and if they auth or register soon, I will finish adding them automatically." },
201 { "CSMSG_ADDUSER_PENDING_ALREADY", "He or she is already pending addition to %s once he/she auths with $b$N$b." },
202 { "CSMSG_ADDUSER_PENDING_HEADER", "Users to add to channels pending logins:" }, /* Remove after testing? */
203 { "CSMSG_ADDUSER_PENDING_LIST", "Channel %s user %s" }, /* Remove after testing? */
204 { "CSMSG_ADDUSER_PENDING_FOOTER", "--------- End of pending list ----------" }, /* Remove after testing? */
205 /*{ "CSMSG_ADDUSER_PENDING_NOTINCHAN", "That user is not in %s, and is not auth'd." }, */
206 { "CSMSG_ADDUSER_PENDING_TARGET", "Channel Services bot here: %s would like to add you to my userlist in channel %s, but you are not authenticated to $b$N$b. Please authenticate now and you will be added. If you do not have an account, type /msg $N help register" },
207 { "CSMSG_CANNOT_TRIM", "You must include a minimum inactivity duration of at least 60 seconds to trim." },
209 { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." },
210 { "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." },
211 { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
212 { "CSMSG_TRANSFER_WAIT", "You must wait %s before you can give ownership of $b%s$b to someone else." },
213 { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
214 { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
217 { "CSMSG_LAMER_ADDED", "Added $b%s$b to %s LAMERs." },
218 { "CSMSG_TIMED_LAMER_ADDED", "LAMERed $b%s$b on %s for %s." },
219 { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
220 { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
221 { "CSMSG_REASON_CHANGE", "Reason for LAMER $b%s$b changed." },
222 { "CSMSG_LAMER_EXTENDED", "Extended LAMER for $b%s$b, now expires in %s." },
223 { "CSMSG_BAN_REMOVED", "Matching ban(s) and LAMER(s) in $b%s$b were removed." },
224 { "CSMSG_TRIMMED_LAMERS", "Trimmed $b%d LAMERs$b from the %s LAMER list that were inactive for at least %s." },
225 { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
226 { "CSMSG_REDUNDANT_LAMER", "$b%s$b is already LAMER'd in %s." },
227 { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
228 { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
229 { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
230 { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
231 { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
232 { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban or LAMER found: $b%s$b." },
233 { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
235 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
237 /* Channel management */
238 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
239 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
240 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
242 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
243 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
244 { "CSMSG_TOPICMASK_CONFLICT1", "I do not know how to make that topic work with the current topic mask in $b%s$b, which is: %s" },
245 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic is at most %d characters and matches the topic mask pattern." },
246 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
247 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
248 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
250 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
251 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
252 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
253 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
254 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
255 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
256 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you must use 'set defaults %s'." },
257 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
258 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
260 { "CSMSG_BAD_GIVEVOICE", "You cannot change GiveVoice to above GiveHalfOps (%d)." },
261 { "CSMSG_BAD_GIVEHOPS", "You cannot change GiveHalfOps to below GiveOps (%d)." },
262 { "CSMSG_BAD_GIVEOPS", "You cannot change GiveOps to below GiveVoice (%d)." },
264 { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
265 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
266 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
267 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
268 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
269 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
270 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
271 { "CSMSG_SET_MODES", "$bModes $b %s" },
272 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
273 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s - +l joinflood protection." },
274 { "CSMSG_SET_OFFCHANNEL", "$bOffChannel $b %s" },
275 { "CSMSG_SET_USERINFO", "$bUserInfo $b %d - and above userinfos are shown." },
277 { "CSMSG_SET_GIVE_VOICE", "$bGiveVoice $b %d" },
278 { "CSMSG_SET_GIVE_HALFOPS", "$bGiveHalfOps $b %d" },
280 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" },
281 { "CSMSG_SET_INVITEME", "$bInviteMe $b %d - Userlevel required to invite self." },
282 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d - level and above can op unknown users." },
283 { "CSMSG_SET_ENFHALFOPS", "$bEnfHalfOps $b %d - level and above can hop unknown users." },
285 { "CSMSG_SET_GIVE_OPS", "$bGiveOps $b %d" },
287 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d - and above can change channel modes." },
288 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d - and above can set the topic." },
289 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d - and above can use public commands." },
290 { "CSMSG_SET_SETTERS", "$bSetters $b %d - and above can change these settings." },
291 /* { "CSMSG_SET_CTCPUSERS", "$bCTCPUsers $b %d - and above can use ctcps." }, */
292 { "CSMSG_SET_VOICE", "$bvoice $b %d - %s" },
293 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
294 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
295 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
296 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
297 { "CSMSG_USET_AUTOOP", "$bAutoOp $b %s" },
298 { "CSMSG_USET_AUTOVOICE", "$bAutoVoice $b %s" },
299 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
300 { "CSMSG_USET_INFO", "$bInfo $b %s" },
302 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
303 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
304 { "CSMSG_HOPBY_LOCKED", "You may not halfop users who lack halfop or greater access." },
305 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
306 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
307 { "CSMSG_HALFOPPED_USERS", "Halfopped users in $b%s$b." },
308 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
309 { "CSMSG_DEHALFOPPED_USERS", "DeHalfopped users in $b%s$b." },
310 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
311 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
312 { "CSMSG_VOICE_NONE", "Noone will be auto-voiced" },
313 { "CSMSG_VOICE_PEON", "PEONs will be auto-voiced" },
314 { "CSMSG_VOICE_ALL", "Everyone will be auto-voiced" },
315 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
316 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
317 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
318 { "CSMSG_PROTECT_NONE", "No users will be protected." },
319 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
320 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
321 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
322 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
323 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
324 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
325 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
326 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
327 { "CSMSG_CTCPREACTION_NONE", "CTCPs are allowed" },
328 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
329 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
330 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
331 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
333 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
334 { "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" },
335 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s." },
336 { "CSMSG_ALREADY_PRESENT", "%s is already in $b%s$b." },
337 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
338 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s to use this command." },
339 { "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
340 { "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
342 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
343 { "CSMSG_NO_BANS", "No bans found on $b%s$b." },
344 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
346 /* Channel userlist */
347 { "CSMSG_ACCESS_ALL_HEADER", "$b%s Users From Level %s To %s$b" },
348 { "CSMSG_ACCESS_SEARCH_HEADER", "$b%s Users From Level %s To %s Matching %s$b" },
349 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
350 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%s$b (%u) in %s." },
351 { "CSMSG_LAMERS_HEADER", "$bLamers in %s$b" },
353 /* Channel note list */
354 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
355 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
356 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
357 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
358 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
359 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
360 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
361 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
362 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
363 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
364 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
365 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
366 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
367 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
368 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
370 /* Channel [un]suspension */
371 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
372 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
373 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
374 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
375 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
376 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
377 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
379 /* Access information */
380 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
381 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
382 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
383 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
384 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
385 { "CSMSG_USER_HAS_ACCESS", "%s has $b%s$b access (%d) in %s." },
386 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
387 { "CSMSG_HELPER_HAS_ACCESS", "%s has $b%s$b access (%d) in %s and has $bsecurity override$b enabled." },
388 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
389 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
390 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
392 /* Seen information */
393 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
394 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
395 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
396 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
398 /* Names information */
399 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
400 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
402 /* Channel information */
403 { "CSMSG_CHANNEL_INFO", "$bInformation About %s$b" },
404 { "CSMSG_BAR", "----------------------------------------"},
405 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
406 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
407 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
408 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
409 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
410 { "CSMSG_CHANNEL_LAMERS", "$bLamer Count: $b%i" },
411 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
412 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
413 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
414 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
415 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
416 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
417 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
418 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
419 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
420 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
421 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
422 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
423 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
424 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
425 { "CSMSG_CHANNEL_END", "---------------End of Info--------------"},
427 { "CSMSG_PEEK_INFO", "$bStatus of %s$b" },
428 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
429 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
430 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d" },
431 { "CSMSG_PEEK_OPS", "$bOps:$b" },
432 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
433 { "CSMSG_PEEK_END", "-------------End of Status--------------" },
435 /* Network information */
436 { "CSMSG_NETWORK_INFO", "Network Information:" },
437 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
438 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
439 { "CSMSG_NETWORK_LAMERS", "$bTotal Lamer Count: $b%i" },
440 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
441 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
442 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
443 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
444 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
447 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
448 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
449 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
451 /* Channel searches */
452 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
453 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
454 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
455 { "CSMSG_CHANNEL_SEARCH_RESULTS", "$bChannels Found Matching Search$b" },
457 /* Channel configuration */
458 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
459 { "CSMSG_CHANNEL_OPTIONS", "$bChannel Options for %s$b" },
460 { "CSMSG_CHANNEL_OPTIONS_END", "-------------End of Options-------------" },
461 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
464 { "CSMSG_USER_OPTIONS", "User Options:" },
465 // { "CSMSG_USER_PROTECTED", "That user is protected." },
468 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
469 { "CSMSG_PING_RESPONSE", "Pong!" },
470 { "CSMSG_WUT_RESPONSE", "wut" },
471 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
472 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
473 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
474 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
475 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
476 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
477 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
480 { "CSMSG_EVENT_SEARCH_RESULTS", "$bChannel Events for %s$b" },
481 { "CSMSG_LAST_INVALID", "Invalid argument. must be 1-200" },
485 /* eject_user and unban_user flags */
486 #define ACTION_KICK 0x0001
487 #define ACTION_BAN 0x0002
488 #define ACTION_ADD_LAMER 0x0004
489 #define ACTION_ADD_TIMED_LAMER 0x0008
490 #define ACTION_UNBAN 0x0010
491 #define ACTION_DEL_LAMER 0x0020
493 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
494 #define MODELEN 40 + KEYLEN
498 #define CSFUNC_ARGS user, channel, argc, argv, cmd
500 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
501 #define CHANSERV_SYNTAX() svccmd_send_help_brief(user, chanserv, cmd)
502 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
503 reply("MSG_MISSING_PARAMS", argv[0]); \
507 DECLARE_LIST(dnrList
, struct do_not_register
*);
508 DEFINE_LIST(dnrList
, struct do_not_register
*);
510 static int eject_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
);
512 struct userNode
*chanserv
;
515 static dict_t plain_dnrs
, mask_dnrs
, handle_dnrs
;
516 static struct log_type
*CS_LOG
;
517 struct adduserPending
* adduser_pendings
= NULL
;
518 unsigned int adduser_pendings_count
= 0;
522 struct channelList support_channels
;
523 struct mod_chanmode default_modes
;
525 unsigned long db_backup_frequency
;
526 unsigned long channel_expire_frequency
;
529 unsigned int adjust_delay
;
530 long channel_expire_delay
;
531 unsigned int nodelete_level
;
533 unsigned int adjust_threshold
;
534 int join_flood_threshold
;
536 unsigned int greeting_length
;
537 unsigned int refresh_period
;
538 unsigned int giveownership_period
;
540 unsigned int max_owned
;
541 unsigned int max_chan_users
;
542 unsigned int max_chan_bans
; /* lamers */
543 unsigned int max_userinfo_length
;
545 struct string_list
*set_shows
;
546 struct string_list
*eightball
;
547 struct string_list
*old_ban_names
;
549 const char *ctcp_short_ban_duration
;
550 const char *ctcp_long_ban_duration
;
552 const char *irc_operator_epithet
;
553 const char *network_helper_epithet
;
554 const char *support_helper_epithet
;
559 struct userNode
*user
;
560 struct userNode
*bot
;
561 struct chanNode
*channel
;
563 unsigned short lowest
;
564 unsigned short highest
;
565 struct userData
**users
;
566 struct helpfile_table table
;
569 enum note_access_type
571 NOTE_SET_CHANNEL_ACCESS
,
572 NOTE_SET_CHANNEL_SETTER
,
576 enum note_visible_type
579 NOTE_VIS_CHANNEL_USERS
,
585 enum note_access_type set_access_type
;
587 unsigned int min_opserv
;
588 unsigned short min_ulevel
;
590 enum note_visible_type visible_type
;
591 unsigned int max_length
;
598 struct note_type
*type
;
599 char setter
[NICKSERV_HANDLE_LEN
+1];
603 static unsigned int registered_channels
;
604 static unsigned int banCount
;
606 static const struct {
609 unsigned short level
;
611 } accessLevels
[] = { /* MUST be orderd less to most! */
612 { "peon", "Peon", UL_PEON
, '+' },
613 { "halfop", "HalfOp", UL_HALFOP
, '%' },
614 { "op", "Op", UL_OP
, '@' },
615 { "manager", "Manager", UL_MANAGER
, '%' },
616 { "coowner", "Coowner", UL_COOWNER
, '*' },
617 { "owner", "Owner", UL_OWNER
, '!' },
618 { "helper", "BUG:", UL_HELPER
, 'X' }
621 /* If you change this, change the enum in chanserv.h also, or stack smashing will commence. */
622 static const struct {
625 unsigned short default_value
;
626 unsigned int old_idx
;
627 unsigned int old_flag
;
628 unsigned short flag_value
;
630 // { "CSMSG_SET_GIVE_VOICE", "givevoice", 100, ~0, CHANNEL_VOICE_ALL, 0 },
631 // { "CSMSG_SET_GIVE_HALFOPS", "givehalfops", 150, ~0, CHANNEL_HOP_ALL, 0 },
632 // { "CSMSG_SET_GIVE_OPS", "giveops", 200, 2, 0, 0 }, // these 3 need removed, but causes segs if its still in the db..
633 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
634 { "CSMSG_SET_ENFHALFOPS", "enfhalfops", 300, 1, 0, 0 },
635 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
636 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
637 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
638 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
639 // { "CSMSG_SET_CTCPUSERS", "ctcpusers", 0, 9, 0, 0 },
640 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES
, 1 },
641 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE
, 200 },
642 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF
, 1 }
645 struct charOptionValues
{
649 { 'n', "CSMSG_VOICE_NONE" },
650 { 'p', "CSMSG_VOICE_PEON" },
651 { 'a', "CSMSG_VOICE_ALL" }
652 }, protectValues
[] = {
653 { 'a', "CSMSG_PROTECT_ALL" },
654 { 'e', "CSMSG_PROTECT_EQUAL" },
655 { 'l', "CSMSG_PROTECT_LOWER" },
656 { 'n', "CSMSG_PROTECT_NONE" }
658 { 'd', "CSMSG_TOYS_DISABLED" },
659 { 'n', "CSMSG_TOYS_PRIVATE" },
660 { 'p', "CSMSG_TOYS_PUBLIC" }
661 }, topicRefreshValues
[] = {
662 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
663 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
664 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
665 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
666 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
667 }, ctcpReactionValues
[] = {
668 { 'n', "CSMSG_CTCPREACTION_NONE" },
669 { 'k', "CSMSG_CTCPREACTION_KICK" },
670 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
671 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
672 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
675 static const struct {
679 unsigned int old_idx
;
681 struct charOptionValues
*values
;
683 { "CSMSG_SET_VOICE", "voice", 'p', 99, ArrayLength(voiceValues
), voiceValues
},
684 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues
), protectValues
},
685 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues
), toysValues
},
686 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues
), topicRefreshValues
},
687 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 'n', 10, ArrayLength(ctcpReactionValues
), ctcpReactionValues
}
690 struct userData
*helperList
;
691 struct chanData
*channelList
;
692 static struct module *chanserv_module
;
693 static unsigned int userCount
;
694 unsigned int chanserv_read_version
= 0; /* db version control */
696 #define CHANSERV_DB_VERSION 2
698 #define GetChannelUser(channel, handle) _GetChannelUser(channel, handle, 1, 0)
699 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
700 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
703 user_level_from_name(const char *name
, unsigned short clamp_level
)
705 unsigned int level
= 0, ii
;
707 level
= strtoul(name
, NULL
, 10);
708 else for(ii
= 0; (ii
< ArrayLength(accessLevels
)) && !level
; ++ii
)
709 if(!irccasecmp(name
, accessLevels
[ii
].name
))
710 level
= accessLevels
[ii
].level
;
711 if(level
> clamp_level
)
717 user_level_name_from_level(int level
)
725 for(ii
= 0; (ii
< ArrayLength(accessLevels
)); ii
++)
726 if(level
>= accessLevels
[ii
].level
)
727 highest
= accessLevels
[ii
].title
;
733 parse_level_range(unsigned short *minl
, unsigned short *maxl
, const char *arg
)
736 *minl
= strtoul(arg
, &sep
, 10);
744 *maxl
= strtoul(sep
+1, &sep
, 10);
752 _GetChannelUser(struct chanData
*channel
, struct handle_info
*handle
, int override
, int allow_suspended
)
754 struct userData
*uData
, **head
;
756 if(!channel
|| !handle
)
759 if(override
&& HANDLE_FLAGGED(handle
, HELPING
)
760 && ((handle
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
)))
762 for(uData
= helperList
;
763 uData
&& uData
->handle
!= handle
;
764 uData
= uData
->next
);
768 uData
= calloc(1, sizeof(struct userData
));
769 uData
->handle
= handle
;
771 uData
->access
= UL_HELPER
;
777 uData
->next
= helperList
;
779 helperList
->prev
= uData
;
787 for(uData
= channel
->users
; uData
; uData
= uData
->next
)
788 if((uData
->handle
== handle
) && (allow_suspended
|| !IsUserSuspended(uData
)))
791 head
= &(channel
->users
);
794 if(uData
&& (uData
!= *head
))
796 /* Shuffle the user to the head of whatever list he was in. */
798 uData
->next
->prev
= uData
->prev
;
800 uData
->prev
->next
= uData
->next
;
806 (**head
).prev
= uData
;
813 /* Returns non-zero if user has at least the minimum access.
814 * exempt_owner is set when handling !set, so the owner can set things
817 int check_user_level(struct chanNode
*channel
, struct userNode
*user
, enum levelOption opt
, int allow_override
, int exempt_owner
)
819 struct userData
*uData
;
820 struct chanData
*cData
= channel
->channel_info
;
821 unsigned short minimum
= cData
->lvlOpts
[opt
];
824 uData
= _GetChannelUser(cData
, user
->handle_info
, allow_override
, 0);
827 if(minimum
<= uData
->access
)
829 if((minimum
> UL_OWNER
) && (uData
->access
== UL_OWNER
) && exempt_owner
)
834 /* Scan for other users authenticated to the same handle
835 still in the channel. If so, keep them listed as present.
837 user is optional, if not null, it skips checking that userNode
838 (for the handle_part function) */
840 scan_user_presence(struct userData
*uData
, struct userNode
*user
)
844 if(IsSuspended(uData
->channel
)
845 || IsUserSuspended(uData
)
846 || !(mn
= find_handle_in_channel(uData
->channel
->channel
, uData
->handle
, user
)))
858 chanserv_ctcp_check(struct userNode
*user
, struct chanNode
*channel
, char *text
, UNUSED_ARG(struct userNode
*bot
))
860 unsigned int eflags
, argc
;
862 static char *bad_ctcp_reason
= "CTCPs to this channel are forbidden.";
864 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
865 if(!channel
->channel_info
866 || IsSuspended(channel
->channel_info
)
868 || !ircncasecmp(text
, "ACTION ", 7))
870 /* We dont punish people we know -Rubin
871 * * Figure out the minimum level needed to CTCP the channel *
873 * if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
876 /* If they are a user of the channel, they are exempt */
877 if(_GetChannelUser(channel
->channel_info
, user
->handle_info
, 0, 0))
879 /* We need to enforce against them; do so. */
882 argv
[1] = user
->nick
;
884 if(GetUserMode(channel
, user
))
885 eflags
|= ACTION_KICK
;
886 switch(channel
->channel_info
->chOpts
[chCTCPReaction
]) {
887 default: case 'n': return;
889 eflags
|= ACTION_KICK
;
892 eflags
|= ACTION_BAN
;
895 eflags
|= ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
;
896 argv
[argc
++] = (char*)chanserv_conf
.ctcp_short_ban_duration
;
899 eflags
|= ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
;
900 argv
[argc
++] = (char*)chanserv_conf
.ctcp_long_ban_duration
;
903 argv
[argc
++] = bad_ctcp_reason
;
904 eject_user(chanserv
, channel
, argc
, argv
, NULL
, eflags
);
908 chanserv_create_note_type(const char *name
)
910 struct note_type
*ntype
= calloc(1, sizeof(*ntype
) + strlen(name
));
911 strcpy(ntype
->name
, name
);
913 dict_insert(note_types
, ntype
->name
, ntype
);
918 chanserv_deref_note_type(void *data
)
920 struct note_type
*ntype
= data
;
922 if(--ntype
->refs
> 0)
928 chanserv_flush_note_type(struct note_type
*ntype
)
930 struct chanData
*cData
;
931 for(cData
= channelList
; cData
; cData
= cData
->next
)
932 dict_remove(cData
->notes
, ntype
->name
);
936 chanserv_truncate_notes(struct note_type
*ntype
)
938 struct chanData
*cData
;
940 unsigned int size
= sizeof(*note
) + ntype
->max_length
;
942 for(cData
= channelList
; cData
; cData
= cData
->next
) {
943 note
= dict_find(cData
->notes
, ntype
->name
, NULL
);
946 if(strlen(note
->note
) <= ntype
->max_length
)
948 dict_remove2(cData
->notes
, ntype
->name
, 1);
949 note
= realloc(note
, size
);
950 note
->note
[ntype
->max_length
] = 0;
951 dict_insert(cData
->notes
, ntype
->name
, note
);
955 static int note_type_visible_to_user(struct chanData
*channel
, struct note_type
*ntype
, struct userNode
*user
);
958 chanserv_add_channel_note(struct chanData
*channel
, struct note_type
*type
, const char *setter
, const char *text
)
961 unsigned int len
= strlen(text
);
963 if(len
> type
->max_length
) len
= type
->max_length
;
964 note
= calloc(1, sizeof(*note
) + len
);
966 strncpy(note
->setter
, setter
, sizeof(note
->setter
)-1);
967 memcpy(note
->note
, text
, len
);
969 dict_insert(channel
->notes
, type
->name
, note
);
975 chanserv_free_note(void *data
)
977 struct note
*note
= data
;
979 chanserv_deref_note_type(note
->type
);
980 assert(note
->type
->refs
> 0); /* must use delnote to remove the type */
984 static MODCMD_FUNC(cmd_createnote
) {
985 struct note_type
*ntype
;
986 unsigned int arg
= 1, existed
= 0, max_length
;
988 if((ntype
= dict_find(note_types
, argv
[1], NULL
)))
991 ntype
= chanserv_create_note_type(argv
[arg
]);
992 if(!irccasecmp(argv
[++arg
], "privileged"))
995 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
996 ntype
->set_access
.min_opserv
= strtoul(argv
[arg
], NULL
, 0);
998 else if(!irccasecmp(argv
[arg
], "channel"))
1000 unsigned short ulvl
= user_level_from_name(argv
[++arg
], UL_OWNER
);
1003 reply("CSMSG_INVALID_ACCESS", argv
[arg
]);
1006 ntype
->set_access_type
= NOTE_SET_CHANNEL_ACCESS
;
1007 ntype
->set_access
.min_ulevel
= ulvl
;
1009 else if(!irccasecmp(argv
[arg
], "setter"))
1011 ntype
->set_access_type
= NOTE_SET_CHANNEL_SETTER
;
1015 reply("CSMSG_BAD_NOTE_ACCESS", argv
[arg
]);
1019 if(!irccasecmp(argv
[++arg
], "privileged"))
1020 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
1021 else if(!irccasecmp(argv
[arg
], "channel_users"))
1022 ntype
->visible_type
= NOTE_VIS_CHANNEL_USERS
;
1023 else if(!irccasecmp(argv
[arg
], "all"))
1024 ntype
->visible_type
= NOTE_VIS_ALL
;
1026 reply("CSMSG_BAD_NOTE_ACCESS", argv
[arg
]);
1030 if((arg
+1) >= argc
) {
1031 reply("MSG_MISSING_PARAMS", argv
[0]);
1034 max_length
= strtoul(argv
[++arg
], NULL
, 0);
1035 if(max_length
< 20 || max_length
> 450)
1037 reply("CSMSG_BAD_MAX_LENGTH", argv
[arg
]);
1040 if(existed
&& (max_length
< ntype
->max_length
))
1042 ntype
->max_length
= max_length
;
1043 chanserv_truncate_notes(ntype
);
1045 ntype
->max_length
= max_length
;
1048 reply("CSMSG_NOTE_MODIFIED", ntype
->name
);
1050 reply("CSMSG_NOTE_CREATED", ntype
->name
);
1055 dict_remove(note_types
, ntype
->name
);
1059 static MODCMD_FUNC(cmd_removenote
) {
1060 struct note_type
*ntype
;
1063 ntype
= dict_find(note_types
, argv
[1], NULL
);
1064 force
= (argc
> 2) && !irccasecmp(argv
[2], "force");
1067 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
1074 reply("CSMSG_NOTE_TYPE_USED", ntype
->name
);
1077 chanserv_flush_note_type(ntype
);
1079 dict_remove(note_types
, argv
[1]);
1080 reply("CSMSG_NOTE_DELETED", argv
[1]);
1085 mode_lock_violated(const struct mod_chanmode
*orig
, const struct mod_chanmode
*change
)
1089 if(orig
->modes_set
& change
->modes_clear
)
1091 if(orig
->modes_clear
& change
->modes_set
)
1093 if((orig
->modes_set
& MODE_KEY
) && (change
->modes_set
& MODE_KEY
)
1094 && strcmp(orig
->new_key
, change
->new_key
))
1096 if((orig
->modes_set
& MODE_LIMIT
) && (change
->modes_set
& MODE_LIMIT
)
1097 && (orig
->new_limit
!= change
->new_limit
))
1102 static char max_length_text
[MAXLEN
+1][16];
1104 static struct helpfile_expansion
1105 chanserv_expand_variable(const char *variable
)
1107 struct helpfile_expansion exp
;
1109 if(!irccasecmp(variable
, "notes"))
1112 exp
.type
= HF_TABLE
;
1113 exp
.value
.table
.length
= 1;
1114 exp
.value
.table
.width
= 3;
1115 exp
.value
.table
.flags
= 0;
1116 exp
.value
.table
.contents
= calloc(dict_size(note_types
)+1, sizeof(char**));
1117 exp
.value
.table
.contents
[0] = calloc(exp
.value
.table
.width
, sizeof(char*));
1118 exp
.value
.table
.contents
[0][0] = "Note Type";
1119 exp
.value
.table
.contents
[0][1] = "Visibility";
1120 exp
.value
.table
.contents
[0][2] = "Max Length";
1121 for(it
=dict_first(note_types
); it
; it
=iter_next(it
))
1123 struct note_type
*ntype
= iter_data(it
);
1126 if(!note_type_visible_to_user(NULL
, ntype
, message_dest
)) continue;
1127 row
= exp
.value
.table
.length
++;
1128 exp
.value
.table
.contents
[row
] = calloc(exp
.value
.table
.width
, sizeof(char*));
1129 exp
.value
.table
.contents
[row
][0] = ntype
->name
;
1130 exp
.value
.table
.contents
[row
][1] = (ntype
->visible_type
== NOTE_VIS_ALL
) ? "all" :
1131 (ntype
->visible_type
== NOTE_VIS_CHANNEL_USERS
) ? "chan users" :
1133 if(!max_length_text
[ntype
->max_length
][0])
1134 snprintf(max_length_text
[ntype
->max_length
], sizeof(max_length_text
[ntype
->max_length
]), "%u", ntype
->max_length
);
1135 exp
.value
.table
.contents
[row
][2] = max_length_text
[ntype
->max_length
];
1140 exp
.type
= HF_STRING
;
1141 exp
.value
.str
= NULL
;
1145 static struct chanData
*
1146 register_channel(struct chanNode
*cNode
, char *registrar
)
1148 struct chanData
*channel
;
1149 enum levelOption lvlOpt
;
1150 enum charOption chOpt
;
1152 channel
= calloc(1, sizeof(struct chanData
));
1154 channel
->notes
= dict_new();
1155 dict_set_free_data(channel
->notes
, chanserv_free_note
);
1157 channel
->registrar
= strdup(registrar
);
1158 channel
->registered
= now
;
1159 channel
->visited
= now
;
1160 channel
->limitAdjusted
= now
;
1161 channel
->ownerTransfer
= now
;
1162 channel
->flags
= CHANNEL_DEFAULT_FLAGS
;
1163 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
1164 channel
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
1165 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
1166 channel
->chOpts
[chOpt
] = charOptions
[chOpt
].default_value
;
1168 channel
->prev
= NULL
;
1169 channel
->next
= channelList
;
1172 channelList
->prev
= channel
;
1173 channelList
= channel
;
1174 registered_channels
++;
1176 channel
->channel
= cNode
;
1178 cNode
->channel_info
= channel
;
1183 static struct userData
*
1184 add_channel_user(struct chanData
*channel
, struct handle_info
*handle
, unsigned short access
, time_t seen
, const char *info
)
1186 struct userData
*ud
;
1188 if(access
> UL_OWNER
)
1191 ud
= calloc(1, sizeof(*ud
));
1192 ud
->channel
= channel
;
1193 ud
->handle
= handle
;
1195 ud
->access
= access
;
1196 ud
->info
= info
? strdup(info
) : NULL
;
1199 ud
->next
= channel
->users
;
1201 channel
->users
->prev
= ud
;
1202 channel
->users
= ud
;
1204 channel
->userCount
++;
1208 ud
->u_next
= ud
->handle
->channels
;
1210 ud
->u_next
->u_prev
= ud
;
1211 ud
->handle
->channels
= ud
;
1213 ud
->flags
= USER_FLAGS_DEFAULT
;
1217 static void unregister_channel(struct chanData
*channel
, const char *reason
);
1220 del_channel_user(struct userData
*user
, int do_gc
)
1222 struct chanData
*channel
= user
->channel
;
1224 channel
->userCount
--;
1228 user
->prev
->next
= user
->next
;
1230 channel
->users
= user
->next
;
1232 user
->next
->prev
= user
->prev
;
1235 user
->u_prev
->u_next
= user
->u_next
;
1237 user
->handle
->channels
= user
->u_next
;
1239 user
->u_next
->u_prev
= user
->u_prev
;
1243 if(do_gc
&& !channel
->users
&& !IsProtected(channel
))
1244 unregister_channel(channel
, "lost all users.");
1247 static struct adduserPending
*
1248 add_adduser_pending(struct chanNode
*channel
, struct userNode
*user
, int level
)
1250 struct adduserPending
*ap
;
1251 ap
= calloc(1,sizeof(struct adduserPending
));
1252 ap
->channel
= channel
;
1255 ap
->created
= time(NULL
);
1257 /* ap->prev defaults to NULL already.. */
1258 ap
->next
= adduser_pendings
;
1259 if(adduser_pendings
)
1260 adduser_pendings
->prev
= ap
;
1261 adduser_pendings
= ap
;
1262 adduser_pendings_count
++;
1267 del_adduser_pending(struct adduserPending
*ap
)
1270 ap
->prev
->next
= ap
->next
;
1272 adduser_pendings
= ap
->next
;
1275 ap
->next
->prev
= ap
->prev
;
1279 static void expire_adduser_pending();
1281 /* find_adduser_pending(channel, user) will find an arbitrary record
1282 * from user, channel, or user and channel.
1283 * if user or channel are NULL, they will match any records.
1285 static struct adduserPending
*
1286 find_adduser_pending(struct chanNode
*channel
, struct userNode
*user
)
1288 struct adduserPending
*ap
;
1290 expire_adduser_pending(); /* why not here.. */
1292 if(!channel
&& !user
) /* 2 nulls matches all */
1293 return(adduser_pendings
);
1294 for(ap
= adduser_pendings
;ap
;ap
= ap
->next
)
1296 if((channel
== ap
->channel
&& (user
== NULL
|| user
== ap
->user
)) || (user
==ap
->user
&& channel
==NULL
))
1303 /* Remove all pendings for a user or channel
1305 * called in nickserv.c DelUser() and proto-* unregister_channel()
1308 wipe_adduser_pending(struct chanNode
*channel
, struct userNode
*user
)
1310 struct adduserPending
*ap
;
1312 /* So this is a bit wastefull, i hate dealing with linked lists.
1313 * if its a problem we'll rewrite it right */
1314 while((ap
= find_adduser_pending(channel
, user
))) {
1315 del_adduser_pending(ap
);
1319 /* Called from nickserv.c cmd_auth after someone auths */
1321 process_adduser_pending(struct userNode
*user
)
1323 struct adduserPending
*ap
;
1324 if(!user
->handle_info
)
1325 return; /* not associated with an account */
1326 while((ap
= find_adduser_pending(NULL
, user
)))
1328 struct userData
*actee
;
1329 if(GetTrueChannelAccess(ap
->channel
->channel_info
, ap
->user
->handle_info
))
1331 /* Already on the userlist. do nothing*/
1335 actee
= add_channel_user(ap
->channel
->channel_info
, ap
->user
->handle_info
, ap
->level
, 0, NULL
);
1336 scan_user_presence(actee
, NULL
);
1338 del_adduser_pending(ap
);
1343 expire_adduser_pending()
1345 struct adduserPending
*ap
, *ap_next
;
1346 ap
= adduser_pendings
;
1349 if((ap
->created
+ ADDUSER_PENDING_EXPIRE
) < time(NULL
))
1351 ap_next
= ap
->next
; /* save next */
1352 del_adduser_pending(ap
); /* free and relink */
1353 ap
= ap_next
; /* advance */
1360 static void expire_ban(void *data
);
1362 static struct banData
*
1363 add_channel_ban(struct chanData
*channel
, const char *mask
, char *owner
, time_t set
, time_t triggered
, time_t expires
, char *reason
)
1366 unsigned int ii
, l1
, l2
;
1371 bd
= malloc(sizeof(struct banData
));
1373 bd
->channel
= channel
;
1375 bd
->triggered
= triggered
;
1376 bd
->expires
= expires
;
1378 for(ii
= 0; ii
< chanserv_conf
.old_ban_names
->used
; ++ii
)
1380 extern const char *hidden_host_suffix
;
1381 const char *old_name
= chanserv_conf
.old_ban_names
->list
[ii
];
1385 l2
= strlen(old_name
);
1388 if(irccasecmp(mask
+ l1
- l2
, old_name
))
1390 new_mask
= alloca(MAXLEN
);
1391 sprintf(new_mask
, "%.*s%s", (int)(l1
-l2
), mask
, hidden_host_suffix
);
1394 safestrncpy(bd
->mask
, mask
, sizeof(bd
->mask
));
1396 safestrncpy(bd
->owner
, owner
, sizeof(bd
->owner
));
1397 bd
->reason
= strdup(reason
);
1400 timeq_add(expires
, expire_ban
, bd
);
1403 bd
->next
= channel
->bans
; /* lamers */
1405 channel
->bans
->prev
= bd
;
1407 channel
->banCount
++;
1414 del_channel_ban(struct banData
*ban
)
1416 ban
->channel
->banCount
--;
1420 ban
->prev
->next
= ban
->next
;
1422 ban
->channel
->bans
= ban
->next
;
1425 ban
->next
->prev
= ban
->prev
;
1428 timeq_del(0, expire_ban
, ban
, TIMEQ_IGNORE_WHEN
);
1437 expire_ban(void *data
) /* lamer.. */
1439 struct banData
*bd
= data
;
1440 if(!IsSuspended(bd
->channel
))
1442 struct banList bans
;
1443 struct mod_chanmode change
;
1445 bans
= bd
->channel
->channel
->banlist
;
1446 mod_chanmode_init(&change
);
1447 for(ii
=0; ii
<bans
.used
; ii
++)
1449 if(!strcmp(bans
.list
[ii
]->ban
, bd
->mask
))
1452 change
.args
[0].mode
= MODE_REMOVE
|MODE_BAN
;
1453 change
.args
[0].u
.hostmask
= bd
->mask
;
1454 mod_chanmode_announce(chanserv
, bd
->channel
->channel
, &change
);
1460 del_channel_ban(bd
);
1463 static void chanserv_expire_suspension(void *data
);
1466 unregister_channel(struct chanData
*channel
, const char *reason
)
1468 struct mod_chanmode change
;
1469 char msgbuf
[MAXLEN
];
1471 /* After channel unregistration, the following must be cleaned
1473 - Channel information.
1475 - Channel bans. (lamers)
1476 - Channel suspension data.
1477 - adduser_pending data.
1478 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1484 timeq_del(0, NULL
, channel
, TIMEQ_IGNORE_FUNC
| TIMEQ_IGNORE_WHEN
);
1488 mod_chanmode_init(&change
);
1489 change
.modes_clear
|= MODE_REGISTERED
;
1490 mod_chanmode_announce(chanserv
, channel
->channel
, &change
);
1493 wipe_adduser_pending(channel
->channel
, NULL
);
1495 while(channel
->users
)
1496 del_channel_user(channel
->users
, 0);
1498 while(channel
->bans
)
1499 del_channel_ban(channel
->bans
);
1501 free(channel
->topic
);
1502 free(channel
->registrar
);
1503 free(channel
->greeting
);
1504 free(channel
->user_greeting
);
1505 free(channel
->topic_mask
);
1508 channel
->prev
->next
= channel
->next
;
1510 channelList
= channel
->next
;
1513 channel
->next
->prev
= channel
->prev
;
1515 if(channel
->suspended
)
1517 struct chanNode
*cNode
= channel
->channel
;
1518 struct suspended
*suspended
, *next_suspended
;
1520 for(suspended
= channel
->suspended
; suspended
; suspended
= next_suspended
)
1522 next_suspended
= suspended
->previous
;
1523 free(suspended
->suspender
);
1524 free(suspended
->reason
);
1525 if(suspended
->expires
)
1526 timeq_del(suspended
->expires
, chanserv_expire_suspension
, suspended
, 0);
1531 cNode
->channel_info
= NULL
;
1533 channel
->channel
->channel_info
= NULL
;
1535 dict_delete(channel
->notes
);
1536 sprintf(msgbuf
, "%s %s", channel
->channel
->name
, reason
);
1537 if(!IsSuspended(channel
))
1538 DelChannelUser(chanserv
, channel
->channel
, msgbuf
, 0);
1539 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, msgbuf
);
1540 UnlockChannel(channel
->channel
);
1542 registered_channels
--;
1546 expire_channels(UNUSED_ARG(void *data
))
1548 struct chanData
*channel
, *next
;
1549 struct userData
*user
;
1550 char delay
[INTERVALLEN
], reason
[INTERVALLEN
+ 64];
1552 intervalString(delay
, chanserv_conf
.channel_expire_delay
, NULL
);
1553 sprintf(reason
, "Channel registration automatically expired after %s of disuse.", delay
);
1555 for(channel
= channelList
; channel
; channel
= next
)
1557 next
= channel
->next
;
1559 /* See if the channel can be expired. */
1560 if(((now
- channel
->visited
) <= chanserv_conf
.channel_expire_delay
)
1561 || IsProtected(channel
))
1564 /* Make sure there are no high-ranking users still in the channel. */
1565 for(user
=channel
->users
; user
; user
=user
->next
)
1566 if(user
->present
&& (user
->access
>= UL_PRESENT
))
1571 /* Unregister the channel */
1572 log_module(CS_LOG
, LOG_INFO
, "(%s) Channel registration expired.", channel
->channel
->name
);
1573 unregister_channel(channel
, "registration expired.");
1576 if(chanserv_conf
.channel_expire_frequency
)
1577 timeq_add(now
+ chanserv_conf
.channel_expire_frequency
, expire_channels
, NULL
);
1581 protect_user(const struct userNode
*victim
, const struct userNode
*aggressor
, struct chanData
*channel
)
1583 char protect
= channel
->chOpts
[chProtect
];
1584 struct userData
*cs_victim
, *cs_aggressor
;
1586 /* Don't protect if no one is to be protected, someone is attacking
1587 himself, or if the aggressor is an IRC Operator. */
1588 if(protect
== 'n' || victim
== aggressor
/* Opers dont get special treatment :/ || IsOper(aggressor) */)
1591 /* Don't protect if the victim isn't authenticated (because they
1592 can't be a channel user), unless we are to protect non-users
1594 cs_victim
= GetChannelAccess(channel
, victim
->handle_info
);
1595 if(protect
!= 'a' && !cs_victim
)
1598 /* Protect if the aggressor isn't a user because at this point,
1599 the aggressor can only be less than or equal to the victim. */
1600 cs_aggressor
= GetChannelAccess(channel
, aggressor
->handle_info
);
1604 /* If the aggressor was a user, then the victim can't be helped. */
1611 if(cs_victim
->access
> cs_aggressor
->access
)
1616 if(cs_victim
->access
>= cs_aggressor
->access
)
1625 validate_op(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1627 struct chanData
*cData
= channel
->channel_info
;
1628 struct userData
*cs_victim
;
1630 if((!(cs_victim
= GetChannelUser(cData
, victim
->handle_info
))
1631 || (cs_victim
->access
< UL_OP
/* cData->lvlOpts[lvlGiveOps]*/))
1632 && !check_user_level(channel
, user
, lvlEnfOps
, 0, 0))
1634 send_message(user
, chanserv
, "CSMSG_OPBY_LOCKED");
1642 validate_halfop(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1644 struct chanData
*cData
= channel
->channel_info
;
1645 struct userData
*cs_victim
;
1647 if((!(cs_victim
= GetChannelUser(cData
, victim
->handle_info
))
1648 || (cs_victim
->access
< UL_HALFOP
/* cData->lvlOpts[lvlGiveHalfOps] */))
1649 && !check_user_level(channel
, user
, lvlEnfHalfOps
, 0, 0))
1651 send_message(user
, chanserv
, "CSMSG_HOPBY_LOCKED");
1660 validate_deop(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1662 if(IsService(victim
))
1664 send_message(user
, chanserv
, "MSG_SERVICE_IMMUNE", victim
->nick
);
1668 if(protect_user(victim
, user
, channel
->channel_info
))
1670 send_message(user
, chanserv
, "CSMSG_USER_PROTECTED", victim
->nick
);
1678 validate_dehop(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1680 if(IsService(victim
))
1682 send_message(user
, chanserv
, "MSG_SERVICE_IMMUNE", victim
->nick
);
1686 if(protect_user(victim
, user
, channel
->channel_info
))
1688 send_message(user
, chanserv
, "CSMSG_USER_PROTECTED", victim
->nick
);
1695 static struct do_not_register
*
1696 chanserv_add_dnr(const char *chan_name
, const char *setter
, const char *reason
)
1698 struct do_not_register
*dnr
= calloc(1, sizeof(*dnr
)+strlen(reason
));
1699 safestrncpy(dnr
->chan_name
, chan_name
, sizeof(dnr
->chan_name
));
1700 safestrncpy(dnr
->setter
, setter
, sizeof(dnr
->setter
));
1701 strcpy(dnr
->reason
, reason
);
1703 if(dnr
->chan_name
[0] == '*')
1704 dict_insert(handle_dnrs
, dnr
->chan_name
+1, dnr
);
1705 else if(strpbrk(dnr
->chan_name
, "*?"))
1706 dict_insert(mask_dnrs
, dnr
->chan_name
, dnr
);
1708 dict_insert(plain_dnrs
, dnr
->chan_name
, dnr
);
1712 static struct dnrList
1713 chanserv_find_dnrs(const char *chan_name
, const char *handle
)
1715 struct dnrList list
;
1717 struct do_not_register
*dnr
;
1719 dnrList_init(&list
);
1720 if(handle
&& (dnr
= dict_find(handle_dnrs
, handle
, NULL
)))
1721 dnrList_append(&list
, dnr
);
1722 if(chan_name
&& (dnr
= dict_find(plain_dnrs
, chan_name
, NULL
)))
1723 dnrList_append(&list
, dnr
);
1725 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1726 if(match_ircglob(chan_name
, iter_key(it
)))
1727 dnrList_append(&list
, iter_data(it
));
1732 chanserv_show_dnrs(struct userNode
*user
, struct svccmd
*cmd
, const char *chan_name
, const char *handle
)
1734 struct dnrList list
;
1735 struct do_not_register
*dnr
;
1737 char buf
[INTERVALLEN
];
1739 list
= chanserv_find_dnrs(chan_name
, handle
);
1740 for(ii
= 0; (ii
< list
.used
) && (ii
< 10); ++ii
)
1742 dnr
= list
.list
[ii
];
1745 strftime(buf
, sizeof(buf
), "%Y %b %d", localtime(&dnr
->set
));
1746 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, buf
, dnr
->setter
, dnr
->reason
);
1749 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1752 reply("CSMSG_MORE_DNRS", list
.used
- ii
);
1757 struct do_not_register
*
1758 chanserv_is_dnr(const char *chan_name
, struct handle_info
*handle
)
1760 struct do_not_register
*dnr
;
1763 if(handle
&& (dnr
= dict_find(handle_dnrs
, handle
->handle
, NULL
)))
1767 if((dnr
= dict_find(plain_dnrs
, chan_name
, NULL
)))
1769 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1770 if(match_ircglob(chan_name
, iter_key(it
)))
1771 return iter_data(it
);
1776 static CHANSERV_FUNC(cmd_noregister
)
1779 struct do_not_register
*dnr
;
1780 char buf
[INTERVALLEN
];
1781 unsigned int matches
;
1787 reply("CSMSG_DNR_SEARCH_RESULTS");
1790 for(it
= dict_first(handle_dnrs
); it
; it
= iter_next(it
))
1792 dnr
= iter_data(it
);
1794 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
1796 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1799 for(it
= dict_first(plain_dnrs
); it
; it
= iter_next(it
))
1801 dnr
= iter_data(it
);
1803 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
1805 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1808 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1810 dnr
= iter_data(it
);
1812 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
1814 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1819 reply("MSG_MATCH_COUNT", matches
);
1821 reply("MSG_NO_MATCHES");
1827 if(!IsChannelName(target
) && (*target
!= '*'))
1829 reply("CSMSG_NOT_DNR", target
);
1835 const char *reason
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
1836 if((*target
== '*') && !get_handle_info(target
+ 1))
1838 reply("MSG_HANDLE_UNKNOWN", target
+ 1);
1841 chanserv_add_dnr(target
, user
->handle_info
->handle
, reason
);
1842 reply("CSMSG_NOREGISTER_CHANNEL", target
);
1846 reply("CSMSG_DNR_SEARCH_RESULTS");
1849 matches
= chanserv_show_dnrs(user
, cmd
, NULL
, target
+ 1);
1851 matches
= chanserv_show_dnrs(user
, cmd
, target
, NULL
);
1853 reply("MSG_NO_MATCHES");
1857 static CHANSERV_FUNC(cmd_allowregister
)
1859 const char *chan_name
= argv
[1];
1861 if((chan_name
[0] == '*') && dict_find(handle_dnrs
, chan_name
+1, NULL
))
1863 dict_remove(handle_dnrs
, chan_name
+1);
1864 reply("CSMSG_DNR_REMOVED", chan_name
);
1866 else if(dict_find(plain_dnrs
, chan_name
, NULL
))
1868 dict_remove(plain_dnrs
, chan_name
);
1869 reply("CSMSG_DNR_REMOVED", chan_name
);
1871 else if(dict_find(mask_dnrs
, chan_name
, NULL
))
1873 dict_remove(mask_dnrs
, chan_name
);
1874 reply("CSMSG_DNR_REMOVED", chan_name
);
1878 reply("CSMSG_NO_SUCH_DNR", chan_name
);
1885 chanserv_get_owned_count(struct handle_info
*hi
)
1887 struct userData
*cList
;
1890 for(owned
=0, cList
=hi
->channels
; cList
; cList
=cList
->u_next
)
1891 if(cList
->access
== UL_OWNER
)
1896 static CHANSERV_FUNC(cmd_register
)
1898 struct handle_info
*handle
;
1899 struct chanData
*cData
;
1900 struct modeNode
*mn
;
1901 char reason
[MAXLEN
];
1903 unsigned int new_channel
, force
=0;
1904 struct do_not_register
*dnr
;
1910 if(channel
->channel_info
)
1912 reply("CSMSG_ALREADY_REGGED", channel
->name
);
1916 if(channel
->bad_channel
)
1918 reply("CSMSG_ILLEGAL_CHANNEL", channel
->name
);
1922 if(!IsHelping(user
) && (!(mn
= GetUserMode(channel
, user
)) || !(mn
->modes
& MODE_CHANOP
)))
1924 reply("CSMSG_MUST_BE_OPPED", channel
->name
);
1929 chan_name
= channel
->name
;
1935 reply("MSG_MISSING_PARAMS", cmd
->name
);
1936 svccmd_send_help_brief(user
, chanserv
, cmd
);
1939 if(!IsChannelName(argv
[1]))
1941 reply("MSG_NOT_CHANNEL_NAME");
1945 if(opserv_bad_channel(argv
[1]))
1947 reply("CSMSG_ILLEGAL_CHANNEL", argv
[1]);
1952 chan_name
= argv
[1];
1955 if(argc
>= (new_channel
+2))
1957 if(!IsHelping(user
))
1959 reply("CSMSG_PROXY_FORBIDDEN");
1963 if(!(handle
= modcmd_get_handle_info(user
, argv
[new_channel
+1])))
1965 force
= (argc
> (new_channel
+2)) && !irccasecmp(argv
[new_channel
+2], "force");
1966 dnr
= chanserv_is_dnr(chan_name
, handle
);
1968 /* Check if they are over the limit.. */
1969 if((chanserv_get_owned_count(handle
) >= chanserv_conf
.max_owned
) && !force
)
1971 reply("CSMSG_OWN_TOO_MANY", handle
->handle
, chanserv_conf
.max_owned
);
1978 handle
= user
->handle_info
;
1979 dnr
= chanserv_is_dnr(chan_name
, handle
);
1980 /* Check if they are over the limit.. */
1981 if((chanserv_get_owned_count(handle
) >= chanserv_conf
.max_owned
) && !force
)
1983 reply("CSMSG_YOU_OWN_TOO_MANY", chanserv_conf
.max_owned
);
1986 /* Check if another service is in the channel */
1988 for(n
= 0; n
< channel
->members
.used
; n
++)
1990 mn
= channel
->members
.list
[n
];
1991 if((mn
&& mn
->user
&& (mn
->user
->modes
& FLAGS_SERVICE
)) || IsLocal(mn
->user
))
1993 reply("CSMSG_ANOTHER_SERVICE");
2000 if(!IsHelping(user
))
2001 reply("CSMSG_DNR_CHANNEL", chan_name
);
2003 chanserv_show_dnrs(user
, cmd
, chan_name
, handle
->handle
);
2007 /* now handled above for message specilization *
2008 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2010 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
2016 channel
= AddChannel(argv
[1], now
, NULL
, NULL
, NULL
);
2018 cData
= register_channel(channel
, user
->handle_info
->handle
);
2019 scan_user_presence(add_channel_user(cData
, handle
, UL_OWNER
, 0, NULL
), NULL
);
2020 cData
->modes
= chanserv_conf
.default_modes
;
2022 cData
->modes
.modes_set
|= MODE_REGISTERED
;
2023 if (IsOffChannel(cData
))
2025 mod_chanmode_announce(chanserv
, channel
, &cData
->modes
);
2029 struct mod_chanmode
*change
= mod_chanmode_dup(&cData
->modes
, 1);
2030 change
->args
[change
->argc
].mode
= MODE_CHANOP
;
2031 change
->args
[change
->argc
].u
.member
= AddChannelUser(chanserv
, channel
);
2033 mod_chanmode_announce(chanserv
, channel
, change
);
2034 mod_chanmode_free(change
);
2037 /* Initialize the channel's max user record. */
2038 cData
->max
= channel
->members
.used
;
2040 if(handle
!= user
->handle_info
)
2041 reply("CSMSG_PROXY_SUCCESS", handle
->handle
, channel
->name
);
2043 reply("CSMSG_REG_SUCCESS", channel
->name
);
2045 sprintf(reason
, "%s registered to %s by %s.", channel
->name
, handle
->handle
, user
->handle_info
->handle
);
2046 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
2051 make_confirmation_string(struct userData
*uData
)
2053 static char strbuf
[16];
2058 for(src
= uData
->handle
->handle
; *src
; )
2059 accum
= accum
* 31 + toupper(*src
++);
2061 for(src
= uData
->channel
->channel
->name
; *src
; )
2062 accum
= accum
* 31 + toupper(*src
++);
2063 sprintf(strbuf
, "%08x", accum
);
2067 static CHANSERV_FUNC(cmd_unregister
)
2070 char reason
[MAXLEN
];
2071 struct chanData
*cData
;
2072 struct userData
*uData
;
2074 cData
= channel
->channel_info
;
2077 reply("CSMSG_NOT_REGISTERED", channel
->name
);
2081 uData
= GetChannelUser(cData
, user
->handle_info
);
2082 if(!uData
|| (uData
->access
< UL_OWNER
))
2084 reply("CSMSG_NO_ACCESS");
2088 if(IsProtected(cData
))
2090 reply("CSMSG_UNREG_NODELETE", channel
->name
);
2094 if(!IsHelping(user
))
2096 const char *confirm_string
;
2097 if(IsSuspended(cData
))
2099 reply("CSMSG_CHAN_SUSPENDED", channel
->name
, cData
->suspended
->reason
);
2102 confirm_string
= make_confirmation_string(uData
);
2103 if((argc
< 2) || strcmp(argv
[1], confirm_string
))
2105 reply("CSMSG_CONFIRM_UNREG", channel
->name
, confirm_string
);
2110 sprintf(reason
, "unregistered by %s.", user
->handle_info
->handle
);
2111 name
= strdup(channel
->name
);
2112 unregister_channel(cData
, reason
);
2113 reply("CSMSG_UNREG_SUCCESS", name
);
2118 static CHANSERV_FUNC(cmd_move
)
2120 struct mod_chanmode change
;
2121 struct chanNode
*target
;
2122 struct modeNode
*mn
;
2123 struct userData
*uData
;
2124 char reason
[MAXLEN
];
2125 struct do_not_register
*dnr
;
2129 if(IsProtected(channel
->channel_info
))
2131 reply("CSMSG_MOVE_NODELETE", channel
->name
);
2135 if(!IsChannelName(argv
[1]))
2137 reply("MSG_NOT_CHANNEL_NAME");
2141 if(opserv_bad_channel(argv
[1]))
2143 reply("CSMSG_ILLEGAL_CHANNEL", argv
[1]);
2147 if(!IsHelping(user
) || (argc
< 3) || irccasecmp(argv
[2], "force"))
2149 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
2151 if((uData
->access
== UL_OWNER
) && (dnr
= chanserv_is_dnr(argv
[1], uData
->handle
)))
2153 if(!IsHelping(user
))
2154 reply("CSMSG_DNR_CHANNEL_MOVE", argv
[1]);
2156 chanserv_show_dnrs(user
, cmd
, argv
[1], uData
->handle
->handle
);
2162 mod_chanmode_init(&change
);
2163 if(!(target
= GetChannel(argv
[1])))
2165 target
= AddChannel(argv
[1], now
, NULL
, NULL
, NULL
);
2166 if(!IsSuspended(channel
->channel_info
))
2167 AddChannelUser(chanserv
, target
);
2169 else if(target
->channel_info
)
2171 reply("CSMSG_ALREADY_REGGED", target
->name
);
2174 else if((!(mn
= GetUserMode(target
, user
)) || !(mn
->modes
&& MODE_CHANOP
))
2175 && !IsHelping(user
))
2177 reply("CSMSG_MUST_BE_OPPED", target
->name
);
2180 else if(!IsSuspended(channel
->channel_info
))
2183 change
.args
[0].mode
= MODE_CHANOP
;
2184 change
.args
[0].u
.member
= AddChannelUser(chanserv
, target
);
2185 mod_chanmode_announce(chanserv
, target
, &change
);
2190 /* Clear MODE_REGISTERED from old channel, add it to new. */
2192 change
.modes_clear
= MODE_REGISTERED
;
2193 mod_chanmode_announce(chanserv
, channel
, &change
);
2194 change
.modes_clear
= 0;
2195 change
.modes_set
= MODE_REGISTERED
;
2196 mod_chanmode_announce(chanserv
, target
, &change
);
2199 /* Move the channel_info to the target channel; it
2200 shouldn't be necessary to clear timeq callbacks
2201 for the old channel. */
2202 target
->channel_info
= channel
->channel_info
;
2203 target
->channel_info
->channel
= target
;
2204 channel
->channel_info
= NULL
;
2206 reply("CSMSG_MOVE_SUCCESS", target
->name
);
2208 sprintf(reason
, "%s moved to %s by %s.", channel
->name
, target
->name
, user
->handle_info
->handle
);
2209 if(!IsSuspended(target
->channel_info
))
2211 char reason2
[MAXLEN
];
2212 sprintf(reason2
, "Channel moved to %s by %s.", target
->name
, user
->handle_info
->handle
);
2213 DelChannelUser(chanserv
, channel
, reason2
, 0);
2215 UnlockChannel(channel
);
2216 LockChannel(target
);
2217 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
2222 merge_users(struct chanData
*source
, struct chanData
*target
)
2224 struct userData
*suData
, *tuData
, *next
;
2230 /* Insert the source's users into the scratch area. */
2231 for(suData
= source
->users
; suData
; suData
= suData
->next
)
2232 dict_insert(merge
, suData
->handle
->handle
, suData
);
2234 /* Iterate through the target's users, looking for
2235 users common to both channels. The lower access is
2236 removed from either the scratch area or target user
2238 for(tuData
= target
->users
; tuData
; tuData
= next
)
2240 struct userData
*choice
;
2242 next
= tuData
->next
;
2244 /* If a source user exists with the same handle as a target
2245 channel's user, resolve the conflict by removing one. */
2246 suData
= dict_find(merge
, tuData
->handle
->handle
, NULL
);
2250 /* Pick the data we want to keep. */
2251 /* If the access is the same, use the later seen time. */
2252 if(suData
->access
== tuData
->access
)
2253 choice
= (suData
->seen
> tuData
->seen
) ? suData
: tuData
;
2254 else /* Otherwise, keep the higher access level. */
2255 choice
= (suData
->access
> tuData
->access
) ? suData
: tuData
;
2257 /* Remove the user that wasn't picked. */
2258 if(choice
== tuData
)
2260 dict_remove(merge
, suData
->handle
->handle
);
2261 del_channel_user(suData
, 0);
2264 del_channel_user(tuData
, 0);
2267 /* Move the remaining users to the target channel. */
2268 for(it
= dict_first(merge
); it
; it
= iter_next(it
))
2270 suData
= iter_data(it
);
2272 /* Insert the user into the target channel's linked list. */
2273 suData
->prev
= NULL
;
2274 suData
->next
= target
->users
;
2275 suData
->channel
= target
;
2278 target
->users
->prev
= suData
;
2279 target
->users
= suData
;
2281 /* Update the user counts for the target channel; the
2282 source counts are left alone. */
2283 target
->userCount
++;
2286 /* Possible to assert (source->users == NULL) here. */
2287 source
->users
= NULL
;
2292 merge_bans(struct chanData
*source
, struct chanData
*target
)
2294 struct banData
*sbData
, *tbData
, *sNext
, *tNext
, *tFront
;
2296 /* Hold on to the original head of the target ban list
2297 to avoid comparing source bans with source bans. */
2298 tFront
= target
->bans
;
2300 /* Perform a totally expensive O(n*m) merge, ick. */
2301 for(sbData
= source
->bans
; sbData
; sbData
= sNext
)
2303 /* Flag to track whether the ban's been moved
2304 to the destination yet. */
2307 /* Possible to assert (sbData->prev == NULL) here. */
2308 sNext
= sbData
->next
;
2310 for(tbData
= tFront
; tbData
; tbData
= tNext
)
2312 tNext
= tbData
->next
;
2314 /* Perform two comparisons between each source
2315 and target ban, conflicts are resolved by
2316 keeping the broader ban and copying the later
2317 expiration and triggered time. */
2318 if(match_ircglobs(tbData
->mask
, sbData
->mask
))
2320 /* There is a broader ban in the target channel that
2321 overrides one in the source channel; remove the
2322 source ban and break. */
2323 if(sbData
->expires
> tbData
->expires
)
2324 tbData
->expires
= sbData
->expires
;
2325 if(sbData
->triggered
> tbData
->triggered
)
2326 tbData
->triggered
= sbData
->triggered
;
2327 del_channel_ban(sbData
);
2330 else if(match_ircglobs(sbData
->mask
, tbData
->mask
))
2332 /* There is a broader ban in the source channel that
2333 overrides one in the target channel; remove the
2334 target ban, fall through and move the source over. */
2335 if(tbData
->expires
> sbData
->expires
)
2336 sbData
->expires
= tbData
->expires
;
2337 if(tbData
->triggered
> sbData
->triggered
)
2338 sbData
->triggered
= tbData
->triggered
;
2339 if(tbData
== tFront
)
2341 del_channel_ban(tbData
);
2344 /* Source bans can override multiple target bans, so
2345 we allow a source to run through this loop multiple
2346 times, but we can only move it once. */
2351 /* Remove the source ban from the source ban list. */
2353 sbData
->next
->prev
= sbData
->prev
;
2355 /* Modify the source ban's associated channel. */
2356 sbData
->channel
= target
;
2358 /* Insert the ban into the target channel's linked list. */
2359 sbData
->prev
= NULL
;
2360 sbData
->next
= target
->bans
;
2363 target
->bans
->prev
= sbData
;
2364 target
->bans
= sbData
;
2366 /* Update the user counts for the target channel. */
2371 /* Possible to assert (source->bans == NULL) here. */
2372 source
->bans
= NULL
;
2376 merge_data(struct chanData
*source
, struct chanData
*target
)
2378 if(source
->visited
> target
->visited
)
2379 target
->visited
= source
->visited
;
2383 merge_channel(struct chanData
*source
, struct chanData
*target
)
2385 merge_users(source
, target
);
2386 merge_bans(source
, target
);
2387 merge_data(source
, target
);
2390 static CHANSERV_FUNC(cmd_merge
)
2392 struct userData
*target_user
;
2393 struct chanNode
*target
;
2394 char reason
[MAXLEN
];
2398 /* Make sure the target channel exists and is registered to the user
2399 performing the command. */
2400 if(!(target
= GetChannel(argv
[1])))
2402 reply("MSG_INVALID_CHANNEL");
2406 if(!target
->channel_info
)
2408 reply("CSMSG_NOT_REGISTERED", target
->name
);
2412 if(IsProtected(channel
->channel_info
))
2414 reply("CSMSG_MERGE_NODELETE");
2418 if(IsSuspended(target
->channel_info
))
2420 reply("CSMSG_MERGE_SUSPENDED");
2424 if(channel
== target
)
2426 reply("CSMSG_MERGE_SELF");
2430 target_user
= GetChannelUser(target
->channel_info
, user
->handle_info
);
2431 if(!target_user
|| (target_user
->access
< UL_OWNER
))
2433 reply("CSMSG_MERGE_NOT_OWNER");
2437 /* Merge the channel structures and associated data. */
2438 merge_channel(channel
->channel_info
, target
->channel_info
);
2439 sprintf(reason
, "merged into %s by %s.", target
->name
, user
->handle_info
->handle
);
2440 unregister_channel(channel
->channel_info
, reason
);
2441 reply("CSMSG_MERGE_SUCCESS", target
->name
);
2445 static CHANSERV_FUNC(cmd_opchan
)
2447 struct mod_chanmode change
;
2448 if(!IsHelping(user
) && !channel
->channel_info
->may_opchan
)
2450 reply("CSMSG_ALREADY_OPCHANNED", channel
->name
);
2453 channel
->channel_info
->may_opchan
= 0;
2454 mod_chanmode_init(&change
);
2456 change
.args
[0].mode
= MODE_CHANOP
;
2457 change
.args
[0].u
.member
= GetUserMode(channel
, chanserv
);
2458 mod_chanmode_announce(chanserv
, channel
, &change
);
2459 reply("CSMSG_OPCHAN_DONE", channel
->name
);
2463 static CHANSERV_FUNC(cmd_adduser
)
2465 struct userData
*actee
;
2466 struct userData
*actor
;
2467 struct handle_info
*handle
;
2468 unsigned short access
;
2472 if(channel
->channel_info
->userCount
>= chanserv_conf
.max_chan_users
)
2474 reply("CSMSG_MAXIMUM_USERS", chanserv_conf
.max_chan_users
);
2478 access
= user_level_from_name(argv
[2], UL_OWNER
);
2481 reply("CSMSG_INVALID_ACCESS", argv
[2]);
2485 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2486 if(actor
->access
<= access
)
2488 reply("CSMSG_NO_BUMP_ACCESS");
2492 if(!(handle
= modcmd_get_handle_info(user
, argv
[1])))
2494 // 'kevin must first authenticate with AuthServ.' is sent to user
2495 struct userNode
*unode
;
2496 unode
= GetUserH(argv
[1]); /* find user struct by nick */
2499 if(find_adduser_pending(channel
, unode
)) {
2500 reply("CSMSG_ADDUSER_PENDING_ALREADY", channel
->name
);
2503 if(IsInChannel(channel
, unode
)) {
2504 reply("CSMSG_ADDUSER_PENDING");
2505 add_adduser_pending(channel
, unode
, access
);
2506 send_message_type(1,unode
, chanserv
, "CSMSG_ADDUSER_PENDING_TARGET", user
->nick
, channel
->name
);
2508 /* this results in user must auth AND not in chan errors. too confusing..
2510 reply("CSMSG_ADDUSER_PENDING_NOTINCHAN", channel->name);
2518 if((actee
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2520 reply("CSMSG_USER_EXISTS", handle
->handle
, channel
->name
, user_level_name_from_level(actee
->access
));
2524 actee
= add_channel_user(channel
->channel_info
, handle
, access
, 0, NULL
);
2525 scan_user_presence(actee
, NULL
);
2526 reply("CSMSG_ADDED_USER", handle
->handle
, channel
->name
, user_level_name_from_level(access
), access
);
2530 static CHANSERV_FUNC(cmd_clvl
)
2532 struct handle_info
*handle
;
2533 struct userData
*victim
;
2534 struct userData
*actor
;
2535 unsigned short new_access
;
2536 int privileged
= IsHelping(user
) && ((user
->handle_info
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
->channel_info
));
2540 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2542 if(!(handle
= modcmd_get_handle_info(user
, argv
[1])))
2545 if(handle
== user
->handle_info
&& !privileged
)
2547 reply("CSMSG_NO_SELF_CLVL");
2551 if(!(victim
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2553 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
2557 if(actor
->access
<= victim
->access
&& !privileged
)
2559 reply("MSG_USER_OUTRANKED", handle
->handle
);
2563 new_access
= user_level_from_name(argv
[2], UL_OWNER
);
2567 reply("CSMSG_INVALID_ACCESS", argv
[2]);
2571 if(new_access
>= actor
->access
&& !privileged
)
2573 reply("CSMSG_NO_BUMP_ACCESS");
2577 victim
->access
= new_access
;
2578 reply("CSMSG_CHANGED_ACCESS", handle
->handle
, user_level_name_from_level(new_access
), new_access
, channel
->name
);
2582 static CHANSERV_FUNC(cmd_deluser
)
2584 struct handle_info
*handle
;
2585 struct userData
*victim
;
2586 struct userData
*actor
;
2587 unsigned short access
;
2592 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2594 if(!(handle
= modcmd_get_handle_info(user
, argv
[argc
-1])))
2597 if(!(victim
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2599 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
2605 access
= user_level_from_name(argv
[1], UL_OWNER
);
2608 reply("CSMSG_INVALID_ACCESS", argv
[1]);
2611 if(access
!= victim
->access
)
2613 reply("CSMSG_INCORRECT_ACCESS", handle
->handle
, user_level_name_from_level(victim
->access
), argv
[1]);
2619 access
= victim
->access
;
2622 if((actor
->access
<= victim
->access
) && !IsHelping(user
))
2624 reply("MSG_USER_OUTRANKED", victim
->handle
->handle
);
2628 chan_name
= strdup(channel
->name
);
2629 del_channel_user(victim
, 1);
2630 reply("CSMSG_DELETED_USER", handle
->handle
, access
, chan_name
);
2636 cmd_mdel_user(struct userNode
*user
, struct chanNode
*channel
, unsigned short min_access
, unsigned short max_access
, char *mask
, struct svccmd
*cmd
)
2638 struct userData
*actor
, *uData
, *next
;
2640 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2642 if(min_access
> max_access
)
2644 reply("CSMSG_BAD_RANGE", min_access
, max_access
);
2648 if((actor
->access
<= max_access
) && !IsHelping(user
))
2650 reply("CSMSG_NO_ACCESS");
2654 for(uData
= channel
->channel_info
->users
; uData
; uData
= next
)
2658 if((uData
->access
>= min_access
)
2659 && (uData
->access
<= max_access
)
2660 && match_ircglob(uData
->handle
->handle
, mask
))
2661 del_channel_user(uData
, 1);
2664 reply("CSMSG_DELETED_USERS", mask
, min_access
, max_access
, channel
->name
);
2668 static CHANSERV_FUNC(cmd_mdelowner
)
2670 return cmd_mdel_user(user
, channel
, UL_OWNER
, UL_OWNER
, argv
[1], cmd
);
2673 static CHANSERV_FUNC(cmd_mdelcoowner
)
2675 return cmd_mdel_user(user
, channel
, UL_COOWNER
, UL_COOWNER
, argv
[1], cmd
);
2678 static CHANSERV_FUNC(cmd_mdelmanager
)
2680 return cmd_mdel_user(user
, channel
, UL_MANAGER
, UL_MANAGER
, argv
[1], cmd
);
2683 static CHANSERV_FUNC(cmd_mdelop
)
2685 return cmd_mdel_user(user
, channel
, UL_OP
, UL_OP
, argv
[1], cmd
);
2688 static CHANSERV_FUNC(cmd_mdelpeon
)
2690 return cmd_mdel_user(user
, channel
, UL_PEON
, UL_PEON
, argv
[1], cmd
);
2693 static CHANSERV_FUNC(cmd_mdelhalfop
)
2695 return cmd_mdel_user(user
, channel
, UL_HALFOP
, UL_HALFOP
, argv
[1], cmd
);
2701 cmd_trim_bans(struct userNode
*user
, struct chanNode
*channel
, unsigned long duration
)
2703 struct banData
*bData
, *next
;
2704 char interval
[INTERVALLEN
];
2709 limit
= now
- duration
;
2710 for(bData
= channel
->channel_info
->bans
; bData
; bData
= next
)
2714 if((bData
->triggered
&& bData
->triggered
>= limit
) || (bData
->set
&& bData
->set
>= limit
))
2717 del_channel_ban(bData
);
2721 intervalString(interval
, duration
, user
->handle_info
);
2722 send_message(user
, chanserv
, "CSMSG_TRIMMED_LAMERS", count
, channel
->name
, interval
);
2727 cmd_trim_users(struct userNode
*user
, struct chanNode
*channel
, unsigned short min_access
, unsigned short max_access
, unsigned long duration
)
2729 struct userData
*actor
, *uData
, *next
;
2730 char interval
[INTERVALLEN
];
2734 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2735 if(min_access
> max_access
)
2737 send_message(user
, chanserv
, "CSMSG_BAD_RANGE", min_access
, max_access
);
2741 if((actor
->access
<= max_access
) && !IsHelping(user
))
2743 send_message(user
, chanserv
, "CSMSG_NO_ACCESS");
2748 limit
= now
- duration
;
2749 for(uData
= channel
->channel_info
->users
; uData
; uData
= next
)
2753 if((uData
->seen
> limit
) || uData
->present
)
2756 if(((uData
->access
>= min_access
) && (uData
->access
<= max_access
))
2757 || (!max_access
&& (uData
->access
< actor
->access
)))
2759 del_channel_user(uData
, 1);
2767 max_access
= (actor
->access
> UL_OWNER
) ? UL_OWNER
: (actor
->access
- 1);
2769 send_message(user
, chanserv
, "CSMSG_TRIMMED_USERS", count
, min_access
, max_access
, channel
->name
, intervalString(interval
, duration
, user
->handle_info
));
2773 static CHANSERV_FUNC(cmd_trim
)
2775 unsigned long duration
;
2776 unsigned short min_level
, max_level
;
2780 duration
= ParseInterval(argv
[2]);
2783 reply("CSMSG_CANNOT_TRIM");
2787 if(!irccasecmp(argv
[1], "lamers"))
2789 cmd_trim_bans(user
, channel
, duration
); /* trim_lamers.. */
2792 else if(!irccasecmp(argv
[1], "users"))
2794 cmd_trim_users(user
, channel
, 0, 0, duration
);
2797 else if(parse_level_range(&min_level
, &max_level
, argv
[1]))
2799 cmd_trim_users(user
, channel
, min_level
, max_level
, duration
);
2802 else if((min_level
= user_level_from_name(argv
[1], UL_OWNER
)))
2804 cmd_trim_users(user
, channel
, min_level
, min_level
, duration
);
2809 reply("CSMSG_INVALID_TRIM", argv
[1]);
2814 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
2815 to the user. cmd_all takes advantage of this. */
2816 static CHANSERV_FUNC(cmd_up
)
2818 struct mod_chanmode change
;
2819 struct userData
*uData
;
2822 mod_chanmode_init(&change
);
2824 change
.args
[0].u
.member
= GetUserMode(channel
, user
);
2825 if(!change
.args
[0].u
.member
)
2828 reply("MSG_CHANNEL_ABSENT", channel
->name
);
2832 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
2836 reply("CSMSG_GODMODE_UP", argv
[0]);
2839 else if(uData
->access
>= UL_OP
/*channel->channel_info->lvlOpts[lvlGiveOps]*/)
2841 change
.args
[0].mode
= MODE_CHANOP
;
2842 errmsg
= "CSMSG_ALREADY_OPPED";
2844 else if(uData
->access
>= UL_HALFOP
/*channel->channel_info->lvlOpts[lvlGiveHalfOps]*/)
2846 change
.args
[0].mode
= MODE_HALFOP
;
2847 errmsg
= "CSMSG_ALREADY_HALFOPPED";
2849 else if(uData
->access
>= UL_PEON
&& (channel
->channel_info
->chOpts
[chVoice
] == 'p' || channel
->channel_info
->chOpts
[chVoice
] == 'a'))
2851 change
.args
[0].mode
= MODE_VOICE
;
2852 errmsg
= "CSMSG_ALREADY_VOICED";
2857 reply("CSMSG_NO_ACCESS");
2860 change
.args
[0].mode
&= ~change
.args
[0].u
.member
->modes
;
2861 if(!change
.args
[0].mode
)
2864 reply(errmsg
, channel
->name
);
2867 modcmd_chanmode_announce(&change
);
2871 static CHANSERV_FUNC(cmd_down
)
2873 struct mod_chanmode change
;
2875 mod_chanmode_init(&change
);
2877 change
.args
[0].u
.member
= GetUserMode(channel
, user
);
2878 if(!change
.args
[0].u
.member
)
2881 reply("MSG_CHANNEL_ABSENT", channel
->name
);
2885 if(!change
.args
[0].u
.member
->modes
)
2888 reply("CSMSG_ALREADY_DOWN", channel
->name
);
2892 change
.args
[0].mode
= MODE_REMOVE
| change
.args
[0].u
.member
->modes
;
2893 modcmd_chanmode_announce(&change
);
2897 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
)
2899 struct userData
*cList
;
2901 for(cList
= user
->handle_info
->channels
; cList
; cList
= cList
->u_next
)
2903 if(IsSuspended(cList
->channel
)
2904 || IsUserSuspended(cList
)
2905 || !GetUserMode(cList
->channel
->channel
, user
))
2908 mcmd(user
, cList
->channel
->channel
, 0, NULL
, cmd
);
2914 static CHANSERV_FUNC(cmd_upall
)
2916 return cmd_all(CSFUNC_ARGS
, cmd_up
);
2919 static CHANSERV_FUNC(cmd_downall
)
2921 return cmd_all(CSFUNC_ARGS
, cmd_down
);
2924 typedef int validate_func_t(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
);
2925 typedef void process_func_t(unsigned int num
, struct userNode
**newops
, struct chanNode
*channel
, struct userNode
*who
, int announce
);
2928 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
)
2930 unsigned int ii
, valid
;
2931 struct userNode
*victim
;
2932 struct mod_chanmode
*change
;
2934 change
= mod_chanmode_alloc(argc
- 1);
2936 for(ii
=valid
=0; ++ii
< argc
; )
2938 if(!(victim
= GetUserH(argv
[ii
])))
2940 change
->args
[valid
].mode
= mode
;
2941 change
->args
[valid
].u
.member
= GetUserMode(channel
, victim
);
2942 if(!change
->args
[valid
].u
.member
)
2944 if(validate
&& !validate(user
, channel
, victim
))
2949 change
->argc
= valid
;
2950 if(valid
< (argc
-1))
2951 reply("CSMSG_PROCESS_FAILED");
2954 modcmd_chanmode_announce(change
);
2955 reply(action
, channel
->name
);
2957 mod_chanmode_free(change
);
2961 static CHANSERV_FUNC(cmd_op
)
2963 return modify_users(CSFUNC_ARGS
, validate_op
, MODE_CHANOP
, "CSMSG_OPPED_USERS");
2966 static CHANSERV_FUNC(cmd_hop
)
2968 return modify_users(CSFUNC_ARGS
, validate_halfop
, MODE_HALFOP
, "CSMSG_HALFOPPED_USERS");
2971 static CHANSERV_FUNC(cmd_deop
)
2973 return modify_users(CSFUNC_ARGS
, validate_deop
, MODE_REMOVE
|MODE_CHANOP
, "CSMSG_DEOPPED_USERS");
2976 static CHANSERV_FUNC(cmd_dehop
)
2978 return modify_users(CSFUNC_ARGS
, validate_dehop
, MODE_REMOVE
|MODE_HALFOP
, "CSMSG_DEHALFOPPED_USERS");
2981 static CHANSERV_FUNC(cmd_voice
)
2983 return modify_users(CSFUNC_ARGS
, NULL
, MODE_VOICE
, "CSMSG_VOICED_USERS");
2986 static CHANSERV_FUNC(cmd_devoice
)
2988 return modify_users(CSFUNC_ARGS
, NULL
, MODE_REMOVE
|MODE_VOICE
, "CSMSG_DEVOICED_USERS");
2992 bad_channel_ban(struct chanNode
*channel
, struct userNode
*user
, const char *ban
, int *victimCount
, struct modeNode
**victims
)
2998 for(ii
=0; ii
<channel
->members
.used
; ii
++)
3000 struct modeNode
*mn
= channel
->members
.list
[ii
];
3002 if(IsService(mn
->user
))
3005 if(!user_matches_glob(mn
->user
, ban
, 1))
3008 if(protect_user(mn
->user
, user
, channel
->channel_info
))
3012 victims
[(*victimCount
)++] = mn
;
3018 eject_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
)
3020 struct userNode
*victim
;
3021 struct modeNode
**victims
;
3022 unsigned int offset
, n
, victimCount
, duration
= 0;
3023 char *reason
= "Bye.", *ban
, *name
;
3024 char interval
[INTERVALLEN
];
3026 offset
= (action
& ACTION_ADD_TIMED_LAMER
) ? 3 : 2;
3027 REQUIRE_PARAMS(offset
);
3030 reason
= unsplit_string(argv
+ offset
, argc
- offset
, NULL
);
3031 if(strlen(reason
) > (TOPICLEN
- (NICKLEN
+ 3)))
3033 /* Truncate the reason to a length of TOPICLEN, as
3034 the ircd does; however, leave room for an ellipsis
3035 and the kicker's nick. */
3036 sprintf(reason
+ (TOPICLEN
- (NICKLEN
+ 6)), "...");
3040 if((victim
= GetUserH(argv
[1])))
3042 victims
= alloca(sizeof(victims
[0]));
3043 victims
[0] = GetUserMode(channel
, victim
);
3044 /* XXX: The comparison with ACTION_KICK is just because all
3045 * other actions can work on users outside the channel, and we
3046 * want to allow those (e.g. unbans) in that case. If we add
3047 * some other ejection action for in-channel users, change
3049 victimCount
= victims
[0] ? 1 : 0;
3051 if(IsService(victim
))
3054 reply("MSG_SERVICE_IMMUNE", victim
->nick
);
3058 if((action
== ACTION_KICK
) && !victimCount
)
3061 reply("MSG_CHANNEL_USER_ABSENT", victim
->nick
, channel
->name
);
3065 if(protect_user(victim
, user
, channel
->channel_info
))
3067 // This translates to send_message(user, cmd->parent->bot, ...)
3068 // if user is x3 (ctcp action) cmd is null and segfault.
3070 reply("CSMSG_USER_PROTECTED", victim
->nick
);
3074 ban
= generate_hostmask(victim
, GENMASK_STRICT_HOST
|GENMASK_ANY_IDENT
);
3075 name
= victim
->nick
;
3079 if(!is_ircmask(argv
[1]))
3082 reply("MSG_NICK_UNKNOWN", argv
[1]);
3086 victims
= alloca(sizeof(victims
[0]) * channel
->members
.used
);
3088 if(bad_channel_ban(channel
, user
, argv
[1], &victimCount
, victims
))
3091 reply("CSMSG_MASK_PROTECTED", argv
[1]);
3094 /* We dont actually want a victem count if were banning a mask manually, IMO -Rubin*/
3096 victimCount
= 0; /* Dont deop etc ppl who match this */
3098 #ifdef entropy_lameness
3099 if((victimCount
> 4) && ((victimCount
* 3) > channel
->members
.used
) && !IsOper(user
))
3102 reply("CSMSG_LAME_MASK", argv
[1]);
3107 if((action
== ACTION_KICK
) && (victimCount
== 0))
3110 reply("CSMSG_NO_MATCHING_USERS", channel
->name
, argv
[1]);
3114 name
= ban
= strdup(argv
[1]);
3117 /* Truncate the ban in place if necessary; we must ensure
3118 that 'ban' is a valid ban mask before sanitizing it. */
3119 sanitize_ircmask(ban
);
3121 if(action
& ACTION_ADD_LAMER
)
3123 struct banData
*bData
, *next
;
3125 if(channel
->channel_info
->banCount
>= chanserv_conf
.max_chan_bans
) /* ..lamers.. */
3128 reply("CSMSG_MAXIMUM_LAMERS", chanserv_conf
.max_chan_bans
); /* ..lamers.. */
3133 if(action
& ACTION_ADD_TIMED_LAMER
)
3135 duration
= ParseInterval(argv
[2]);
3140 reply("CSMSG_DURATION_TOO_LOW");
3144 else if(duration
> (86400 * 365 * 2))
3147 reply("CSMSG_DURATION_TOO_HIGH");
3154 for(bData
= channel
->channel_info
->bans
; bData
; bData
= next
)
3156 if(match_ircglobs(bData
->mask
, ban
))
3158 int exact
= !irccasecmp(bData
->mask
, ban
);
3160 /* The ban is redundant; there is already a ban
3161 with the same effect in place. */
3165 free(bData
->reason
);
3166 bData
->reason
= strdup(reason
);
3167 safestrncpy(bData
->owner
, (user
->handle_info
? user
->handle_info
->handle
: user
->nick
), sizeof(bData
->owner
));
3169 reply("CSMSG_REASON_CHANGE", ban
);
3173 if(exact
&& bData
->expires
)
3177 /* If the ban matches an existing one exactly,
3178 extend the expiration time if the provided
3179 duration is longer. */
3180 if(duration
&& ((time_t)(now
+ duration
) > bData
->expires
))
3182 bData
->expires
= now
+ duration
;
3193 /* Delete the expiration timeq entry and
3194 requeue if necessary. */
3195 timeq_del(0, expire_ban
, bData
, TIMEQ_IGNORE_WHEN
);
3198 timeq_add(bData
->expires
, expire_ban
, bData
);
3202 /* automated kickban, dont reply */
3205 reply("CSMSG_LAMER_EXTENDED", ban
, intervalString(interval
, duration
, user
->handle_info
));
3207 reply("CSMSG_LAMER_ADDED", name
, channel
->name
);
3213 reply("CSMSG_REDUNDANT_LAMER", name
, channel
->name
);
3220 if(match_ircglobs(ban
, bData
->mask
))
3222 /* The ban we are adding makes previously existing
3223 bans redundant; silently remove them. */
3224 del_channel_ban(bData
);
3228 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
);
3230 name
= ban
= strdup(bData
->mask
);
3234 /* WHAT DOES THIS DO?? -Rubin */
3235 for(n
= 0; n
< chanserv_conf
.old_ban_names
->used
; ++n
)
3237 extern const char *hidden_host_suffix
;
3238 const char *old_name
= chanserv_conf
.old_ban_names
->list
[n
];
3240 unsigned int l1
, l2
;
3243 l2
= strlen(old_name
);
3246 if(irccasecmp(ban
+ l1
- l2
, old_name
))
3248 new_mask
= malloc(MAXLEN
);
3249 sprintf(new_mask
, "%.*s%s", (int)(l1
-l2
), ban
, hidden_host_suffix
);
3251 name
= ban
= new_mask
;
3256 if(action
& ACTION_BAN
)
3258 unsigned int exists
;
3259 struct mod_chanmode
*change
;
3261 if(channel
->banlist
.used
>= MAXBANS
)
3264 reply("CSMSG_BANLIST_FULL", channel
->name
);
3269 exists
= ChannelBanExists(channel
, ban
);
3270 change
= mod_chanmode_alloc(victimCount
+ 1);
3271 for(n
= 0; n
< victimCount
; ++n
)
3273 change
->args
[n
].mode
= MODE_REMOVE
|MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
;
3274 change
->args
[n
].u
.member
= victims
[n
];
3278 change
->args
[n
].mode
= MODE_BAN
;
3279 change
->args
[n
++].u
.hostmask
= ban
;
3283 modcmd_chanmode_announce(change
);
3285 mod_chanmode_announce(chanserv
, channel
, change
);
3286 mod_chanmode_free(change
);
3288 if(exists
&& (action
== ACTION_BAN
))
3291 reply("CSMSG_REDUNDANT_BAN", name
, channel
->name
);
3297 if(action
& ACTION_KICK
)
3299 char kick_reason
[MAXLEN
];
3300 sprintf(kick_reason
, "(%s) %s", user
->nick
, reason
);
3302 for(n
= 0; n
< victimCount
; n
++)
3303 KickChannelUser(victims
[n
]->user
, channel
, chanserv
, kick_reason
);
3308 /* No response, since it was automated. */
3310 else if(action
& ACTION_ADD_LAMER
)
3313 reply("CSMSG_TIMED_LAMER_ADDED", name
, channel
->name
, intervalString(interval
, duration
, user
->handle_info
));
3315 reply("CSMSG_LAMER_ADDED", name
, channel
->name
);
3317 else if((action
& (ACTION_BAN
| ACTION_KICK
)) == (ACTION_BAN
| ACTION_KICK
))
3318 reply("CSMSG_KICK_BAN_DONE", name
, channel
->name
);
3319 else if(action
& ACTION_BAN
)
3320 reply("CSMSG_BAN_DONE", name
, channel
->name
);
3321 else if(action
& ACTION_KICK
&& victimCount
)
3322 reply("CSMSG_KICK_DONE", name
, channel
->name
);
3328 static CHANSERV_FUNC(cmd_kickban
)
3330 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
);
3333 static CHANSERV_FUNC(cmd_kick
)
3335 return eject_user(CSFUNC_ARGS
, ACTION_KICK
);
3338 static CHANSERV_FUNC(cmd_ban
)
3340 return eject_user(CSFUNC_ARGS
, ACTION_BAN
);
3343 static CHANSERV_FUNC(cmd_addlamer
)
3345 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
| ACTION_ADD_LAMER
);
3348 static CHANSERV_FUNC(cmd_addtimedlamer
)
3350 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
);
3353 static struct mod_chanmode
*
3354 find_matching_bans(struct banList
*bans
, struct userNode
*actee
, const char *mask
)
3356 struct mod_chanmode
*change
;
3357 unsigned char *match
;
3358 unsigned int ii
, count
;
3360 match
= alloca(bans
->used
);
3363 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3365 match
[ii
] = user_matches_glob(actee
, bans
->list
[ii
]->ban
, 1);
3372 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3374 match
[ii
] = match_ircglobs(mask
, bans
->list
[ii
]->ban
);
3381 change
= mod_chanmode_alloc(count
);
3382 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3386 change
->args
[count
].mode
= MODE_REMOVE
| MODE_BAN
;
3387 change
->args
[count
++].u
.hostmask
= strdup(bans
->list
[ii
]->ban
);
3389 assert(count
== change
->argc
);
3394 unban_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
)
3396 struct userNode
*actee
;
3402 /* may want to allow a comma delimited list of users... */
3403 if(!(actee
= GetUserH(argv
[1])))
3405 if(!is_ircmask(argv
[1]))
3407 reply("MSG_NICK_UNKNOWN", argv
[1]);
3411 mask
= strdup(argv
[1]);
3414 /* We don't sanitize the mask here because ircu
3416 if(action
& ACTION_UNBAN
)
3418 struct mod_chanmode
*change
;
3419 change
= find_matching_bans(&channel
->banlist
, actee
, mask
);
3424 modcmd_chanmode_announce(change
);
3425 for(ii
= 0; ii
< change
->argc
; ++ii
)
3426 free((char*)change
->args
[ii
].u
.hostmask
);
3427 mod_chanmode_free(change
);
3432 if(action
& ACTION_DEL_LAMER
)
3434 struct banData
*ban
, *next
;
3436 ban
= channel
->channel_info
->bans
; /* lamers */
3440 for( ; ban
&& !user_matches_glob(actee
, ban
->mask
, 1);
3443 for( ; ban
&& !match_ircglobs(mask
, ban
->mask
);
3448 del_channel_ban(ban
);
3455 reply("CSMSG_BAN_NOT_FOUND", actee
? actee
->nick
: mask
);
3457 reply("CSMSG_BAN_REMOVED", actee
? actee
->nick
: mask
);
3463 static CHANSERV_FUNC(cmd_unban
)
3465 return unban_user(CSFUNC_ARGS
, ACTION_UNBAN
);
3468 static CHANSERV_FUNC(cmd_dellamer
)
3470 /* it doesn't necessarily have to remove the channel ban - may want
3471 to make that an option. */
3472 return unban_user(CSFUNC_ARGS
, ACTION_UNBAN
| ACTION_DEL_LAMER
);
3475 static CHANSERV_FUNC(cmd_unbanme
)
3477 struct userData
*uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
3478 long flags
= ACTION_UNBAN
;
3480 /* remove permanent bans if the user has the proper access. */
3481 if(uData
->access
>= UL_MANAGER
)
3482 flags
|= ACTION_DEL_LAMER
;
3484 argv
[1] = user
->nick
;
3485 return unban_user(user
, channel
, 2, argv
, cmd
, flags
);
3488 static CHANSERV_FUNC(cmd_unbanall
)
3490 struct mod_chanmode
*change
;
3493 if(!channel
->banlist
.used
)
3495 reply("CSMSG_NO_BANS", channel
->name
);
3499 change
= mod_chanmode_alloc(channel
->banlist
.used
);
3500 for(ii
=0; ii
<channel
->banlist
.used
; ii
++)
3502 change
->args
[ii
].mode
= MODE_REMOVE
| MODE_BAN
;
3503 change
->args
[ii
].u
.hostmask
= strdup(channel
->banlist
.list
[ii
]->ban
);
3505 modcmd_chanmode_announce(change
);
3506 for(ii
= 0; ii
< change
->argc
; ++ii
)
3507 free((char*)change
->args
[ii
].u
.hostmask
);
3508 mod_chanmode_free(change
);
3509 reply("CSMSG_BANS_REMOVED", channel
->name
);
3513 static CHANSERV_FUNC(cmd_open
)
3515 struct mod_chanmode
*change
;
3518 change
= find_matching_bans(&channel
->banlist
, user
, NULL
);
3520 change
= mod_chanmode_alloc(0);
3521 change
->modes_clear
|= MODE_INVITEONLY
| MODE_LIMIT
| MODE_KEY
;
3522 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
3523 && channel
->channel_info
->modes
.modes_set
)
3524 change
->modes_clear
&= ~channel
->channel_info
->modes
.modes_set
;
3525 modcmd_chanmode_announce(change
);
3526 reply("CSMSG_CHANNEL_OPENED", channel
->name
);
3527 for(ii
= 0; ii
< change
->argc
; ++ii
)
3528 free((char*)change
->args
[ii
].u
.hostmask
);
3529 mod_chanmode_free(change
);
3533 static CHANSERV_FUNC(cmd_myaccess
)
3535 static struct string_buffer sbuf
;
3536 struct handle_info
*target_handle
;
3537 struct userData
*uData
;
3540 target_handle
= user
->handle_info
;
3541 else if(!IsHelping(user
))
3543 reply("CSMSG_MYACCESS_SELF_ONLY", argv
[0]);
3546 else if(!(target_handle
= modcmd_get_handle_info(user
, argv
[1])))
3549 if(!target_handle
->channels
)
3551 reply("CSMSG_SQUAT_ACCESS", target_handle
->handle
);
3555 reply("CSMSG_INFOLINE_LIST", target_handle
->handle
);
3556 for(uData
= target_handle
->channels
; uData
; uData
= uData
->u_next
)
3558 struct chanData
*cData
= uData
->channel
;
3560 if(uData
->access
> UL_OWNER
)
3562 if(IsProtected(cData
)
3563 && (target_handle
!= user
->handle_info
)
3564 && !GetTrueChannelAccess(cData
, user
->handle_info
))
3567 string_buffer_append_printf(&sbuf
, "[%s (%d", cData
->channel
->name
, uData
->access
);
3568 if(uData
->flags
== USER_AUTO_OP
)
3569 string_buffer_append(&sbuf
, ',');
3570 if(IsUserSuspended(uData
))
3571 string_buffer_append(&sbuf
, 's');
3572 if(IsUserAutoOp(uData
))
3574 if(uData
->access
>= UL_OP
/*cData->lvlOpts[lvlGiveOps]*/)
3575 string_buffer_append(&sbuf
, 'o');
3576 else if(uData
->access
>= UL_HALFOP
/*cData->lvlOpts[lvlGiveHalfOps]*/)
3577 string_buffer_append(&sbuf
, 'h');
3578 else if(uData
->access
>= UL_PEON
/*cData->lvlOpts[lvlGiveVoice]*/)
3579 string_buffer_append(&sbuf
, 'v');
3581 if(IsUserAutoInvite(uData
) && (uData
->access
>= cData
->lvlOpts
[lvlInviteMe
]))
3582 string_buffer_append(&sbuf
, 'i');
3584 string_buffer_append_printf(&sbuf
, ")] %s", uData
->info
);
3586 string_buffer_append_string(&sbuf
, ")]");
3587 string_buffer_append(&sbuf
, '\0');
3588 send_message_type(4, user
, cmd
->parent
->bot
, "%s", sbuf
.list
);
3594 static CHANSERV_FUNC(cmd_access
)
3596 struct userNode
*target
;
3597 struct handle_info
*target_handle
;
3598 struct userData
*uData
;
3600 char prefix
[MAXLEN
];
3605 target_handle
= target
->handle_info
;
3607 else if((target
= GetUserH(argv
[1])))
3609 target_handle
= target
->handle_info
;
3611 else if(argv
[1][0] == '*')
3613 if(!(target_handle
= get_handle_info(argv
[1]+1)))
3615 reply("MSG_HANDLE_UNKNOWN", argv
[1]+1);
3621 reply("MSG_NICK_UNKNOWN", argv
[1]);
3625 assert(target
|| target_handle
);
3627 if(target
== chanserv
)
3629 reply("CSMSG_IS_CHANSERV");
3637 reply("CSMSG_LAZY_SMURF_TARGET", target
->nick
, chanserv_conf
.irc_operator_epithet
);
3642 reply("MSG_USER_AUTHENTICATE", target
->nick
);
3645 reply("MSG_AUTHENTICATE");
3651 const char *epithet
= NULL
, *type
= NULL
;
3654 epithet
= chanserv_conf
.irc_operator_epithet
;
3657 else if(IsNetworkHelper(target
))
3659 epithet
= chanserv_conf
.network_helper_epithet
;
3660 type
= "network helper";
3662 else if(IsSupportHelper(target
))
3664 epithet
= chanserv_conf
.support_helper_epithet
;
3665 type
= "support helper";
3669 if(target_handle
->epithet
)
3670 reply("CSMSG_SMURF_TARGET", target
->nick
, target_handle
->epithet
, type
);
3672 reply("CSMSG_SMURF_TARGET", target
->nick
, epithet
, type
);
3674 sprintf(prefix
, "%s (%s)", target
->nick
, target_handle
->handle
);
3678 sprintf(prefix
, "%s", target_handle
->handle
);
3681 if(!channel
->channel_info
)
3683 reply("CSMSG_NOT_REGISTERED", channel
->name
);
3687 helping
= HANDLE_FLAGGED(target_handle
, HELPING
)
3688 && ((target_handle
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
->channel_info
));
3689 if((uData
= GetTrueChannelAccess(channel
->channel_info
, target_handle
)))
3691 reply((helping
? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix
, user_level_name_from_level(uData
->access
), uData
->access
, channel
->name
);
3692 /* To prevent possible information leaks, only show infolines
3693 * if the requestor is in the channel or it's their own
3695 if(uData
->info
&& (GetUserMode(channel
, user
) || (target_handle
== user
->handle_info
)))
3697 send_message_type(4, user
, cmd
->parent
->bot
, "[%s] %s", (target
? target
->nick
: target_handle
->handle
), uData
->info
);
3699 /* Likewise, only say it's suspended if the user has active
3700 * access in that channel or it's their own entry. */
3701 if(IsUserSuspended(uData
)
3702 && (GetChannelUser(channel
->channel_info
, user
->handle_info
)
3703 || (user
->handle_info
== uData
->handle
)))
3705 reply("CSMSG_USER_SUSPENDED", (target
? target
->nick
: target_handle
->handle
), channel
->name
);
3710 reply((helping
? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix
, channel
->name
);
3716 /* This is never used...
3718 zoot_list(struct listData *list)
3720 struct userData *uData;
3721 unsigned int start, curr, highest, lowest;
3722 struct helpfile_table tmp_table;
3723 const char **temp, *msg;
3725 if(list->table.length == 1)
3728 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);
3730 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));
3731 msg = user_find_message(list->user, "MSG_NONE");
3732 send_message_type(4, list->user, list->bot, " %s", msg);
3734 tmp_table.width = list->table.width;
3735 tmp_table.flags = list->table.flags;
3736 list->table.contents[0][0] = " ";
3737 highest = list->highest;
3738 if(list->lowest != 0)
3739 lowest = list->lowest;
3740 else if(highest < 100)
3743 lowest = highest - 100;
3744 for(start = curr = 1; curr < list->table.length; )
3746 uData = list->users[curr-1];
3747 list->table.contents[curr++][0] = " ";
3748 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
3751 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);
3753 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));
3754 temp = list->table.contents[--start];
3755 list->table.contents[start] = list->table.contents[0];
3756 tmp_table.contents = list->table.contents + start;
3757 tmp_table.length = curr - start;
3758 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
3759 list->table.contents[start] = temp;
3761 highest = lowest - 1;
3762 lowest = (highest < 100) ? 0 : (highest - 99);
3769 def_list(struct listData
*list
)
3773 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
);
3775 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
));
3776 if(list
->table
.length
== 1)
3778 msg
= user_find_message(list
->user
, "MSG_NONE");
3779 send_message_type(4, list
->user
, list
->bot
, " %s", msg
);
3782 table_send(list
->bot
, list
->user
->nick
, 0, NULL
, list
->table
);
3786 userData_access_comp(const void *arg_a
, const void *arg_b
)
3788 const struct userData
*a
= *(struct userData
**)arg_a
;
3789 const struct userData
*b
= *(struct userData
**)arg_b
;
3791 if(a
->access
!= b
->access
)
3792 res
= b
->access
- a
->access
;
3794 res
= irccasecmp(a
->handle
->handle
, b
->handle
->handle
);
3799 cmd_list_users(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, unsigned short lowest
, unsigned short highest
)
3801 void (*send_list
)(struct listData
*);
3802 struct userData
*uData
;
3803 struct listData lData
;
3804 unsigned int matches
;
3808 lData
.bot
= cmd
->parent
->bot
;
3809 lData
.channel
= channel
;
3810 lData
.lowest
= lowest
;
3811 lData
.highest
= highest
;
3812 lData
.search
= (argc
> 1) ? argv
[1] : NULL
;
3813 send_list
= def_list
;
3814 /* What does the following line do exactly?? */
3815 /*(void)zoot_list; ** since it doesn't show user levels */
3817 /* this does nothing!! -rubin
3818 if(user->handle_info)
3820 switch(user->handle_info->userlist_style)
3822 case HI_STYLE_DEF: send_list = def_list; break;
3823 case HI_STYLE_ZOOT: send_list = def_list; break;
3828 lData
.users
= alloca(channel
->channel_info
->userCount
* sizeof(struct userData
*));
3830 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
3832 if((uData
->access
< lowest
)
3833 || (uData
->access
> highest
)
3834 || (lData
.search
&& !match_ircglob(uData
->handle
->handle
, lData
.search
)))
3836 lData
.users
[matches
++] = uData
;
3838 qsort(lData
.users
, matches
, sizeof(lData
.users
[0]), userData_access_comp
);
3840 lData
.table
.length
= matches
+1;
3841 lData
.table
.width
= 5;
3842 lData
.table
.flags
= TABLE_NO_FREE
;
3843 lData
.table
.contents
= malloc(lData
.table
.length
*sizeof(*lData
.table
.contents
));
3844 ary
= malloc(lData
.table
.width
*sizeof(**lData
.table
.contents
));
3845 lData
.table
.contents
[0] = ary
;
3849 ary
[3] = "Last Seen";
3851 for(matches
= 1; matches
< lData
.table
.length
; ++matches
)
3853 struct userData
*uData
= lData
.users
[matches
-1];
3854 char seen
[INTERVALLEN
];
3856 ary
= malloc(lData
.table
.width
*sizeof(**lData
.table
.contents
));
3857 lData
.table
.contents
[matches
] = ary
;
3858 /* ary[0] = strtab(uData->access);*/
3859 ary
[0] = user_level_name_from_level(uData
->access
);
3860 ary
[1] = strtab(uData
->access
);
3861 ary
[2] = uData
->handle
->handle
;
3864 else if(!uData
->seen
)
3867 ary
[3] = intervalString(seen
, now
- uData
->seen
, user
->handle_info
);
3868 ary
[3] = strdup(ary
[3]);
3869 if(IsUserSuspended(uData
))
3870 ary
[4] = "Suspended";
3871 else if(HANDLE_FLAGGED(uData
->handle
, FROZEN
))
3872 ary
[4] = "Vacation";
3877 for(matches
= 1; matches
< lData
.table
.length
; ++matches
)
3879 free((char*)lData
.table
.contents
[matches
][3]);
3880 free(lData
.table
.contents
[matches
]);
3882 free(lData
.table
.contents
[0]);
3883 free(lData
.table
.contents
);
3887 /* Remove this now that debugging is over? or improve it for
3888 * users? Would it be better tied into USERS somehow? -Rubin */
3889 static CHANSERV_FUNC(cmd_pending
)
3891 struct adduserPending
*ap
;
3892 reply("CSMSG_ADDUSER_PENDING_HEADER");
3894 for(ap
= adduser_pendings
;ap
;ap
= ap
->next
)
3895 reply("CSMSG_ADDUSER_PENDING_LIST", ap
->channel
->name
, ap
->user
->nick
);
3896 reply("CSMSG_ADDUSER_PENDING_FOOTER");
3900 static CHANSERV_FUNC(cmd_users
)
3902 return cmd_list_users(CSFUNC_ARGS
, 1, UL_OWNER
);
3905 static CHANSERV_FUNC(cmd_wlist
)
3907 return cmd_list_users(CSFUNC_ARGS
, UL_OWNER
, UL_OWNER
);
3910 static CHANSERV_FUNC(cmd_clist
)
3912 return cmd_list_users(CSFUNC_ARGS
, UL_COOWNER
, UL_OWNER
-1);
3915 static CHANSERV_FUNC(cmd_mlist
)
3917 return cmd_list_users(CSFUNC_ARGS
, UL_MANAGER
, UL_COOWNER
-1);
3920 static CHANSERV_FUNC(cmd_olist
)
3922 return cmd_list_users(CSFUNC_ARGS
, UL_OP
, UL_MANAGER
-1);
3925 static CHANSERV_FUNC(cmd_hlist
)
3927 return cmd_list_users(CSFUNC_ARGS
, UL_HALFOP
, UL_OP
-1);
3930 static CHANSERV_FUNC(cmd_plist
)
3932 return cmd_list_users(CSFUNC_ARGS
, 1, UL_HALFOP
-1);
3935 static CHANSERV_FUNC(cmd_lamers
)
3937 struct helpfile_table tbl
;
3938 unsigned int matches
= 0, timed
= 0, ii
;
3939 char t_buffer
[INTERVALLEN
], e_buffer
[INTERVALLEN
], *search
;
3940 const char *msg_never
, *triggered
, *expires
;
3941 struct banData
*ban
, **bans
; /* lamers */
3948 reply("CSMSG_LAMERS_HEADER", channel
->name
);
3949 bans
= alloca(channel
->channel_info
->banCount
* sizeof(struct banData
*)); /* lamers */
3952 for(ban
= channel
->channel_info
->bans
; ban
; ban
= ban
->next
)
3954 if(search
&& !match_ircglobs(search
, ban
->mask
))
3956 bans
[matches
++] = ban
;
3961 tbl
.length
= matches
+ 1;
3962 tbl
.width
= 4 + timed
;
3964 tbl
.flags
= TABLE_NO_FREE
;
3965 tbl
.contents
= malloc(tbl
.length
* sizeof(tbl
.contents
[0]));
3966 tbl
.contents
[0] = malloc(tbl
.width
* sizeof(tbl
.contents
[0][0]));
3967 tbl
.contents
[0][0] = "Mask";
3968 tbl
.contents
[0][1] = "Set By";
3969 tbl
.contents
[0][2] = "Triggered";
3972 tbl
.contents
[0][3] = "Expires";
3973 tbl
.contents
[0][4] = "Reason";
3976 tbl
.contents
[0][3] = "Reason";
3979 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
3981 free(tbl
.contents
[0]);
3986 msg_never
= user_find_message(user
, "MSG_NEVER");
3987 for(ii
= 0; ii
< matches
; )
3993 else if(ban
->expires
)
3994 expires
= intervalString(e_buffer
, ban
->expires
- now
, user
->handle_info
);
3996 expires
= msg_never
;
3999 triggered
= intervalString(t_buffer
, now
- ban
->triggered
, user
->handle_info
);
4001 triggered
= msg_never
;
4003 tbl
.contents
[++ii
] = malloc(tbl
.width
* sizeof(tbl
.contents
[0][0]));
4004 tbl
.contents
[ii
][0] = ban
->mask
;
4005 tbl
.contents
[ii
][1] = ban
->owner
;
4006 tbl
.contents
[ii
][2] = strdup(triggered
);
4009 tbl
.contents
[ii
][3] = strdup(expires
);
4010 tbl
.contents
[ii
][4] = ban
->reason
;
4013 tbl
.contents
[ii
][3] = ban
->reason
;
4015 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
4016 /* reply("MSG_MATCH_COUNT", matches); */
4017 for(ii
= 1; ii
< tbl
.length
; ++ii
)
4019 free((char*)tbl
.contents
[ii
][2]);
4021 free((char*)tbl
.contents
[ii
][3]);
4022 free(tbl
.contents
[ii
]);
4024 free(tbl
.contents
[0]);
4031 * return + if the user does NOT have the right to set the topic, and
4032 * the topic is changed.
4035 bad_topic(struct chanNode
*channel
, struct userNode
*user
, const char *new_topic
)
4037 struct chanData
*cData
= channel
->channel_info
;
4038 if(check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
4040 else if(cData
->topic
)
4041 return irccasecmp(new_topic
, cData
->topic
);
4048 * Makes a givin topic fit into a givin topic mask and returns
4051 * topic_mask - the mask to conform to
4052 * topic - the topic to make conform
4053 * new_topic - the pre-allocated char* to put the new topic into
4055 * modifies: new_topic
4058 conform_topic(char* topic_mask
, char* topic
, char *new_topic
)
4060 //char *topic_mask = cData->topic_mask;
4062 int pos
=0, starpos
=-1, dpos
=0, len
;
4064 while((tchar
= topic_mask
[pos
++]) && (dpos
<= TOPICLEN
))
4071 strcpy(new_topic
, "");
4074 len
= strlen(topic
);
4075 if((dpos
+ len
) > TOPICLEN
)
4076 len
= TOPICLEN
+ 1 - dpos
;
4077 memcpy(new_topic
+dpos
, topic
, len
);
4081 case '\\': tchar
= topic_mask
[pos
++]; /* and fall through */
4082 default: new_topic
[dpos
++] = tchar
; break;
4085 if((dpos
> TOPICLEN
) || tchar
)
4087 strcpy(new_topic
, "");
4090 new_topic
[dpos
] = 0;
4094 static CHANSERV_FUNC(cmd_topic
)
4096 struct chanData
*cData
;
4099 cData
= channel
->channel_info
;
4104 SetChannelTopic(channel
, chanserv
, cData
->topic
, 1);
4105 reply("CSMSG_TOPIC_SET", cData
->topic
);
4109 reply("CSMSG_NO_TOPIC", channel
->name
);
4113 topic
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
4114 /* If they say "!topic *", use an empty topic. */
4115 if((topic
[0] == '*') && (topic
[1] == 0))
4118 if(bad_topic(channel
, user
, topic
))
4120 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
4125 /* If there is a topicmask set, and the new topic doesnt match, make it */
4126 if(cData
->topic_mask
&& !match_ircglob(topic
, cData
->topic_mask
))
4128 char *topic_mask
= cData
->topic_mask
;
4129 char new_topic
[TOPICLEN
+1];
4131 /* make a new topic fitting mask */
4132 conform_topic(topic_mask
, topic
, new_topic
);
4135 /* Topic couldnt fit into mask, was too long */
4136 reply("CSMSG_TOPICMASK_CONFLICT1", channel
->name
, topic_mask
);
4137 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN
);
4140 SetChannelTopic(channel
, chanserv
, new_topic
, 1);
4142 else /* No mask set, just set the topic */
4143 SetChannelTopic(channel
, chanserv
, topic
, 1);
4146 if(check_user_level(channel
, user
, lvlTopicSnarf
, 1, 0))
4148 /* Grab the topic and save it as the default topic. */
4150 cData
->topic
= strdup(channel
->topic
);
4156 static CHANSERV_FUNC(cmd_mode
)
4158 struct mod_chanmode
*change
;
4162 change
= &channel
->channel_info
->modes
;
4163 if(change
->modes_set
|| change
->modes_clear
) {
4164 modcmd_chanmode_announce(change
);
4165 reply("CSMSG_DEFAULTED_MODES", channel
->name
);
4167 reply("CSMSG_NO_MODES", channel
->name
);
4171 change
= mod_chanmode_parse(channel
, argv
+1, argc
-1, MCP_KEY_FREE
|MCP_REGISTERED
);
4174 reply("MSG_INVALID_MODES", unsplit_string(argv
+1, argc
-1, NULL
));
4178 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
4179 && mode_lock_violated(&channel
->channel_info
->modes
, change
))
4182 mod_chanmode_format(&channel
->channel_info
->modes
, modes
);
4183 reply("CSMSG_MODE_LOCKED", modes
, channel
->name
);
4187 modcmd_chanmode_announce(change
);
4188 mod_chanmode_free(change
);
4189 reply("CSMSG_MODES_SET", unsplit_string(argv
+1, argc
-1, NULL
));
4193 static CHANSERV_FUNC(cmd_invite
)
4195 struct userData
*uData
;
4196 struct userNode
*invite
;
4198 uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
4202 if(!(invite
= GetUserH(argv
[1])))
4204 reply("MSG_NICK_UNKNOWN", argv
[1]);
4211 if(GetUserMode(channel
, invite
))
4213 reply("CSMSG_ALREADY_PRESENT", invite
->nick
, channel
->name
);
4221 char *reason
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
4222 send_message(invite
, chanserv
, "CSMSG_INVITING_YOU_REASON", user
->nick
, channel
->name
, reason
);
4225 send_message(invite
, chanserv
, "CSMSG_INVITING_YOU", user
->nick
, channel
->name
);
4227 irc_invite(chanserv
, invite
, channel
);
4229 reply("CSMSG_INVITED_USER", argv
[1], channel
->name
);
4234 static CHANSERV_FUNC(cmd_inviteme
)
4236 if(GetUserMode(channel
, user
))
4238 reply("CSMSG_YOU_ALREADY_PRESENT", channel
->name
);
4241 if(channel
->channel_info
4242 && !check_user_level(channel
, user
, lvlInviteMe
, 1, 0))
4244 reply("CSMSG_LOW_CHANNEL_ACCESS", channel
->name
);
4247 irc_invite(cmd
->parent
->bot
, user
, channel
);
4252 show_suspension_info(struct svccmd
*cmd
, struct userNode
*user
, struct suspended
*suspended
)
4255 char buf1
[INTERVALLEN
], buf2
[INTERVALLEN
];
4257 /* We display things based on two dimensions:
4258 * - Issue time: present or absent
4259 * - Expiration: revoked, expired, expires in future, or indefinite expiration
4260 * (in order of precedence, so something both expired and revoked
4261 * only counts as revoked)
4263 combo
= (suspended
->issued
? 4 : 0)
4264 + (suspended
->revoked
? 3 : suspended
->expires
? ((suspended
->expires
< now
) ? 2 : 1) : 0);
4266 case 0: /* no issue time, indefinite expiration */
4267 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended
->suspender
, suspended
->reason
);
4269 case 1: /* no issue time, expires in future */
4270 intervalString(buf1
, suspended
->expires
-now
, user
->handle_info
);
4271 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended
->suspender
, buf1
, suspended
->reason
);
4273 case 2: /* no issue time, expired */
4274 intervalString(buf1
, now
-suspended
->expires
, user
->handle_info
);
4275 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended
->suspender
, buf1
, suspended
->reason
);
4277 case 3: /* no issue time, revoked */
4278 intervalString(buf1
, now
-suspended
->revoked
, user
->handle_info
);
4279 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended
->suspender
, buf1
, suspended
->reason
);
4281 case 4: /* issue time set, indefinite expiration */
4282 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4283 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1
, suspended
->suspender
, suspended
->reason
);
4285 case 5: /* issue time set, expires in future */
4286 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4287 intervalString(buf2
, suspended
->expires
-now
, user
->handle_info
);
4288 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4290 case 6: /* issue time set, expired */
4291 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4292 intervalString(buf2
, now
-suspended
->expires
, user
->handle_info
);
4293 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4295 case 7: /* issue time set, revoked */
4296 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4297 intervalString(buf2
, now
-suspended
->revoked
, user
->handle_info
);
4298 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4301 log_module(CS_LOG
, LOG_ERROR
, "Invalid combo value %d in show_suspension_info()", combo
);
4306 static CHANSERV_FUNC(cmd_info
)
4308 char modes
[MAXLEN
], buffer
[INTERVALLEN
];
4309 struct userData
*uData
, *owner
;
4310 struct chanData
*cData
;
4311 struct do_not_register
*dnr
;
4316 cData
= channel
->channel_info
;
4317 reply("CSMSG_CHANNEL_INFO", channel
->name
);
4320 uData
= GetChannelUser(cData
, user
->handle_info
);
4321 if(uData
&& (uData
->access
>= UL_OP
/*cData->lvlOpts[lvlGiveOps]*/))
4323 mod_chanmode_format(&cData
->modes
, modes
);
4324 reply("CSMSG_CHANNEL_TOPIC", cData
->topic
);
4325 reply("CSMSG_CHANNEL_MODES", modes
[0] ? modes
: user_find_message(user
, "MSG_NONE"));
4328 for(it
= dict_first(cData
->notes
); it
; it
= iter_next(it
))
4332 note
= iter_data(it
);
4333 if(!note_type_visible_to_user(cData
, note
->type
, user
))
4336 padding
= PADLEN
- 1 - strlen(iter_key(it
));
4337 reply("CSMSG_CHANNEL_NOTE", iter_key(it
), padding
> 0 ? padding
: 1, "", note
->note
);
4340 reply("CSMSG_CHANNEL_MAX", cData
->max
);
4341 for(owner
= cData
->users
; owner
; owner
= owner
->next
)
4342 if(owner
->access
== UL_OWNER
)
4343 reply("CSMSG_CHANNEL_OWNER", owner
->handle
->handle
);
4344 reply("CSMSG_CHANNEL_USERS", cData
->userCount
);
4345 reply("CSMSG_CHANNEL_LAMERS", cData
->banCount
);
4346 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer
, now
- cData
->visited
, user
->handle_info
));
4347 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer
, now
- cData
->registered
, user
->handle_info
));
4349 privileged
= IsStaff(user
);
4350 if(((uData
&& uData
->access
>= UL_COOWNER
) || privileged
) && cData
->registrar
)
4351 reply("CSMSG_CHANNEL_REGISTRAR", cData
->registrar
);
4353 if(privileged
&& (dnr
= chanserv_is_dnr(channel
->name
, NULL
)))
4354 chanserv_show_dnrs(user
, cmd
, channel
->name
, NULL
);
4356 if(cData
->suspended
&& ((uData
&& (uData
->access
>= UL_COOWNER
)) || IsHelping(user
)))
4358 struct suspended
*suspended
;
4359 reply((IsSuspended(cData
) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel
->name
);
4360 for(suspended
= cData
->suspended
; suspended
; suspended
= suspended
->previous
)
4361 show_suspension_info(cmd
, user
, suspended
);
4363 else if(IsSuspended(cData
))
4365 reply("CSMSG_CHANNEL_SUSPENDED", channel
->name
);
4366 show_suspension_info(cmd
, user
, cData
->suspended
);
4368 reply("CSMSG_CHANNEL_END");
4372 static CHANSERV_FUNC(cmd_netinfo
)
4374 extern time_t boot_time
;
4375 extern unsigned long burst_length
;
4376 char interval
[INTERVALLEN
];
4378 reply("CSMSG_NETWORK_INFO");
4379 reply("CSMSG_NETWORK_SERVERS", dict_size(servers
));
4380 reply("CSMSG_NETWORK_USERS", dict_size(clients
));
4381 reply("CSMSG_NETWORK_OPERS", curr_opers
.used
);
4382 reply("CSMSG_NETWORK_CHANNELS", registered_channels
);
4383 reply("CSMSG_NETWORK_LAMERS", banCount
);
4384 reply("CSMSG_NETWORK_CHANUSERS", userCount
);
4385 reply("CSMSG_SERVICES_UPTIME", intervalString(interval
, time(NULL
) - boot_time
, user
->handle_info
));
4386 reply("CSMSG_BURST_LENGTH", intervalString(interval
, burst_length
, user
->handle_info
));
4391 send_staff_list(struct userNode
*to
, struct userList
*list
, int skip_flags
)
4393 struct helpfile_table table
;
4395 struct userNode
*user
;
4400 table
.flags
= TABLE_REPEAT_ROWS
| TABLE_NO_FREE
| TABLE_NO_HEADERS
;
4401 table
.contents
= alloca(list
->used
*sizeof(*table
.contents
));
4402 for(nn
=0; nn
<list
->used
; nn
++)
4404 user
= list
->list
[nn
];
4405 if(user
->modes
& skip_flags
)
4409 table
.contents
[table
.length
] = alloca(table
.width
*sizeof(**table
.contents
));
4412 nick
= alloca(strlen(user
->nick
)+3);
4413 sprintf(nick
, "(%s)", user
->nick
);
4417 table
.contents
[table
.length
][0] = nick
;
4420 table_send(chanserv
, to
->nick
, 0, NULL
, table
);
4423 static CHANSERV_FUNC(cmd_ircops
)
4425 reply("CSMSG_STAFF_OPERS");
4426 send_staff_list(user
, &curr_opers
, FLAGS_SERVICE
);
4430 static CHANSERV_FUNC(cmd_helpers
)
4432 reply("CSMSG_STAFF_HELPERS");
4433 send_staff_list(user
, &curr_helpers
, FLAGS_OPER
);
4437 static CHANSERV_FUNC(cmd_staff
)
4439 reply("CSMSG_NETWORK_STAFF");
4440 cmd_ircops(CSFUNC_ARGS
);
4441 cmd_helpers(CSFUNC_ARGS
);
4445 static CHANSERV_FUNC(cmd_peek
)
4447 struct modeNode
*mn
;
4448 char modes
[MODELEN
];
4450 struct helpfile_table table
;
4452 irc_make_chanmode(channel
, modes
);
4454 reply("CSMSG_PEEK_INFO", channel
->name
);
4456 reply("CSMSG_PEEK_TOPIC", channel
->topic
);
4457 reply("CSMSG_PEEK_MODES", modes
);
4458 reply("CSMSG_PEEK_USERS", channel
->members
.used
);
4462 table
.flags
= TABLE_REPEAT_ROWS
| TABLE_NO_FREE
| TABLE_NO_HEADERS
;
4463 table
.contents
= alloca(channel
->members
.used
*sizeof(*table
.contents
));
4464 for(n
= 0; n
< channel
->members
.used
; n
++)
4466 mn
= channel
->members
.list
[n
];
4467 if(!(mn
->modes
& MODE_CHANOP
) || IsLocal(mn
->user
))
4469 table
.contents
[table
.length
] = alloca(sizeof(**table
.contents
));
4470 table
.contents
[table
.length
][0] = mn
->user
->nick
;
4475 reply("CSMSG_PEEK_OPS");
4476 table_send(chanserv
, user
->nick
, 0, NULL
, table
);
4479 reply("CSMSG_PEEK_NO_OPS");
4480 reply("CSMSG_PEEK_END");
4484 static MODCMD_FUNC(cmd_wipeinfo
)
4486 struct handle_info
*victim
;
4487 struct userData
*ud
, *actor
;
4490 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
4491 if(!(victim
= modcmd_get_handle_info(user
, argv
[1])))
4493 if(!(ud
= GetTrueChannelAccess(channel
->channel_info
, victim
)))
4495 reply("CSMSG_NO_CHAN_USER", argv
[1], channel
->name
);
4498 if((ud
->access
>= actor
->access
) && (ud
!= actor
))
4500 reply("MSG_USER_OUTRANKED", victim
->handle
);
4506 reply("CSMSG_WIPED_INFO_LINE", argv
[1], channel
->name
);
4510 static CHANSERV_FUNC(cmd_resync
)
4512 struct mod_chanmode
*changes
;
4513 struct chanData
*cData
= channel
->channel_info
;
4514 unsigned int ii
, used
;
4516 changes
= mod_chanmode_alloc(channel
->members
.used
* 2);
4517 for(ii
= used
= 0; ii
< channel
->members
.used
; ++ii
)
4519 struct modeNode
*mn
= channel
->members
.list
[ii
];
4520 struct userData
*uData
;
4522 if(IsService(mn
->user
))
4525 uData
= GetChannelAccess(cData
, mn
->user
->handle_info
);
4526 if(uData
&& uData
->access
>= UL_OP
/* cData->lvlOpts[lvlGiveOps]*/)
4528 if(!(mn
->modes
& MODE_CHANOP
))
4530 changes
->args
[used
].mode
= MODE_CHANOP
;
4531 changes
->args
[used
++].u
.member
= mn
;
4534 else if(uData
&& uData
->access
>= UL_HALFOP
/*cData->lvlOpts[lvlGiveHalfOps]*/)
4536 if(mn
->modes
& MODE_CHANOP
)
4538 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_CHANOP
;
4539 changes
->args
[used
++].u
.member
= mn
;
4541 if(!(mn
->modes
& MODE_HALFOP
))
4543 changes
->args
[used
].mode
= MODE_HALFOP
;
4544 changes
->args
[used
++].u
.member
= mn
;
4546 /* why cant halfops keep voice
4547 if(mn->modes & MODE_VOICE)
4549 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4550 changes->args[used++].u.member = mn;
4554 else if(uData
&& uData
->access
>= UL_PEON
/* cData->lvlOpts[lvlGiveVoice]*/)
4556 if(mn
->modes
& MODE_CHANOP
)
4558 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_CHANOP
;
4559 changes
->args
[used
++].u
.member
= mn
;
4561 if(mn
->modes
& MODE_HALFOP
)
4563 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_HALFOP
;
4564 changes
->args
[used
++].u
.member
= mn
;
4566 if(!(mn
->modes
& MODE_VOICE
))
4568 changes
->args
[used
].mode
= MODE_VOICE
;
4569 changes
->args
[used
++].u
.member
= mn
;
4576 changes
->args
[used
].mode
= MODE_REMOVE
| mn
->modes
;
4577 changes
->args
[used
++].u
.member
= mn
;
4581 changes
->argc
= used
;
4582 modcmd_chanmode_announce(changes
);
4583 mod_chanmode_free(changes
);
4584 reply("CSMSG_RESYNCED_USERS", channel
->name
);
4588 static CHANSERV_FUNC(cmd_seen
)
4590 struct userData
*uData
;
4591 struct handle_info
*handle
;
4592 char seen
[INTERVALLEN
];
4596 if(!irccasecmp(argv
[1], chanserv
->nick
))
4598 reply("CSMSG_IS_CHANSERV");
4602 if(!(handle
= get_handle_info(argv
[1])))
4604 reply("MSG_HANDLE_UNKNOWN", argv
[1]);
4608 if(!(uData
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
4610 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
4615 reply("CSMSG_USER_PRESENT", handle
->handle
);
4616 else if(uData
->seen
)
4617 reply("CSMSG_USER_SEEN", handle
->handle
, channel
->name
, intervalString(seen
, now
- uData
->seen
, user
->handle_info
));
4619 reply("CSMSG_NEVER_SEEN", handle
->handle
, channel
->name
);
4621 if(!uData
->present
&& HANDLE_FLAGGED(handle
, FROZEN
))
4622 reply("CSMSG_USER_VACATION", handle
->handle
);
4627 static MODCMD_FUNC(cmd_names
)
4629 struct userNode
*targ
;
4630 struct userData
*targData
;
4631 unsigned int ii
, pos
;
4634 for(ii
=pos
=0; ii
<channel
->members
.used
; ++ii
)
4636 targ
= channel
->members
.list
[ii
]->user
;
4637 targData
= GetTrueChannelAccess(channel
->channel_info
, targ
->handle_info
);
4640 if(pos
+ strlen(targ
->nick
) + strlen(targ
->handle_info
->handle
) + 8 > sizeof(buf
))
4643 reply("CSMSG_CHANNEL_NAMES", channel
->name
, buf
);
4647 if(IsUserSuspended(targData
))
4649 pos
+= sprintf(buf
+pos
, "%d:%s(%s)", targData
->access
, targ
->nick
, targ
->handle_info
->handle
);
4652 reply("CSMSG_CHANNEL_NAMES", channel
->name
, buf
);
4653 reply("CSMSG_END_NAMES", channel
->name
);
4658 note_type_visible_to_user(struct chanData
*channel
, struct note_type
*ntype
, struct userNode
*user
)
4660 switch(ntype
->visible_type
)
4662 case NOTE_VIS_ALL
: return 1;
4663 case NOTE_VIS_CHANNEL_USERS
: return !channel
|| !user
|| (user
->handle_info
&& GetChannelUser(channel
, user
->handle_info
));
4664 case NOTE_VIS_PRIVILEGED
: default: return user
&& (IsOper(user
) || IsSupportHelper(user
) || IsNetworkHelper(user
));
4669 note_type_settable_by_user(struct chanNode
*channel
, struct note_type
*ntype
, struct userNode
*user
)
4671 struct userData
*uData
;
4673 switch(ntype
->set_access_type
)
4675 case NOTE_SET_CHANNEL_ACCESS
:
4676 if(!user
->handle_info
)
4678 if(!(uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
)))
4680 return uData
->access
>= ntype
->set_access
.min_ulevel
;
4681 case NOTE_SET_CHANNEL_SETTER
:
4682 return check_user_level(channel
, user
, lvlSetters
, 1, 0);
4683 case NOTE_SET_PRIVILEGED
: default:
4684 return IsHelping(user
) && (user
->handle_info
->opserv_level
>= ntype
->set_access
.min_opserv
);
4688 static CHANSERV_FUNC(cmd_note
)
4690 struct chanData
*cData
;
4692 struct note_type
*ntype
;
4694 cData
= channel
->channel_info
;
4697 reply("CSMSG_NOT_REGISTERED", channel
->name
);
4701 /* If no arguments, show all visible notes for the channel. */
4707 for(count
=0, it
=dict_first(cData
->notes
); it
; it
=iter_next(it
))
4709 note
= iter_data(it
);
4710 if(!note_type_visible_to_user(cData
, note
->type
, user
))
4713 reply("CSMSG_NOTELIST_HEADER", channel
->name
);
4714 reply("CSMSG_NOTE_FORMAT", iter_key(it
), note
->setter
, note
->note
);
4717 reply("CSMSG_NOTELIST_END", channel
->name
);
4719 reply("CSMSG_NOTELIST_EMPTY", channel
->name
);
4721 /* If one argument, show the named note. */
4724 if((note
= dict_find(cData
->notes
, argv
[1], NULL
))
4725 && note_type_visible_to_user(cData
, note
->type
, user
))
4727 reply("CSMSG_NOTE_FORMAT", note
->type
->name
, note
->setter
, note
->note
);
4729 else if((ntype
= dict_find(note_types
, argv
[1], NULL
))
4730 && note_type_visible_to_user(NULL
, ntype
, user
))
4732 reply("CSMSG_NO_SUCH_NOTE", channel
->name
, ntype
->name
);
4737 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
4741 /* Assume they're trying to set a note. */
4745 ntype
= dict_find(note_types
, argv
[1], NULL
);
4748 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
4751 else if(note_type_settable_by_user(channel
, ntype
, user
))
4753 note_text
= unsplit_string(argv
+2, argc
-2, NULL
);
4754 if((note
= dict_find(cData
->notes
, argv
[1], NULL
)))
4755 reply("CSMSG_REPLACED_NOTE", ntype
->name
, channel
->name
, note
->setter
, note
->note
);
4756 chanserv_add_channel_note(cData
, ntype
, user
->handle_info
->handle
, note_text
);
4757 reply("CSMSG_NOTE_SET", ntype
->name
, channel
->name
);
4759 if(ntype
->visible_type
== NOTE_VIS_PRIVILEGED
)
4761 /* The note is viewable to staff only, so return 0
4762 to keep the invocation from getting logged (or
4763 regular users can see it in !events). */
4769 reply("CSMSG_NO_ACCESS");
4776 static CHANSERV_FUNC(cmd_delnote
)
4781 if(!(note
= dict_find(channel
->channel_info
->notes
, argv
[1], NULL
))
4782 || !note_type_settable_by_user(channel
, note
->type
, user
))
4784 reply("CSMSG_NO_SUCH_NOTE", channel
->name
, argv
[1]);
4787 dict_remove(channel
->channel_info
->notes
, note
->type
->name
);
4788 reply("CSMSG_NOTE_REMOVED", argv
[1], channel
->name
);
4792 static CHANSERV_FUNC(cmd_last
)
4798 numoflines
= (argc
> 1) ? atoi(argv
[1]) : 10;
4800 if(numoflines
< 1 || numoflines
> 200)
4802 reply("CSMSG_LAST_INVALID");
4805 ShowLog(user
, channel
, "*", "*", "*", "*", numoflines
);
4809 static CHANSERV_FUNC(cmd_events
)
4811 struct logSearch discrim
;
4812 struct logReport report
;
4813 unsigned int matches
, limit
;
4815 limit
= (argc
> 1) ? atoi(argv
[1]) : 10;
4816 if(limit
< 1 || limit
> 200)
4819 memset(&discrim
, 0, sizeof(discrim
));
4820 discrim
.masks
.bot
= chanserv
;
4821 discrim
.masks
.channel_name
= channel
->name
;
4823 discrim
.masks
.command
= argv
[2];
4824 discrim
.limit
= limit
;
4825 discrim
.max_time
= INT_MAX
;
4826 discrim
.severities
= 1 << LOG_COMMAND
;
4827 report
.reporter
= chanserv
;
4829 reply("CSMSG_EVENT_SEARCH_RESULTS", channel
->name
);
4831 matches
= log_entry_search(&discrim
, log_report_entry
, &report
);
4833 reply("MSG_MATCH_COUNT", matches
);
4835 reply("MSG_NO_MATCHES");
4839 static CHANSERV_FUNC(cmd_say
)
4845 msg
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
4846 send_channel_message(channel
, cmd
->parent
->bot
, "%s", msg
);
4848 else if(GetUserH(argv
[1]))
4851 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
4852 send_target_message(5, argv
[1], cmd
->parent
->bot
, "%s", msg
);
4856 reply("MSG_NOT_TARGET_NAME");
4862 static CHANSERV_FUNC(cmd_emote
)
4868 /* CTCP is so annoying. */
4869 msg
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
4870 send_channel_message(channel
, cmd
->parent
->bot
, "\001ACTION %s\001", msg
);
4872 else if(GetUserH(argv
[1]))
4874 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
4875 send_target_message(5, argv
[1], cmd
->parent
->bot
, "\001ACTION %s\001", msg
);
4879 reply("MSG_NOT_TARGET_NAME");
4885 struct channelList
*
4886 chanserv_support_channels(void)
4888 return &chanserv_conf
.support_channels
;
4891 static CHANSERV_FUNC(cmd_expire
)
4893 int channel_count
= registered_channels
;
4894 expire_channels(NULL
);
4895 reply("CSMSG_CHANNELS_EXPIRED", channel_count
- registered_channels
);
4900 chanserv_expire_suspension(void *data
)
4902 struct suspended
*suspended
= data
;
4903 struct chanNode
*channel
;
4905 if(!suspended
->expires
|| (now
< suspended
->expires
))
4906 suspended
->revoked
= now
;
4907 channel
= suspended
->cData
->channel
;
4908 suspended
->cData
->channel
= channel
;
4909 suspended
->cData
->flags
&= ~CHANNEL_SUSPENDED
;
4910 if(!IsOffChannel(suspended
->cData
))
4912 struct mod_chanmode change
;
4913 mod_chanmode_init(&change
);
4915 change
.args
[0].mode
= MODE_CHANOP
;
4916 change
.args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
4917 mod_chanmode_announce(chanserv
, channel
, &change
);
4921 static CHANSERV_FUNC(cmd_csuspend
)
4923 struct suspended
*suspended
;
4924 char reason
[MAXLEN
];
4925 time_t expiry
, duration
;
4926 struct userData
*uData
;
4930 if(IsProtected(channel
->channel_info
))
4932 reply("CSMSG_SUSPEND_NODELETE", channel
->name
);
4936 if(argv
[1][0] == '!')
4938 else if(IsSuspended(channel
->channel_info
))
4940 reply("CSMSG_ALREADY_SUSPENDED", channel
->name
);
4941 show_suspension_info(cmd
, user
, channel
->channel_info
->suspended
);
4945 if(!strcmp(argv
[1], "0"))
4947 else if((duration
= ParseInterval(argv
[1])))
4948 expiry
= now
+ duration
;
4951 reply("MSG_INVALID_DURATION", argv
[1]);
4955 unsplit_string(argv
+ 2, argc
- 2, reason
);
4957 suspended
= calloc(1, sizeof(*suspended
));
4958 suspended
->revoked
= 0;
4959 suspended
->issued
= now
;
4960 suspended
->suspender
= strdup(user
->handle_info
->handle
);
4961 suspended
->expires
= expiry
;
4962 suspended
->reason
= strdup(reason
);
4963 suspended
->cData
= channel
->channel_info
;
4964 suspended
->previous
= suspended
->cData
->suspended
;
4965 suspended
->cData
->suspended
= suspended
;
4967 if(suspended
->expires
)
4968 timeq_add(suspended
->expires
, chanserv_expire_suspension
, suspended
);
4970 if(IsSuspended(channel
->channel_info
))
4972 suspended
->previous
->revoked
= now
;
4973 if(suspended
->previous
->expires
)
4974 timeq_del(suspended
->previous
->expires
, chanserv_expire_suspension
, suspended
->previous
, 0);
4975 sprintf(reason
, "%s suspension modified by %s.", channel
->name
, suspended
->suspender
);
4976 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
4980 /* Mark all users in channel as absent. */
4981 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
4990 /* Mark the channel as suspended, then part. */
4991 channel
->channel_info
->flags
|= CHANNEL_SUSPENDED
;
4992 DelChannelUser(chanserv
, channel
, suspended
->reason
, 0);
4993 reply("CSMSG_SUSPENDED", channel
->name
);
4994 sprintf(reason
, "%s suspended by %s.", channel
->name
, suspended
->suspender
);
4995 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
5000 static CHANSERV_FUNC(cmd_cunsuspend
)
5002 struct suspended
*suspended
;
5003 char message
[MAXLEN
];
5005 if(!IsSuspended(channel
->channel_info
))
5007 reply("CSMSG_NOT_SUSPENDED", channel
->name
);
5011 suspended
= channel
->channel_info
->suspended
;
5013 /* Expire the suspension and join ChanServ to the channel. */
5014 timeq_del(suspended
->expires
, chanserv_expire_suspension
, suspended
, 0);
5015 chanserv_expire_suspension(suspended
);
5016 reply("CSMSG_UNSUSPENDED", channel
->name
);
5017 sprintf(message
, "%s unsuspended by %s.", channel
->name
, user
->handle_info
->handle
);
5018 global_message(MESSAGE_RECIPIENT_OPERS
|MESSAGE_RECIPIENT_HELPERS
, message
);
5022 typedef struct chanservSearch
5030 unsigned long flags
;
5034 typedef void (*channel_search_func
)(struct chanData
*channel
, void *data
);
5037 chanserv_search_create(struct userNode
*user
, unsigned int argc
, char *argv
[])
5042 search
= malloc(sizeof(struct chanservSearch
));
5043 memset(search
, 0, sizeof(*search
));
5046 for(i
= 0; i
< argc
; i
++)
5048 /* Assume all criteria require arguments. */
5051 send_message(user
, chanserv
, "MSG_MISSING_PARAMS", argv
[i
]);
5055 if(!irccasecmp(argv
[i
], "name"))
5056 search
->name
= argv
[++i
];
5057 else if(!irccasecmp(argv
[i
], "registrar"))
5058 search
->registrar
= argv
[++i
];
5059 else if(!irccasecmp(argv
[i
], "unvisited"))
5060 search
->unvisited
= ParseInterval(argv
[++i
]);
5061 else if(!irccasecmp(argv
[i
], "registered"))
5062 search
->registered
= ParseInterval(argv
[++i
]);
5063 else if(!irccasecmp(argv
[i
], "flags"))
5066 if(!irccasecmp(argv
[i
], "nodelete"))
5067 search
->flags
|= CHANNEL_NODELETE
;
5068 else if(!irccasecmp(argv
[i
], "suspended"))
5069 search
->flags
|= CHANNEL_SUSPENDED
;
5072 send_message(user
, chanserv
, "CSMSG_INVALID_CFLAG", argv
[i
]);
5076 else if(!irccasecmp(argv
[i
], "limit"))
5077 search
->limit
= strtoul(argv
[++i
], NULL
, 10);
5080 send_message(user
, chanserv
, "MSG_INVALID_CRITERIA", argv
[i
]);
5085 if(search
->name
&& !strcmp(search
->name
, "*"))
5087 if(search
->registrar
&& !strcmp(search
->registrar
, "*"))
5088 search
->registrar
= 0;
5097 chanserv_channel_match(struct chanData
*channel
, search_t search
)
5099 const char *name
= channel
->channel
->name
;
5100 if((search
->name
&& !match_ircglob(name
, search
->name
)) ||
5101 (search
->registrar
&& !channel
->registrar
) ||
5102 (search
->registrar
&& !match_ircglob(channel
->registrar
, search
->registrar
)) ||
5103 (search
->unvisited
&& (now
- channel
->visited
) < search
->unvisited
) ||
5104 (search
->registered
&& (now
- channel
->registered
) > search
->registered
) ||
5105 (search
->flags
&& ((search
->flags
& channel
->flags
) != search
->flags
)))
5112 chanserv_channel_search(search_t search
, channel_search_func smf
, void *data
)
5114 struct chanData
*channel
;
5115 unsigned int matches
= 0;
5117 for(channel
= channelList
; channel
&& matches
< search
->limit
; channel
= channel
->next
)
5119 if(!chanserv_channel_match(channel
, search
))
5129 search_count(UNUSED_ARG(struct chanData
*channel
), UNUSED_ARG(void *data
))
5134 search_print(struct chanData
*channel
, void *data
)
5136 send_message_type(4, data
, chanserv
, "%s", channel
->channel
->name
);
5139 static CHANSERV_FUNC(cmd_search
)
5142 unsigned int matches
;
5143 channel_search_func action
;
5147 if(!irccasecmp(argv
[1], "count"))
5148 action
= search_count
;
5149 else if(!irccasecmp(argv
[1], "print"))
5150 action
= search_print
;
5153 reply("CSMSG_ACTION_INVALID", argv
[1]);
5157 search
= chanserv_search_create(user
, argc
- 2, argv
+ 2);
5161 if(action
== search_count
)
5162 search
->limit
= INT_MAX
;
5164 if(action
== search_print
)
5166 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
5170 matches
= chanserv_channel_search(search
, action
, user
);
5173 reply("MSG_MATCH_COUNT", matches
);
5175 reply("MSG_NO_MATCHES");
5181 static CHANSERV_FUNC(cmd_unvisited
)
5183 struct chanData
*cData
;
5184 time_t interval
= chanserv_conf
.channel_expire_delay
;
5185 char buffer
[INTERVALLEN
];
5186 unsigned int limit
= 25, matches
= 0;
5190 interval
= ParseInterval(argv
[1]);
5192 limit
= atoi(argv
[2]);
5195 intervalString(buffer
, interval
, user
->handle_info
);
5196 reply("CSMSG_UNVISITED_HEADER", limit
, buffer
);
5198 for(cData
= channelList
; cData
&& matches
< limit
; cData
= cData
->next
)
5200 if((now
- cData
->visited
) < interval
)
5203 intervalString(buffer
, now
- cData
->visited
, user
->handle_info
);
5204 reply("CSMSG_UNVISITED_DATA", cData
->channel
->name
, buffer
);
5211 static MODCMD_FUNC(chan_opt_defaulttopic
)
5217 if(!check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
5219 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
5223 topic
= unsplit_string(argv
+1, argc
-1, NULL
);
5225 free(channel
->channel_info
->topic
);
5226 if(topic
[0] == '*' && topic
[1] == 0)
5228 topic
= channel
->channel_info
->topic
= NULL
;
5232 topic
= channel
->channel_info
->topic
= strdup(topic
);
5233 if(channel
->channel_info
->topic_mask
5234 && !match_ircglob(channel
->channel_info
->topic
, channel
->channel_info
->topic_mask
))
5235 reply("CSMSG_TOPIC_MISMATCH", channel
->name
);
5237 SetChannelTopic(channel
, chanserv
, topic
? topic
: "", 1);
5240 if(channel
->channel_info
->topic
)
5241 reply("CSMSG_SET_DEFAULT_TOPIC", channel
->channel_info
->topic
);
5243 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user
, "MSG_NONE"));
5247 static MODCMD_FUNC(chan_opt_topicmask
)
5251 struct chanData
*cData
= channel
->channel_info
;
5254 if(!check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
5256 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
5260 mask
= unsplit_string(argv
+1, argc
-1, NULL
);
5262 if(cData
->topic_mask
)
5263 free(cData
->topic_mask
);
5264 if(mask
[0] == '*' && mask
[1] == 0)
5266 cData
->topic_mask
= 0;
5270 cData
->topic_mask
= strdup(mask
);
5272 reply("CSMSG_MASK_BUT_NO_TOPIC", channel
->name
);
5273 else if(!match_ircglob(cData
->topic
, cData
->topic_mask
))
5274 reply("CSMSG_TOPIC_MISMATCH", channel
->name
);
5278 if(channel
->channel_info
->topic_mask
)
5279 reply("CSMSG_SET_TOPICMASK", channel
->channel_info
->topic_mask
);
5281 reply("CSMSG_SET_TOPICMASK", user_find_message(user
, "MSG_NONE"));
5285 int opt_greeting_common(struct userNode
*user
, struct svccmd
*cmd
, int argc
, char *argv
[], char *name
, char **data
)
5289 char *greeting
= unsplit_string(argv
+1, argc
-1, NULL
);
5293 if(greeting
[0] == '*' && greeting
[1] == 0)
5297 unsigned int length
= strlen(greeting
);
5298 if(length
> chanserv_conf
.greeting_length
)
5300 reply("CSMSG_GREETING_TOO_LONG", length
, chanserv_conf
.greeting_length
);
5303 *data
= strdup(greeting
);
5312 reply(name
, user_find_message(user
, "MSG_NONE"));
5316 static MODCMD_FUNC(chan_opt_greeting
)
5318 return opt_greeting_common(user
, cmd
, argc
, argv
, "CSMSG_SET_GREETING", &channel
->channel_info
->greeting
);
5321 static MODCMD_FUNC(chan_opt_usergreeting
)
5323 return opt_greeting_common(user
, cmd
, argc
, argv
, "CSMSG_SET_USERGREETING", &channel
->channel_info
->user_greeting
);
5326 static MODCMD_FUNC(chan_opt_modes
)
5328 struct mod_chanmode
*new_modes
;
5329 char modes
[MODELEN
];
5333 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0))
5335 reply("CSMSG_NO_ACCESS");
5338 if(argv
[1][0] == '*' && argv
[1][1] == 0)
5340 memset(&channel
->channel_info
->modes
, 0, sizeof(channel
->channel_info
->modes
));
5342 else if(!(new_modes
= mod_chanmode_parse(channel
, argv
+1, argc
-1, MCP_KEY_FREE
|MCP_REGISTERED
)))
5344 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv
+1, argc
-1, NULL
));
5347 else if(new_modes
->argc
> 1)
5349 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv
+1, argc
-1, NULL
));
5350 mod_chanmode_free(new_modes
);
5355 channel
->channel_info
->modes
= *new_modes
;
5356 modcmd_chanmode_announce(new_modes
);
5357 mod_chanmode_free(new_modes
);
5361 mod_chanmode_format(&channel
->channel_info
->modes
, modes
);
5363 reply("CSMSG_SET_MODES", modes
);
5365 reply("CSMSG_SET_MODES", user_find_message(user
, "MSG_NONE"));
5369 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5371 channel_binary_option(char *name
, unsigned long mask
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5373 struct chanData
*cData
= channel
->channel_info
;
5378 /* Set flag according to value. */
5379 if(enabled_string(argv
[1]))
5381 cData
->flags
|= mask
;
5384 else if(disabled_string(argv
[1]))
5386 cData
->flags
&= ~mask
;
5391 reply("MSG_INVALID_BINARY", argv
[1]);
5397 /* Find current option value. */
5398 value
= (cData
->flags
& mask
) ? 1 : 0;
5402 reply(name
, user_find_message(user
, "MSG_ON"));
5404 reply(name
, user_find_message(user
, "MSG_OFF"));
5408 static MODCMD_FUNC(chan_opt_nodelete
)
5410 if((argc
> 1) && (!IsOper(user
) || !user
->handle_info
|| (user
->handle_info
->opserv_level
< chanserv_conf
.nodelete_level
)))
5412 reply("MSG_SETTING_PRIVILEGED", argv
[0]);
5416 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE
);
5419 static MODCMD_FUNC(chan_opt_dynlimit
)
5421 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT
);
5424 static MODCMD_FUNC(chan_opt_offchannel
)
5426 struct chanData
*cData
= channel
->channel_info
;
5431 /* Set flag according to value. */
5432 if(enabled_string(argv
[1]))
5434 if(!IsOffChannel(cData
))
5435 DelChannelUser(chanserv
, channel
, "Going off-channel.", 0);
5436 cData
->flags
|= CHANNEL_OFFCHANNEL
;
5439 else if(disabled_string(argv
[1]))
5441 if(IsOffChannel(cData
))
5443 struct mod_chanmode change
;
5444 mod_chanmode_init(&change
);
5446 change
.args
[0].mode
= MODE_CHANOP
;
5447 change
.args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
5448 mod_chanmode_announce(chanserv
, channel
, &change
);
5450 cData
->flags
&= ~CHANNEL_OFFCHANNEL
;
5455 reply("MSG_INVALID_BINARY", argv
[1]);
5461 /* Find current option value. */
5462 value
= (cData
->flags
& CHANNEL_OFFCHANNEL
) ? 1 : 0;
5466 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user
, "MSG_ON"));
5468 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user
, "MSG_OFF"));
5472 static MODCMD_FUNC(chan_opt_defaults
)
5474 struct userData
*uData
;
5475 struct chanData
*cData
;
5476 const char *confirm
;
5477 enum levelOption lvlOpt
;
5478 enum charOption chOpt
;
5480 cData
= channel
->channel_info
;
5481 uData
= GetChannelUser(cData
, user
->handle_info
);
5482 if(!uData
|| (uData
->access
< UL_OWNER
))
5484 reply("CSMSG_OWNER_DEFAULTS", channel
->name
);
5487 confirm
= make_confirmation_string(uData
);
5488 if((argc
< 2) || strcmp(argv
[1], confirm
))
5490 reply("CSMSG_CONFIRM_DEFAULTS", channel
->name
, confirm
);
5493 cData
->flags
= CHANNEL_DEFAULT_FLAGS
;
5494 cData
->modes
= chanserv_conf
.default_modes
;
5495 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
5496 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
5497 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
5498 cData
->chOpts
[chOpt
] = charOptions
[chOpt
].default_value
;
5499 reply("CSMSG_SETTINGS_DEFAULTED", channel
->name
);
5504 channel_level_option(enum levelOption option
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5506 struct chanData
*cData
= channel
->channel_info
;
5507 struct userData
*uData
;
5508 unsigned short value
;
5512 if(!check_user_level(channel
, user
, option
, 1, 1))
5514 reply("CSMSG_CANNOT_SET");
5517 value
= user_level_from_name(argv
[1], UL_OWNER
+1);
5518 if(!value
&& strcmp(argv
[1], "0"))
5520 reply("CSMSG_INVALID_ACCESS", argv
[1]);
5523 uData
= GetChannelUser(cData
, user
->handle_info
);
5524 if(!uData
|| ((uData
->access
< UL_OWNER
) && (value
> uData
->access
)))
5526 reply("CSMSG_BAD_SETLEVEL");
5531 /* removing these level sets..
5533 if(value > cData->lvlOpts[lvlGiveOps])
5535 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
5539 case lvlGiveHalfOps:
5540 if(value < cData->lvlOpts[lvlGiveVoice])
5542 reply("CSMSG_BAD_GIVEHOPS", cData->lvlOpts[lvlGiveHalfOps]);
5547 if(value < cData->lvlOpts[lvlGiveVoice])
5549 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
5555 /* This test only applies to owners, since non-owners
5556 * trying to set an option to above their level get caught
5557 * by the CSMSG_BAD_SETLEVEL test above.
5559 if(value
> uData
->access
)
5561 reply("CSMSG_BAD_SETTERS");
5568 cData
->lvlOpts
[option
] = value
;
5570 reply(levelOptions
[option
].format_name
, cData
->lvlOpts
[option
]);
5574 static MODCMD_FUNC(chan_opt_enfops
)
5576 return channel_level_option(lvlEnfOps
, CSFUNC_ARGS
);
5579 static MODCMD_FUNC(chan_opt_enfhalfops
)
5581 return channel_level_option(lvlEnfHalfOps
, CSFUNC_ARGS
);
5584 static MODCMD_FUNC(chan_opt_giveops)
5586 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
5589 static MODCMD_FUNC(chan_opt_givehalfops)
5591 return channel_level_option(lvlGiveHalfOps, CSFUNC_ARGS);
5594 static MODCMD_FUNC(chan_opt_enfmodes
)
5596 return channel_level_option(lvlEnfModes
, CSFUNC_ARGS
);
5599 static MODCMD_FUNC(chan_opt_enftopic
)
5601 return channel_level_option(lvlEnfTopic
, CSFUNC_ARGS
);
5604 static MODCMD_FUNC(chan_opt_pubcmd
)
5606 return channel_level_option(lvlPubCmd
, CSFUNC_ARGS
);
5609 static MODCMD_FUNC(chan_opt_setters
)
5611 return channel_level_option(lvlSetters
, CSFUNC_ARGS
);
5615 static MODCMD_FUNC(chan_opt_ctcpusers)
5617 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
5621 static MODCMD_FUNC(chan_opt_userinfo
)
5623 return channel_level_option(lvlUserInfo
, CSFUNC_ARGS
);
5627 static MODCMD_FUNC(chan_opt_givevoice)
5629 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
5633 static MODCMD_FUNC(chan_opt_topicsnarf
)
5635 return channel_level_option(lvlTopicSnarf
, CSFUNC_ARGS
);
5638 static MODCMD_FUNC(chan_opt_inviteme
)
5640 return channel_level_option(lvlInviteMe
, CSFUNC_ARGS
);
5644 channel_multiple_option(enum charOption option
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5646 struct chanData
*cData
= channel
->channel_info
;
5647 int count
= charOptions
[option
].count
, index
;
5651 index
= atoi(argv
[1]);
5653 if(!isdigit(argv
[1][0]) || (index
< 0) || (index
>= count
))
5655 reply("CSMSG_INVALID_NUMERIC", index
);
5656 /* Show possible values. */
5657 for(index
= 0; index
< count
; index
++)
5658 reply(charOptions
[option
].format_name
, index
, user_find_message(user
, charOptions
[option
].values
[index
].format_name
));
5662 cData
->chOpts
[option
] = charOptions
[option
].values
[index
].value
;
5666 /* Find current option value. */
5669 (index
< count
) && (cData
->chOpts
[option
] != charOptions
[option
].values
[index
].value
);
5673 /* Somehow, the option value is corrupt; reset it to the default. */
5674 cData
->chOpts
[option
] = charOptions
[option
].default_value
;
5679 reply(charOptions
[option
].format_name
, index
, user_find_message(user
, charOptions
[option
].values
[index
].format_name
));
5683 static MODCMD_FUNC(chan_opt_voice
)
5685 return channel_multiple_option(chVoice
, CSFUNC_ARGS
);
5688 static MODCMD_FUNC(chan_opt_protect
)
5690 return channel_multiple_option(chProtect
, CSFUNC_ARGS
);
5693 static MODCMD_FUNC(chan_opt_toys
)
5695 return channel_multiple_option(chToys
, CSFUNC_ARGS
);
5698 static MODCMD_FUNC(chan_opt_ctcpreaction
)
5700 return channel_multiple_option(chCTCPReaction
, CSFUNC_ARGS
);
5703 static MODCMD_FUNC(chan_opt_topicrefresh
)
5705 return channel_multiple_option(chTopicRefresh
, CSFUNC_ARGS
);
5708 static struct svccmd_list set_shows_list
;
5711 handle_svccmd_unbind(struct svccmd
*target
) {
5713 for(ii
=0; ii
<set_shows_list
.used
; ++ii
)
5714 if(target
== set_shows_list
.list
[ii
])
5715 set_shows_list
.used
= 0;
5718 static CHANSERV_FUNC(cmd_set
)
5720 struct svccmd
*subcmd
;
5724 /* Check if we need to (re-)initialize set_shows_list. */
5725 if(!set_shows_list
.used
)
5727 if(!set_shows_list
.size
)
5729 set_shows_list
.size
= chanserv_conf
.set_shows
->used
;
5730 set_shows_list
.list
= calloc(set_shows_list
.size
, sizeof(set_shows_list
.list
[0]));
5732 for(ii
= 0; ii
< chanserv_conf
.set_shows
->used
; ii
++)
5734 const char *name
= chanserv_conf
.set_shows
->list
[ii
];
5735 sprintf(buf
, "%s %s", argv
[0], name
);
5736 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
5739 log_module(CS_LOG
, LOG_ERROR
, "Unable to find set option \"%s\".", name
);
5742 svccmd_list_append(&set_shows_list
, subcmd
);
5748 reply("CSMSG_CHANNEL_OPTIONS", channel
->name
);
5750 for(ii
= 0; ii
< set_shows_list
.used
; ii
++)
5752 subcmd
= set_shows_list
.list
[ii
];
5753 subcmd
->command
->func(user
, channel
, 1, argv
+1, subcmd
);
5755 reply("CSMSG_CHANNEL_OPTIONS_END");
5759 sprintf(buf
, "%s %s", argv
[0], argv
[1]);
5760 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
5763 reply("CSMSG_INVALID_OPTION", argv
[1], argv
[0]);
5766 if((argc
> 2) && !check_user_level(channel
, user
, lvlSetters
, 1, 0))
5768 reply("CSMSG_NO_ACCESS");
5772 return subcmd
->command
->func(user
, channel
, argc
- 1, argv
+ 1, subcmd
);
5776 user_binary_option(char *name
, unsigned long mask
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5778 struct userData
*uData
;
5780 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
5783 reply("CSMSG_NOT_USER", channel
->name
);
5789 /* Just show current option value. */
5791 else if(enabled_string(argv
[1]))
5793 uData
->flags
|= mask
;
5795 else if(disabled_string(argv
[1]))
5797 uData
->flags
&= ~mask
;
5801 reply("MSG_INVALID_BINARY", argv
[1]);
5805 reply(name
, user_find_message(user
, (uData
->flags
& mask
) ? "MSG_ON" : "MSG_OFF"));
5809 static MODCMD_FUNC(user_opt_autoop
)
5811 struct userData
*uData
;
5813 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
5816 reply("CSMSG_NOT_USER", channel
->name
);
5819 if(uData
->access
< UL_OP
/*channel->channel_info->lvlOpts[lvlGiveOps]*/)
5820 return user_binary_option("CSMSG_USET_AUTOVOICE", USER_AUTO_OP
, CSFUNC_ARGS
);
5822 return user_binary_option("CSMSG_USET_AUTOOP", USER_AUTO_OP
, CSFUNC_ARGS
);
5823 /* TODO: add halfops error message? or is the op one generic enough? */
5826 static MODCMD_FUNC(user_opt_autoinvite
)
5828 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE
, CSFUNC_ARGS
);
5831 static MODCMD_FUNC(user_opt_info
)
5833 struct userData
*uData
;
5836 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
5840 /* If they got past the command restrictions (which require access)
5841 * but fail this test, we have some fool with security override on.
5843 reply("CSMSG_NOT_USER", channel
->name
);
5850 infoline
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
5851 if(strlen(infoline
) > chanserv_conf
.max_userinfo_length
)
5853 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf
.max_userinfo_length
);
5856 bp
= strcspn(infoline
, "\001");
5859 reply("CSMSG_BAD_INFOLINE", infoline
[bp
]);
5864 if(infoline
[0] == '*' && infoline
[1] == 0)
5867 uData
->info
= strdup(infoline
);
5870 reply("CSMSG_USET_INFO", uData
->info
);
5872 reply("CSMSG_USET_INFO", user_find_message(user
, "MSG_NONE"));
5876 struct svccmd_list uset_shows_list
;
5878 static CHANSERV_FUNC(cmd_uset
)
5880 struct svccmd
*subcmd
;
5884 /* Check if we need to (re-)initialize uset_shows_list. */
5885 if(!uset_shows_list
.used
)
5889 "AutoOp", "AutoInvite", "Info"
5892 if(!uset_shows_list
.size
)
5894 uset_shows_list
.size
= ArrayLength(options
);
5895 uset_shows_list
.list
= calloc(uset_shows_list
.size
, sizeof(uset_shows_list
.list
[0]));
5897 for(ii
= 0; ii
< ArrayLength(options
); ii
++)
5899 const char *name
= options
[ii
];
5900 sprintf(buf
, "%s %s", argv
[0], name
);
5901 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
5904 log_module(CS_LOG
, LOG_ERROR
, "Unable to find uset option %s.", name
);
5907 svccmd_list_append(&uset_shows_list
, subcmd
);
5913 /* Do this so options are presented in a consistent order. */
5914 reply("CSMSG_USER_OPTIONS");
5915 for(ii
= 0; ii
< uset_shows_list
.used
; ii
++)
5916 uset_shows_list
.list
[ii
]->command
->func(user
, channel
, 1, argv
+1, uset_shows_list
.list
[ii
]);
5920 sprintf(buf
, "%s %s", argv
[0], argv
[1]);
5921 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
5924 reply("CSMSG_INVALID_OPTION", argv
[1], argv
[0]);
5928 return subcmd
->command
->func(user
, channel
, argc
- 1, argv
+ 1, subcmd
);
5931 static CHANSERV_FUNC(cmd_giveownership
)
5933 struct handle_info
*new_owner_hi
;
5934 struct userData
*new_owner
, *curr_user
;
5935 struct chanData
*cData
= channel
->channel_info
;
5936 struct do_not_register
*dnr
;
5938 unsigned short co_access
;
5939 char reason
[MAXLEN
];
5942 curr_user
= GetChannelAccess(cData
, user
->handle_info
);
5943 force
= IsHelping(user
) && (argc
> 2) && !irccasecmp(argv
[2], "force");
5944 if(!curr_user
|| (curr_user
->access
!= UL_OWNER
))
5946 struct userData
*owner
= NULL
;
5947 for(curr_user
= channel
->channel_info
->users
;
5949 curr_user
= curr_user
->next
)
5951 if(curr_user
->access
!= UL_OWNER
)
5955 reply("CSMSG_MULTIPLE_OWNERS", channel
->name
);
5962 else if (!force
&& (now
< (time_t)(cData
->ownerTransfer
+ chanserv_conf
.giveownership_period
)))
5964 char delay
[INTERVALLEN
];
5965 intervalString(delay
, cData
->ownerTransfer
+ chanserv_conf
.giveownership_period
- now
, user
->handle_info
);
5966 reply("CSMSG_TRANSFER_WAIT", delay
, channel
->name
);
5969 if(!(new_owner_hi
= modcmd_get_handle_info(user
, argv
[1])))
5971 if(new_owner_hi
== user
->handle_info
)
5973 reply("CSMSG_NO_TRANSFER_SELF");
5976 new_owner
= GetChannelAccess(cData
, new_owner_hi
);
5981 new_owner
= add_channel_user(cData
, new_owner_hi
, UL_COOWNER
, 0, NULL
);
5985 reply("CSMSG_NO_CHAN_USER", new_owner_hi
->handle
, channel
->name
);
5989 if((chanserv_get_owned_count(new_owner_hi
) >= chanserv_conf
.max_owned
) && !force
)
5991 reply("CSMSG_OWN_TOO_MANY", new_owner_hi
->handle
, chanserv_conf
.max_owned
);
5994 if((dnr
= chanserv_is_dnr(NULL
, new_owner_hi
)) && !force
) {
5995 if(!IsHelping(user
))
5996 reply("CSMSG_DNR_ACCOUNT", new_owner_hi
->handle
);
5998 chanserv_show_dnrs(user
, cmd
, NULL
, new_owner_hi
->handle
);
6001 if(new_owner
->access
>= UL_COOWNER
)
6002 co_access
= new_owner
->access
;
6004 co_access
= UL_COOWNER
;
6005 new_owner
->access
= UL_OWNER
;
6007 curr_user
->access
= co_access
;
6008 cData
->ownerTransfer
= now
;
6009 reply("CSMSG_OWNERSHIP_GIVEN", channel
->name
, new_owner_hi
->handle
);
6010 sprintf(reason
, "%s ownership transferred to %s by %s.", channel
->name
, new_owner_hi
->handle
, user
->handle_info
->handle
);
6011 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
6015 static CHANSERV_FUNC(cmd_suspend
)
6017 struct handle_info
*hi
;
6018 struct userData
*self
, *target
;
6021 if(!(hi
= modcmd_get_handle_info(user
, argv
[1]))) return 0;
6022 self
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
6023 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
6025 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
6028 if(target
->access
>= self
->access
)
6030 reply("MSG_USER_OUTRANKED", hi
->handle
);
6033 if(target
->flags
& USER_SUSPENDED
)
6035 reply("CSMSG_ALREADY_SUSPENDED", hi
->handle
);
6040 target
->present
= 0;
6043 target
->flags
|= USER_SUSPENDED
;
6044 reply("CSMSG_USER_SUSPENDED", hi
->handle
, channel
->name
);
6048 static CHANSERV_FUNC(cmd_unsuspend
)
6050 struct handle_info
*hi
;
6051 struct userData
*self
, *target
;
6054 if(!(hi
= modcmd_get_handle_info(user
, argv
[1]))) return 0;
6055 self
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
6056 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
6058 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
6061 if(target
->access
>= self
->access
)
6063 reply("MSG_USER_OUTRANKED", hi
->handle
);
6066 if(!(target
->flags
& USER_SUSPENDED
))
6068 reply("CSMSG_NOT_SUSPENDED", hi
->handle
);
6071 target
->flags
&= ~USER_SUSPENDED
;
6072 scan_user_presence(target
, NULL
);
6073 reply("CSMSG_USER_UNSUSPENDED", hi
->handle
, channel
->name
);
6077 static MODCMD_FUNC(cmd_deleteme
)
6079 struct handle_info
*hi
;
6080 struct userData
*target
;
6081 const char *confirm_string
;
6082 unsigned short access
;
6085 hi
= user
->handle_info
;
6086 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
6088 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
6091 if(target
->access
== UL_OWNER
)
6093 reply("CSMSG_NO_OWNER_DELETEME", channel
->name
);
6096 confirm_string
= make_confirmation_string(target
);
6097 if((argc
< 2) || strcmp(argv
[1], confirm_string
))
6099 reply("CSMSG_CONFIRM_DELETEME", confirm_string
);
6102 access
= target
->access
;
6103 channel_name
= strdup(channel
->name
);
6104 del_channel_user(target
, 1);
6105 reply("CSMSG_DELETED_YOU", access
, channel_name
);
6111 chanserv_refresh_topics(UNUSED_ARG(void *data
))
6113 unsigned int refresh_num
= (now
- self
->link
) / chanserv_conf
.refresh_period
;
6114 struct chanData
*cData
;
6117 for(cData
= channelList
; cData
; cData
= cData
->next
)
6119 if(IsSuspended(cData
))
6121 opt
= cData
->chOpts
[chTopicRefresh
];
6124 if((refresh_num
- cData
->last_refresh
) < (unsigned int)(1 << (opt
- '1')))
6127 SetChannelTopic(cData
->channel
, chanserv
, cData
->topic
, 1);
6128 cData
->last_refresh
= refresh_num
;
6130 timeq_add(now
+ chanserv_conf
.refresh_period
, chanserv_refresh_topics
, NULL
);
6133 static CHANSERV_FUNC(cmd_unf
)
6137 char response
[MAXLEN
];
6138 const char *fmt
= user_find_message(user
, "CSMSG_UNF_RESPONSE");
6139 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
6140 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6143 reply("CSMSG_UNF_RESPONSE");
6147 static CHANSERV_FUNC(cmd_ping
)
6151 char response
[MAXLEN
];
6152 const char *fmt
= user_find_message(user
, "CSMSG_PING_RESPONSE");
6153 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
6154 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6157 reply("CSMSG_PING_RESPONSE");
6161 static CHANSERV_FUNC(cmd_wut
)
6165 char response
[MAXLEN
];
6166 const char *fmt
= user_find_message(user
, "CSMSG_WUT_RESPONSE");
6167 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
6168 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6171 reply("CSMSG_WUT_RESPONSE");
6175 static CHANSERV_FUNC(cmd_8ball
)
6177 unsigned int i
, j
, accum
;
6182 for(i
=1; i
<argc
; i
++)
6183 for(j
=0; argv
[i
][j
]; j
++)
6184 accum
= (accum
<< 5) - accum
+ toupper(argv
[i
][j
]);
6185 resp
= chanserv_conf
.eightball
->list
[accum
% chanserv_conf
.eightball
->used
];
6188 char response
[MAXLEN
];
6189 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, resp
);
6190 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6193 send_message_type(4, user
, cmd
->parent
->bot
, "%s", resp
);
6197 static CHANSERV_FUNC(cmd_d
)
6199 unsigned long sides
, count
, modifier
, ii
, total
;
6200 char response
[MAXLEN
], *sep
;
6204 if((count
= strtoul(argv
[1], &sep
, 10)) < 1)
6214 else if(((sep
[0] == 'd') || (sep
[0] == 'D')) && isdigit(sep
[1])
6215 && (sides
= strtoul(sep
+1, &sep
, 10)) > 1)
6219 else if((sep
[0] == '-') && isdigit(sep
[1]))
6220 modifier
= strtoul(sep
, NULL
, 10);
6221 else if((sep
[0] == '+') && isdigit(sep
[1]))
6222 modifier
= strtoul(sep
+1, NULL
, 10);
6229 reply("CSMSG_BAD_DIE_FORMAT", argv
[1]);
6234 reply("CSMSG_BAD_DICE_COUNT", count
, 10);
6237 for(total
= ii
= 0; ii
< count
; ++ii
)
6238 total
+= (rand() % sides
) + 1;
6241 if((count
> 1) || modifier
)
6243 fmt
= user_find_message(user
, "CSMSG_DICE_ROLL");
6244 sprintf(response
, fmt
, total
, count
, sides
, modifier
);
6248 fmt
= user_find_message(user
, "CSMSG_DIE_ROLL");
6249 sprintf(response
, fmt
, total
, sides
);
6252 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, response
);
6254 send_message_type(4, user
, cmd
->parent
->bot
, "%s", response
);
6258 static CHANSERV_FUNC(cmd_huggle
)
6260 /* CTCP must be via PRIVMSG, never notice */
6262 send_target_message(1, channel
->name
, cmd
->parent
->bot
, "CSMSG_HUGGLES_HIM", user
->nick
);
6264 send_target_message(1, user
->nick
, cmd
->parent
->bot
, "CSMSG_HUGGLES_YOU");
6268 static CHANSERV_FUNC(cmd_calc
)
6270 char response
[MAXLEN
];
6273 do_math(response
, unsplit_string(argv
+ 1, argc
- 1, NULL
));
6276 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, response
);
6278 send_message_type(4, user
, cmd
->parent
->bot
, "%s", response
);
6283 chanserv_adjust_limit(void *data
)
6285 struct mod_chanmode change
;
6286 struct chanData
*cData
= data
;
6287 struct chanNode
*channel
= cData
->channel
;
6290 if(IsSuspended(cData
))
6293 cData
->limitAdjusted
= now
;
6294 limit
= channel
->members
.used
+ chanserv_conf
.adjust_threshold
+ 5;
6295 if(cData
->modes
.modes_set
& MODE_LIMIT
)
6297 if(limit
> cData
->modes
.new_limit
)
6298 limit
= cData
->modes
.new_limit
;
6299 else if(limit
== cData
->modes
.new_limit
)
6303 mod_chanmode_init(&change
);
6304 change
.modes_set
= MODE_LIMIT
;
6305 change
.new_limit
= limit
;
6306 mod_chanmode_announce(chanserv
, channel
, &change
);
6310 handle_new_channel(struct chanNode
*channel
)
6312 struct chanData
*cData
;
6314 if(!(cData
= channel
->channel_info
))
6317 if(cData
->modes
.modes_set
|| cData
->modes
.modes_clear
)
6318 mod_chanmode_announce(chanserv
, cData
->channel
, &cData
->modes
);
6320 if(self
->uplink
&& !self
->uplink
->burst
&& channel
->channel_info
->topic
)
6321 SetChannelTopic(channel
, chanserv
, channel
->channel_info
->topic
, 1);
6324 /* Welcome to my worst nightmare. Warning: Read (or modify)
6325 the code below at your own risk. */
6327 handle_join(struct modeNode
*mNode
)
6329 struct mod_chanmode change
;
6330 struct userNode
*user
= mNode
->user
;
6331 struct chanNode
*channel
= mNode
->channel
;
6332 struct chanData
*cData
;
6333 struct userData
*uData
= NULL
;
6334 struct banData
*bData
;
6335 struct handle_info
*handle
;
6336 unsigned int modes
= 0, info
= 0;
6339 if(IsLocal(user
) || !channel
->channel_info
|| IsSuspended(channel
->channel_info
))
6342 cData
= channel
->channel_info
;
6343 if(channel
->members
.used
> cData
->max
)
6344 cData
->max
= channel
->members
.used
;
6346 /* Check for bans. If they're joining through a ban, one of two
6348 * 1: Join during a netburst, by riding the break. Kick them
6349 * unless they have ops or voice in the channel.
6350 * 2: They're allowed to join through the ban (an invite in
6351 * ircu2.10, or a +e on Hybrid, or something).
6352 * If they're not joining through a ban, and the banlist is not
6353 * full, see if they're on the banlist for the channel. If so,
6356 /* This is really, really stupid. not all banned people are kicked.
6357 * sometimes we like to leave them unkicked.
6358 * I tried to explain this to the srvx developers and
6359 * got insulted.. hence one reason for this fork.
6361 if(user->uplink->burst && !mNode->modes)
6364 for(ii = 0; ii < channel->banlist.used; ii++)
6366 if(user_matches_glob(user, channel->banlist.list[ii]->ban, 1))
6368 ** Riding a netburst. Naughty. **
6369 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
6376 mod_chanmode_init(&change
);
6378 if(channel
->banlist
.used
< MAXBANS
)
6380 /* Not joining through a ban. */
6381 for(bData
= cData
->bans
;
6382 bData
&& !user_matches_glob(user
, bData
->mask
, 1);
6383 bData
= bData
->next
);
6387 char kick_reason
[MAXLEN
];
6388 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
6390 bData
->triggered
= now
;
6391 if(bData
!= cData
->bans
)
6393 /* Shuffle the ban to the head of the list. */
6395 bData
->next
->prev
= bData
->prev
;
6397 bData
->prev
->next
= bData
->next
;
6400 bData
->next
= cData
->bans
;
6403 cData
->bans
->prev
= bData
;
6404 cData
->bans
= bData
;
6407 change
.args
[0].mode
= MODE_BAN
;
6408 change
.args
[0].u
.hostmask
= bData
->mask
;
6409 mod_chanmode_announce(chanserv
, channel
, &change
);
6410 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
6415 /* ChanServ will not modify the limits in join-flooded channels.
6416 It will also skip DynLimit processing when the user (or srvx)
6417 is bursting in, because there are likely more incoming. */
6418 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
)
6419 && !user
->uplink
->burst
6420 && !channel
->join_flooded
6421 && (channel
->limit
- channel
->members
.used
) < chanserv_conf
.adjust_threshold
)
6423 /* The user count has begun "bumping" into the channel limit,
6424 so set a timer to raise the limit a bit. Any previous
6425 timers are removed so three incoming users within the delay
6426 results in one limit change, not three. */
6428 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
6429 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
6432 if(channel
->join_flooded
)
6434 /* don't automatically give non users ops or voice during a join flood */
6436 /* EVERYONE is to get voice */
6437 else if(cData
->chOpts
[chVoice
] == 'a')
6438 modes
|= MODE_VOICE
;
6440 greeting
= cData
->greeting
;
6441 if(user
->handle_info
)
6443 handle
= user
->handle_info
;
6445 if(IsHelper(user
) && !IsHelping(user
))
6448 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
6450 if(channel
== chanserv_conf
.support_channels
.list
[ii
])
6452 HANDLE_SET_FLAG(user
->handle_info
, HELPING
);
6458 uData
= GetTrueChannelAccess(cData
, handle
);
6459 if(uData
&& !IsUserSuspended(uData
))
6461 /* non users getting voice are handled above. */
6462 if(IsUserAutoOp(uData
))
6464 if(uData
->access
>= UL_OP
/*cData->lvlOpts[lvlGiveOps]*/)
6465 modes
|= MODE_CHANOP
;
6466 if(uData
->access
>= UL_HALFOP
/*cData->lvlOpts[lvlGiveHalfOps]*/)
6467 modes
|= MODE_HALFOP
;
6468 else if(uData
->access
>= UL_PEON
&& cData
->chOpts
[chVoice
] == 'p')
6469 modes
|= MODE_VOICE
;
6471 if(uData
->access
>= UL_PRESENT
)
6472 cData
->visited
= now
;
6473 if(cData
->user_greeting
)
6474 greeting
= cData
->user_greeting
;
6476 && (uData
->access
>= cData
->lvlOpts
[lvlUserInfo
])
6477 && ((now
- uData
->seen
) >= chanserv_conf
.info_delay
)
6484 if(!user
->uplink
->burst
)
6488 if(modes
& MODE_CHANOP
) {
6489 modes
&= ~MODE_HALFOP
;
6490 modes
&= ~MODE_VOICE
;
6492 change
.args
[0].mode
= modes
;
6493 change
.args
[0].u
.member
= mNode
;
6494 mod_chanmode_announce(chanserv
, channel
, &change
);
6496 if(greeting
&& !user
->uplink
->burst
)
6497 send_message_type(4, user
, chanserv
, "(%s) %s", channel
->name
, greeting
);
6499 send_target_message(5, channel
->name
, chanserv
, "[%s] %s", user
->nick
, uData
->info
);
6505 handle_auth(struct userNode
*user
, UNUSED_ARG(struct handle_info
*old_handle
))
6507 struct mod_chanmode change
;
6508 struct userData
*channel
;
6509 unsigned int ii
, jj
;
6511 if(!user
->handle_info
)
6514 mod_chanmode_init(&change
);
6516 for(channel
= user
->handle_info
->channels
; channel
; channel
= channel
->u_next
)
6518 struct chanNode
*cn
;
6519 struct modeNode
*mn
;
6520 if(IsUserSuspended(channel
)
6521 || IsSuspended(channel
->channel
)
6522 || !(cn
= channel
->channel
->channel
))
6525 mn
= GetUserMode(cn
, user
);
6528 if(!IsUserSuspended(channel
)
6529 && IsUserAutoInvite(channel
)
6530 && (channel
->access
>= channel
->channel
->lvlOpts
[lvlInviteMe
])
6532 && !user
->uplink
->burst
)
6533 irc_invite(chanserv
, user
, cn
);
6537 if(channel
->access
>= UL_PRESENT
)
6538 channel
->channel
->visited
= now
;
6540 if(IsUserAutoOp(channel
))
6542 if(channel
->access
>= UL_OP
/* cn->channel_info->lvlOpts[lvlGiveOps] */)
6543 change
.args
[0].mode
= MODE_CHANOP
;
6544 else if(channel
->access
>= UL_HALFOP
/* cn->channel_info->lvlOpts[lvlGiveHalfOps]*/)
6545 change
.args
[0].mode
= MODE_HALFOP
;
6546 else if(channel
->access
>= UL_PEON
/* cn->channel_info->lvlOpts[lvlGiveVoice]*/)
6547 change
.args
[0].mode
= MODE_VOICE
;
6549 change
.args
[0].mode
= 0;
6550 change
.args
[0].u
.member
= mn
;
6551 if(change
.args
[0].mode
)
6552 mod_chanmode_announce(chanserv
, cn
, &change
);
6555 channel
->seen
= now
;
6556 channel
->present
= 1;
6559 for(ii
= 0; ii
< user
->channels
.used
; ++ii
)
6561 struct chanNode
*channel
= user
->channels
.list
[ii
]->channel
;
6562 struct banData
*ban
;
6564 if((user
->channels
.list
[ii
]->modes
& (MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
))
6565 || !channel
->channel_info
6566 || IsSuspended(channel
->channel_info
))
6568 for(jj
= 0; jj
< channel
->banlist
.used
; ++jj
)
6569 if(user_matches_glob(user
, channel
->banlist
.list
[jj
]->ban
, 1))
6571 if(jj
< channel
->banlist
.used
)
6573 for(ban
= channel
->channel_info
->bans
; ban
; ban
= ban
->next
)
6575 char kick_reason
[MAXLEN
];
6576 if(!user_matches_glob(user
, ban
->mask
, 1))
6578 change
.args
[0].mode
= MODE_BAN
;
6579 change
.args
[0].u
.hostmask
= ban
->mask
;
6580 mod_chanmode_announce(chanserv
, channel
, &change
);
6581 sprintf(kick_reason
, "(%s) %s", ban
->owner
, ban
->reason
);
6582 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
6583 ban
->triggered
= now
;
6588 if(IsSupportHelper(user
))
6590 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
6592 if(GetUserMode(chanserv_conf
.support_channels
.list
[ii
], user
))
6594 HANDLE_SET_FLAG(user
->handle_info
, HELPING
);
6602 handle_part(struct modeNode
*mn
, UNUSED_ARG(const char *reason
))
6604 struct chanData
*cData
;
6605 struct userData
*uData
;
6607 cData
= mn
->channel
->channel_info
;
6608 if(!cData
|| IsSuspended(cData
) || IsLocal(mn
->user
))
6611 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
) && !mn
->channel
->join_flooded
)
6613 /* Allow for a bit of padding so that the limit doesn't
6614 track the user count exactly, which could get annoying. */
6615 if((mn
->channel
->limit
- mn
->channel
->members
.used
) > chanserv_conf
.adjust_threshold
+ 5)
6617 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
6618 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
6622 if((uData
= GetTrueChannelAccess(cData
, mn
->user
->handle_info
)))
6624 scan_user_presence(uData
, mn
->user
);
6628 if(IsHelping(mn
->user
) && IsSupportHelper(mn
->user
))
6630 unsigned int ii
, jj
;
6631 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
6633 for(jj
= 0; jj
< mn
->user
->channels
.used
; ++jj
)
6634 if(mn
->user
->channels
.list
[jj
]->channel
== chanserv_conf
.support_channels
.list
[ii
])
6636 if(jj
< mn
->user
->channels
.used
)
6639 if(ii
== chanserv_conf
.support_channels
.used
)
6640 HANDLE_CLEAR_FLAG(mn
->user
->handle_info
, HELPING
);
6645 handle_kick(struct userNode
*kicker
, struct userNode
*victim
, struct chanNode
*channel
)
6647 struct userData
*uData
;
6649 if(!channel
->channel_info
|| !kicker
|| IsService(kicker
)
6650 || (kicker
== victim
) || IsSuspended(channel
->channel_info
)
6651 || (kicker
->handle_info
&& kicker
->handle_info
== victim
->handle_info
))
6654 if(protect_user(victim
, kicker
, channel
->channel_info
))
6656 const char *reason
= user_find_message(kicker
, "CSMSG_USER_PROTECTED");
6657 KickChannelUser(kicker
, channel
, chanserv
, reason
);
6660 if((uData
= GetTrueChannelAccess(channel
->channel_info
, victim
->handle_info
)))
6665 handle_topic(struct userNode
*user
, struct chanNode
*channel
, const char *old_topic
)
6667 struct chanData
*cData
;
6669 if(!channel
->channel_info
|| !user
|| IsSuspended(channel
->channel_info
) || IsService(user
))
6672 cData
= channel
->channel_info
;
6673 if(bad_topic(channel
, user
, channel
->topic
))
6674 { /* User doesnt have privs to set topics. Undo it */
6675 send_message(user
, chanserv
, "CSMSG_TOPIC_LOCKED", channel
->name
);
6676 SetChannelTopic(channel
, chanserv
, old_topic
, 1);
6679 /* If there is a topic mask set, and the new topic doesnt match,
6680 * set the topic to mask + new_topic */
6681 if(cData
->topic_mask
&& !match_ircglob(channel
->topic
, cData
->topic_mask
))
6683 char new_topic
[TOPICLEN
+1];
6684 conform_topic(cData
->topic_mask
, channel
->topic
, new_topic
);
6687 SetChannelTopic(channel
, chanserv
, new_topic
, 1);
6688 /* and fall through to topicsnarf code below.. */
6690 else /* Topic couldnt fit into mask, was too long */
6692 SetChannelTopic(channel
, chanserv
, old_topic
, 1);
6693 send_message(user
, chanserv
, "CSMSG_TOPICMASK_CONFLICT1", channel
->name
, cData
->topic_mask
);
6694 send_message(user
, chanserv
, "CSMSG_TOPICMASK_CONFLICT2", TOPICLEN
);
6698 /* With topicsnarf, grab the topic and save it as the default topic. */
6699 if(check_user_level(channel
, user
, lvlTopicSnarf
, 0, 0))
6702 cData
->topic
= strdup(channel
->topic
);
6708 handle_mode(struct chanNode
*channel
, struct userNode
*user
, const struct mod_chanmode
*change
)
6710 struct mod_chanmode
*bounce
= NULL
;
6711 unsigned int bnc
, ii
;
6714 if(!channel
->channel_info
|| IsLocal(user
) || IsSuspended(channel
->channel_info
) || IsService(user
))
6717 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
6718 && mode_lock_violated(&channel
->channel_info
->modes
, change
))
6720 char correct
[MAXLEN
];
6721 bounce
= mod_chanmode_dup(&channel
->channel_info
->modes
, change
->argc
+ 1);
6722 mod_chanmode_format(&channel
->channel_info
->modes
, correct
);
6723 send_message(user
, chanserv
, "CSMSG_MODE_LOCKED", correct
, channel
->name
);
6725 for(ii
= bnc
= 0; ii
< change
->argc
; ++ii
)
6727 if((change
->args
[ii
].mode
& (MODE_REMOVE
|MODE_CHANOP
)) == (MODE_REMOVE
|MODE_CHANOP
))
6729 const struct userNode
*victim
= change
->args
[ii
].u
.member
->user
;
6730 if(!protect_user(victim
, user
, channel
->channel_info
))
6733 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
6736 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_CHANOP
;
6737 bounce
->args
[bnc
].u
.member
= GetUserMode(channel
, user
);
6738 if(bounce
->args
[bnc
].u
.member
)
6742 bounce
->args
[bnc
].mode
= MODE_CHANOP
;
6743 bounce
->args
[bnc
].u
.member
= change
->args
[ii
].u
.member
;
6745 send_message(user
, chanserv
, "CSMSG_USER_PROTECTED", victim
->nick
);
6747 else if(change
->args
[ii
].mode
& MODE_CHANOP
)
6749 const struct userNode
*victim
= change
->args
[ii
].u
.member
->user
;
6750 if(IsService(victim
) || validate_op(user
, channel
, (struct userNode
*)victim
))
6753 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
6754 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_CHANOP
;
6755 bounce
->args
[bnc
].u
.member
= change
->args
[ii
].u
.member
;
6758 else if((change
->args
[ii
].mode
& (MODE_REMOVE
| MODE_BAN
)) == MODE_BAN
)
6760 const char *ban
= change
->args
[ii
].u
.hostmask
;
6761 if(!bad_channel_ban(channel
, user
, ban
, NULL
, NULL
))
6764 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
6765 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_BAN
;
6766 bounce
->args
[bnc
].u
.hostmask
= strdup(ban
);
6768 send_message(user
, chanserv
, "CSMSG_MASK_PROTECTED", ban
);
6773 if((bounce
->argc
= bnc
) || bounce
->modes_set
|| bounce
->modes_clear
)
6774 mod_chanmode_announce(chanserv
, channel
, bounce
);
6775 for(ii
= 0; ii
< change
->argc
; ++ii
)
6776 if(bounce
->args
[ii
].mode
== (MODE_REMOVE
| MODE_BAN
))
6777 free((char*)bounce
->args
[ii
].u
.hostmask
);
6778 mod_chanmode_free(bounce
);
6783 handle_nick_change(struct userNode
*user
, UNUSED_ARG(const char *old_nick
))
6785 struct chanNode
*channel
;
6786 struct banData
*bData
;
6787 struct mod_chanmode change
;
6788 unsigned int ii
, jj
;
6789 char kick_reason
[MAXLEN
];
6791 mod_chanmode_init(&change
);
6793 change
.args
[0].mode
= MODE_BAN
;
6794 for(ii
= 0; ii
< user
->channels
.used
; ++ii
)
6796 channel
= user
->channels
.list
[ii
]->channel
;
6797 /* Need not check for bans if they're opped or voiced. */
6798 if(user
->channels
.list
[ii
]->modes
& (MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
))
6800 /* Need not check for bans unless channel registration is active. */
6801 if(!channel
->channel_info
|| IsSuspended(channel
->channel_info
))
6803 /* Look for a matching ban already on the channel. */
6804 for(jj
= 0; jj
< channel
->banlist
.used
; ++jj
)
6805 if(user_matches_glob(user
, channel
->banlist
.list
[jj
]->ban
, 1))
6807 /* Need not act if we found one. */
6808 if(jj
< channel
->banlist
.used
)
6810 /* Look for a matching ban in this channel. */
6811 for(bData
= channel
->channel_info
->bans
; bData
; bData
= bData
->next
)
6813 if(!user_matches_glob(user
, bData
->mask
, 1))
6815 change
.args
[0].u
.hostmask
= bData
->mask
;
6816 mod_chanmode_announce(chanserv
, channel
, &change
);
6817 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
6818 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
6819 bData
->triggered
= now
;
6820 break; /* we don't need to check any more bans in the channel */
6825 static void handle_rename(struct handle_info
*handle
, const char *old_handle
)
6827 struct do_not_register
*dnr
= dict_find(handle_dnrs
, old_handle
, NULL
);
6831 dict_remove2(handle_dnrs
, old_handle
, 1);
6832 safestrncpy(dnr
->chan_name
+ 1, handle
->handle
, sizeof(dnr
->chan_name
) - 1);
6833 dict_insert(handle_dnrs
, dnr
->chan_name
+ 1, dnr
);
6838 handle_unreg(UNUSED_ARG(struct userNode
*user
), struct handle_info
*handle
)
6840 struct userNode
*h_user
;
6842 if(handle
->channels
)
6844 for(h_user
= handle
->users
; h_user
; h_user
= h_user
->next_authed
)
6845 send_message(h_user
, chanserv
, "CSMSG_HANDLE_UNREGISTERED");
6847 while(handle
->channels
)
6848 del_channel_user(handle
->channels
, 1);
6853 handle_server_link(UNUSED_ARG(struct server
*server
))
6855 struct chanData
*cData
;
6857 for(cData
= channelList
; cData
; cData
= cData
->next
)
6859 if(!IsSuspended(cData
))
6860 cData
->may_opchan
= 1;
6861 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
)
6862 && !cData
->channel
->join_flooded
6863 && ((cData
->channel
->limit
- cData
->channel
->members
.used
)
6864 < chanserv_conf
.adjust_threshold
))
6866 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
6867 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
6873 chanserv_conf_read(void)
6877 char mode_line
[MAXLEN
], *modes
[MAXNUMPARAMS
];
6878 struct mod_chanmode
*change
;
6879 struct string_list
*strlist
;
6880 struct chanNode
*chan
;
6883 if(!(conf_node
= conf_get_data(CHANSERV_CONF_NAME
, RECDB_OBJECT
)))
6885 log_module(CS_LOG
, LOG_ERROR
, "Invalid config node `%s'.", CHANSERV_CONF_NAME
);
6888 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
6889 UnlockChannel(chanserv_conf
.support_channels
.list
[ii
]);
6890 chanserv_conf
.support_channels
.used
= 0;
6891 if((strlist
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL
, RECDB_STRING_LIST
)))
6893 for(ii
= 0; ii
< strlist
->used
; ++ii
)
6895 const char *str2
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL_MODES
, RECDB_QSTRING
);
6898 chan
= AddChannel(strlist
->list
[ii
], now
, str2
, NULL
, NULL
);
6900 channelList_append(&chanserv_conf
.support_channels
, chan
);
6903 else if((str
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL
, RECDB_QSTRING
)))
6906 str2
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL_MODES
, RECDB_QSTRING
);
6909 chan
= AddChannel(str
, now
, str2
, NULL
, NULL
);
6911 channelList_append(&chanserv_conf
.support_channels
, chan
);
6913 str
= database_get_data(conf_node
, KEY_DB_BACKUP_FREQ
, RECDB_QSTRING
);
6914 chanserv_conf
.db_backup_frequency
= str
? ParseInterval(str
) : 7200;
6915 str
= database_get_data(conf_node
, KEY_INFO_DELAY
, RECDB_QSTRING
);
6916 chanserv_conf
.info_delay
= str
? ParseInterval(str
) : 180;
6917 str
= database_get_data(conf_node
, KEY_MAX_GREETLEN
, RECDB_QSTRING
);
6918 chanserv_conf
.greeting_length
= str
? atoi(str
) : 200;
6919 str
= database_get_data(conf_node
, KEY_ADJUST_THRESHOLD
, RECDB_QSTRING
);
6920 chanserv_conf
.adjust_threshold
= str
? atoi(str
) : 15;
6921 str
= database_get_data(conf_node
, KEY_ADJUST_DELAY
, RECDB_QSTRING
);
6922 chanserv_conf
.adjust_delay
= str
? ParseInterval(str
) : 30;
6923 str
= database_get_data(conf_node
, KEY_CHAN_EXPIRE_FREQ
, RECDB_QSTRING
);
6924 chanserv_conf
.channel_expire_frequency
= str
? ParseInterval(str
) : 86400;
6925 str
= database_get_data(conf_node
, KEY_CHAN_EXPIRE_DELAY
, RECDB_QSTRING
);
6926 chanserv_conf
.channel_expire_delay
= str
? ParseInterval(str
) : 86400*30;
6927 str
= database_get_data(conf_node
, KEY_NODELETE_LEVEL
, RECDB_QSTRING
);
6928 chanserv_conf
.nodelete_level
= str
? atoi(str
) : 1;
6929 str
= database_get_data(conf_node
, KEY_MAX_CHAN_USERS
, RECDB_QSTRING
);
6930 chanserv_conf
.max_chan_users
= str
? atoi(str
) : 512;
6931 str
= database_get_data(conf_node
, KEY_MAX_CHAN_BANS
, RECDB_QSTRING
);
6932 chanserv_conf
.max_chan_bans
= str
? atoi(str
) : 512;
6933 str
= database_get_data(conf_node
, KEY_MAX_USERINFO_LENGTH
, RECDB_QSTRING
);
6934 chanserv_conf
.max_userinfo_length
= str
? atoi(str
) : 400;
6935 str
= database_get_data(conf_node
, KEY_NICK
, RECDB_QSTRING
);
6937 NickChange(chanserv
, str
, 0);
6938 str
= database_get_data(conf_node
, KEY_REFRESH_PERIOD
, RECDB_QSTRING
);
6939 chanserv_conf
.refresh_period
= str
? ParseInterval(str
) : 3*60*60;
6940 str
= database_get_data(conf_node
, KEY_GIVEOWNERSHIP_PERIOD
, RECDB_QSTRING
);
6941 chanserv_conf
.giveownership_period
= str
? ParseInterval(str
) : 0;
6942 str
= database_get_data(conf_node
, KEY_CTCP_SHORT_BAN_DURATION
, RECDB_QSTRING
);
6943 chanserv_conf
.ctcp_short_ban_duration
= str
? str
: "3m";
6944 str
= database_get_data(conf_node
, KEY_CTCP_LONG_BAN_DURATION
, RECDB_QSTRING
);
6945 chanserv_conf
.ctcp_long_ban_duration
= str
? str
: "1h";
6946 str
= database_get_data(conf_node
, KEY_MAX_OWNED
, RECDB_QSTRING
);
6947 chanserv_conf
.max_owned
= str
? atoi(str
) : 5;
6948 str
= database_get_data(conf_node
, KEY_IRC_OPERATOR_EPITHET
, RECDB_QSTRING
);
6949 chanserv_conf
.irc_operator_epithet
= str
? str
: "a megalomaniacal power hungry tyrant";
6950 str
= database_get_data(conf_node
, KEY_NETWORK_HELPER_EPITHET
, RECDB_QSTRING
);
6951 chanserv_conf
.network_helper_epithet
= str
? str
: "a wannabe tyrant";
6952 str
= database_get_data(conf_node
, KEY_SUPPORT_HELPER_EPITHET
, RECDB_QSTRING
);
6953 chanserv_conf
.support_helper_epithet
= str
? str
: "a wannabe tyrant";
6954 str
= database_get_data(conf_node
, "default_modes", RECDB_QSTRING
);
6957 safestrncpy(mode_line
, str
, sizeof(mode_line
));
6958 ii
= split_line(mode_line
, 0, ArrayLength(modes
), modes
);
6959 if((change
= mod_chanmode_parse(NULL
, modes
, ii
, MCP_KEY_FREE
)) && (change
->argc
< 2))
6961 chanserv_conf
.default_modes
= *change
;
6962 mod_chanmode_free(change
);
6964 free_string_list(chanserv_conf
.set_shows
);
6965 strlist
= database_get_data(conf_node
, "set_shows", RECDB_STRING_LIST
);
6967 strlist
= string_list_copy(strlist
);
6970 static const char *list
[] = {
6971 /* free form text */
6972 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
6973 /* options based on user level */
6974 "PubCmd", "InviteMe", "UserInfo",/* "GiveVoice", "GiveHalfOps", "GiveOps", */ "EnfOps",
6975 "EnfHalfOps", "EnfModes", "EnfTopic", "TopicSnarf", "Setters", /*"CtcpUsers", */
6976 /* multiple choice options */
6977 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
6978 /* binary options */
6979 "DynLimit", "NoDelete",
6984 strlist
= alloc_string_list(ArrayLength(list
)-1);
6985 for(ii
=0; list
[ii
]; ii
++)
6986 string_list_append(strlist
, strdup(list
[ii
]));
6988 chanserv_conf
.set_shows
= strlist
;
6989 /* We don't look things up now, in case the list refers to options
6990 * defined by modules initialized after this point. Just mark the
6991 * function list as invalid, so it will be initialized.
6993 set_shows_list
.used
= 0;
6994 free_string_list(chanserv_conf
.eightball
);
6995 strlist
= database_get_data(conf_node
, KEY_8BALL_RESPONSES
, RECDB_STRING_LIST
);
6998 strlist
= string_list_copy(strlist
);
7002 strlist
= alloc_string_list(4);
7003 string_list_append(strlist
, strdup("Yes."));
7004 string_list_append(strlist
, strdup("No."));
7005 string_list_append(strlist
, strdup("Maybe so."));
7007 chanserv_conf
.eightball
= strlist
;
7008 free_string_list(chanserv_conf
.old_ban_names
);
7009 strlist
= database_get_data(conf_node
, KEY_OLD_BAN_NAMES
, RECDB_STRING_LIST
);
7011 strlist
= string_list_copy(strlist
);
7013 strlist
= alloc_string_list(2);
7014 chanserv_conf
.old_ban_names
= strlist
;
7015 str
= database_get_data(conf_node
, "off_channel", RECDB_QSTRING
);
7016 off_channel
= str
? atoi(str
) : 0;
7020 chanserv_note_type_read(const char *key
, struct record_data
*rd
)
7023 struct note_type
*ntype
;
7026 if(!(obj
= GET_RECORD_OBJECT(rd
)))
7028 log_module(CS_LOG
, LOG_ERROR
, "Invalid note type %s.", key
);
7031 if(!(ntype
= chanserv_create_note_type(key
)))
7033 log_module(CS_LOG
, LOG_ERROR
, "Memory allocation failed for note %s.", key
);
7037 /* Figure out set access */
7038 if((str
= database_get_data(obj
, KEY_NOTE_OPSERV_ACCESS
, RECDB_QSTRING
)))
7040 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
7041 ntype
->set_access
.min_opserv
= strtoul(str
, NULL
, 0);
7043 else if((str
= database_get_data(obj
, KEY_NOTE_CHANNEL_ACCESS
, RECDB_QSTRING
)))
7045 ntype
->set_access_type
= NOTE_SET_CHANNEL_ACCESS
;
7046 ntype
->set_access
.min_ulevel
= strtoul(str
, NULL
, 0);
7048 else if((str
= database_get_data(obj
, KEY_NOTE_SETTER_ACCESS
, RECDB_QSTRING
)))
7050 ntype
->set_access_type
= NOTE_SET_CHANNEL_SETTER
;
7054 log_module(CS_LOG
, LOG_ERROR
, "Could not find access type for note %s; defaulting to OpServ access level 0.", key
);
7055 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
7056 ntype
->set_access
.min_opserv
= 0;
7059 /* Figure out visibility */
7060 if(!(str
= database_get_data(obj
, KEY_NOTE_VISIBILITY
, RECDB_QSTRING
)))
7061 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
7062 else if(!irccasecmp(str
, KEY_NOTE_VIS_PRIVILEGED
))
7063 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
7064 else if(!irccasecmp(str
, KEY_NOTE_VIS_CHANNEL_USERS
))
7065 ntype
->visible_type
= NOTE_VIS_CHANNEL_USERS
;
7066 else if(!irccasecmp(str
, KEY_NOTE_VIS_ALL
))
7067 ntype
->visible_type
= NOTE_VIS_ALL
;
7069 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
7071 str
= database_get_data(obj
, KEY_NOTE_MAX_LENGTH
, RECDB_QSTRING
);
7072 ntype
->max_length
= str
? strtoul(str
, NULL
, 0) : 400;
7076 user_read_helper(const char *key
, struct record_data
*rd
, struct chanData
*chan
)
7078 struct handle_info
*handle
;
7079 struct userData
*uData
;
7080 char *seen
, *inf
, *flags
;
7082 unsigned short access
;
7084 if(rd
->type
!= RECDB_OBJECT
|| !dict_size(rd
->d
.object
))
7086 log_module(CS_LOG
, LOG_ERROR
, "Invalid user in %s.", chan
->channel
->name
);
7090 access
= atoi(database_get_data(rd
->d
.object
, KEY_LEVEL
, RECDB_QSTRING
));
7091 if(access
> UL_OWNER
)
7093 log_module(CS_LOG
, LOG_ERROR
, "Invalid access level for %s in %s.", key
, chan
->channel
->name
);
7097 inf
= database_get_data(rd
->d
.object
, KEY_INFO
, RECDB_QSTRING
);
7098 seen
= database_get_data(rd
->d
.object
, KEY_SEEN
, RECDB_QSTRING
);
7099 last_seen
= seen
? (signed)strtoul(seen
, NULL
, 0) : now
;
7100 flags
= database_get_data(rd
->d
.object
, KEY_FLAGS
, RECDB_QSTRING
);
7101 handle
= get_handle_info(key
);
7104 log_module(CS_LOG
, LOG_ERROR
, "Nonexistent account %s in %s.", key
, chan
->channel
->name
);
7108 uData
= add_channel_user(chan
, handle
, access
, last_seen
, inf
);
7109 uData
->flags
= flags
? strtoul(flags
, NULL
, 0) : 0;
7111 /* Upgrade: set autoop to the inverse of noautoop */
7112 if(chanserv_read_version
< 2)
7114 /* if noautoop is true, set autoop false, and vice versa */
7115 if(uData
->flags
& USER_NOAUTO_OP
)
7116 uData
->flags
= uData
->flags
& ~USER_AUTO_OP
;
7118 uData
->flags
= uData
->flags
| USER_AUTO_OP
;
7119 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
);
7125 ban_read_helper(const char *key
, struct record_data
*rd
, struct chanData
*chan
)
7127 struct banData
*bData
;
7128 char *set
, *triggered
, *s_duration
, *s_expires
, *reason
, *owner
;
7129 time_t set_time
, triggered_time
, expires_time
;
7131 if(rd
->type
!= RECDB_OBJECT
|| !dict_size(rd
->d
.object
))
7133 log_module(CS_LOG
, LOG_ERROR
, "Invalid ban in %s.", chan
->channel
->name
);
7137 set
= database_get_data(rd
->d
.object
, KEY_SET
, RECDB_QSTRING
);
7138 triggered
= database_get_data(rd
->d
.object
, KEY_TRIGGERED
, RECDB_QSTRING
);
7139 s_duration
= database_get_data(rd
->d
.object
, KEY_DURATION
, RECDB_QSTRING
);
7140 s_expires
= database_get_data(rd
->d
.object
, KEY_EXPIRES
, RECDB_QSTRING
);
7141 owner
= database_get_data(rd
->d
.object
, KEY_OWNER
, RECDB_QSTRING
);
7142 reason
= database_get_data(rd
->d
.object
, KEY_REASON
, RECDB_QSTRING
);
7143 if (!reason
|| !owner
)
7146 set_time
= set
? (time_t)strtoul(set
, NULL
, 0) : now
;
7147 triggered_time
= triggered
? (time_t)strtoul(triggered
, NULL
, 0) : 0;
7149 expires_time
= (time_t)strtoul(s_expires
, NULL
, 0);
7151 expires_time
= set_time
+ atoi(s_duration
);
7155 if(!reason
|| (expires_time
&& (expires_time
< now
)))
7158 bData
= add_channel_ban(chan
, key
, owner
, set_time
, triggered_time
, expires_time
, reason
);
7161 static struct suspended
*
7162 chanserv_read_suspended(dict_t obj
)
7164 struct suspended
*suspended
= calloc(1, sizeof(*suspended
));
7168 str
= database_get_data(obj
, KEY_EXPIRES
, RECDB_QSTRING
);
7169 suspended
->expires
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7170 str
= database_get_data(obj
, KEY_REVOKED
, RECDB_QSTRING
);
7171 suspended
->revoked
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7172 str
= database_get_data(obj
, KEY_ISSUED
, RECDB_QSTRING
);
7173 suspended
->issued
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7174 suspended
->suspender
= strdup(database_get_data(obj
, KEY_SUSPENDER
, RECDB_QSTRING
));
7175 suspended
->reason
= strdup(database_get_data(obj
, KEY_REASON
, RECDB_QSTRING
));
7176 previous
= database_get_data(obj
, KEY_PREVIOUS
, RECDB_OBJECT
);
7177 suspended
->previous
= previous
? chanserv_read_suspended(previous
) : NULL
;
7182 chanserv_channel_read(const char *key
, struct record_data
*hir
)
7184 struct suspended
*suspended
;
7185 struct mod_chanmode
*modes
;
7186 struct chanNode
*cNode
;
7187 struct chanData
*cData
;
7188 struct dict
*channel
, *obj
;
7189 char *str
, *argv
[10];
7193 channel
= hir
->d
.object
;
7195 str
= database_get_data(channel
, KEY_REGISTRAR
, RECDB_QSTRING
);
7198 cNode
= AddChannel(key
, now
, NULL
, NULL
, NULL
);
7201 log_module(CS_LOG
, LOG_ERROR
, "Unable to create registered channel %s.", key
);
7204 cData
= register_channel(cNode
, str
);
7207 log_module(CS_LOG
, LOG_ERROR
, "Unable to register channel %s from database.", key
);
7211 if((obj
= database_get_data(channel
, KEY_OPTIONS
, RECDB_OBJECT
)))
7213 enum levelOption lvlOpt
;
7214 enum charOption chOpt
;
7216 if((str
= database_get_data(obj
, KEY_FLAGS
, RECDB_QSTRING
)))
7217 cData
->flags
= atoi(str
);
7219 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
7221 str
= database_get_data(obj
, levelOptions
[lvlOpt
].db_name
, RECDB_QSTRING
);
7223 cData
->lvlOpts
[lvlOpt
] = user_level_from_name(str
, UL_OWNER
+1);
7224 else if(levelOptions
[lvlOpt
].old_flag
)
7226 if(cData
->flags
& levelOptions
[lvlOpt
].old_flag
)
7227 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].flag_value
;
7229 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
7233 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
7235 if(!(str
= database_get_data(obj
, charOptions
[chOpt
].db_name
, RECDB_QSTRING
)))
7237 cData
->chOpts
[chOpt
] = str
[0];
7240 else if((str
= database_get_data(channel
, KEY_FLAGS
, RECDB_QSTRING
)))
7242 enum levelOption lvlOpt
;
7243 enum charOption chOpt
;
7246 cData
->flags
= base64toint(str
, 5);
7247 count
= strlen(str
+= 5);
7248 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
7251 if(levelOptions
[lvlOpt
].old_flag
)
7253 if(cData
->flags
& levelOptions
[lvlOpt
].old_flag
)
7254 lvl
= levelOptions
[lvlOpt
].flag_value
;
7256 lvl
= levelOptions
[lvlOpt
].default_value
;
7258 else switch(((count
<= levelOptions
[lvlOpt
].old_idx
) ? str
: CHANNEL_DEFAULT_OPTIONS
)[levelOptions
[lvlOpt
].old_idx
])
7260 case 'c': lvl
= UL_COOWNER
; break;
7261 case 'm': lvl
= UL_MANAGER
; break;
7262 case 'n': lvl
= UL_OWNER
+1; break;
7263 case 'o': lvl
= UL_OP
; break;
7264 case 'p': lvl
= UL_PEON
; break;
7265 case 'h': lvl
= UL_HALFOP
; break;
7266 case 'w': lvl
= UL_OWNER
; break;
7267 default: lvl
= 0; break;
7269 cData
->lvlOpts
[lvlOpt
] = lvl
;
7271 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
7272 cData
->chOpts
[chOpt
] = ((count
<= charOptions
[chOpt
].old_idx
) ? str
: CHANNEL_DEFAULT_OPTIONS
)[charOptions
[chOpt
].old_idx
];
7275 if((obj
= database_get_data(hir
->d
.object
, KEY_SUSPENDED
, RECDB_OBJECT
)))
7277 suspended
= chanserv_read_suspended(obj
);
7278 cData
->suspended
= suspended
;
7279 suspended
->cData
= cData
;
7280 /* We could use suspended->expires and suspended->revoked to
7281 * set the CHANNEL_SUSPENDED flag, but we don't. */
7283 else if(IsSuspended(cData
) && (str
= database_get_data(hir
->d
.object
, KEY_SUSPENDER
, RECDB_QSTRING
)))
7285 suspended
= calloc(1, sizeof(*suspended
));
7286 suspended
->issued
= 0;
7287 suspended
->revoked
= 0;
7288 suspended
->suspender
= strdup(str
);
7289 str
= database_get_data(hir
->d
.object
, KEY_SUSPEND_EXPIRES
, RECDB_QSTRING
);
7290 suspended
->expires
= str
? atoi(str
) : 0;
7291 str
= database_get_data(hir
->d
.object
, KEY_SUSPEND_REASON
, RECDB_QSTRING
);
7292 suspended
->reason
= strdup(str
? str
: "No reason");
7293 suspended
->previous
= NULL
;
7294 cData
->suspended
= suspended
;
7295 suspended
->cData
= cData
;
7299 cData
->flags
&= ~CHANNEL_SUSPENDED
;
7300 suspended
= NULL
; /* to squelch a warning */
7303 if(IsSuspended(cData
)) {
7304 if(suspended
->expires
> now
)
7305 timeq_add(suspended
->expires
, chanserv_expire_suspension
, suspended
);
7306 else if(suspended
->expires
)
7307 cData
->flags
&= ~CHANNEL_SUSPENDED
;
7310 if((!off_channel
|| !IsOffChannel(cData
)) && !IsSuspended(cData
)) {
7311 struct mod_chanmode change
;
7312 mod_chanmode_init(&change
);
7314 change
.args
[0].mode
= MODE_CHANOP
;
7315 change
.args
[0].u
.member
= AddChannelUser(chanserv
, cNode
);
7316 mod_chanmode_announce(chanserv
, cNode
, &change
);
7319 str
= database_get_data(channel
, KEY_REGISTERED
, RECDB_QSTRING
);
7320 cData
->registered
= str
? (time_t)strtoul(str
, NULL
, 0) : now
;
7321 str
= database_get_data(channel
, KEY_VISITED
, RECDB_QSTRING
);
7322 cData
->visited
= str
? (time_t)strtoul(str
, NULL
, 0) : now
;
7323 str
= database_get_data(channel
, KEY_OWNER_TRANSFER
, RECDB_QSTRING
);
7324 cData
->ownerTransfer
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7325 str
= database_get_data(channel
, KEY_MAX
, RECDB_QSTRING
);
7326 cData
->max
= str
? atoi(str
) : 0;
7327 str
= database_get_data(channel
, KEY_GREETING
, RECDB_QSTRING
);
7328 cData
->greeting
= str
? strdup(str
) : NULL
;
7329 str
= database_get_data(channel
, KEY_USER_GREETING
, RECDB_QSTRING
);
7330 cData
->user_greeting
= str
? strdup(str
) : NULL
;
7331 str
= database_get_data(channel
, KEY_TOPIC_MASK
, RECDB_QSTRING
);
7332 cData
->topic_mask
= str
? strdup(str
) : NULL
;
7333 str
= database_get_data(channel
, KEY_TOPIC
, RECDB_QSTRING
);
7334 cData
->topic
= str
? strdup(str
) : NULL
;
7336 if(!IsSuspended(cData
)
7337 && (str
= database_get_data(channel
, KEY_MODES
, RECDB_QSTRING
))
7338 && (argc
= split_line(str
, 0, ArrayLength(argv
), argv
))
7339 && (modes
= mod_chanmode_parse(cNode
, argv
, argc
, MCP_KEY_FREE
))) {
7340 cData
->modes
= *modes
;
7342 cData
->modes
.modes_set
|= MODE_REGISTERED
;
7343 if(cData
->modes
.argc
> 1)
7344 cData
->modes
.argc
= 1;
7345 mod_chanmode_announce(chanserv
, cNode
, &cData
->modes
);
7346 mod_chanmode_free(modes
);
7349 obj
= database_get_data(channel
, KEY_USERS
, RECDB_OBJECT
);
7350 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
7351 user_read_helper(iter_key(it
), iter_data(it
), cData
);
7353 if(!cData
->users
&& !IsProtected(cData
))
7355 log_module(CS_LOG
, LOG_ERROR
, "Channel %s had no users in database, unregistering it.", key
);
7356 unregister_channel(cData
, "has empty user list.");
7360 obj
= database_get_data(channel
, KEY_BANS
, RECDB_OBJECT
);
7361 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
7362 ban_read_helper(iter_key(it
), iter_data(it
), cData
);
7364 obj
= database_get_data(channel
, KEY_NOTES
, RECDB_OBJECT
);
7365 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
7367 struct note_type
*ntype
= dict_find(note_types
, iter_key(it
), NULL
);
7368 struct record_data
*rd
= iter_data(it
);
7369 const char *note
, *setter
;
7371 if(rd
->type
!= RECDB_OBJECT
)
7373 log_module(CS_LOG
, LOG_ERROR
, "Bad record type for note %s in channel %s.", iter_key(it
), key
);
7377 log_module(CS_LOG
, LOG_ERROR
, "Bad note type name %s in channel %s.", iter_key(it
), key
);
7379 else if(!(note
= database_get_data(rd
->d
.object
, KEY_NOTE_NOTE
, RECDB_QSTRING
)))
7381 log_module(CS_LOG
, LOG_ERROR
, "Missing note text for note %s in channel %s.", iter_key(it
), key
);
7385 setter
= database_get_data(rd
->d
.object
, KEY_NOTE_SETTER
, RECDB_QSTRING
);
7386 if(!setter
) setter
= "<unknown>";
7387 chanserv_add_channel_note(cData
, ntype
, setter
, note
);
7395 chanserv_dnr_read(const char *key
, struct record_data
*hir
)
7397 const char *setter
, *reason
, *str
;
7398 struct do_not_register
*dnr
;
7400 setter
= database_get_data(hir
->d
.object
, KEY_DNR_SETTER
, RECDB_QSTRING
);
7403 log_module(CS_LOG
, LOG_ERROR
, "Missing setter for DNR %s.", key
);
7406 reason
= database_get_data(hir
->d
.object
, KEY_DNR_REASON
, RECDB_QSTRING
);
7409 log_module(CS_LOG
, LOG_ERROR
, "Missing reason for DNR %s.", key
);
7412 dnr
= chanserv_add_dnr(key
, setter
, reason
);
7415 str
= database_get_data(hir
->d
.object
, KEY_DNR_SET
, RECDB_QSTRING
);
7417 dnr
->set
= atoi(str
);
7423 chanserv_version_read(struct dict
*section
)
7427 str
= database_get_data(section
, KEY_VERSION_NUMBER
, RECDB_QSTRING
);
7429 chanserv_read_version
= atoi(str
);
7430 log_module(CS_LOG
, LOG_DEBUG
, "Chanserv db version is %d.", chanserv_read_version
);
7434 chanserv_saxdb_read(struct dict
*database
)
7436 struct dict
*section
;
7439 if((section
= database_get_data(database
, KEY_VERSION_CONTROL
, RECDB_OBJECT
)))
7440 chanserv_version_read(section
);
7442 if((section
= database_get_data(database
, KEY_NOTE_TYPES
, RECDB_OBJECT
)))
7443 for(it
= dict_first(section
); it
; it
= iter_next(it
))
7444 chanserv_note_type_read(iter_key(it
), iter_data(it
));
7446 if((section
= database_get_data(database
, KEY_CHANNELS
, RECDB_OBJECT
)))
7447 for(it
= dict_first(section
); it
; it
= iter_next(it
))
7448 chanserv_channel_read(iter_key(it
), iter_data(it
));
7450 if((section
= database_get_data(database
, KEY_DNR
, RECDB_OBJECT
)))
7451 for(it
= dict_first(section
); it
; it
= iter_next(it
))
7452 chanserv_dnr_read(iter_key(it
), iter_data(it
));
7458 chanserv_write_users(struct saxdb_context
*ctx
, struct userData
*uData
)
7460 int high_present
= 0;
7461 saxdb_start_record(ctx
, KEY_USERS
, 1);
7462 for(; uData
; uData
= uData
->next
)
7464 if((uData
->access
>= UL_PRESENT
) && uData
->present
)
7466 saxdb_start_record(ctx
, uData
->handle
->handle
, 0);
7467 saxdb_write_int(ctx
, KEY_LEVEL
, uData
->access
);
7468 saxdb_write_int(ctx
, KEY_SEEN
, uData
->seen
);
7470 saxdb_write_int(ctx
, KEY_FLAGS
, uData
->flags
);
7472 saxdb_write_string(ctx
, KEY_INFO
, uData
->info
);
7473 saxdb_end_record(ctx
);
7475 saxdb_end_record(ctx
);
7476 return high_present
;
7480 chanserv_write_bans(struct saxdb_context
*ctx
, struct banData
*bData
)
7484 saxdb_start_record(ctx
, KEY_BANS
, 1);
7485 for(; bData
; bData
= bData
->next
)
7487 saxdb_start_record(ctx
, bData
->mask
, 0);
7488 saxdb_write_int(ctx
, KEY_SET
, bData
->set
);
7489 if(bData
->triggered
)
7490 saxdb_write_int(ctx
, KEY_TRIGGERED
, bData
->triggered
);
7492 saxdb_write_int(ctx
, KEY_EXPIRES
, bData
->expires
);
7494 saxdb_write_string(ctx
, KEY_OWNER
, bData
->owner
);
7496 saxdb_write_string(ctx
, KEY_REASON
, bData
->reason
);
7497 saxdb_end_record(ctx
);
7499 saxdb_end_record(ctx
);
7503 chanserv_write_suspended(struct saxdb_context
*ctx
, const char *name
, struct suspended
*susp
)
7505 saxdb_start_record(ctx
, name
, 0);
7506 saxdb_write_string(ctx
, KEY_SUSPENDER
, susp
->suspender
);
7507 saxdb_write_string(ctx
, KEY_REASON
, susp
->reason
);
7509 saxdb_write_int(ctx
, KEY_ISSUED
, susp
->issued
);
7511 saxdb_write_int(ctx
, KEY_EXPIRES
, susp
->expires
);
7513 saxdb_write_int(ctx
, KEY_REVOKED
, susp
->revoked
);
7515 chanserv_write_suspended(ctx
, KEY_PREVIOUS
, susp
->previous
);
7516 saxdb_end_record(ctx
);
7520 chanserv_write_channel(struct saxdb_context
*ctx
, struct chanData
*channel
)
7524 enum levelOption lvlOpt
;
7525 enum charOption chOpt
;
7527 saxdb_start_record(ctx
, channel
->channel
->name
, 1);
7529 saxdb_write_int(ctx
, KEY_REGISTERED
, channel
->registered
);
7530 saxdb_write_int(ctx
, KEY_MAX
, channel
->max
);
7532 saxdb_write_string(ctx
, KEY_TOPIC
, channel
->topic
);
7533 if(channel
->registrar
)
7534 saxdb_write_string(ctx
, KEY_REGISTRAR
, channel
->registrar
);
7535 if(channel
->greeting
)
7536 saxdb_write_string(ctx
, KEY_GREETING
, channel
->greeting
);
7537 if(channel
->user_greeting
)
7538 saxdb_write_string(ctx
, KEY_USER_GREETING
, channel
->user_greeting
);
7539 if(channel
->topic_mask
)
7540 saxdb_write_string(ctx
, KEY_TOPIC_MASK
, channel
->topic_mask
);
7541 if(channel
->suspended
)
7542 chanserv_write_suspended(ctx
, "suspended", channel
->suspended
);
7544 saxdb_start_record(ctx
, KEY_OPTIONS
, 0);
7545 saxdb_write_int(ctx
, KEY_FLAGS
, channel
->flags
);
7546 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
7547 saxdb_write_int(ctx
, levelOptions
[lvlOpt
].db_name
, channel
->lvlOpts
[lvlOpt
]);
7548 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
7550 buf
[0] = channel
->chOpts
[chOpt
];
7552 saxdb_write_string(ctx
, charOptions
[chOpt
].db_name
, buf
);
7554 saxdb_end_record(ctx
);
7556 if(channel
->modes
.modes_set
|| channel
->modes
.modes_clear
)
7558 mod_chanmode_format(&channel
->modes
, buf
);
7559 saxdb_write_string(ctx
, KEY_MODES
, buf
);
7562 high_present
= chanserv_write_users(ctx
, channel
->users
);
7563 chanserv_write_bans(ctx
, channel
->bans
);
7565 if(dict_size(channel
->notes
))
7569 saxdb_start_record(ctx
, KEY_NOTES
, 1);
7570 for(it
= dict_first(channel
->notes
); it
; it
= iter_next(it
))
7572 struct note
*note
= iter_data(it
);
7573 saxdb_start_record(ctx
, iter_key(it
), 0);
7574 saxdb_write_string(ctx
, KEY_NOTE_SETTER
, note
->setter
);
7575 saxdb_write_string(ctx
, KEY_NOTE_NOTE
, note
->note
);
7576 saxdb_end_record(ctx
);
7578 saxdb_end_record(ctx
);
7581 if(channel
->ownerTransfer
)
7582 saxdb_write_int(ctx
, KEY_OWNER_TRANSFER
, channel
->ownerTransfer
);
7583 saxdb_write_int(ctx
, KEY_VISITED
, high_present
? now
: channel
->visited
);
7584 saxdb_end_record(ctx
);
7588 chanserv_write_note_type(struct saxdb_context
*ctx
, struct note_type
*ntype
)
7592 saxdb_start_record(ctx
, ntype
->name
, 0);
7593 switch(ntype
->set_access_type
)
7595 case NOTE_SET_CHANNEL_ACCESS
:
7596 saxdb_write_int(ctx
, KEY_NOTE_CHANNEL_ACCESS
, ntype
->set_access
.min_ulevel
);
7598 case NOTE_SET_CHANNEL_SETTER
:
7599 saxdb_write_int(ctx
, KEY_NOTE_SETTER_ACCESS
, 1);
7601 case NOTE_SET_PRIVILEGED
: default:
7602 saxdb_write_int(ctx
, KEY_NOTE_OPSERV_ACCESS
, ntype
->set_access
.min_opserv
);
7605 switch(ntype
->visible_type
)
7607 case NOTE_VIS_ALL
: str
= KEY_NOTE_VIS_ALL
; break;
7608 case NOTE_VIS_CHANNEL_USERS
: str
= KEY_NOTE_VIS_CHANNEL_USERS
; break;
7609 case NOTE_VIS_PRIVILEGED
: default: str
= KEY_NOTE_VIS_PRIVILEGED
; break;
7611 saxdb_write_string(ctx
, KEY_NOTE_VISIBILITY
, str
);
7612 saxdb_write_int(ctx
, KEY_NOTE_MAX_LENGTH
, ntype
->max_length
);
7613 saxdb_end_record(ctx
);
7617 write_dnrs_helper(struct saxdb_context
*ctx
, struct dict
*dnrs
)
7619 struct do_not_register
*dnr
;
7622 for(it
= dict_first(dnrs
); it
; it
= iter_next(it
))
7624 dnr
= iter_data(it
);
7625 saxdb_start_record(ctx
, dnr
->chan_name
, 0);
7627 saxdb_write_int(ctx
, KEY_DNR_SET
, dnr
->set
);
7628 saxdb_write_string(ctx
, KEY_DNR_SETTER
, dnr
->setter
);
7629 saxdb_write_string(ctx
, KEY_DNR_REASON
, dnr
->reason
);
7630 saxdb_end_record(ctx
);
7635 chanserv_saxdb_write(struct saxdb_context
*ctx
)
7638 struct chanData
*channel
;
7640 /* Version Control*/
7641 saxdb_start_record(ctx
, KEY_VERSION_CONTROL
, 1);
7642 saxdb_write_int(ctx
, KEY_VERSION_NUMBER
, CHANSERV_DB_VERSION
);
7643 saxdb_end_record(ctx
);
7646 saxdb_start_record(ctx
, KEY_NOTE_TYPES
, 1);
7647 for(it
= dict_first(note_types
); it
; it
= iter_next(it
))
7648 chanserv_write_note_type(ctx
, iter_data(it
));
7649 saxdb_end_record(ctx
);
7652 saxdb_start_record(ctx
, KEY_DNR
, 1);
7653 write_dnrs_helper(ctx
, handle_dnrs
);
7654 write_dnrs_helper(ctx
, plain_dnrs
);
7655 write_dnrs_helper(ctx
, mask_dnrs
);
7656 saxdb_end_record(ctx
);
7659 saxdb_start_record(ctx
, KEY_CHANNELS
, 1);
7660 for(channel
= channelList
; channel
; channel
= channel
->next
)
7661 chanserv_write_channel(ctx
, channel
);
7662 saxdb_end_record(ctx
);
7668 chanserv_db_cleanup(void) {
7670 unreg_part_func(handle_part
);
7672 unregister_channel(channelList
, "terminating.");
7673 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
7674 UnlockChannel(chanserv_conf
.support_channels
.list
[ii
]);
7675 free(chanserv_conf
.support_channels
.list
);
7676 dict_delete(handle_dnrs
);
7677 dict_delete(plain_dnrs
);
7678 dict_delete(mask_dnrs
);
7679 dict_delete(note_types
);
7680 free_string_list(chanserv_conf
.eightball
);
7681 free_string_list(chanserv_conf
.old_ban_names
);
7682 free_string_list(chanserv_conf
.set_shows
);
7683 free(set_shows_list
.list
);
7684 free(uset_shows_list
.list
);
7687 struct userData
*helper
= helperList
;
7688 helperList
= helperList
->next
;
7693 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
7694 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
7695 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
7698 init_chanserv(const char *nick
)
7700 CS_LOG
= log_register_type("ChanServ", "file:chanserv.log");
7701 conf_register_reload(chanserv_conf_read
);
7703 reg_server_link_func(handle_server_link
);
7705 reg_new_channel_func(handle_new_channel
);
7706 reg_join_func(handle_join
);
7707 reg_part_func(handle_part
);
7708 reg_kick_func(handle_kick
);
7709 reg_topic_func(handle_topic
);
7710 reg_mode_change_func(handle_mode
);
7711 reg_nick_change_func(handle_nick_change
);
7713 reg_auth_func(handle_auth
);
7714 reg_handle_rename_func(handle_rename
);
7715 reg_unreg_func(handle_unreg
);
7717 handle_dnrs
= dict_new();
7718 dict_set_free_data(handle_dnrs
, free
);
7719 plain_dnrs
= dict_new();
7720 dict_set_free_data(plain_dnrs
, free
);
7721 mask_dnrs
= dict_new();
7722 dict_set_free_data(mask_dnrs
, free
);
7724 reg_svccmd_unbind_func(handle_svccmd_unbind
);
7725 chanserv_module
= module_register("ChanServ", CS_LOG
, "chanserv.help", chanserv_expand_variable
);
7726 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+acceptchan,+helping", NULL
);
7727 DEFINE_COMMAND(noregister
, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+helping", NULL
);
7728 DEFINE_COMMAND(allowregister
, 2, 0, "template", "noregister", NULL
);
7729 DEFINE_COMMAND(move
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "template", "register", NULL
);
7730 DEFINE_COMMAND(csuspend
, 2, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+helping", NULL
);
7731 DEFINE_COMMAND(cunsuspend
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+helping", NULL
);
7732 DEFINE_COMMAND(createnote
, 5, 0, "level", "800", NULL
);
7733 DEFINE_COMMAND(removenote
, 2, 0, "level", "800", NULL
);
7735 DEFINE_COMMAND(pending
, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+helping", NULL
);
7737 DEFINE_COMMAND(unregister
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+loghostmask", NULL
);
7738 DEFINE_COMMAND(merge
, 2, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "access", "owner", NULL
);
7740 DEFINE_COMMAND(adduser
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7741 DEFINE_COMMAND(deluser
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7742 DEFINE_COMMAND(suspend
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7743 DEFINE_COMMAND(unsuspend
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7744 DEFINE_COMMAND(deleteme
, 1, MODCMD_REQUIRE_CHANUSER
, NULL
);
7746 DEFINE_COMMAND(mdelowner
, 2, MODCMD_REQUIRE_CHANUSER
, "flags", "+helping", NULL
);
7747 DEFINE_COMMAND(mdelcoowner
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "owner", NULL
);
7748 DEFINE_COMMAND(mdelmanager
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "coowner", NULL
);
7749 DEFINE_COMMAND(mdelop
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7750 DEFINE_COMMAND(mdelpeon
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7751 DEFINE_COMMAND(mdelhalfop
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7753 DEFINE_COMMAND(trim
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7754 DEFINE_COMMAND(opchan
, 1, MODCMD_REQUIRE_REGCHAN
|MODCMD_NEVER_CSUSPEND
, "access", "1", NULL
);
7755 DEFINE_COMMAND(clvl
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7756 DEFINE_COMMAND(giveownership
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "owner", "flags", "+loghostmask", NULL
);
7758 DEFINE_COMMAND(up
, 1, MODCMD_REQUIRE_CHANUSER
, NULL
);
7759 DEFINE_COMMAND(down
, 1, MODCMD_REQUIRE_REGCHAN
, NULL
);
7760 DEFINE_COMMAND(upall
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
7761 DEFINE_COMMAND(downall
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
7762 DEFINE_COMMAND(hop
, 2, MODCMD_REQUIRE_CHANNEL
, "access", "op", NULL
);
7763 DEFINE_COMMAND(op
, 2, MODCMD_REQUIRE_CHANNEL
, "access", "op", NULL
);
7764 DEFINE_COMMAND(deop
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
7765 DEFINE_COMMAND(dehop
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
7766 DEFINE_COMMAND(voice
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
7767 DEFINE_COMMAND(devoice
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
7769 DEFINE_COMMAND(kickban
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
7770 DEFINE_COMMAND(kick
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
7771 DEFINE_COMMAND(ban
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
7772 DEFINE_COMMAND(unban
, 2, 0, "template", "hop", NULL
);
7773 DEFINE_COMMAND(unbanall
, 1, 0, "template", "hop", NULL
);
7774 DEFINE_COMMAND(unbanme
, 1, MODCMD_REQUIRE_CHANUSER
, "template", "hop", NULL
);
7775 DEFINE_COMMAND(open
, 1, MODCMD_REQUIRE_CHANUSER
, "template", "op", NULL
);
7776 DEFINE_COMMAND(topic
, 1, MODCMD_REQUIRE_REGCHAN
, "template", "hop", "flags", "+never_csuspend", NULL
);
7777 DEFINE_COMMAND(mode
, 1, MODCMD_REQUIRE_REGCHAN
, "template", "op", NULL
);
7778 DEFINE_COMMAND(inviteme
, 1, MODCMD_REQUIRE_CHANNEL
, "access", "1", NULL
);
7779 DEFINE_COMMAND(invite
, 1, MODCMD_REQUIRE_CHANNEL
, "access", "manager", NULL
);
7780 DEFINE_COMMAND(set
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "op", NULL
);
7781 DEFINE_COMMAND(wipeinfo
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7782 DEFINE_COMMAND(resync
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7784 DEFINE_COMMAND(events
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog", "access", "manager", NULL
);
7785 DEFINE_COMMAND(last
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog", "access", "manager", NULL
);
7786 DEFINE_COMMAND(addlamer
, 2, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
7787 DEFINE_COMMAND(addtimedlamer
, 3, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
7789 /* if you change dellamer access, see also places
7790 * like unbanme which have manager hardcoded. */
7791 DEFINE_COMMAND(dellamer
, 2, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
7792 DEFINE_COMMAND(uset
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "1", NULL
);
7794 DEFINE_COMMAND(lamers
, 1, MODCMD_REQUIRE_REGCHAN
, "access", "1", "flags", "+nolog", NULL
);
7796 DEFINE_COMMAND(peek
, 1, MODCMD_REQUIRE_REGCHAN
, "access", "op", "flags", "+nolog", NULL
);
7798 DEFINE_COMMAND(myaccess
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
7799 DEFINE_COMMAND(access
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7800 DEFINE_COMMAND(users
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7801 DEFINE_COMMAND(wlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7802 DEFINE_COMMAND(clist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7803 DEFINE_COMMAND(mlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7804 DEFINE_COMMAND(olist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7805 DEFINE_COMMAND(hlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7806 DEFINE_COMMAND(plist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7807 DEFINE_COMMAND(info
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7808 DEFINE_COMMAND(seen
, 2, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7809 DEFINE_COMMAND(names
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7811 DEFINE_COMMAND(note
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+joinable,+acceptchan", NULL
);
7812 DEFINE_COMMAND(delnote
, 2, MODCMD_REQUIRE_CHANUSER
, NULL
);
7814 DEFINE_COMMAND(netinfo
, 1, 0, "flags", "+nolog", NULL
);
7815 DEFINE_COMMAND(ircops
, 1, 0, "flags", "+nolog", NULL
);
7816 DEFINE_COMMAND(helpers
, 1, 0, "flags", "+nolog", NULL
);
7817 DEFINE_COMMAND(staff
, 1, 0, "flags", "+nolog", NULL
);
7819 DEFINE_COMMAND(say
, 2, 0, "flags", "+oper,+acceptchan", NULL
);
7820 DEFINE_COMMAND(emote
, 2, 0, "flags", "+oper,+acceptchan", NULL
);
7821 DEFINE_COMMAND(expire
, 1, 0, "flags", "+oper", NULL
);
7822 DEFINE_COMMAND(search
, 3, 0, "flags", "+nolog,+helping", NULL
);
7823 DEFINE_COMMAND(unvisited
, 1, 0, "flags", "+nolog,+helping", NULL
);
7825 DEFINE_COMMAND(unf
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
7826 DEFINE_COMMAND(ping
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
7827 DEFINE_COMMAND(wut
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
7828 DEFINE_COMMAND(8ball
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
7829 DEFINE_COMMAND(d
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
7830 DEFINE_COMMAND(huggle
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
7831 DEFINE_COMMAND(calc
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
7833 /* Channel options */
7834 DEFINE_CHANNEL_OPTION(defaulttopic
);
7835 DEFINE_CHANNEL_OPTION(topicmask
);
7836 DEFINE_CHANNEL_OPTION(greeting
);
7837 DEFINE_CHANNEL_OPTION(usergreeting
);
7838 DEFINE_CHANNEL_OPTION(modes
);
7839 DEFINE_CHANNEL_OPTION(enfops
);
7840 DEFINE_CHANNEL_OPTION(enfhalfops
);
7841 /*DEFINE_CHANNEL_OPTION(giveops);
7842 DEFINE_CHANNEL_OPTION(givehalfops);
7844 DEFINE_CHANNEL_OPTION(voice
);
7845 DEFINE_CHANNEL_OPTION(protect
);
7846 DEFINE_CHANNEL_OPTION(enfmodes
);
7847 DEFINE_CHANNEL_OPTION(enftopic
);
7848 DEFINE_CHANNEL_OPTION(pubcmd
);
7849 /*DEFINE_CHANNEL_OPTION(givevoice);
7851 DEFINE_CHANNEL_OPTION(userinfo
);
7852 DEFINE_CHANNEL_OPTION(dynlimit
);
7853 DEFINE_CHANNEL_OPTION(topicsnarf
);
7854 DEFINE_CHANNEL_OPTION(nodelete
);
7855 DEFINE_CHANNEL_OPTION(toys
);
7856 DEFINE_CHANNEL_OPTION(setters
);
7857 DEFINE_CHANNEL_OPTION(topicrefresh
);
7858 // DEFINE_CHANNEL_OPTION(ctcpusers);
7859 DEFINE_CHANNEL_OPTION(ctcpreaction
);
7860 DEFINE_CHANNEL_OPTION(inviteme
);
7862 DEFINE_CHANNEL_OPTION(offchannel
);
7863 modcmd_register(chanserv_module
, "set defaults", chan_opt_defaults
, 1, 0, "access", "owner", NULL
);
7865 /* Alias set topic to set defaulttopic for compatibility. */
7866 modcmd_register(chanserv_module
, "set topic", chan_opt_defaulttopic
, 1, 0, NULL
);
7869 DEFINE_USER_OPTION(autoinvite
);
7870 DEFINE_USER_OPTION(info
);
7871 DEFINE_USER_OPTION(autoop
);
7873 /* Alias uset autovoice to uset autoop. */
7874 modcmd_register(chanserv_module
, "uset autovoice", user_opt_autoop
, 1, 0, NULL
);
7876 note_types
= dict_new();
7877 dict_set_free_data(note_types
, chanserv_deref_note_type
);
7880 const char *modes
= conf_get_data("services/chanserv/modes", RECDB_QSTRING
);
7881 chanserv
= AddService(nick
, modes
? modes
: NULL
, "Channel Services", NULL
);
7882 service_register(chanserv
)->trigger
= '!';
7883 reg_chanmsg_func('\001', chanserv
, chanserv_ctcp_check
);
7886 saxdb_register("ChanServ", chanserv_saxdb_read
, chanserv_saxdb_write
);
7888 if(chanserv_conf
.channel_expire_frequency
)
7889 timeq_add(now
+ chanserv_conf
.channel_expire_frequency
, expire_channels
, NULL
);
7891 if(chanserv_conf
.refresh_period
)
7893 time_t next_refresh
;
7894 next_refresh
= (now
+ chanserv_conf
.refresh_period
- 1) / chanserv_conf
.refresh_period
* chanserv_conf
.refresh_period
;
7895 timeq_add(next_refresh
, chanserv_refresh_topics
, NULL
);
7898 reg_exit_func(chanserv_db_cleanup
);
7899 message_register_table(msgtab
);