1 /* chanserv.c - Channel service bot
2 * Copyright 2000-2004 srvx Development Team
4 * This file is part of x3.
6 * x3 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() */
30 #define CHANSERV_CONF_NAME "services/chanserv"
32 /* ChanServ options */
33 #define KEY_SUPPORT_CHANNEL "support_channel"
34 #define KEY_SUPPORT_CHANNEL_MODES "support_channel_modes"
35 #define KEY_DB_BACKUP_FREQ "db_backup_freq"
36 #define KEY_INFO_DELAY "info_delay"
37 #define KEY_MAX_GREETLEN "max_greetlen"
38 #define KEY_ADJUST_THRESHOLD "adjust_threshold"
39 #define KEY_ADJUST_DELAY "adjust_delay"
40 #define KEY_CHAN_EXPIRE_FREQ "chan_expire_freq"
41 #define KEY_CHAN_EXPIRE_DELAY "chan_expire_delay"
42 #define KEY_BAN_TIMEOUT_FREQ "ban_timeout_freq"
43 #define KEY_MAX_CHAN_USERS "max_chan_users"
44 #define KEY_MAX_CHAN_BANS "max_chan_bans"
45 #define KEY_NICK "nick"
46 #define KEY_OLD_CHANSERV_NAME "old_chanserv_name"
47 #define KEY_8BALL_RESPONSES "8ball"
48 #define KEY_OLD_BAN_NAMES "old_ban_names"
49 #define KEY_REFRESH_PERIOD "refresh_period"
50 #define KEY_CTCP_SHORT_BAN_DURATION "ctcp_short_ban_duration"
51 #define KEY_CTCP_LONG_BAN_DURATION "ctcp_long_ban_duration"
52 #define KEY_MAX_OWNED "max_owned"
53 #define KEY_IRC_OPERATOR_EPITHET "irc_operator_epithet"
54 #define KEY_NETWORK_HELPER_EPITHET "network_helper_epithet"
55 #define KEY_SUPPORT_HELPER_EPITHET "support_helper_epithet"
56 #define KEY_NODELETE_LEVEL "nodelete_level"
57 #define KEY_MAX_USERINFO_LENGTH "max_userinfo_length"
58 #define KEY_GIVEOWNERSHIP_PERIOD "giveownership_timeout"
60 /* ChanServ database */
61 #define KEY_VERSION_CONTROL "version_control"
62 #define KEY_CHANNELS "channels"
63 #define KEY_NOTE_TYPES "note_types"
65 /* version control paramiter */
66 #define KEY_VERSION_NUMBER "version_number"
68 /* Note type parameters */
69 #define KEY_NOTE_OPSERV_ACCESS "opserv_access"
70 #define KEY_NOTE_CHANNEL_ACCESS "channel_access"
71 #define KEY_NOTE_SETTER_ACCESS "setter_access"
72 #define KEY_NOTE_VISIBILITY "visibility"
73 #define KEY_NOTE_VIS_PRIVILEGED "privileged"
74 #define KEY_NOTE_VIS_CHANNEL_USERS "channel_users"
75 #define KEY_NOTE_VIS_ALL "all"
76 #define KEY_NOTE_MAX_LENGTH "max_length"
77 #define KEY_NOTE_SETTER "setter"
78 #define KEY_NOTE_NOTE "note"
80 /* Do-not-register channels */
82 #define KEY_DNR_SET "set"
83 #define KEY_DNR_SETTER "setter"
84 #define KEY_DNR_REASON "reason"
87 #define KEY_REGISTERED "registered"
88 #define KEY_REGISTRAR "registrar"
89 #define KEY_SUSPENDED "suspended"
90 #define KEY_PREVIOUS "previous"
91 #define KEY_SUSPENDER "suspender"
92 #define KEY_ISSUED "issued"
93 #define KEY_REVOKED "revoked"
94 #define KEY_SUSPEND_EXPIRES "suspend_expires"
95 #define KEY_SUSPEND_REASON "suspend_reason"
96 #define KEY_GIVEOWNERSHIP "giveownership"
97 #define KEY_STAFF_ISSUER "staff_issuer"
98 #define KEY_OLD_OWNER "old_owner"
99 #define KEY_TARGET "target"
100 #define KEY_TARGET_ACCESS "target_access"
101 #define KEY_VISITED "visited"
102 #define KEY_TOPIC "topic"
103 #define KEY_GREETING "greeting"
104 #define KEY_USER_GREETING "user_greeting"
105 #define KEY_MODES "modes"
106 #define KEY_FLAGS "flags"
107 #define KEY_OPTIONS "options"
108 #define KEY_USERS "users"
109 #define KEY_BANS "bans" /* for lamers */
110 #define KEY_MAX "max"
111 #define KEY_NOTES "notes"
112 #define KEY_TOPIC_MASK "topic_mask"
113 #define KEY_OWNER_TRANSFER "owner_transfer"
116 #define KEY_LEVEL "level"
117 #define KEY_INFO "info"
118 #define KEY_SEEN "seen"
121 #define KEY_OWNER "owner"
122 #define KEY_REASON "reason"
123 #define KEY_SET "set"
124 #define KEY_DURATION "duration"
125 #define KEY_EXPIRES "expires"
126 #define KEY_TRIGGERED "triggered"
128 #define KEY_GOD_TIMEOUT "god_timeout"
130 #define CHANNEL_DEFAULT_FLAGS (CHANNEL_OFFCHANNEL)
131 #define CHANNEL_DEFAULT_OPTIONS "lmoooanpcnat"
133 /* Administrative messages */
134 static const struct message_entry msgtab
[] = {
135 { "CSMSG_CHANNELS_EXPIRED", "%i channels expired." },
137 /* Channel registration */
138 { "CSMSG_REG_SUCCESS", "You now have ownership of $b%s$b." },
139 { "CSMSG_PROXY_SUCCESS", "%s now has ownership of $b%s$b." },
140 { "CSMSG_ALREADY_REGGED", "$b%s$b is registered to someone else." },
141 { "CSMSG_MUST_BE_OPPED", "You must be a channel operator (+o) in $b%s$b to register it." },
142 { "CSMSG_PROXY_FORBIDDEN", "You may not register a channel for someone else." },
143 { "CSMSG_OWN_TOO_MANY", "%s already owns more than the limit of %d channels. Use FORCE to override." },
144 { "CSMSG_YOU_OWN_TOO_MANY", "You already own more than the limit of %d channels. Ask a staff member for help." },
145 { "CSMSG_ANOTHER_SERVICE", "Another service bot is in that channel already. Ask a staff member for help." },
147 /* Do-not-register channels */
148 { "CSMSG_NOT_DNR", "$b%s$b is not a valid channel name or *account." },
149 { "CSMSG_DNR_SEARCH_RESULTS", "$bDo-Not-Register Channels$b" },
150 { "CSMSG_DNR_INFO", "$b%s$b (set by $b%s$b): %s" },
151 { "CSMSG_DNR_INFO_SET", "$b%s$b (set %s ago by $b%s$b): %s" },
152 { "CSMSG_MORE_DNRS", "%d more do-not-register entries skipped." },
153 { "CSMSG_DNR_CHANNEL", "Only network staff may register $b%s$b." },
154 { "CSMSG_DNR_CHANNEL_MOVE", "Only network staff may move $b%s$b." },
155 { "CSMSG_DNR_ACCOUNT", "Only network staff may register channels to $b%s$b." },
156 { "CSMSG_NOREGISTER_CHANNEL", "$b%s$b has been added to the do-not-register list." },
157 { "CSMSG_NO_SUCH_DNR", "$b%s$b is not in the do-not-register list." },
158 { "CSMSG_DNR_REMOVED", "$b%s$b has been removed from the do-not-register list." },
160 /* Channel unregistration */
161 { "CSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
162 { "CSMSG_UNREG_NODELETE", "$b%s$b is protected from unregistration." },
163 { "CSMSG_CHAN_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended (%s)." },
164 { "CSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must use 'unregister %s %s'." },
167 { "CSMSG_MOVE_SUCCESS", "Channel registration has been moved to $b%s$b." },
168 { "CSMSG_MOVE_NODELETE", "$b%s$b is protected from unregistration, and cannot be moved." },
170 /* Channel merging */
171 { "CSMSG_MERGE_SUCCESS", "Channel successfully merged into $b%s$b." },
172 { "CSMSG_MERGE_SELF", "Merging cannot be performed if the source and target channels are the same." },
173 { "CSMSG_MERGE_NODELETE", "You may not merge a channel that is marked NoDelete." },
174 { "CSMSG_MERGE_SUSPENDED", "Merging cannot be performed if the source or target channel is suspended." },
175 { "CSMSG_MERGE_NOT_OWNER", "You must be the owner of the target channel (or a helper) to merge into the channel." },
177 /* Handle unregistration */
178 { "CSMSG_HANDLE_UNREGISTERED", "As a result of your account unregistration, you have been deleted from all of your channels' userlists." },
181 { "CSMSG_NOT_USER", "You lack access to $b%s$b." },
182 { "CSMSG_NO_CHAN_USER", "%s lacks access to $b%s$b." },
183 { "CSMSG_NO_ACCESS", "You lack sufficient access to use this command." },
184 { "CSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$C$b." },
185 { "CSMSG_MAXIMUM_LAMERS", "This channel has reached the lamer count limit of $b%d$b." },
186 { "CSMSG_MAXIMUM_USERS", "This channel has reached the user count limit of $b%d$b." },
187 { "CSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel, and cannot be registered." },
188 { "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." },
189 { "CSMSG_ALREADY_OPPED", "You are already opped in $b%s$b." },
190 { "CSMSG_ALREADY_HALFOPPED", "You are already halfopped in $b%s$b." },
191 { "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." },
192 { "CSMSG_ALREADY_DOWN", "You are not opped, halfopped, or voiced in $b%s$b." },
193 { "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." },
194 { "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." },
196 /* Removing yourself from a channel. */
197 { "CSMSG_NO_OWNER_DELETEME", "You cannot delete your owner access in $b%s$b." },
198 { "CSMSG_CONFIRM_DELETEME", "To really remove yourself, you must use 'deleteme %s'." },
199 { "CSMSG_DELETED_YOU", "Your $b%d$b access has been deleted from $b%s$b." },
201 /* User management */
202 { "CSMSG_ADDED_USER", "Added %s to the %s user list with access %s (%d)." },
203 { "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." },
204 { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be greater than maximum (%d)." },
205 { "CSMSG_DELETED_USERS", "Deleted accounts matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list." },
206 { "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." },
207 { "CSMSG_INCORRECT_ACCESS", "%s has access $b%s$b, not %s." },
208 { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with %s access)." },
209 { "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." },
210 { "CSMSG_ADDUSER_PENDING_ALREADY", "He or she is already pending addition to %s once he/she auths with $b$N$b." },
211 { "CSMSG_ADDUSER_PENDING_HEADER", "Users to add to channels pending logins:" }, /* Remove after testing? */
212 { "CSMSG_ADDUSER_PENDING_LIST", "Channel %s user %s" }, /* Remove after testing? */
213 { "CSMSG_ADDUSER_PENDING_FOOTER", "--------- End of pending list ----------" }, /* Remove after testing? */
214 /*{ "CSMSG_ADDUSER_PENDING_NOTINCHAN", "That user is not in %s, and is not auth'd." }, */
215 { "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" },
216 { "CSMSG_CANNOT_TRIM", "You must include a minimum inactivity duration of at least 60 seconds to trim." },
218 { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." },
219 { "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." },
220 { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
221 { "CSMSG_TRANSFER_WAIT", "You must wait %s before you can give ownership of $b%s$b to someone else." },
222 { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
223 { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
226 { "CSMSG_LAMER_ADDED", "Added $b%s$b to %s LAMERs." },
227 { "CSMSG_TIMED_LAMER_ADDED", "LAMERed $b%s$b on %s for %s." },
228 { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
229 { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
230 { "CSMSG_REASON_CHANGE", "Reason for LAMER $b%s$b changed." },
231 { "CSMSG_LAMER_EXTENDED", "Extended LAMER for $b%s$b, now expires in %s." },
232 { "CSMSG_BAN_REMOVED", "Matching ban(s) and LAMER(s) in $b%s$b were removed." },
233 { "CSMSG_TRIMMED_LAMERS", "Trimmed $b%d LAMERs$b from the %s LAMER list that were inactive for at least %s." },
234 { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
235 { "CSMSG_REDUNDANT_LAMER", "$b%s$b is already LAMER'd in %s." },
236 { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
237 { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
238 { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
239 { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
240 { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
241 { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban or LAMER found: $b%s$b." },
242 { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
244 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
246 /* Channel management */
247 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
248 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
249 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
251 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
252 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
253 { "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" },
254 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic is at most %d characters and matches the topic mask pattern." },
255 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
256 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
257 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
259 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
260 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
261 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
262 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
263 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
264 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
265 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you must use 'set defaults %s'." },
266 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
267 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
268 { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
269 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
270 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
271 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
272 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
273 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
274 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
275 { "CSMSG_SET_MODES", "$bModes $b %s" },
276 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
277 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s - +l joinflood protection." },
278 { "CSMSG_SET_OFFCHANNEL", "$bOffChannel $b %s" },
279 { "CSMSG_SET_USERINFO", "$bUserInfo $b %d - and above userinfos are shown." },
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." },
284 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d - and above can change channel modes." },
285 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d - and above can set the topic." },
286 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d - and above can use public commands." },
287 { "CSMSG_SET_SETTERS", "$bSetters $b %d - and above can change these settings." },
288 { "CSMSG_SET_AUTOMODE", "$bAutoMode $b %d - %s" },
289 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
290 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
291 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
292 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
293 { "CSMSG_SET_RESYNC", "$bResync $b %d - %s" },
294 { "CSMSG_SET_BANTIMEOUT", "$bBanTimeout $b %d - %s" },
296 { "CSMSG_USET_AUTOOP", "$bAutoOp $b %s" },
297 { "CSMSG_USET_AUTOVOICE", "$bAutoVoice $b %s" },
298 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
299 { "CSMSG_USET_INFO", "$bInfo $b %s" },
301 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
302 { "CSMSG_USER_PROTECTED_KICK", "That user is protected." }, /* No $ or %s replacements! */
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." },
313 { "CSMSG_AUTOMODE_NONE", "Noone will be automatically oped, half-oped, or voiced." },
314 { "CSMSG_AUTOMODE_NORMAL", "Give voice to peons, half-op to halfops, and op to ops." },
315 { "CSMSG_AUTOMODE_VOICE", "#1 plus give voice to everyone." },
316 { "CSMSG_AUTOMODE_HOP", "#1 plus give halfops to everyone." },
317 { "CSMSG_AUTOMODE_OP", "#1 plus give ops to everyone (not advised)" },
318 { "CSMSG_AUTOMODE_MUTE", "Give half-op to halfops, and op to ops only." },
320 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
321 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
322 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
323 { "CSMSG_PROTECT_NONE", "No users will be protected." },
324 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
325 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
326 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
328 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
329 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
330 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
331 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
332 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
334 { "CSMSG_RESYNC_NEVER", "Never automaticly resync userlist." },
335 { "CSMSG_RESYNC_3_HOURS", "Resync userlist every 3 hours." },
336 { "CSMSG_RESYNC_6_HOURS", "Resync userlist every 6 hours." },
337 { "CSMSG_RESYNC_12_HOURS", "Resync userlist every 12 hours." },
338 { "CSMSG_RESYNC_24_HOURS", "Resync userlist every 24 hours." },
340 { "CSMSG_CTCPREACTION_NONE", "CTCPs are allowed" },
341 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
342 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
343 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
344 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
346 { "CSMSG_BANTIMEOUT_NONE", "Bans will not be removed automatically."},
347 { "CSMSG_BANTIMEOUT_10M", "Bans will be removed after 10 minutes."},
348 { "CSMSG_BANTIMEOUT_2H", "Bans will be removed after 2 hours."},
349 { "CSMSG_BANTIMEOUT_4H", "Bans will be removed after 4 hours."},
350 { "CSMSG_BANTIMEOUT_1D", "Bans will be removed after 24 hours."},
351 { "CSMSG_BANTIMEOUT_1W", "Bans will be removed after 1 week."},
353 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
354 { "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" },
355 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s." },
356 { "CSMSG_CANNOT_INVITE", "You cannot invite %s to %s." },
357 { "CSMSG_ALREADY_PRESENT", "%s is already in $b%s$b." },
358 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
359 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s to use this command." },
360 { "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
361 { "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
363 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
364 { "CSMSG_NO_BANS", "No bans found on $b%s$b." },
365 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
367 /* Channel userlist */
368 { "CSMSG_ACCESS_ALL_HEADER_NORMAL", "$b%s Users From Level %s To %s$b" },
369 { "CSMSG_ACCESS_SEARCH_HEADER_NORMAL", "$b%s Users From Level %s To %s Matching %s$b" },
370 /* uncomment if needed to adujust styles (and change code below)
371 { "CSMSG_ACCESS_ALL_HEADER_CLEAN", "$b%s Users From Level %s To %s$b" },
372 { "CSMSG_ACCESS_SEARCH_HEADER_CLEAN", "$b%s Users From Level %s To %s Matching %s$b" },
373 { "CSMSG_ACCESS_ALL_HEADER_ADVANCED", "$b%s Users From Level %s To %s$b" },
374 { "CSMSG_ACCESS_SEARCH_HEADER_ADVANCED", "$b%s Users From Level %s To %s Matching %s$b" },
375 { "CSMSG_ACCESS_ALL_HEADER_CLASSIC", "$b%s Users From Level %s To %s$b" },
376 { "CSMSG_ACCESS_SEARCH_HEADER_CLASSIC", "$b%s Users From Level %s To %s Matching %s$b" },
378 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
379 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%s$b (%u) in %s." },
380 { "CSMSG_LAMERS_HEADER", "$bLamers in %s$b" },
382 /* Channel note list */
383 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
384 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
385 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
386 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
387 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
388 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
389 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
390 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
391 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
392 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
393 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
394 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
395 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
396 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
397 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
399 /* Channel [un]suspension */
400 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
401 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
402 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
403 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
404 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
405 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
406 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
408 /* Access information */
409 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
410 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
411 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
412 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
413 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
414 { "CSMSG_USER_HAS_ACCESS", "%s has $b%s$b access (%d) in %s." },
415 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
416 { "CSMSG_HELPER_HAS_ACCESS", "%s has $b%s$b access (%d) in %s and has $bsecurity override$b enabled." },
417 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
418 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
419 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
421 /* Seen information */
422 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
423 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
424 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
425 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
427 /* Names information */
428 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
429 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
431 /* Channel information */
432 { "CSMSG_CHANNEL_INFO", "$bInformation About %s$b" },
433 { "CSMSG_BAR", "----------------------------------------"},
434 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
435 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
436 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
437 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
438 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
439 { "CSMSG_CHANNEL_LAMERS", "$bLamer Count: $b%i" },
440 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
441 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
442 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
443 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
444 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
445 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
446 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
447 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
448 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
449 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
450 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
451 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
452 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
453 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
454 { "CSMSG_CHANNEL_OWNERSHIP_HISTORY", "Ownership transfer history for $b%s$b" },
455 { "CSMSG_CHANNEL_OWNERSHIP_NORMAL", "from %s to %s (%d access) on %s" },
456 { "CSMSG_CHANNEL_OWNERSHIP_STAFF_REASON", "from %s to %s (%d access) by %s on %s reason: %s" },
457 { "CSMSG_CHANNEL_OWNERSHIP_STAFF", "from %s to %s (%d access) by %s on %s" },
458 { "CSMSG_CHANNEL_END", "---------------End of Info--------------"},
459 { "CSMSG_CHANNEL_END_CLEAN", "End of Info"},
461 { "CSMSG_PEEK_INFO", "$bStatus of %s$b" },
462 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
463 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
464 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d" },
465 { "CSMSG_PEEK_OPS", "$bOps:$b" },
466 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
467 { "CSMSG_PEEK_END", "-------------End of Status--------------" },
469 /* Network information */
470 { "CSMSG_NETWORK_INFO", "Network Information:" },
471 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
472 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
473 { "CSMSG_NETWORK_LAMERS", "$bTotal Lamer Count: $b%i" },
474 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
475 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
476 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
477 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
478 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
481 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
482 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
483 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
485 /* Channel searches */
486 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
487 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
488 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
489 { "CSMSG_CHANNEL_SEARCH_RESULTS", "$bChannels Found Matching Search$b" },
491 /* Channel configuration */
492 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
493 { "CSMSG_CHANNEL_OPTIONS", "$bChannel Options for %s$b" },
494 { "CSMSG_CHANNEL_OPTIONS_END", "-------------End of Options-------------" },
495 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
498 { "CSMSG_USER_OPTIONS", "User Options:" },
499 // { "CSMSG_USER_PROTECTED", "That user is protected." },
502 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
503 { "CSMSG_PING_RESPONSE", "Pong!" },
504 { "CSMSG_WUT_RESPONSE", "wut" },
505 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
506 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
507 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
508 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
509 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
510 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
511 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
514 { "CSMSG_EVENT_SEARCH_RESULTS", "$bChannel Events for %s$b" },
515 { "CSMSG_LAST_INVALID", "Invalid argument. must be 1-200" },
519 /* eject_user and unban_user flags */
520 #define ACTION_KICK 0x0001
521 #define ACTION_BAN 0x0002
522 #define ACTION_ADD_LAMER 0x0004
523 #define ACTION_ADD_TIMED_LAMER 0x0008
524 #define ACTION_UNBAN 0x0010
525 #define ACTION_DEL_LAMER 0x0020
527 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
528 #define MODELEN 40 + KEYLEN
532 #define CSFUNC_ARGS user, channel, argc, argv, cmd
534 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
535 #define CHANSERV_SYNTAX() svccmd_send_help_brief(user, chanserv, cmd)
536 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
537 reply("MSG_MISSING_PARAMS", argv[0]); \
541 DECLARE_LIST(dnrList
, struct do_not_register
*);
542 DEFINE_LIST(dnrList
, struct do_not_register
*);
544 static int eject_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
);
546 struct userNode
*chanserv
;
549 extern struct string_list
*autojoin_channels
;
550 static dict_t plain_dnrs
, mask_dnrs
, handle_dnrs
;
551 static struct log_type
*CS_LOG
;
552 struct adduserPending
* adduser_pendings
= NULL
;
553 unsigned int adduser_pendings_count
= 0;
554 unsigned long god_timeout
;
558 struct channelList support_channels
;
559 struct mod_chanmode default_modes
;
561 unsigned long db_backup_frequency
;
562 unsigned long channel_expire_frequency
;
563 unsigned long ban_timeout_frequency
;
566 unsigned int adjust_delay
;
567 long channel_expire_delay
;
568 unsigned int nodelete_level
;
570 unsigned int adjust_threshold
;
571 int join_flood_threshold
;
573 unsigned int greeting_length
;
574 unsigned int refresh_period
;
575 unsigned int giveownership_period
;
577 unsigned int max_owned
;
578 unsigned int max_chan_users
;
579 unsigned int max_chan_bans
; /* lamers */
580 unsigned int max_userinfo_length
;
582 struct string_list
*set_shows
;
583 struct string_list
*eightball
;
584 struct string_list
*old_ban_names
;
586 const char *ctcp_short_ban_duration
;
587 const char *ctcp_long_ban_duration
;
589 const char *irc_operator_epithet
;
590 const char *network_helper_epithet
;
591 const char *support_helper_epithet
;
596 struct userNode
*user
;
597 struct userNode
*bot
;
598 struct chanNode
*channel
;
600 unsigned short lowest
;
601 unsigned short highest
;
602 struct userData
**users
;
603 struct helpfile_table table
;
606 enum note_access_type
608 NOTE_SET_CHANNEL_ACCESS
,
609 NOTE_SET_CHANNEL_SETTER
,
613 enum note_visible_type
616 NOTE_VIS_CHANNEL_USERS
,
622 enum note_access_type set_access_type
;
624 unsigned int min_opserv
;
625 unsigned short min_ulevel
;
627 enum note_visible_type visible_type
;
628 unsigned int max_length
;
635 struct note_type
*type
;
636 char setter
[NICKSERV_HANDLE_LEN
+1];
640 static unsigned int registered_channels
;
641 static unsigned int banCount
;
643 static const struct {
646 unsigned short level
;
648 } accessLevels
[] = { /* MUST be orderd less to most! */
649 { "peon", "Peon", UL_PEON
, '+' },
650 { "halfop", "HalfOp", UL_HALFOP
, '%' },
651 { "op", "Op", UL_OP
, '@' },
652 { "manager", "Manager", UL_MANAGER
, '%' },
653 { "coowner", "Coowner", UL_COOWNER
, '*' },
654 { "owner", "Owner", UL_OWNER
, '!' },
655 { "helper", "BUG:", UL_HELPER
, 'X' }
658 /* If you change this, change the enum in chanserv.h also, or stack smashing will commence. */
659 static const struct {
662 unsigned short default_value
;
663 unsigned int old_idx
;
664 unsigned int old_flag
;
665 unsigned short flag_value
;
667 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
668 { "CSMSG_SET_ENFHALFOPS", "enfhalfops", 300, 1, 0, 0 },
669 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
670 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
671 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
672 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
673 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES
, 1 },
674 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE
, 200 },
675 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF
, 1 }
678 struct charOptionValues
{
681 } automodeValues
[] = {
682 { 'n', "CSMSG_AUTOMODE_NONE" },
683 { 'y', "CSMSG_AUTOMODE_NORMAL" },
684 { 'v', "CSMSG_AUTOMODE_VOICE" },
685 { 'h', "CSMSG_AUTOMODE_HOP" },
686 { 'o', "CSMSG_AUTOMODE_OP" },
687 { 'm', "CSMSG_AUTOMODE_MUTE" }
688 }, protectValues
[] = {
689 { 'a', "CSMSG_PROTECT_ALL" },
690 { 'e', "CSMSG_PROTECT_EQUAL" },
691 { 'l', "CSMSG_PROTECT_LOWER" },
692 { 'n', "CSMSG_PROTECT_NONE" }
694 { 'd', "CSMSG_TOYS_DISABLED" },
695 { 'n', "CSMSG_TOYS_PRIVATE" },
696 { 'p', "CSMSG_TOYS_PUBLIC" }
697 }, topicRefreshValues
[] = {
698 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
699 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
700 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
701 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
702 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
703 }, ctcpReactionValues
[] = {
704 { 'n', "CSMSG_CTCPREACTION_NONE" },
705 { 'k', "CSMSG_CTCPREACTION_KICK" },
706 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
707 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
708 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
709 }, banTimeoutValues
[] = {
710 { '0', "CSMSG_BANTIMEOUT_NONE" },
711 { '1', "CSMSG_BANTIMEOUT_10M" },
712 { '2', "CSMSG_BANTIMEOUT_2H" },
713 { '3', "CSMSG_BANTIMEOUT_4H" },
714 { '4', "CSMSG_BANTIMEOUT_1D" },
715 { '5', "CSMSG_BANTIMEOUT_1W" }
718 { 'n', "CSMSG_RESYNC_NEVER" },
719 { '1', "CSMSG_RESYNC_3_HOURS" },
720 { '2', "CSMSG_RESYNC_6_HOURS" },
721 { '3', "CSMSG_RESYNC_12_HOURS" },
722 { '4', "CSMSG_RESYNC_24_HOURS" }
725 static const struct {
729 unsigned int old_idx
;
731 struct charOptionValues
*values
;
733 { "CSMSG_SET_AUTOMODE", "automode", 'y', 99, ArrayLength(automodeValues
), automodeValues
},
734 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues
), protectValues
},
735 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues
), toysValues
},
736 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues
), topicRefreshValues
},
737 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 'n', 10, ArrayLength(ctcpReactionValues
), ctcpReactionValues
},
738 { "CSMSG_SET_BANTIMEOUT", "bantimeout", '0', 11, ArrayLength(banTimeoutValues
), banTimeoutValues
},
739 { "CSMSG_SET_RESYNC", "resync", 'n', 12, ArrayLength(resyncValues
), resyncValues
},
742 struct userData
*helperList
;
743 struct chanData
*channelList
;
744 static struct module *chanserv_module
;
745 static unsigned int userCount
;
746 unsigned int chanserv_read_version
= 0; /* db version control */
748 #define CHANSERV_DB_VERSION 2
750 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
751 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
754 user_level_from_name(const char *name
, unsigned short clamp_level
)
756 unsigned int level
= 0, ii
;
758 level
= strtoul(name
, NULL
, 10);
759 else for(ii
= 0; (ii
< ArrayLength(accessLevels
)) && !level
; ++ii
)
760 if(!irccasecmp(name
, accessLevels
[ii
].name
))
761 level
= accessLevels
[ii
].level
;
762 if(level
> clamp_level
)
768 user_level_name_from_level(int level
)
776 for(ii
= 0; (ii
< ArrayLength(accessLevels
)); ii
++)
777 if(level
>= accessLevels
[ii
].level
)
778 highest
= accessLevels
[ii
].title
;
784 parse_level_range(unsigned short *minl
, unsigned short *maxl
, const char *arg
)
787 *minl
= strtoul(arg
, &sep
, 10);
795 *maxl
= strtoul(sep
+1, &sep
, 10);
803 _GetChannelUser(struct chanData
*channel
, struct handle_info
*handle
, int override
, int allow_suspended
)
805 struct userData
*uData
, **head
;
807 if(!channel
|| !handle
)
810 if(override
&& HANDLE_FLAGGED(handle
, HELPING
)
811 && ((handle
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
)))
813 for(uData
= helperList
;
814 uData
&& uData
->handle
!= handle
;
815 uData
= uData
->next
);
819 uData
= calloc(1, sizeof(struct userData
));
820 uData
->handle
= handle
;
822 uData
->access
= UL_HELPER
;
828 uData
->next
= helperList
;
830 helperList
->prev
= uData
;
838 for(uData
= channel
->users
; uData
; uData
= uData
->next
)
839 if((uData
->handle
== handle
) && (allow_suspended
|| !IsUserSuspended(uData
)))
842 head
= &(channel
->users
);
845 if(uData
&& (uData
!= *head
))
847 /* Shuffle the user to the head of whatever list he was in. */
849 uData
->next
->prev
= uData
->prev
;
851 uData
->prev
->next
= uData
->next
;
857 (**head
).prev
= uData
;
864 /* Returns non-zero if user has at least the minimum access.
865 * exempt_owner is set when handling !set, so the owner can set things
868 int check_user_level(struct chanNode
*channel
, struct userNode
*user
, enum levelOption opt
, int allow_override
, int exempt_owner
)
870 struct userData
*uData
;
871 struct chanData
*cData
= channel
->channel_info
;
872 unsigned short minimum
= cData
->lvlOpts
[opt
];
875 uData
= _GetChannelUser(cData
, user
->handle_info
, allow_override
, 0);
878 if(minimum
<= uData
->access
)
880 if((minimum
> UL_OWNER
) && (uData
->access
== UL_OWNER
) && exempt_owner
)
885 /* Scan for other users authenticated to the same handle
886 still in the channel. If so, keep them listed as present.
888 user is optional, if not null, it skips checking that userNode
889 (for the handle_part function) */
891 scan_user_presence(struct userData
*uData
, struct userNode
*user
)
895 if(IsSuspended(uData
->channel
)
896 || IsUserSuspended(uData
)
897 || !(mn
= find_handle_in_channel(uData
->channel
->channel
, uData
->handle
, user
)))
909 chanserv_ctcp_check(struct userNode
*user
, struct chanNode
*channel
, char *text
, UNUSED_ARG(struct userNode
*bot
))
911 unsigned int eflags
, argc
;
913 static char *bad_ctcp_reason
= "CTCPs to this channel are forbidden.";
915 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
916 if(!channel
->channel_info
917 || IsSuspended(channel
->channel_info
)
919 || !ircncasecmp(text
, "ACTION ", 7))
921 /* We dont punish people we know -Rubin
922 * * Figure out the minimum level needed to CTCP the channel *
924 * if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
927 /* If they are a user of the channel, they are exempt */
928 if(_GetChannelUser(channel
->channel_info
, user
->handle_info
, 0, 0))
930 /* We need to enforce against them; do so. */
933 argv
[1] = user
->nick
;
935 if(GetUserMode(channel
, user
))
936 eflags
|= ACTION_KICK
;
937 switch(channel
->channel_info
->chOpts
[chCTCPReaction
]) {
938 default: case 'n': return;
940 eflags
|= ACTION_KICK
;
943 eflags
|= ACTION_BAN
;
946 eflags
|= ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
;
947 argv
[argc
++] = (char*)chanserv_conf
.ctcp_short_ban_duration
;
950 eflags
|= ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
;
951 argv
[argc
++] = (char*)chanserv_conf
.ctcp_long_ban_duration
;
954 argv
[argc
++] = bad_ctcp_reason
;
955 eject_user(chanserv
, channel
, argc
, argv
, NULL
, eflags
);
959 chanserv_create_note_type(const char *name
)
961 struct note_type
*ntype
= calloc(1, sizeof(*ntype
) + strlen(name
));
962 strcpy(ntype
->name
, name
);
964 dict_insert(note_types
, ntype
->name
, ntype
);
969 chanserv_deref_note_type(void *data
)
971 struct note_type
*ntype
= data
;
973 if(--ntype
->refs
> 0)
979 chanserv_flush_note_type(struct note_type
*ntype
)
981 struct chanData
*cData
;
982 for(cData
= channelList
; cData
; cData
= cData
->next
)
983 dict_remove(cData
->notes
, ntype
->name
);
987 chanserv_truncate_notes(struct note_type
*ntype
)
989 struct chanData
*cData
;
991 unsigned int size
= sizeof(*note
) + ntype
->max_length
;
993 for(cData
= channelList
; cData
; cData
= cData
->next
) {
994 note
= dict_find(cData
->notes
, ntype
->name
, NULL
);
997 if(strlen(note
->note
) <= ntype
->max_length
)
999 dict_remove2(cData
->notes
, ntype
->name
, 1);
1000 note
= realloc(note
, size
);
1001 note
->note
[ntype
->max_length
] = 0;
1002 dict_insert(cData
->notes
, ntype
->name
, note
);
1006 static int note_type_visible_to_user(struct chanData
*channel
, struct note_type
*ntype
, struct userNode
*user
);
1008 static struct note
*
1009 chanserv_add_channel_note(struct chanData
*channel
, struct note_type
*type
, const char *setter
, const char *text
)
1012 unsigned int len
= strlen(text
);
1014 if(len
> type
->max_length
) len
= type
->max_length
;
1015 note
= calloc(1, sizeof(*note
) + len
);
1017 strncpy(note
->setter
, setter
, sizeof(note
->setter
)-1);
1018 memcpy(note
->note
, text
, len
);
1019 note
->note
[len
] = 0;
1020 dict_insert(channel
->notes
, type
->name
, note
);
1026 chanserv_free_note(void *data
)
1028 struct note
*note
= data
;
1030 chanserv_deref_note_type(note
->type
);
1031 assert(note
->type
->refs
> 0); /* must use delnote to remove the type */
1035 static MODCMD_FUNC(cmd_createnote
) {
1036 struct note_type
*ntype
;
1037 unsigned int arg
= 1, existed
= 0, max_length
;
1039 if((ntype
= dict_find(note_types
, argv
[1], NULL
)))
1042 ntype
= chanserv_create_note_type(argv
[arg
]);
1043 if(!irccasecmp(argv
[++arg
], "privileged"))
1046 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
1047 ntype
->set_access
.min_opserv
= strtoul(argv
[arg
], NULL
, 0);
1049 else if(!irccasecmp(argv
[arg
], "channel"))
1051 unsigned short ulvl
= user_level_from_name(argv
[++arg
], UL_OWNER
);
1054 reply("CSMSG_INVALID_ACCESS", argv
[arg
]);
1057 ntype
->set_access_type
= NOTE_SET_CHANNEL_ACCESS
;
1058 ntype
->set_access
.min_ulevel
= ulvl
;
1060 else if(!irccasecmp(argv
[arg
], "setter"))
1062 ntype
->set_access_type
= NOTE_SET_CHANNEL_SETTER
;
1066 reply("CSMSG_BAD_NOTE_ACCESS", argv
[arg
]);
1070 if(!irccasecmp(argv
[++arg
], "privileged"))
1071 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
1072 else if(!irccasecmp(argv
[arg
], "channel_users"))
1073 ntype
->visible_type
= NOTE_VIS_CHANNEL_USERS
;
1074 else if(!irccasecmp(argv
[arg
], "all"))
1075 ntype
->visible_type
= NOTE_VIS_ALL
;
1077 reply("CSMSG_BAD_NOTE_ACCESS", argv
[arg
]);
1081 if((arg
+1) >= argc
) {
1082 reply("MSG_MISSING_PARAMS", argv
[0]);
1085 max_length
= strtoul(argv
[++arg
], NULL
, 0);
1086 if(max_length
< 20 || max_length
> 450)
1088 reply("CSMSG_BAD_MAX_LENGTH", argv
[arg
]);
1091 if(existed
&& (max_length
< ntype
->max_length
))
1093 ntype
->max_length
= max_length
;
1094 chanserv_truncate_notes(ntype
);
1096 ntype
->max_length
= max_length
;
1099 reply("CSMSG_NOTE_MODIFIED", ntype
->name
);
1101 reply("CSMSG_NOTE_CREATED", ntype
->name
);
1106 dict_remove(note_types
, ntype
->name
);
1110 static MODCMD_FUNC(cmd_removenote
) {
1111 struct note_type
*ntype
;
1114 ntype
= dict_find(note_types
, argv
[1], NULL
);
1115 force
= (argc
> 2) && !irccasecmp(argv
[2], "force");
1118 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
1125 reply("CSMSG_NOTE_TYPE_USED", ntype
->name
);
1128 chanserv_flush_note_type(ntype
);
1130 dict_remove(note_types
, argv
[1]);
1131 reply("CSMSG_NOTE_DELETED", argv
[1]);
1136 mode_lock_violated(const struct mod_chanmode
*orig
, const struct mod_chanmode
*change
)
1140 if(orig
->modes_set
& change
->modes_clear
)
1142 if(orig
->modes_clear
& change
->modes_set
)
1144 if((orig
->modes_set
& MODE_KEY
) && (change
->modes_set
& MODE_KEY
)
1145 && strcmp(orig
->new_key
, change
->new_key
))
1147 if((orig
->modes_set
& MODE_LIMIT
) && (change
->modes_set
& MODE_LIMIT
)
1148 && (orig
->new_limit
!= change
->new_limit
))
1153 static char max_length_text
[MAXLEN
+1][16];
1155 static struct helpfile_expansion
1156 chanserv_expand_variable(const char *variable
)
1158 struct helpfile_expansion exp
;
1160 if(!irccasecmp(variable
, "notes"))
1163 exp
.type
= HF_TABLE
;
1164 exp
.value
.table
.length
= 1;
1165 exp
.value
.table
.width
= 3;
1166 exp
.value
.table
.flags
= 0;
1167 exp
.value
.table
.contents
= calloc(dict_size(note_types
)+1, sizeof(char**));
1168 exp
.value
.table
.contents
[0] = calloc(exp
.value
.table
.width
, sizeof(char*));
1169 exp
.value
.table
.contents
[0][0] = "Note Type";
1170 exp
.value
.table
.contents
[0][1] = "Visibility";
1171 exp
.value
.table
.contents
[0][2] = "Max Length";
1172 for(it
=dict_first(note_types
); it
; it
=iter_next(it
))
1174 struct note_type
*ntype
= iter_data(it
);
1177 if(!note_type_visible_to_user(NULL
, ntype
, message_dest
)) continue;
1178 row
= exp
.value
.table
.length
++;
1179 exp
.value
.table
.contents
[row
] = calloc(exp
.value
.table
.width
, sizeof(char*));
1180 exp
.value
.table
.contents
[row
][0] = ntype
->name
;
1181 exp
.value
.table
.contents
[row
][1] = (ntype
->visible_type
== NOTE_VIS_ALL
) ? "all" :
1182 (ntype
->visible_type
== NOTE_VIS_CHANNEL_USERS
) ? "chan users" :
1184 if(!max_length_text
[ntype
->max_length
][0])
1185 snprintf(max_length_text
[ntype
->max_length
], sizeof(max_length_text
[ntype
->max_length
]), "%u", ntype
->max_length
);
1186 exp
.value
.table
.contents
[row
][2] = max_length_text
[ntype
->max_length
];
1191 exp
.type
= HF_STRING
;
1192 exp
.value
.str
= NULL
;
1196 static struct chanData
*
1197 register_channel(struct chanNode
*cNode
, char *registrar
)
1199 struct chanData
*channel
;
1200 enum levelOption lvlOpt
;
1201 enum charOption chOpt
;
1203 channel
= calloc(1, sizeof(struct chanData
));
1205 channel
->notes
= dict_new();
1206 dict_set_free_data(channel
->notes
, chanserv_free_note
);
1208 channel
->registrar
= strdup(registrar
);
1209 channel
->registered
= now
;
1210 channel
->visited
= now
;
1211 channel
->limitAdjusted
= now
;
1212 channel
->ownerTransfer
= now
;
1213 channel
->flags
= CHANNEL_DEFAULT_FLAGS
;
1214 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
1215 channel
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
1216 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
1217 channel
->chOpts
[chOpt
] = charOptions
[chOpt
].default_value
;
1219 channel
->prev
= NULL
;
1220 channel
->next
= channelList
;
1223 channelList
->prev
= channel
;
1224 channelList
= channel
;
1225 registered_channels
++;
1227 channel
->channel
= cNode
;
1229 cNode
->channel_info
= channel
;
1234 static struct userData
*
1235 add_channel_user(struct chanData
*channel
, struct handle_info
*handle
, unsigned short access
, time_t seen
, const char *info
)
1237 struct userData
*ud
;
1239 if(access
> UL_OWNER
)
1242 ud
= calloc(1, sizeof(*ud
));
1243 ud
->channel
= channel
;
1244 ud
->handle
= handle
;
1246 ud
->access
= access
;
1247 ud
->info
= info
? strdup(info
) : NULL
;
1250 ud
->next
= channel
->users
;
1252 channel
->users
->prev
= ud
;
1253 channel
->users
= ud
;
1255 channel
->userCount
++;
1259 ud
->u_next
= ud
->handle
->channels
;
1261 ud
->u_next
->u_prev
= ud
;
1262 ud
->handle
->channels
= ud
;
1264 ud
->flags
= USER_FLAGS_DEFAULT
;
1268 static void unregister_channel(struct chanData
*channel
, const char *reason
);
1271 del_channel_user(struct userData
*user
, int do_gc
)
1273 struct chanData
*channel
= user
->channel
;
1275 channel
->userCount
--;
1279 user
->prev
->next
= user
->next
;
1281 channel
->users
= user
->next
;
1283 user
->next
->prev
= user
->prev
;
1286 user
->u_prev
->u_next
= user
->u_next
;
1288 user
->handle
->channels
= user
->u_next
;
1290 user
->u_next
->u_prev
= user
->u_prev
;
1294 if(do_gc
&& !channel
->users
&& !IsProtected(channel
)) {
1295 spamserv_cs_unregister(NULL
, channel
->channel
, lost_all_users
, NULL
);
1296 unregister_channel(channel
, "lost all users.");
1300 static struct adduserPending
*
1301 add_adduser_pending(struct chanNode
*channel
, struct userNode
*user
, int level
)
1303 struct adduserPending
*ap
;
1304 ap
= calloc(1,sizeof(struct adduserPending
));
1305 ap
->channel
= channel
;
1308 ap
->created
= time(NULL
);
1310 /* ap->prev defaults to NULL already.. */
1311 ap
->next
= adduser_pendings
;
1312 if(adduser_pendings
)
1313 adduser_pendings
->prev
= ap
;
1314 adduser_pendings
= ap
;
1315 adduser_pendings_count
++;
1320 del_adduser_pending(struct adduserPending
*ap
)
1323 ap
->prev
->next
= ap
->next
;
1325 adduser_pendings
= ap
->next
;
1328 ap
->next
->prev
= ap
->prev
;
1332 static void expire_adduser_pending();
1334 /* find_adduser_pending(channel, user) will find an arbitrary record
1335 * from user, channel, or user and channel.
1336 * if user or channel are NULL, they will match any records.
1338 static struct adduserPending
*
1339 find_adduser_pending(struct chanNode
*channel
, struct userNode
*user
)
1341 struct adduserPending
*ap
;
1343 expire_adduser_pending(); /* why not here.. */
1345 if(!channel
&& !user
) /* 2 nulls matches all */
1346 return(adduser_pendings
);
1347 for(ap
= adduser_pendings
;ap
;ap
= ap
->next
)
1349 if((channel
== ap
->channel
&& (user
== NULL
|| user
== ap
->user
)) || (user
==ap
->user
&& channel
==NULL
))
1356 /* Remove all pendings for a user or channel
1358 * called in nickserv.c DelUser() and proto-* unregister_channel()
1361 wipe_adduser_pending(struct chanNode
*channel
, struct userNode
*user
)
1363 struct adduserPending
*ap
;
1365 /* So this is a bit wastefull, i hate dealing with linked lists.
1366 * if its a problem we'll rewrite it right */
1367 while((ap
= find_adduser_pending(channel
, user
))) {
1368 del_adduser_pending(ap
);
1372 /* Called from nickserv.c cmd_auth after someone auths */
1374 process_adduser_pending(struct userNode
*user
)
1376 struct adduserPending
*ap
;
1377 if(!user
->handle_info
)
1378 return; /* not associated with an account */
1379 while((ap
= find_adduser_pending(NULL
, user
)))
1381 struct userData
*actee
;
1382 if(GetTrueChannelAccess(ap
->channel
->channel_info
, ap
->user
->handle_info
))
1384 /* Already on the userlist. do nothing*/
1388 actee
= add_channel_user(ap
->channel
->channel_info
, ap
->user
->handle_info
, ap
->level
, 0, NULL
);
1389 scan_user_presence(actee
, NULL
);
1391 del_adduser_pending(ap
);
1396 expire_adduser_pending()
1398 struct adduserPending
*ap
, *ap_next
;
1399 ap
= adduser_pendings
;
1402 if((ap
->created
+ ADDUSER_PENDING_EXPIRE
) < time(NULL
))
1404 ap_next
= ap
->next
; /* save next */
1405 del_adduser_pending(ap
); /* free and relink */
1406 ap
= ap_next
; /* advance */
1413 static void expire_ban(void *data
);
1416 add_channel_ban(struct chanData
*channel
, const char *mask
, char *owner
, time_t set
, time_t triggered
, time_t expires
, char *reason
)
1419 unsigned int ii
, l1
, l2
;
1424 bd
= malloc(sizeof(struct banData
));
1426 bd
->channel
= channel
;
1428 bd
->triggered
= triggered
;
1429 bd
->expires
= expires
;
1431 for(ii
= 0; ii
< chanserv_conf
.old_ban_names
->used
; ++ii
)
1433 extern const char *hidden_host_suffix
;
1434 const char *old_name
= chanserv_conf
.old_ban_names
->list
[ii
];
1438 l2
= strlen(old_name
);
1441 if(irccasecmp(mask
+ l1
- l2
, old_name
))
1443 new_mask
= alloca(MAXLEN
);
1444 sprintf(new_mask
, "%.*s%s", (int)(l1
-l2
), mask
, hidden_host_suffix
);
1447 safestrncpy(bd
->mask
, mask
, sizeof(bd
->mask
));
1449 safestrncpy(bd
->owner
, owner
, sizeof(bd
->owner
));
1450 bd
->reason
= strdup(reason
);
1453 timeq_add(expires
, expire_ban
, bd
);
1456 bd
->next
= channel
->bans
; /* lamers */
1458 channel
->bans
->prev
= bd
;
1460 channel
->banCount
++;
1467 del_channel_ban(struct banData
*ban
)
1469 ban
->channel
->banCount
--;
1473 ban
->prev
->next
= ban
->next
;
1475 ban
->channel
->bans
= ban
->next
;
1478 ban
->next
->prev
= ban
->prev
;
1481 timeq_del(0, expire_ban
, ban
, TIMEQ_IGNORE_WHEN
);
1490 expire_ban(void *data
) /* lamer.. */
1492 struct banData
*bd
= data
;
1493 if(!IsSuspended(bd
->channel
))
1495 struct banList bans
;
1496 struct mod_chanmode change
;
1498 bans
= bd
->channel
->channel
->banlist
;
1499 mod_chanmode_init(&change
);
1500 for(ii
=0; ii
<bans
.used
; ii
++)
1502 if(!strcmp(bans
.list
[ii
]->ban
, bd
->mask
))
1505 change
.args
[0].mode
= MODE_REMOVE
|MODE_BAN
;
1506 change
.args
[0].u
.hostmask
= bd
->mask
;
1507 mod_chanmode_announce(chanserv
, bd
->channel
->channel
, &change
);
1513 del_channel_ban(bd
);
1516 static void chanserv_expire_suspension(void *data
);
1519 unregister_channel(struct chanData
*channel
, const char *reason
)
1521 struct mod_chanmode change
;
1522 char msgbuf
[MAXLEN
];
1524 /* After channel unregistration, the following must be cleaned
1526 - Channel information.
1528 - Channel bans. (lamers)
1529 - Channel suspension data.
1530 - adduser_pending data.
1531 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1537 timeq_del(0, NULL
, channel
, TIMEQ_IGNORE_FUNC
| TIMEQ_IGNORE_WHEN
);
1541 mod_chanmode_init(&change
);
1542 change
.modes_clear
|= MODE_REGISTERED
;
1543 mod_chanmode_announce(chanserv
, channel
->channel
, &change
);
1546 wipe_adduser_pending(channel
->channel
, NULL
);
1548 while(channel
->users
)
1549 del_channel_user(channel
->users
, 0);
1551 while(channel
->bans
)
1552 del_channel_ban(channel
->bans
);
1554 free(channel
->topic
);
1555 free(channel
->registrar
);
1556 free(channel
->greeting
);
1557 free(channel
->user_greeting
);
1558 free(channel
->topic_mask
);
1561 channel
->prev
->next
= channel
->next
;
1563 channelList
= channel
->next
;
1566 channel
->next
->prev
= channel
->prev
;
1568 if(channel
->suspended
)
1570 struct chanNode
*cNode
= channel
->channel
;
1571 struct suspended
*suspended
, *next_suspended
;
1573 for(suspended
= channel
->suspended
; suspended
; suspended
= next_suspended
)
1575 next_suspended
= suspended
->previous
;
1576 free(suspended
->suspender
);
1577 free(suspended
->reason
);
1578 if(suspended
->expires
)
1579 timeq_del(suspended
->expires
, chanserv_expire_suspension
, suspended
, 0);
1584 cNode
->channel_info
= NULL
;
1586 channel
->channel
->channel_info
= NULL
;
1588 dict_delete(channel
->notes
);
1589 sprintf(msgbuf
, "%s %s", channel
->channel
->name
, reason
);
1590 if(!IsSuspended(channel
))
1591 DelChannelUser(chanserv
, channel
->channel
, msgbuf
, 0);
1592 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, msgbuf
);
1593 UnlockChannel(channel
->channel
);
1595 registered_channels
--;
1599 expire_channels(UNUSED_ARG(void *data
))
1601 struct chanData
*channel
, *next
;
1602 struct userData
*user
;
1603 char delay
[INTERVALLEN
], reason
[INTERVALLEN
+ 64];
1605 intervalString(delay
, chanserv_conf
.channel_expire_delay
, NULL
);
1606 sprintf(reason
, "Channel registration automatically expired after %s of disuse.", delay
);
1608 for(channel
= channelList
; channel
; channel
= next
)
1610 next
= channel
->next
;
1612 /* See if the channel can be expired. */
1613 if(((now
- channel
->visited
) <= chanserv_conf
.channel_expire_delay
)
1614 || IsProtected(channel
))
1617 /* Make sure there are no high-ranking users still in the channel. */
1618 for(user
=channel
->users
; user
; user
=user
->next
)
1619 if(user
->present
&& (user
->access
>= UL_PRESENT
))
1624 /* Unregister the channel */
1625 log_module(CS_LOG
, LOG_INFO
, "(%s) Channel registration expired.", channel
->channel
->name
);
1626 spamserv_cs_unregister(NULL
, channel
->channel
, expire
, NULL
);
1627 unregister_channel(channel
, "registration expired.");
1630 if(chanserv_conf
.channel_expire_frequency
)
1631 timeq_add(now
+ chanserv_conf
.channel_expire_frequency
, expire_channels
, NULL
);
1635 protect_user(const struct userNode
*victim
, const struct userNode
*aggressor
, struct chanData
*channel
)
1637 char protect
= channel
->chOpts
[chProtect
];
1638 struct userData
*cs_victim
, *cs_aggressor
;
1640 /* Don't protect if no one is to be protected, someone is attacking
1641 himself, or if the aggressor is an IRC Operator. */
1642 if(protect
== 'n' || victim
== aggressor
/* Opers dont get special treatment :/ || IsOper(aggressor) */)
1645 /* Don't protect if the victim isn't authenticated (because they
1646 can't be a channel user), unless we are to protect non-users
1648 cs_victim
= GetChannelAccess(channel
, victim
->handle_info
);
1649 if(protect
!= 'a' && !cs_victim
)
1652 /* Protect if the aggressor isn't a user because at this point,
1653 the aggressor can only be less than or equal to the victim. */
1654 cs_aggressor
= GetChannelAccess(channel
, aggressor
->handle_info
);
1658 /* If the aggressor was a user, then the victim can't be helped. */
1665 if(cs_victim
->access
> cs_aggressor
->access
)
1670 if(cs_victim
->access
>= cs_aggressor
->access
)
1679 validate_op(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1681 struct chanData
*cData
= channel
->channel_info
;
1682 struct userData
*cs_victim
;
1684 if((!(cs_victim
= GetChannelUser(cData
, victim
->handle_info
))
1685 || (cs_victim
->access
< UL_OP
/* cData->lvlOpts[lvlGiveOps]*/))
1686 && !check_user_level(channel
, user
, lvlEnfOps
, 0, 0))
1689 reply("CSMSG_OPBY_LOCKED");
1691 send_message(user
, chanserv
, "CSMSG_OPBY_LOCKED");
1699 validate_halfop(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1701 struct chanData
*cData
= channel
->channel_info
;
1702 struct userData
*cs_victim
;
1704 if((!(cs_victim
= GetChannelUser(cData
, victim
->handle_info
))
1705 || (cs_victim
->access
< UL_HALFOP
/* cData->lvlOpts[lvlGiveHalfOps] */))
1706 && !check_user_level(channel
, user
, lvlEnfHalfOps
, 0, 0))
1708 reply("CSMSG_HOPBY_LOCKED");
1717 validate_deop(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1719 if(IsService(victim
))
1721 reply("MSG_SERVICE_IMMUNE", victim
->nick
);
1725 if(protect_user(victim
, user
, channel
->channel_info
))
1727 reply("CSMSG_USER_PROTECTED", victim
->nick
);
1735 validate_dehop(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1737 if(IsService(victim
))
1739 reply("MSG_SERVICE_IMMUNE", victim
->nick
);
1743 if(protect_user(victim
, user
, channel
->channel_info
))
1745 reply("CSMSG_USER_PROTECTED", victim
->nick
);
1752 static struct do_not_register
*
1753 chanserv_add_dnr(const char *chan_name
, const char *setter
, const char *reason
)
1755 struct do_not_register
*dnr
= calloc(1, sizeof(*dnr
)+strlen(reason
));
1756 safestrncpy(dnr
->chan_name
, chan_name
, sizeof(dnr
->chan_name
));
1757 safestrncpy(dnr
->setter
, setter
, sizeof(dnr
->setter
));
1758 strcpy(dnr
->reason
, reason
);
1760 if(dnr
->chan_name
[0] == '*')
1761 dict_insert(handle_dnrs
, dnr
->chan_name
+1, dnr
);
1762 else if(strpbrk(dnr
->chan_name
, "*?"))
1763 dict_insert(mask_dnrs
, dnr
->chan_name
, dnr
);
1765 dict_insert(plain_dnrs
, dnr
->chan_name
, dnr
);
1769 static struct dnrList
1770 chanserv_find_dnrs(const char *chan_name
, const char *handle
)
1772 struct dnrList list
;
1774 struct do_not_register
*dnr
;
1776 dnrList_init(&list
);
1777 if(handle
&& (dnr
= dict_find(handle_dnrs
, handle
, NULL
)))
1778 dnrList_append(&list
, dnr
);
1779 if(chan_name
&& (dnr
= dict_find(plain_dnrs
, chan_name
, NULL
)))
1780 dnrList_append(&list
, dnr
);
1782 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1783 if(match_ircglob(chan_name
, iter_key(it
)))
1784 dnrList_append(&list
, iter_data(it
));
1789 chanserv_show_dnrs(struct userNode
*user
, struct svccmd
*cmd
, const char *chan_name
, const char *handle
)
1791 struct dnrList list
;
1792 struct do_not_register
*dnr
;
1794 char buf
[INTERVALLEN
];
1796 list
= chanserv_find_dnrs(chan_name
, handle
);
1797 for(ii
= 0; (ii
< list
.used
) && (ii
< 10); ++ii
)
1799 dnr
= list
.list
[ii
];
1802 strftime(buf
, sizeof(buf
), "%Y %b %d", localtime(&dnr
->set
));
1803 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, buf
, dnr
->setter
, dnr
->reason
);
1806 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1809 reply("CSMSG_MORE_DNRS", list
.used
- ii
);
1814 struct do_not_register
*
1815 chanserv_is_dnr(const char *chan_name
, struct handle_info
*handle
)
1817 struct do_not_register
*dnr
;
1820 if(handle
&& (dnr
= dict_find(handle_dnrs
, handle
->handle
, NULL
)))
1824 if((dnr
= dict_find(plain_dnrs
, chan_name
, NULL
)))
1826 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1827 if(match_ircglob(chan_name
, iter_key(it
)))
1828 return iter_data(it
);
1833 static CHANSERV_FUNC(cmd_noregister
)
1836 struct do_not_register
*dnr
;
1837 char buf
[INTERVALLEN
];
1838 unsigned int matches
;
1844 reply("CSMSG_DNR_SEARCH_RESULTS");
1845 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
1848 for(it
= dict_first(handle_dnrs
); it
; it
= iter_next(it
))
1850 dnr
= iter_data(it
);
1852 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
1854 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1857 for(it
= dict_first(plain_dnrs
); it
; it
= iter_next(it
))
1859 dnr
= iter_data(it
);
1861 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
1863 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1866 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1868 dnr
= iter_data(it
);
1870 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
1872 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1877 reply("MSG_MATCH_COUNT", matches
);
1879 reply("MSG_NO_MATCHES");
1885 if(!IsChannelName(target
) && (*target
!= '*'))
1887 reply("CSMSG_NOT_DNR", target
);
1893 const char *reason
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
1894 if((*target
== '*') && !get_handle_info(target
+ 1))
1896 reply("MSG_HANDLE_UNKNOWN", target
+ 1);
1899 chanserv_add_dnr(target
, user
->handle_info
->handle
, reason
);
1900 reply("CSMSG_NOREGISTER_CHANNEL", target
);
1904 reply("CSMSG_DNR_SEARCH_RESULTS");
1905 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
1908 matches
= chanserv_show_dnrs(user
, cmd
, NULL
, target
+ 1);
1910 matches
= chanserv_show_dnrs(user
, cmd
, target
, NULL
);
1912 reply("MSG_NO_MATCHES");
1916 static CHANSERV_FUNC(cmd_allowregister
)
1918 const char *chan_name
= argv
[1];
1920 if((chan_name
[0] == '*') && dict_find(handle_dnrs
, chan_name
+1, NULL
))
1922 dict_remove(handle_dnrs
, chan_name
+1);
1923 reply("CSMSG_DNR_REMOVED", chan_name
);
1925 else if(dict_find(plain_dnrs
, chan_name
, NULL
))
1927 dict_remove(plain_dnrs
, chan_name
);
1928 reply("CSMSG_DNR_REMOVED", chan_name
);
1930 else if(dict_find(mask_dnrs
, chan_name
, NULL
))
1932 dict_remove(mask_dnrs
, chan_name
);
1933 reply("CSMSG_DNR_REMOVED", chan_name
);
1937 reply("CSMSG_NO_SUCH_DNR", chan_name
);
1944 chanserv_get_owned_count(struct handle_info
*hi
)
1946 struct userData
*cList
;
1949 for(owned
=0, cList
=hi
->channels
; cList
; cList
=cList
->u_next
)
1950 if(cList
->access
== UL_OWNER
)
1955 static CHANSERV_FUNC(cmd_register
)
1957 struct handle_info
*handle
;
1958 struct chanData
*cData
;
1959 struct modeNode
*mn
;
1960 char reason
[MAXLEN
];
1962 unsigned int new_channel
, force
=0;
1963 struct do_not_register
*dnr
;
1969 if(channel
->channel_info
)
1971 reply("CSMSG_ALREADY_REGGED", channel
->name
);
1975 if(channel
->bad_channel
)
1977 reply("CSMSG_ILLEGAL_CHANNEL", channel
->name
);
1981 if(!IsHelping(user
) && (!(mn
= GetUserMode(channel
, user
)) || !(mn
->modes
& MODE_CHANOP
)))
1983 reply("CSMSG_MUST_BE_OPPED", channel
->name
);
1988 chan_name
= channel
->name
;
1994 reply("MSG_MISSING_PARAMS", cmd
->name
);
1995 svccmd_send_help_brief(user
, chanserv
, cmd
);
1998 if(!IsChannelName(argv
[1]))
2000 reply("MSG_NOT_CHANNEL_NAME");
2004 if(opserv_bad_channel(argv
[1]))
2006 reply("CSMSG_ILLEGAL_CHANNEL", argv
[1]);
2011 chan_name
= argv
[1];
2014 if(argc
>= (new_channel
+2))
2016 if(!IsHelping(user
))
2018 reply("CSMSG_PROXY_FORBIDDEN");
2022 if(!(handle
= modcmd_get_handle_info(user
, argv
[new_channel
+1])))
2024 force
= (argc
> (new_channel
+2)) && !irccasecmp(argv
[new_channel
+2], "force");
2025 dnr
= chanserv_is_dnr(chan_name
, handle
);
2027 /* Check if they are over the limit.. */
2028 if((chanserv_get_owned_count(handle
) >= chanserv_conf
.max_owned
) && !force
)
2030 reply("CSMSG_OWN_TOO_MANY", handle
->handle
, chanserv_conf
.max_owned
);
2037 handle
= user
->handle_info
;
2038 dnr
= chanserv_is_dnr(chan_name
, handle
);
2039 /* Check if they are over the limit.. */
2040 if((chanserv_get_owned_count(handle
) >= chanserv_conf
.max_owned
) && !force
)
2042 reply("CSMSG_YOU_OWN_TOO_MANY", chanserv_conf
.max_owned
);
2045 /* Check if another service is in the channel */
2047 for(n
= 0; n
< channel
->members
.used
; n
++)
2049 mn
= channel
->members
.list
[n
];
2050 if((mn
&& mn
->user
&& (mn
->user
->modes
& FLAGS_SERVICE
)) || IsLocal(mn
->user
))
2052 reply("CSMSG_ANOTHER_SERVICE");
2059 if(!IsHelping(user
))
2060 reply("CSMSG_DNR_CHANNEL", chan_name
);
2062 chanserv_show_dnrs(user
, cmd
, chan_name
, handle
->handle
);
2066 /* now handled above for message specilization *
2067 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2069 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
2075 channel
= AddChannel(argv
[1], now
, NULL
, NULL
, NULL
);
2077 cData
= register_channel(channel
, user
->handle_info
->handle
);
2078 scan_user_presence(add_channel_user(cData
, handle
, UL_OWNER
, 0, NULL
), NULL
);
2079 cData
->modes
= chanserv_conf
.default_modes
;
2081 cData
->modes
.modes_set
|= MODE_REGISTERED
;
2082 if (IsOffChannel(cData
))
2084 mod_chanmode_announce(chanserv
, channel
, &cData
->modes
);
2088 struct mod_chanmode
*change
= mod_chanmode_dup(&cData
->modes
, 1);
2089 change
->args
[change
->argc
].mode
= MODE_CHANOP
;
2090 change
->args
[change
->argc
].u
.member
= AddChannelUser(chanserv
, channel
);
2092 mod_chanmode_announce(chanserv
, channel
, change
);
2093 mod_chanmode_free(change
);
2096 /* Initialize the channel's max user record. */
2097 cData
->max
= channel
->members
.used
;
2099 if(handle
!= user
->handle_info
)
2100 reply("CSMSG_PROXY_SUCCESS", handle
->handle
, channel
->name
);
2102 reply("CSMSG_REG_SUCCESS", channel
->name
);
2104 sprintf(reason
, "%s registered to %s by %s.", channel
->name
, handle
->handle
, user
->handle_info
->handle
);
2105 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
2110 make_confirmation_string(struct userData
*uData
)
2112 static char strbuf
[16];
2117 for(src
= uData
->handle
->handle
; *src
; )
2118 accum
= accum
* 31 + toupper(*src
++);
2120 for(src
= uData
->channel
->channel
->name
; *src
; )
2121 accum
= accum
* 31 + toupper(*src
++);
2122 sprintf(strbuf
, "%08x", accum
);
2126 static CHANSERV_FUNC(cmd_unregister
)
2129 char reason
[MAXLEN
];
2130 struct chanData
*cData
;
2131 struct userData
*uData
;
2133 cData
= channel
->channel_info
;
2136 reply("CSMSG_NOT_REGISTERED", channel
->name
);
2140 uData
= GetChannelUser(cData
, user
->handle_info
);
2141 if(!uData
|| (uData
->access
< UL_OWNER
))
2143 reply("CSMSG_NO_ACCESS");
2147 if(IsProtected(cData
))
2149 reply("CSMSG_UNREG_NODELETE", channel
->name
);
2153 if(!IsHelping(user
))
2155 const char *confirm_string
;
2156 if(IsSuspended(cData
))
2158 reply("CSMSG_CHAN_SUSPENDED", channel
->name
, cData
->suspended
->reason
);
2161 confirm_string
= make_confirmation_string(uData
);
2162 if((argc
< 2) || strcmp(argv
[1], confirm_string
))
2164 reply("CSMSG_CONFIRM_UNREG", channel
->name
, confirm_string
);
2169 sprintf(reason
, "unregistered by %s.", user
->handle_info
->handle
);
2170 name
= strdup(channel
->name
);
2171 unregister_channel(cData
, reason
);
2172 spamserv_cs_unregister(user
, channel
, manually
, "unregistered");
2173 reply("CSMSG_UNREG_SUCCESS", name
);
2179 ss_cs_join_channel(struct chanNode
*channel
, int spamserv_join
)
2181 extern struct userNode
*spamserv
;
2182 struct mod_chanmode
*change
;
2184 if(spamserv
&& spamserv_join
&& get_chanInfo(channel
->name
))
2186 change
= mod_chanmode_alloc(2);
2188 change
->args
[0].mode
= MODE_CHANOP
;
2189 change
->args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
2190 change
->args
[1].mode
= MODE_CHANOP
;
2191 change
->args
[1].u
.member
= AddChannelUser(spamserv
, channel
);
2195 change
= mod_chanmode_alloc(1);
2197 change
->args
[0].mode
= MODE_CHANOP
;
2198 change
->args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
2201 mod_chanmode_announce(chanserv
, channel
, change
);
2202 mod_chanmode_free(change
);
2205 static CHANSERV_FUNC(cmd_move
)
2207 struct mod_chanmode change
;
2208 struct chanNode
*target
;
2209 struct modeNode
*mn
;
2210 struct userData
*uData
;
2211 char reason
[MAXLEN
];
2212 struct do_not_register
*dnr
;
2213 int chanserv_join
= 0, spamserv_join
;
2217 if(IsProtected(channel
->channel_info
))
2219 reply("CSMSG_MOVE_NODELETE", channel
->name
);
2223 if(!IsChannelName(argv
[1]))
2225 reply("MSG_NOT_CHANNEL_NAME");
2229 if(opserv_bad_channel(argv
[1]))
2231 reply("CSMSG_ILLEGAL_CHANNEL", argv
[1]);
2235 if(!IsHelping(user
) || (argc
< 3) || irccasecmp(argv
[2], "force"))
2237 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
2239 if((uData
->access
== UL_OWNER
) && (dnr
= chanserv_is_dnr(argv
[1], uData
->handle
)))
2241 if(!IsHelping(user
))
2242 reply("CSMSG_DNR_CHANNEL_MOVE", argv
[1]);
2244 chanserv_show_dnrs(user
, cmd
, argv
[1], uData
->handle
->handle
);
2250 mod_chanmode_init(&change
);
2251 if(!(target
= GetChannel(argv
[1])))
2253 target
= AddChannel(argv
[1], now
, NULL
, NULL
, NULL
);
2254 if(!IsSuspended(channel
->channel_info
))
2257 else if(target
->channel_info
)
2259 reply("CSMSG_ALREADY_REGGED", target
->name
);
2262 else if((!(mn
= GetUserMode(target
, user
)) || !(mn
->modes
&& MODE_CHANOP
))
2263 && !IsHelping(user
))
2265 reply("CSMSG_MUST_BE_OPPED", target
->name
);
2268 else if(!IsSuspended(channel
->channel_info
))
2273 /* Clear MODE_REGISTERED from old channel, add it to new. */
2275 change
.modes_clear
= MODE_REGISTERED
;
2276 mod_chanmode_announce(chanserv
, channel
, &change
);
2277 change
.modes_clear
= 0;
2278 change
.modes_set
= MODE_REGISTERED
;
2279 mod_chanmode_announce(chanserv
, target
, &change
);
2282 /* Move the channel_info to the target channel; it
2283 shouldn't be necessary to clear timeq callbacks
2284 for the old channel. */
2285 target
->channel_info
= channel
->channel_info
;
2286 target
->channel_info
->channel
= target
;
2287 channel
->channel_info
= NULL
;
2289 spamserv_join
= spamserv_cs_move_merge(user
, channel
, target
, 1);
2292 ss_cs_join_channel(target
, spamserv_join
);
2294 sprintf(reason
, "%s moved to %s by %s.", channel
->name
, target
->name
, user
->handle_info
->handle
);
2295 if(!IsSuspended(target
->channel_info
))
2297 char reason2
[MAXLEN
];
2298 sprintf(reason2
, "Channel moved to %s by %s.", target
->name
, user
->handle_info
->handle
);
2299 DelChannelUser(chanserv
, channel
, reason2
, 0);
2301 UnlockChannel(channel
);
2302 LockChannel(target
);
2303 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
2304 reply("CSMSG_MOVE_SUCCESS", target
->name
);
2309 merge_users(struct chanData
*source
, struct chanData
*target
)
2311 struct userData
*suData
, *tuData
, *next
;
2317 /* Insert the source's users into the scratch area. */
2318 for(suData
= source
->users
; suData
; suData
= suData
->next
)
2319 dict_insert(merge
, suData
->handle
->handle
, suData
);
2321 /* Iterate through the target's users, looking for
2322 users common to both channels. The lower access is
2323 removed from either the scratch area or target user
2325 for(tuData
= target
->users
; tuData
; tuData
= next
)
2327 struct userData
*choice
;
2329 next
= tuData
->next
;
2331 /* If a source user exists with the same handle as a target
2332 channel's user, resolve the conflict by removing one. */
2333 suData
= dict_find(merge
, tuData
->handle
->handle
, NULL
);
2337 /* Pick the data we want to keep. */
2338 /* If the access is the same, use the later seen time. */
2339 if(suData
->access
== tuData
->access
)
2340 choice
= (suData
->seen
> tuData
->seen
) ? suData
: tuData
;
2341 else /* Otherwise, keep the higher access level. */
2342 choice
= (suData
->access
> tuData
->access
) ? suData
: tuData
;
2344 /* Remove the user that wasn't picked. */
2345 if(choice
== tuData
)
2347 dict_remove(merge
, suData
->handle
->handle
);
2348 del_channel_user(suData
, 0);
2351 del_channel_user(tuData
, 0);
2354 /* Move the remaining users to the target channel. */
2355 for(it
= dict_first(merge
); it
; it
= iter_next(it
))
2357 suData
= iter_data(it
);
2359 /* Insert the user into the target channel's linked list. */
2360 suData
->prev
= NULL
;
2361 suData
->next
= target
->users
;
2362 suData
->channel
= target
;
2365 target
->users
->prev
= suData
;
2366 target
->users
= suData
;
2368 /* Update the user counts for the target channel; the
2369 source counts are left alone. */
2370 target
->userCount
++;
2373 /* Possible to assert (source->users == NULL) here. */
2374 source
->users
= NULL
;
2379 merge_bans(struct chanData
*source
, struct chanData
*target
)
2381 struct banData
*sbData
, *tbData
, *sNext
, *tNext
, *tFront
;
2383 /* Hold on to the original head of the target ban list
2384 to avoid comparing source bans with source bans. */
2385 tFront
= target
->bans
;
2387 /* Perform a totally expensive O(n*m) merge, ick. */
2388 for(sbData
= source
->bans
; sbData
; sbData
= sNext
)
2390 /* Flag to track whether the ban's been moved
2391 to the destination yet. */
2394 /* Possible to assert (sbData->prev == NULL) here. */
2395 sNext
= sbData
->next
;
2397 for(tbData
= tFront
; tbData
; tbData
= tNext
)
2399 tNext
= tbData
->next
;
2401 /* Perform two comparisons between each source
2402 and target ban, conflicts are resolved by
2403 keeping the broader ban and copying the later
2404 expiration and triggered time. */
2405 if(match_ircglobs(tbData
->mask
, sbData
->mask
))
2407 /* There is a broader ban in the target channel that
2408 overrides one in the source channel; remove the
2409 source ban and break. */
2410 if(sbData
->expires
> tbData
->expires
)
2411 tbData
->expires
= sbData
->expires
;
2412 if(sbData
->triggered
> tbData
->triggered
)
2413 tbData
->triggered
= sbData
->triggered
;
2414 del_channel_ban(sbData
);
2417 else if(match_ircglobs(sbData
->mask
, tbData
->mask
))
2419 /* There is a broader ban in the source channel that
2420 overrides one in the target channel; remove the
2421 target ban, fall through and move the source over. */
2422 if(tbData
->expires
> sbData
->expires
)
2423 sbData
->expires
= tbData
->expires
;
2424 if(tbData
->triggered
> sbData
->triggered
)
2425 sbData
->triggered
= tbData
->triggered
;
2426 if(tbData
== tFront
)
2428 del_channel_ban(tbData
);
2431 /* Source bans can override multiple target bans, so
2432 we allow a source to run through this loop multiple
2433 times, but we can only move it once. */
2438 /* Remove the source ban from the source ban list. */
2440 sbData
->next
->prev
= sbData
->prev
;
2442 /* Modify the source ban's associated channel. */
2443 sbData
->channel
= target
;
2445 /* Insert the ban into the target channel's linked list. */
2446 sbData
->prev
= NULL
;
2447 sbData
->next
= target
->bans
;
2450 target
->bans
->prev
= sbData
;
2451 target
->bans
= sbData
;
2453 /* Update the user counts for the target channel. */
2458 /* Possible to assert (source->bans == NULL) here. */
2459 source
->bans
= NULL
;
2463 merge_data(struct chanData
*source
, struct chanData
*target
)
2465 /* Use more recent visited and owner-transfer time; use older
2466 * registered time. Bitwise or may_opchan. Use higher max.
2467 * Do not touch last_refresh, ban count or user counts.
2469 if(source
->visited
> target
->visited
)
2470 target
->visited
= source
->visited
;
2471 if(source
->registered
< target
->registered
)
2472 target
->registered
= source
->registered
;
2473 if(source
->ownerTransfer
> target
->ownerTransfer
)
2474 target
->ownerTransfer
= source
->ownerTransfer
;
2475 if(source
->may_opchan
)
2476 target
->may_opchan
= 1;
2477 if(source
->max
> target
->max
)
2478 target
->max
= source
->max
;
2482 merge_channel(struct chanData
*source
, struct chanData
*target
)
2484 merge_users(source
, target
);
2485 merge_bans(source
, target
);
2486 merge_data(source
, target
);
2489 static CHANSERV_FUNC(cmd_merge
)
2491 struct userData
*target_user
;
2492 struct chanNode
*target
;
2493 char reason
[MAXLEN
];
2497 /* Make sure the target channel exists and is registered to the user
2498 performing the command. */
2499 if(!(target
= GetChannel(argv
[1])))
2501 reply("MSG_INVALID_CHANNEL");
2505 if(!target
->channel_info
)
2507 reply("CSMSG_NOT_REGISTERED", target
->name
);
2511 if(IsProtected(channel
->channel_info
))
2513 reply("CSMSG_MERGE_NODELETE");
2517 if(IsSuspended(target
->channel_info
))
2519 reply("CSMSG_MERGE_SUSPENDED");
2523 if(channel
== target
)
2525 reply("CSMSG_MERGE_SELF");
2529 target_user
= GetChannelUser(target
->channel_info
, user
->handle_info
);
2530 if(!target_user
|| (target_user
->access
< UL_OWNER
))
2532 reply("CSMSG_MERGE_NOT_OWNER");
2536 /* Merge the channel structures and associated data. */
2537 merge_channel(channel
->channel_info
, target
->channel_info
);
2538 spamserv_cs_move_merge(user
, channel
, target
, 0);
2539 sprintf(reason
, "merged into %s by %s.", target
->name
, user
->handle_info
->handle
);
2540 unregister_channel(channel
->channel_info
, reason
);
2541 reply("CSMSG_MERGE_SUCCESS", target
->name
);
2545 static CHANSERV_FUNC(cmd_opchan
)
2547 struct mod_chanmode change
;
2548 if(!IsHelping(user
) && !channel
->channel_info
->may_opchan
)
2550 reply("CSMSG_ALREADY_OPCHANNED", channel
->name
);
2553 channel
->channel_info
->may_opchan
= 0;
2554 mod_chanmode_init(&change
);
2556 change
.args
[0].mode
= MODE_CHANOP
;
2557 change
.args
[0].u
.member
= GetUserMode(channel
, chanserv
);
2558 mod_chanmode_announce(chanserv
, channel
, &change
);
2559 reply("CSMSG_OPCHAN_DONE", channel
->name
);
2563 static CHANSERV_FUNC(cmd_adduser
)
2565 struct userData
*actee
;
2566 struct userData
*actor
;
2567 struct handle_info
*handle
;
2568 unsigned short access
;
2572 if(channel
->channel_info
->userCount
>= chanserv_conf
.max_chan_users
)
2574 reply("CSMSG_MAXIMUM_USERS", chanserv_conf
.max_chan_users
);
2578 access
= user_level_from_name(argv
[2], UL_OWNER
);
2581 reply("CSMSG_INVALID_ACCESS", argv
[2]);
2585 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2586 if(actor
->access
<= access
)
2588 reply("CSMSG_NO_BUMP_ACCESS");
2592 if(!(handle
= modcmd_get_handle_info(user
, argv
[1])))
2594 // 'kevin must first authenticate with AuthServ.' is sent to user
2595 struct userNode
*unode
;
2596 unode
= GetUserH(argv
[1]); /* find user struct by nick */
2599 if(find_adduser_pending(channel
, unode
)) {
2600 reply("CSMSG_ADDUSER_PENDING_ALREADY", channel
->name
);
2603 if(IsInChannel(channel
, unode
)) {
2604 reply("CSMSG_ADDUSER_PENDING");
2605 add_adduser_pending(channel
, unode
, access
);
2606 send_message_type(1,unode
, chanserv
, "CSMSG_ADDUSER_PENDING_TARGET", user
->nick
, channel
->name
);
2608 /* this results in user must auth AND not in chan errors. too confusing..
2610 reply("CSMSG_ADDUSER_PENDING_NOTINCHAN", channel->name);
2618 if((actee
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2620 reply("CSMSG_USER_EXISTS", handle
->handle
, channel
->name
, user_level_name_from_level(actee
->access
));
2624 actee
= add_channel_user(channel
->channel_info
, handle
, access
, 0, NULL
);
2625 scan_user_presence(actee
, NULL
);
2626 reply("CSMSG_ADDED_USER", handle
->handle
, channel
->name
, user_level_name_from_level(access
), access
);
2630 static CHANSERV_FUNC(cmd_clvl
)
2632 struct handle_info
*handle
;
2633 struct userData
*victim
;
2634 struct userData
*actor
;
2635 unsigned short new_access
;
2636 int privileged
= IsHelping(user
) && ((user
->handle_info
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
->channel_info
));
2640 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2642 if(!(handle
= modcmd_get_handle_info(user
, argv
[1])))
2645 if(handle
== user
->handle_info
&& !privileged
)
2647 reply("CSMSG_NO_SELF_CLVL");
2651 if(!(victim
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2653 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
2657 if(actor
->access
<= victim
->access
&& !privileged
)
2659 reply("MSG_USER_OUTRANKED", handle
->handle
);
2663 new_access
= user_level_from_name(argv
[2], UL_OWNER
);
2667 reply("CSMSG_INVALID_ACCESS", argv
[2]);
2671 if(new_access
>= actor
->access
&& !privileged
)
2673 reply("CSMSG_NO_BUMP_ACCESS");
2677 victim
->access
= new_access
;
2678 reply("CSMSG_CHANGED_ACCESS", handle
->handle
, user_level_name_from_level(new_access
), new_access
, channel
->name
);
2682 static CHANSERV_FUNC(cmd_deluser
)
2684 struct handle_info
*handle
;
2685 struct userData
*victim
;
2686 struct userData
*actor
;
2687 unsigned short access
;
2692 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2694 if(!(handle
= modcmd_get_handle_info(user
, argv
[argc
-1])))
2697 if(!(victim
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2699 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
2705 access
= user_level_from_name(argv
[1], UL_OWNER
);
2708 reply("CSMSG_INVALID_ACCESS", argv
[1]);
2711 if(access
!= victim
->access
)
2713 reply("CSMSG_INCORRECT_ACCESS", handle
->handle
, user_level_name_from_level(victim
->access
), argv
[1]);
2719 access
= victim
->access
;
2722 if((actor
->access
<= victim
->access
) && !IsHelping(user
))
2724 reply("MSG_USER_OUTRANKED", victim
->handle
->handle
);
2728 chan_name
= strdup(channel
->name
);
2729 del_channel_user(victim
, 1);
2730 reply("CSMSG_DELETED_USER", handle
->handle
, access
, chan_name
);
2736 cmd_mdel_user(struct userNode
*user
, struct chanNode
*channel
, unsigned short min_access
, unsigned short max_access
, char *mask
, struct svccmd
*cmd
)
2738 struct userData
*actor
, *uData
, *next
;
2740 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2742 if(min_access
> max_access
)
2744 reply("CSMSG_BAD_RANGE", min_access
, max_access
);
2748 if((actor
->access
<= max_access
) && !IsHelping(user
))
2750 reply("CSMSG_NO_ACCESS");
2754 for(uData
= channel
->channel_info
->users
; uData
; uData
= next
)
2758 if((uData
->access
>= min_access
)
2759 && (uData
->access
<= max_access
)
2760 && match_ircglob(uData
->handle
->handle
, mask
))
2761 del_channel_user(uData
, 1);
2764 reply("CSMSG_DELETED_USERS", mask
, min_access
, max_access
, channel
->name
);
2768 static CHANSERV_FUNC(cmd_mdelowner
)
2770 return cmd_mdel_user(user
, channel
, UL_OWNER
, UL_OWNER
, argv
[1], cmd
);
2773 static CHANSERV_FUNC(cmd_mdelcoowner
)
2775 return cmd_mdel_user(user
, channel
, UL_COOWNER
, UL_COOWNER
, argv
[1], cmd
);
2778 static CHANSERV_FUNC(cmd_mdelmanager
)
2780 return cmd_mdel_user(user
, channel
, UL_MANAGER
, UL_MANAGER
, argv
[1], cmd
);
2783 static CHANSERV_FUNC(cmd_mdelop
)
2785 return cmd_mdel_user(user
, channel
, UL_OP
, UL_OP
, argv
[1], cmd
);
2788 static CHANSERV_FUNC(cmd_mdelpeon
)
2790 return cmd_mdel_user(user
, channel
, UL_PEON
, UL_PEON
, argv
[1], cmd
);
2793 static CHANSERV_FUNC(cmd_mdelhalfop
)
2795 return cmd_mdel_user(user
, channel
, UL_HALFOP
, UL_HALFOP
, argv
[1], cmd
);
2801 cmd_trim_bans(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, unsigned long duration
)
2803 struct banData
*bData
, *next
;
2804 char interval
[INTERVALLEN
];
2809 limit
= now
- duration
;
2810 for(bData
= channel
->channel_info
->bans
; bData
; bData
= next
)
2814 if((bData
->triggered
&& bData
->triggered
>= limit
) || (bData
->set
&& bData
->set
>= limit
))
2817 del_channel_ban(bData
);
2821 intervalString(interval
, duration
, user
->handle_info
);
2822 reply("CSMSG_TRIMMED_LAMERS", count
, channel
->name
, interval
);
2827 cmd_trim_users(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, unsigned short min_access
, unsigned short max_access
, unsigned long duration
, int vacation
)
2829 struct userData
*actor
, *uData
, *next
;
2830 char interval
[INTERVALLEN
];
2834 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2835 if(min_access
> max_access
)
2837 reply("CSMSG_BAD_RANGE", min_access
, max_access
);
2841 if((actor
->access
<= max_access
) && !IsHelping(user
))
2843 reply("CSMSG_NO_ACCESS");
2848 limit
= now
- duration
;
2849 for(uData
= channel
->channel_info
->users
; uData
; uData
= next
)
2853 if((uData
->seen
> limit
)
2855 || (HANDLE_FLAGGED(uData
->handle
, FROZEN
) && !vacation
))
2858 if(((uData
->access
>= min_access
) && (uData
->access
<= max_access
))
2859 || (!max_access
&& (uData
->access
< actor
->access
)))
2861 del_channel_user(uData
, 1);
2869 max_access
= (actor
->access
> UL_OWNER
) ? UL_OWNER
: (actor
->access
- 1);
2871 reply("CSMSG_TRIMMED_USERS", count
, min_access
, max_access
, channel
->name
, intervalString(interval
, duration
, user
->handle_info
));
2875 static CHANSERV_FUNC(cmd_trim
)
2877 unsigned long duration
;
2878 unsigned short min_level
, max_level
;
2883 vacation
= argc
> 3 && !strcmp(argv
[3], "vacation");
2884 duration
= ParseInterval(argv
[2]);
2887 reply("CSMSG_CANNOT_TRIM");
2891 if(!irccasecmp(argv
[1], "lamers"))
2893 cmd_trim_bans(cmd
, user
, channel
, duration
); /* trim_lamers.. */
2896 else if(!irccasecmp(argv
[1], "users"))
2898 cmd_trim_users(cmd
, user
, channel
, 0, 0, duration
, vacation
);
2901 else if(parse_level_range(&min_level
, &max_level
, argv
[1]))
2903 cmd_trim_users(cmd
, user
, channel
, min_level
, max_level
, duration
, vacation
);
2906 else if((min_level
= user_level_from_name(argv
[1], UL_OWNER
)))
2908 cmd_trim_users(cmd
, user
, channel
, min_level
, min_level
, duration
, vacation
);
2913 reply("CSMSG_INVALID_TRIM", argv
[1]);
2918 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
2919 to the user. cmd_all takes advantage of this. */
2920 static CHANSERV_FUNC(cmd_up
)
2922 struct mod_chanmode change
;
2923 struct userData
*uData
;
2926 mod_chanmode_init(&change
);
2928 change
.args
[0].u
.member
= GetUserMode(channel
, user
);
2929 if(!change
.args
[0].u
.member
)
2932 reply("MSG_CHANNEL_ABSENT", channel
->name
);
2936 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
2940 reply("CSMSG_GODMODE_UP", argv
[0]);
2943 else if(uData
->access
>= UL_OP
)
2945 change
.args
[0].mode
= MODE_CHANOP
;
2946 errmsg
= "CSMSG_ALREADY_OPPED";
2948 else if(uData
->access
>= UL_HALFOP
)
2950 change
.args
[0].mode
= MODE_HALFOP
;
2951 errmsg
= "CSMSG_ALREADY_HALFOPPED";
2953 else if(uData
->access
>= UL_PEON
&& (channel
->channel_info
->chOpts
[chAutomode
] != 'm' ))
2955 change
.args
[0].mode
= MODE_VOICE
;
2956 errmsg
= "CSMSG_ALREADY_VOICED";
2961 reply("CSMSG_NO_ACCESS");
2964 change
.args
[0].mode
&= ~change
.args
[0].u
.member
->modes
;
2965 if(!change
.args
[0].mode
)
2968 reply(errmsg
, channel
->name
);
2971 modcmd_chanmode_announce(&change
);
2975 static CHANSERV_FUNC(cmd_down
)
2977 struct mod_chanmode change
;
2979 mod_chanmode_init(&change
);
2981 change
.args
[0].u
.member
= GetUserMode(channel
, user
);
2982 if(!change
.args
[0].u
.member
)
2985 reply("MSG_CHANNEL_ABSENT", channel
->name
);
2989 if(!change
.args
[0].u
.member
->modes
)
2992 reply("CSMSG_ALREADY_DOWN", channel
->name
);
2996 change
.args
[0].mode
= MODE_REMOVE
| change
.args
[0].u
.member
->modes
;
2997 modcmd_chanmode_announce(&change
);
3001 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
)
3003 struct userData
*cList
;
3005 for(cList
= user
->handle_info
->channels
; cList
; cList
= cList
->u_next
)
3007 if(IsSuspended(cList
->channel
)
3008 || IsUserSuspended(cList
)
3009 || !GetUserMode(cList
->channel
->channel
, user
))
3012 mcmd(user
, cList
->channel
->channel
, 0, NULL
, cmd
);
3018 static CHANSERV_FUNC(cmd_upall
)
3020 return cmd_all(CSFUNC_ARGS
, cmd_up
);
3023 static CHANSERV_FUNC(cmd_downall
)
3025 return cmd_all(CSFUNC_ARGS
, cmd_down
);
3028 typedef int validate_func_t(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
);
3029 typedef void process_func_t(unsigned int num
, struct userNode
**newops
, struct chanNode
*channel
, struct userNode
*who
, int announce
);
3032 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
)
3034 unsigned int ii
, valid
;
3035 struct userNode
*victim
;
3036 struct mod_chanmode
*change
;
3038 change
= mod_chanmode_alloc(argc
- 1);
3040 for(ii
=valid
=0; ++ii
< argc
; )
3042 if(!(victim
= GetUserH(argv
[ii
])))
3044 change
->args
[valid
].mode
= mode
;
3045 change
->args
[valid
].u
.member
= GetUserMode(channel
, victim
);
3046 if(!change
->args
[valid
].u
.member
)
3048 if(validate
&& !validate(cmd
, user
, channel
, victim
))
3053 change
->argc
= valid
;
3054 if(valid
< (argc
-1))
3055 reply("CSMSG_PROCESS_FAILED");
3058 modcmd_chanmode_announce(change
);
3059 reply(action
, channel
->name
);
3061 mod_chanmode_free(change
);
3065 static CHANSERV_FUNC(cmd_op
)
3067 return modify_users(CSFUNC_ARGS
, validate_op
, MODE_CHANOP
, "CSMSG_OPPED_USERS");
3070 static CHANSERV_FUNC(cmd_hop
)
3072 return modify_users(CSFUNC_ARGS
, validate_halfop
, MODE_HALFOP
, "CSMSG_HALFOPPED_USERS");
3075 static CHANSERV_FUNC(cmd_deop
)
3077 return modify_users(CSFUNC_ARGS
, validate_deop
, MODE_REMOVE
|MODE_CHANOP
, "CSMSG_DEOPPED_USERS");
3080 static CHANSERV_FUNC(cmd_dehop
)
3082 return modify_users(CSFUNC_ARGS
, validate_dehop
, MODE_REMOVE
|MODE_HALFOP
, "CSMSG_DEHALFOPPED_USERS");
3085 static CHANSERV_FUNC(cmd_voice
)
3087 return modify_users(CSFUNC_ARGS
, NULL
, MODE_VOICE
, "CSMSG_VOICED_USERS");
3090 static CHANSERV_FUNC(cmd_devoice
)
3092 return modify_users(CSFUNC_ARGS
, NULL
, MODE_REMOVE
|MODE_VOICE
, "CSMSG_DEVOICED_USERS");
3096 bad_channel_ban(struct chanNode
*channel
, struct userNode
*user
, const char *ban
, unsigned int *victimCount
, struct modeNode
**victims
)
3102 for(ii
=0; ii
<channel
->members
.used
; ii
++)
3104 struct modeNode
*mn
= channel
->members
.list
[ii
];
3106 if(IsService(mn
->user
))
3109 if(!user_matches_glob(mn
->user
, ban
, MATCH_USENICK
| MATCH_VISIBLE
))
3112 if(protect_user(mn
->user
, user
, channel
->channel_info
))
3116 victims
[(*victimCount
)++] = mn
;
3122 eject_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
)
3124 struct userNode
*victim
;
3125 struct modeNode
**victims
;
3126 unsigned int offset
, n
, victimCount
, duration
= 0;
3127 char *reason
= "Bye.", *ban
, *name
;
3128 char interval
[INTERVALLEN
];
3130 offset
= (action
& ACTION_ADD_TIMED_LAMER
) ? 3 : 2;
3131 REQUIRE_PARAMS(offset
);
3134 reason
= unsplit_string(argv
+ offset
, argc
- offset
, NULL
);
3135 if(strlen(reason
) > (TOPICLEN
- (NICKLEN
+ 3)))
3137 /* Truncate the reason to a length of TOPICLEN, as
3138 the ircd does; however, leave room for an ellipsis
3139 and the kicker's nick. */
3140 sprintf(reason
+ (TOPICLEN
- (NICKLEN
+ 6)), "...");
3144 if((victim
= GetUserH(argv
[1])))
3146 victims
= alloca(sizeof(victims
[0]));
3147 victims
[0] = GetUserMode(channel
, victim
);
3148 /* XXX: The comparison with ACTION_KICK is just because all
3149 * other actions can work on users outside the channel, and we
3150 * want to allow those (e.g. unbans) in that case. If we add
3151 * some other ejection action for in-channel users, change
3153 victimCount
= victims
[0] ? 1 : 0;
3155 if(IsService(victim
))
3158 reply("MSG_SERVICE_IMMUNE", victim
->nick
);
3162 if((action
== ACTION_KICK
) && !victimCount
)
3165 reply("MSG_CHANNEL_USER_ABSENT", victim
->nick
, channel
->name
);
3169 if(protect_user(victim
, user
, channel
->channel_info
))
3171 // This translates to send_message(user, cmd->parent->bot, ...)
3172 // if user is x3 (ctcp action) cmd is null and segfault.
3174 reply("CSMSG_USER_PROTECTED", victim
->nick
);
3178 ban
= generate_hostmask(victim
, GENMASK_STRICT_HOST
|GENMASK_ANY_IDENT
);
3179 name
= victim
->nick
;
3183 if(!is_ircmask(argv
[1]))
3186 reply("MSG_NICK_UNKNOWN", argv
[1]);
3190 victims
= alloca(sizeof(victims
[0]) * channel
->members
.used
);
3192 if(bad_channel_ban(channel
, user
, argv
[1], &victimCount
, victims
))
3195 reply("CSMSG_MASK_PROTECTED", argv
[1]);
3198 /* If i want to ban *.nl and theres 5 of them, what is it to the bot?!?
3199 // if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
3201 We use x2 style over-mask detection instead because it doesnt stop channel owners from doing
3202 reasonable bans, but does stop *@*, *@*a* *@*b* etc type masks. Yes, you can defeat it with
3203 some creativity, but its not x3's job to be the ban censor anyway. */
3204 if(is_overmask(argv
[1]))
3207 reply("CSMSG_LAME_MASK", argv
[1]);
3211 if((action
== ACTION_KICK
) && (victimCount
== 0))
3214 reply("CSMSG_NO_MATCHING_USERS", channel
->name
, argv
[1]);
3218 name
= ban
= strdup(argv
[1]);
3221 /* Truncate the ban in place if necessary; we must ensure
3222 that 'ban' is a valid ban mask before sanitizing it. */
3223 sanitize_ircmask(ban
);
3225 if(action
& ACTION_ADD_LAMER
)
3227 struct banData
*bData
, *next
;
3229 if(channel
->channel_info
->banCount
>= chanserv_conf
.max_chan_bans
) /* ..lamers.. */
3232 reply("CSMSG_MAXIMUM_LAMERS", chanserv_conf
.max_chan_bans
); /* ..lamers.. */
3237 if(action
& ACTION_ADD_TIMED_LAMER
)
3239 duration
= ParseInterval(argv
[2]);
3244 reply("CSMSG_DURATION_TOO_LOW");
3248 else if(duration
> (86400 * 365 * 2))
3251 reply("CSMSG_DURATION_TOO_HIGH");
3258 for(bData
= channel
->channel_info
->bans
; bData
; bData
= next
)
3260 if(match_ircglobs(bData
->mask
, ban
))
3262 int exact
= !irccasecmp(bData
->mask
, ban
);
3264 /* The ban is redundant; there is already a ban
3265 with the same effect in place. */
3269 free(bData
->reason
);
3270 bData
->reason
= strdup(reason
);
3271 safestrncpy(bData
->owner
, (user
->handle_info
? user
->handle_info
->handle
: user
->nick
), sizeof(bData
->owner
));
3273 reply("CSMSG_REASON_CHANGE", ban
);
3277 if(exact
&& bData
->expires
)
3281 /* If the ban matches an existing one exactly,
3282 extend the expiration time if the provided
3283 duration is longer. */
3284 if(duration
&& ((time_t)(now
+ duration
) > bData
->expires
))
3286 bData
->expires
= now
+ duration
;
3297 /* Delete the expiration timeq entry and
3298 requeue if necessary. */
3299 timeq_del(0, expire_ban
, bData
, TIMEQ_IGNORE_WHEN
);
3302 timeq_add(bData
->expires
, expire_ban
, bData
);
3306 /* automated kickban, dont reply */
3309 reply("CSMSG_LAMER_EXTENDED", ban
, intervalString(interval
, duration
, user
->handle_info
));
3311 reply("CSMSG_LAMER_ADDED", name
, channel
->name
);
3317 reply("CSMSG_REDUNDANT_LAMER", name
, channel
->name
);
3324 if(match_ircglobs(ban
, bData
->mask
))
3326 /* The ban we are adding makes previously existing
3327 bans redundant; silently remove them. */
3328 del_channel_ban(bData
);
3332 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
);
3334 name
= ban
= strdup(bData
->mask
);
3338 /* WHAT DOES THIS DO?? -Rubin */
3339 for(n
= 0; n
< chanserv_conf
.old_ban_names
->used
; ++n
)
3341 extern const char *hidden_host_suffix
;
3342 const char *old_name
= chanserv_conf
.old_ban_names
->list
[n
];
3344 unsigned int l1
, l2
;
3347 l2
= strlen(old_name
);
3350 if(irccasecmp(ban
+ l1
- l2
, old_name
))
3352 new_mask
= malloc(MAXLEN
);
3353 sprintf(new_mask
, "%.*s%s", (int)(l1
-l2
), ban
, hidden_host_suffix
);
3355 name
= ban
= new_mask
;
3360 if(action
& ACTION_BAN
)
3362 unsigned int exists
;
3363 struct mod_chanmode
*change
;
3365 if(channel
->banlist
.used
>= MAXBANS
)
3368 reply("CSMSG_BANLIST_FULL", channel
->name
);
3373 exists
= ChannelBanExists(channel
, ban
);
3374 change
= mod_chanmode_alloc(victimCount
+ 1);
3375 for(n
= 0; n
< victimCount
; ++n
)
3377 change
->args
[n
].mode
= MODE_REMOVE
|MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
;
3378 change
->args
[n
].u
.member
= victims
[n
];
3382 change
->args
[n
].mode
= MODE_BAN
;
3383 change
->args
[n
++].u
.hostmask
= ban
;
3387 modcmd_chanmode_announce(change
);
3389 mod_chanmode_announce(chanserv
, channel
, change
);
3390 mod_chanmode_free(change
);
3392 if(exists
&& (action
== ACTION_BAN
))
3395 reply("CSMSG_REDUNDANT_BAN", name
, channel
->name
);
3401 if(action
& ACTION_KICK
)
3403 char kick_reason
[MAXLEN
];
3404 sprintf(kick_reason
, "(%s) %s", user
->nick
, reason
);
3406 for(n
= 0; n
< victimCount
; n
++)
3407 KickChannelUser(victims
[n
]->user
, channel
, chanserv
, kick_reason
);
3412 /* No response, since it was automated. */
3414 else if(action
& ACTION_ADD_LAMER
)
3417 reply("CSMSG_TIMED_LAMER_ADDED", name
, channel
->name
, intervalString(interval
, duration
, user
->handle_info
));
3419 reply("CSMSG_LAMER_ADDED", name
, channel
->name
);
3421 else if((action
& (ACTION_BAN
| ACTION_KICK
)) == (ACTION_BAN
| ACTION_KICK
))
3422 reply("CSMSG_KICK_BAN_DONE", name
, channel
->name
);
3423 else if(action
& ACTION_BAN
)
3424 reply("CSMSG_BAN_DONE", name
, channel
->name
);
3425 else if(action
& ACTION_KICK
&& victimCount
)
3426 reply("CSMSG_KICK_DONE", name
, channel
->name
);
3432 static CHANSERV_FUNC(cmd_kickban
)
3434 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
);
3437 static CHANSERV_FUNC(cmd_kick
)
3439 return eject_user(CSFUNC_ARGS
, ACTION_KICK
);
3442 static CHANSERV_FUNC(cmd_ban
)
3444 return eject_user(CSFUNC_ARGS
, ACTION_BAN
);
3447 static CHANSERV_FUNC(cmd_addlamer
)
3449 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
| ACTION_ADD_LAMER
);
3452 static CHANSERV_FUNC(cmd_addtimedlamer
)
3454 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
);
3457 static struct mod_chanmode
*
3458 find_matching_bans(struct banList
*bans
, struct userNode
*actee
, const char *mask
)
3460 struct mod_chanmode
*change
;
3461 unsigned char *match
;
3462 unsigned int ii
, count
;
3464 match
= alloca(bans
->used
);
3467 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3469 match
[ii
] = user_matches_glob(actee
, bans
->list
[ii
]->ban
,
3470 MATCH_USENICK
| MATCH_VISIBLE
);
3477 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3479 match
[ii
] = match_ircglobs(mask
, bans
->list
[ii
]->ban
);
3486 change
= mod_chanmode_alloc(count
);
3487 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3491 change
->args
[count
].mode
= MODE_REMOVE
| MODE_BAN
;
3492 change
->args
[count
++].u
.hostmask
= strdup(bans
->list
[ii
]->ban
);
3494 assert(count
== change
->argc
);
3498 void expire_bans(UNUSED_ARG(void* data
)) /* Real bans, not lamers */
3500 unsigned int jj
, ii
, count
;
3502 struct chanData
*channel
;
3504 struct mod_chanmode
*change
;
3506 log_module(CS_LOG
, LOG_DEBUG
, "Checking for expired bans");
3507 /* Walk through every channel */
3508 for(channel
= channelList
; channel
; channel
= channel
->next
) {
3509 switch(channel
->chOpts
[chBanTimeout
])
3511 default: case '0': continue; /* Dont remove bans in this chan */
3512 case '1': bantimeout
= now
- (10 * 60); break; /* 10 minutes */
3513 case '2': bantimeout
= now
- (2 * 60 * 60); break; /* 2 hours */
3514 case '3': bantimeout
= now
- (4 * 60 * 60); break; /* 4 hours */
3515 case '4': bantimeout
= now
- (24 * 60 * 60); break; /* 24 hours */
3516 case '5': bantimeout
= now
- (7 * 24 * 60 * 60); break; /* 1 week */
3519 /* First find out how many bans were going to unset */
3520 for (jj
=0; jj
< channel
->channel
->banlist
.used
; ++jj
) {
3521 if(channel
->channel
->banlist
.list
[jj
]->set
< bantimeout
)
3525 /* At least one ban, so setup a removal */
3526 change
= mod_chanmode_alloc(count
);
3528 /* Walk over every ban in this channel.. */
3529 for (jj
=0; jj
< channel
->channel
->banlist
.used
; ++jj
) {
3530 bn
= channel
->channel
->banlist
.list
[jj
];
3531 if (bn
->set
< bantimeout
) {
3532 log_module(CS_LOG
, LOG_DEBUG
, "Removing ban %s from %s", bn
->ban
, channel
->channel
->name
);
3534 /* Add this ban to the mode change */
3535 change
->args
[ii
].mode
= MODE_REMOVE
| MODE_BAN
;
3536 change
->args
[ii
].u
.hostmask
= strdup(bn
->ban
);
3538 /* Pull this ban out of the list */
3539 banList_remove(&(channel
->channel
->banlist
), bn
);
3544 /* Send the modes to IRC */
3545 mod_chanmode_announce(chanserv
, channel
->channel
, change
);
3547 /* free memory from strdup above */
3548 for(ii
= 0; ii
< count
; ++ii
)
3549 free((char*)change
->args
[ii
].u
.hostmask
);
3551 mod_chanmode_free(change
);
3554 /* Set this function to run again */
3555 if(chanserv_conf
.ban_timeout_frequency
)
3556 timeq_add(now
+ chanserv_conf
.ban_timeout_frequency
, expire_bans
, NULL
);
3561 unban_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
)
3563 struct userNode
*actee
;
3569 /* may want to allow a comma delimited list of users... */
3570 if(!(actee
= GetUserH(argv
[1])))
3572 if(!is_ircmask(argv
[1]))
3574 reply("MSG_NICK_UNKNOWN", argv
[1]);
3578 mask
= strdup(argv
[1]);
3581 /* We don't sanitize the mask here because ircu
3583 if(action
& ACTION_UNBAN
)
3585 struct mod_chanmode
*change
;
3586 change
= find_matching_bans(&channel
->banlist
, actee
, mask
);
3591 modcmd_chanmode_announce(change
);
3592 for(ii
= 0; ii
< change
->argc
; ++ii
)
3593 free((char*)change
->args
[ii
].u
.hostmask
);
3594 mod_chanmode_free(change
);
3599 if(action
& ACTION_DEL_LAMER
)
3601 struct banData
*ban
, *next
;
3603 ban
= channel
->channel_info
->bans
; /* lamers */
3607 for( ; ban
&& !user_matches_glob(actee
, ban
->mask
, MATCH_USENICK
| MATCH_VISIBLE
);
3610 for( ; ban
&& !match_ircglobs(mask
, ban
->mask
);
3615 del_channel_ban(ban
);
3622 reply("CSMSG_BAN_NOT_FOUND", actee
? actee
->nick
: mask
);
3624 reply("CSMSG_BAN_REMOVED", actee
? actee
->nick
: mask
);
3630 static CHANSERV_FUNC(cmd_unban
)
3632 return unban_user(CSFUNC_ARGS
, ACTION_UNBAN
);
3635 static CHANSERV_FUNC(cmd_dellamer
)
3637 /* it doesn't necessarily have to remove the channel ban - may want
3638 to make that an option. */
3639 return unban_user(CSFUNC_ARGS
, ACTION_UNBAN
| ACTION_DEL_LAMER
);
3642 static CHANSERV_FUNC(cmd_unbanme
)
3644 struct userData
*uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
3645 long flags
= ACTION_UNBAN
;
3647 /* remove permanent bans if the user has the proper access. */
3648 if(uData
->access
>= UL_MANAGER
)
3649 flags
|= ACTION_DEL_LAMER
;
3651 argv
[1] = user
->nick
;
3652 return unban_user(user
, channel
, 2, argv
, cmd
, flags
);
3655 static CHANSERV_FUNC(cmd_unbanall
)
3657 struct mod_chanmode
*change
;
3660 if(!channel
->banlist
.used
)
3662 reply("CSMSG_NO_BANS", channel
->name
);
3666 change
= mod_chanmode_alloc(channel
->banlist
.used
);
3667 for(ii
=0; ii
<channel
->banlist
.used
; ii
++)
3669 change
->args
[ii
].mode
= MODE_REMOVE
| MODE_BAN
;
3670 change
->args
[ii
].u
.hostmask
= strdup(channel
->banlist
.list
[ii
]->ban
);
3672 modcmd_chanmode_announce(change
);
3673 for(ii
= 0; ii
< change
->argc
; ++ii
)
3674 free((char*)change
->args
[ii
].u
.hostmask
);
3675 mod_chanmode_free(change
);
3676 reply("CSMSG_BANS_REMOVED", channel
->name
);
3680 static CHANSERV_FUNC(cmd_open
)
3682 struct mod_chanmode
*change
;
3685 change
= find_matching_bans(&channel
->banlist
, user
, NULL
);
3687 change
= mod_chanmode_alloc(0);
3688 change
->modes_clear
|= MODE_INVITEONLY
| MODE_LIMIT
| MODE_KEY
;
3689 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
3690 && channel
->channel_info
->modes
.modes_set
)
3691 change
->modes_clear
&= ~channel
->channel_info
->modes
.modes_set
;
3692 modcmd_chanmode_announce(change
);
3693 reply("CSMSG_CHANNEL_OPENED", channel
->name
);
3694 for(ii
= 0; ii
< change
->argc
; ++ii
)
3695 free((char*)change
->args
[ii
].u
.hostmask
);
3696 mod_chanmode_free(change
);
3700 static CHANSERV_FUNC(cmd_myaccess
)
3702 static struct string_buffer sbuf
;
3703 struct handle_info
*target_handle
;
3704 struct userData
*uData
;
3707 target_handle
= user
->handle_info
;
3708 else if(!IsHelping(user
))
3710 reply("CSMSG_MYACCESS_SELF_ONLY", argv
[0]);
3713 else if(!(target_handle
= modcmd_get_handle_info(user
, argv
[1])))
3716 if(!target_handle
->channels
)
3718 reply("CSMSG_SQUAT_ACCESS", target_handle
->handle
);
3722 reply("CSMSG_INFOLINE_LIST", target_handle
->handle
);
3723 for(uData
= target_handle
->channels
; uData
; uData
= uData
->u_next
)
3725 struct chanData
*cData
= uData
->channel
;
3727 if(uData
->access
> UL_OWNER
)
3729 if(IsProtected(cData
)
3730 && (target_handle
!= user
->handle_info
)
3731 && !GetTrueChannelAccess(cData
, user
->handle_info
))
3734 string_buffer_append_printf(&sbuf
, "[%s (%d", cData
->channel
->name
, uData
->access
);
3735 if(uData
->flags
== USER_AUTO_OP
)
3736 string_buffer_append(&sbuf
, ',');
3737 if(IsUserSuspended(uData
))
3738 string_buffer_append(&sbuf
, 's');
3739 if(IsUserAutoOp(uData
))
3741 if(uData
->access
>= UL_OP
)
3742 string_buffer_append(&sbuf
, 'o');
3743 else if(uData
->access
>= UL_HALFOP
)
3744 string_buffer_append(&sbuf
, 'h');
3745 else if(uData
->access
>= UL_PEON
)
3746 string_buffer_append(&sbuf
, 'v');
3748 if(IsUserAutoInvite(uData
) && (uData
->access
>= cData
->lvlOpts
[lvlInviteMe
]))
3749 string_buffer_append(&sbuf
, 'i');
3751 string_buffer_append_printf(&sbuf
, ")] %s", uData
->info
);
3753 string_buffer_append_string(&sbuf
, ")]");
3754 string_buffer_append(&sbuf
, '\0');
3755 send_message_type(4, user
, cmd
->parent
->bot
, "%s", sbuf
.list
);
3761 static CHANSERV_FUNC(cmd_access
)
3763 struct userNode
*target
;
3764 struct handle_info
*target_handle
;
3765 struct userData
*uData
;
3767 char prefix
[MAXLEN
];
3772 target_handle
= target
->handle_info
;
3774 else if((target
= GetUserH(argv
[1])))
3776 target_handle
= target
->handle_info
;
3778 else if(argv
[1][0] == '*')
3780 if(!(target_handle
= get_handle_info(argv
[1]+1)))
3782 reply("MSG_HANDLE_UNKNOWN", argv
[1]+1);
3788 reply("MSG_NICK_UNKNOWN", argv
[1]);
3792 assert(target
|| target_handle
);
3794 if(target
== chanserv
)
3796 reply("CSMSG_IS_CHANSERV");
3804 reply("CSMSG_LAZY_SMURF_TARGET", target
->nick
, chanserv_conf
.irc_operator_epithet
);
3809 reply("MSG_USER_AUTHENTICATE", target
->nick
);
3812 reply("MSG_AUTHENTICATE");
3818 const char *epithet
= NULL
, *type
= NULL
;
3821 epithet
= chanserv_conf
.irc_operator_epithet
;
3824 else if(IsNetworkHelper(target
))
3826 epithet
= chanserv_conf
.network_helper_epithet
;
3827 type
= "network helper";
3829 else if(IsSupportHelper(target
))
3831 epithet
= chanserv_conf
.support_helper_epithet
;
3832 type
= "support helper";
3836 if(target_handle
->epithet
)
3837 reply("CSMSG_SMURF_TARGET", target
->nick
, target_handle
->epithet
, type
);
3839 reply("CSMSG_SMURF_TARGET", target
->nick
, epithet
, type
);
3841 sprintf(prefix
, "%s (%s)", target
->nick
, target_handle
->handle
);
3845 sprintf(prefix
, "%s", target_handle
->handle
);
3848 if(!channel
->channel_info
)
3850 reply("CSMSG_NOT_REGISTERED", channel
->name
);
3854 helping
= HANDLE_FLAGGED(target_handle
, HELPING
)
3855 && ((target_handle
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
->channel_info
));
3856 if((uData
= GetTrueChannelAccess(channel
->channel_info
, target_handle
)))
3858 reply((helping
? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix
, user_level_name_from_level(uData
->access
), uData
->access
, channel
->name
);
3859 /* To prevent possible information leaks, only show infolines
3860 * if the requestor is in the channel or it's their own
3862 if(uData
->info
&& (GetUserMode(channel
, user
) || (target_handle
== user
->handle_info
)))
3864 send_message_type(4, user
, cmd
->parent
->bot
, "[%s] %s", (target
? target
->nick
: target_handle
->handle
), uData
->info
);
3866 /* Likewise, only say it's suspended if the user has active
3867 * access in that channel or it's their own entry. */
3868 if(IsUserSuspended(uData
)
3869 && (GetChannelUser(channel
->channel_info
, user
->handle_info
)
3870 || (user
->handle_info
== uData
->handle
)))
3872 reply("CSMSG_USER_SUSPENDED", (target
? target
->nick
: target_handle
->handle
), channel
->name
);
3877 reply((helping
? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix
, channel
->name
);
3883 /* This is never used...
3885 zoot_list(struct listData *list)
3887 struct userData *uData;
3888 unsigned int start, curr, highest, lowest;
3889 struct helpfile_table tmp_table;
3890 const char **temp, *msg;
3892 if(list->table.length == 1)
3895 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);
3897 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));
3898 msg = user_find_message(list->user, "MSG_NONE");
3899 send_message_type(4, list->user, list->bot, " %s", msg);
3901 tmp_table.width = list->table.width;
3902 tmp_table.flags = list->table.flags;
3903 list->table.contents[0][0] = " ";
3904 highest = list->highest;
3905 if(list->lowest != 0)
3906 lowest = list->lowest;
3907 else if(highest < 100)
3910 lowest = highest - 100;
3911 for(start = curr = 1; curr < list->table.length; )
3913 uData = list->users[curr-1];
3914 list->table.contents[curr++][0] = " ";
3915 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
3918 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);
3920 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));
3921 temp = list->table.contents[--start];
3922 list->table.contents[start] = list->table.contents[0];
3923 tmp_table.contents = list->table.contents + start;
3924 tmp_table.length = curr - start;
3925 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
3926 list->table.contents[start] = temp;
3928 highest = lowest - 1;
3929 lowest = (highest < 100) ? 0 : (highest - 99);
3936 normal_list(struct listData
*list
)
3940 send_message(list
->user
, list
->bot
, "CSMSG_ACCESS_SEARCH_HEADER_NORMAL", list
->channel
->name
, user_level_name_from_level(list
->lowest
), user_level_name_from_level(list
->highest
), list
->search
);
3942 send_message(list
->user
, list
->bot
, "CSMSG_ACCESS_ALL_HEADER_NORMAL", list
->channel
->name
, user_level_name_from_level(list
->lowest
), user_level_name_from_level(list
->highest
));
3943 if(list
->table
.length
== 1)
3945 msg
= user_find_message(list
->user
, "MSG_NONE");
3946 send_message_type(4, list
->user
, list
->bot
, " %s", msg
);
3949 table_send(list
->bot
, list
->user
->nick
, 0, NULL
, list
->table
);
3952 /* if these need changed, uncomment and customize
3954 clean_list(struct listData *list)
3958 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER_CLEAN", list->channel->name, user_level_name_from_level(list->lowest), user_level_name_from_level(list->highest), list->search);
3960 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER_CLEAN", list->channel->name, user_level_name_from_level(list->lowest), user_level_name_from_level(list->highest));
3961 if(list->table.length == 1)
3963 msg = user_find_message(list->user, "MSG_NONE");
3964 send_message_type(4, list->user, list->bot, " %s", msg);
3967 table_send(list->bot, list->user->nick, 0, NULL, list->table);
3971 advanced_list(struct listData *list)
3975 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER_ADVANCED", list->channel->name, user_level_name_from_level(list->lowest), user_level_name_from_level(list->highest), list->search);
3977 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER_ADVANCED", list->channel->name, user_level_name_from_level(list->lowest), user_level_name_from_level(list->highest));
3978 if(list->table.length == 1)
3980 msg = user_find_message(list->user, "MSG_NONE");
3981 send_message_type(4, list->user, list->bot, " %s", msg);
3984 table_send(list->bot, list->user->nick, 0, NULL, list->table);
3988 classic_list(struct listData *list)
3992 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER_CLASSIC", list->channel->name, list->lowest, list->highest, list->search);
3994 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER_CLASSIC", list->channel->name, list->lowest, list->highest);
3995 if(list->table.length == 1)
3997 msg = user_find_message(list->user, "MSG_NONE");
3998 send_message_type(4, list->user, list->bot, " %s", msg);
4001 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4006 userData_access_comp(const void *arg_a
, const void *arg_b
)
4008 const struct userData
*a
= *(struct userData
**)arg_a
;
4009 const struct userData
*b
= *(struct userData
**)arg_b
;
4011 if(a
->access
!= b
->access
)
4012 res
= b
->access
- a
->access
;
4014 res
= irccasecmp(a
->handle
->handle
, b
->handle
->handle
);
4019 cmd_list_users(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, unsigned short lowest
, unsigned short highest
)
4021 void (*send_list
)(struct listData
*);
4022 struct userData
*uData
;
4023 struct listData lData
;
4024 unsigned int matches
;
4030 lData
.bot
= cmd
->parent
->bot
;
4031 lData
.channel
= channel
;
4032 lData
.lowest
= lowest
;
4033 lData
.highest
= highest
;
4034 lData
.search
= (argc
> 1) ? argv
[1] : NULL
;
4035 send_list
= normal_list
;
4036 /* What does the following line do exactly?? */
4037 /*(void)zoot_list; ** since it doesn't show user levels */
4040 if(user->handle_info)
4042 switch(user->handle_info->userlist_style)
4044 case HI_STYLE_CLEAN:
4045 send_list = clean_list;
4047 case HI_STYLE_ADVANCED:
4048 send_list = advanced_list;
4050 case HI_STYLE_CLASSIC:
4051 send_list = classic_list;
4053 case HI_STYLE_NORMAL:
4055 send_list = normal_list;
4060 send_list
= normal_list
;
4062 lData
.users
= alloca(channel
->channel_info
->userCount
* sizeof(struct userData
*));
4064 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
4066 if((uData
->access
< lowest
)
4067 || (uData
->access
> highest
)
4068 || (lData
.search
&& !match_ircglob(uData
->handle
->handle
, lData
.search
)))
4070 lData
.users
[matches
++] = uData
;
4072 qsort(lData
.users
, matches
, sizeof(lData
.users
[0]), userData_access_comp
);
4074 lData
.table
.length
= matches
+1;
4075 lData
.table
.flags
= TABLE_NO_FREE
;
4076 lData
.table
.contents
= malloc(lData
.table
.length
*sizeof(*lData
.table
.contents
));
4078 if(user
->handle_info
&& user
->handle_info
->userlist_style
== HI_STYLE_ADVANCED
)
4079 lData
.table
.width
= 5; /* with level = 5 */
4081 lData
.table
.width
= 4; /* without = 4 */
4082 ary
= malloc(lData
.table
.width
*sizeof(**lData
.table
.contents
));
4083 lData
.table
.contents
[0] = ary
;
4084 if(user
->handle_info
) {
4085 switch(user
->handle_info
->userlist_style
) {
4086 case HI_STYLE_CLASSIC
:
4089 case HI_STYLE_ADVANCED
:
4090 ary
[i
++] = "Access";
4093 case HI_STYLE_CLEAN
:
4094 ary
[i
++] = "Access";
4096 case HI_STYLE_NORMAL
:
4098 ary
[i
++] = "Access";
4103 ary
[i
++] = "Access";
4105 ary
[i
++] = "Account";
4106 ary
[i
] = "Last Seen";
4108 ary
[i
++] = "Status";
4109 for(matches
= 1; matches
< lData
.table
.length
; ++matches
)
4111 struct userData
*uData
= lData
.users
[matches
-1];
4112 char seen
[INTERVALLEN
];
4115 ary
= malloc(lData
.table
.width
*sizeof(**lData
.table
.contents
));
4116 lData
.table
.contents
[matches
] = ary
;
4117 if(user
->handle_info
) {
4118 switch(user
->handle_info
->userlist_style
) {
4119 case HI_STYLE_CLASSIC
:
4120 ary
[i
++] = strtab(uData
->access
);
4122 case HI_STYLE_ADVANCED
:
4123 ary
[i
++] = user_level_name_from_level(uData
->access
);
4124 ary
[i
++] = strtab(uData
->access
);
4126 case HI_STYLE_CLEAN
:
4127 ary
[i
++] = user_level_name_from_level(uData
->access
);
4129 case HI_STYLE_NORMAL
:
4131 ary
[i
++] = user_level_name_from_level(uData
->access
);
4136 ary
[i
++] = user_level_name_from_level(uData
->access
);
4138 ary
[i
++] = uData
->handle
->handle
;
4141 else if(!uData
->seen
)
4144 ary
[i
] = intervalString(seen
, now
- uData
->seen
, user
->handle_info
);
4145 ary
[i
] = strdup(ary
[i
]);
4147 if(IsUserSuspended(uData
))
4148 ary
[i
++] = "Suspended";
4149 else if(HANDLE_FLAGGED(uData
->handle
, FROZEN
))
4150 ary
[i
++] = "Vacation";
4152 ary
[i
++] = "Normal";
4155 for(matches
= 1; matches
< lData
.table
.length
; ++matches
)
4157 /* Free strdup above */
4158 free((char*)lData
.table
.contents
[matches
][seen_index
]);
4159 free(lData
.table
.contents
[matches
]);
4161 free(lData
.table
.contents
[0]);
4162 free(lData
.table
.contents
);
4166 /* Remove this now that debugging is over? or improve it for
4167 * users? Would it be better tied into USERS somehow? -Rubin */
4168 static CHANSERV_FUNC(cmd_pending
)
4170 struct adduserPending
*ap
;
4171 reply("CSMSG_ADDUSER_PENDING_HEADER");
4172 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
4174 for(ap
= adduser_pendings
;ap
;ap
= ap
->next
)
4175 reply("CSMSG_ADDUSER_PENDING_LIST", ap
->channel
->name
, ap
->user
->nick
);
4176 reply("CSMSG_ADDUSER_PENDING_FOOTER");
4180 static CHANSERV_FUNC(cmd_users
)
4182 return cmd_list_users(CSFUNC_ARGS
, 1, UL_OWNER
);
4185 static CHANSERV_FUNC(cmd_wlist
)
4187 return cmd_list_users(CSFUNC_ARGS
, UL_OWNER
, UL_OWNER
);
4190 static CHANSERV_FUNC(cmd_clist
)
4192 return cmd_list_users(CSFUNC_ARGS
, UL_COOWNER
, UL_OWNER
-1);
4195 static CHANSERV_FUNC(cmd_mlist
)
4197 return cmd_list_users(CSFUNC_ARGS
, UL_MANAGER
, UL_COOWNER
-1);
4200 static CHANSERV_FUNC(cmd_olist
)
4202 return cmd_list_users(CSFUNC_ARGS
, UL_OP
, UL_MANAGER
-1);
4205 static CHANSERV_FUNC(cmd_hlist
)
4207 return cmd_list_users(CSFUNC_ARGS
, UL_HALFOP
, UL_OP
-1);
4210 static CHANSERV_FUNC(cmd_plist
)
4212 return cmd_list_users(CSFUNC_ARGS
, 1, UL_HALFOP
-1);
4215 static CHANSERV_FUNC(cmd_lamers
)
4217 struct helpfile_table tbl
;
4218 unsigned int matches
= 0, timed
= 0, ii
;
4219 char t_buffer
[INTERVALLEN
], e_buffer
[INTERVALLEN
], *search
;
4220 const char *msg_never
, *triggered
, *expires
;
4221 struct banData
*ban
, **bans
; /* lamers */
4228 reply("CSMSG_LAMERS_HEADER", channel
->name
);
4229 bans
= alloca(channel
->channel_info
->banCount
* sizeof(struct banData
*)); /* lamers */
4232 for(ban
= channel
->channel_info
->bans
; ban
; ban
= ban
->next
)
4234 if(search
&& !match_ircglobs(search
, ban
->mask
))
4236 bans
[matches
++] = ban
;
4241 tbl
.length
= matches
+ 1;
4242 tbl
.width
= 4 + timed
;
4244 tbl
.flags
= TABLE_NO_FREE
;
4245 tbl
.contents
= malloc(tbl
.length
* sizeof(tbl
.contents
[0]));
4246 tbl
.contents
[0] = malloc(tbl
.width
* sizeof(tbl
.contents
[0][0]));
4247 tbl
.contents
[0][0] = "Mask";
4248 tbl
.contents
[0][1] = "Set By";
4249 tbl
.contents
[0][2] = "Triggered";
4252 tbl
.contents
[0][3] = "Expires";
4253 tbl
.contents
[0][4] = "Reason";
4256 tbl
.contents
[0][3] = "Reason";
4259 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
4261 free(tbl
.contents
[0]);
4266 msg_never
= user_find_message(user
, "MSG_NEVER");
4267 for(ii
= 0; ii
< matches
; )
4273 else if(ban
->expires
)
4274 expires
= intervalString(e_buffer
, ban
->expires
- now
, user
->handle_info
);
4276 expires
= msg_never
;
4279 triggered
= intervalString(t_buffer
, now
- ban
->triggered
, user
->handle_info
);
4281 triggered
= msg_never
;
4283 tbl
.contents
[++ii
] = malloc(tbl
.width
* sizeof(tbl
.contents
[0][0]));
4284 tbl
.contents
[ii
][0] = ban
->mask
;
4285 tbl
.contents
[ii
][1] = ban
->owner
;
4286 tbl
.contents
[ii
][2] = strdup(triggered
);
4289 tbl
.contents
[ii
][3] = strdup(expires
);
4290 tbl
.contents
[ii
][4] = ban
->reason
;
4293 tbl
.contents
[ii
][3] = ban
->reason
;
4295 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
4296 /* reply("MSG_MATCH_COUNT", matches); */
4297 for(ii
= 1; ii
< tbl
.length
; ++ii
)
4299 free((char*)tbl
.contents
[ii
][2]);
4301 free((char*)tbl
.contents
[ii
][3]);
4302 free(tbl
.contents
[ii
]);
4304 free(tbl
.contents
[0]);
4311 * return + if the user does NOT have the right to set the topic, and
4312 * the topic is changed.
4315 bad_topic(struct chanNode
*channel
, struct userNode
*user
, const char *new_topic
)
4317 struct chanData
*cData
= channel
->channel_info
;
4318 if(check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
4320 else if(cData
->topic
)
4321 return irccasecmp(new_topic
, cData
->topic
);
4328 * Makes a givin topic fit into a givin topic mask and returns
4331 * topic_mask - the mask to conform to
4332 * topic - the topic to make conform
4333 * new_topic - the pre-allocated char* to put the new topic into
4335 * modifies: new_topic
4338 conform_topic(char* topic_mask
, char* topic
, char *new_topic
)
4340 //char *topic_mask = cData->topic_mask;
4342 int pos
=0, starpos
=-1, dpos
=0, len
;
4344 while((tchar
= topic_mask
[pos
++]) && (dpos
<= TOPICLEN
))
4351 strcpy(new_topic
, "");
4354 len
= strlen(topic
);
4355 if((dpos
+ len
) > TOPICLEN
)
4356 len
= TOPICLEN
+ 1 - dpos
;
4357 memcpy(new_topic
+dpos
, topic
, len
);
4361 case '\\': tchar
= topic_mask
[pos
++]; /* and fall through */
4362 default: new_topic
[dpos
++] = tchar
; break;
4365 if((dpos
> TOPICLEN
) || tchar
)
4367 strcpy(new_topic
, "");
4370 new_topic
[dpos
] = 0;
4374 static CHANSERV_FUNC(cmd_topic
)
4376 struct chanData
*cData
;
4380 #ifdef WITH_PROTOCOL_P10
4384 cData
= channel
->channel_info
;
4389 /*XXX Why would we ever want to send chanserv as the setter? I dont understand -Rubin */
4390 SetChannelTopic(channel
, chanserv
, p10
? user
: chanserv
, cData
->topic
, 1);
4391 reply("CSMSG_TOPIC_SET", cData
->topic
);
4395 reply("CSMSG_NO_TOPIC", channel
->name
);
4399 topic
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
4400 /* If they say "!topic *", use an empty topic. */
4401 if((topic
[0] == '*') && (topic
[1] == 0))
4404 if(bad_topic(channel
, user
, topic
))
4406 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
4411 /* If there is a topicmask set, and the new topic doesnt match, make it */
4412 if(cData
->topic_mask
&& !match_ircglob(topic
, cData
->topic_mask
))
4414 char *topic_mask
= cData
->topic_mask
;
4415 char new_topic
[TOPICLEN
+1];
4417 /* make a new topic fitting mask */
4418 conform_topic(topic_mask
, topic
, new_topic
);
4421 /* Topic couldnt fit into mask, was too long */
4422 reply("CSMSG_TOPICMASK_CONFLICT1", channel
->name
, topic_mask
);
4423 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN
);
4426 SetChannelTopic(channel
, chanserv
, p10
? user
: chanserv
, new_topic
, 1);
4428 else /* No mask set, just set the topic */
4429 SetChannelTopic(channel
, chanserv
, p10
? user
: chanserv
, topic
, 1);
4432 if(check_user_level(channel
, user
, lvlTopicSnarf
, 1, 0))
4434 /* Grab the topic and save it as the default topic. */
4436 cData
->topic
= strdup(channel
->topic
);
4442 static CHANSERV_FUNC(cmd_mode
)
4444 struct userData
*uData
;
4445 struct mod_chanmode
*change
;
4450 change
= &channel
->channel_info
->modes
;
4451 if(change
->modes_set
|| change
->modes_clear
) {
4452 modcmd_chanmode_announce(change
);
4453 reply("CSMSG_DEFAULTED_MODES", channel
->name
);
4455 reply("CSMSG_NO_MODES", channel
->name
);
4459 uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
4461 base_oplevel
= MAXOPLEVEL
;
4462 else if (uData
->access
>= UL_OWNER
)
4465 base_oplevel
= 1 + UL_OWNER
- uData
->access
;
4466 change
= mod_chanmode_parse(channel
, argv
+1, argc
-1, MCP_KEY_FREE
|MCP_REGISTERED
, base_oplevel
);
4470 reply("MSG_INVALID_MODES", unsplit_string(argv
+1, argc
-1, NULL
));
4474 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
4475 && mode_lock_violated(&channel
->channel_info
->modes
, change
))
4478 mod_chanmode_format(&channel
->channel_info
->modes
, modes
);
4479 reply("CSMSG_MODE_LOCKED", modes
, channel
->name
);
4483 modcmd_chanmode_announce(change
);
4484 mod_chanmode_free(change
);
4485 reply("CSMSG_MODES_SET", unsplit_string(argv
+1, argc
-1, NULL
));
4489 static CHANSERV_FUNC(cmd_invite
)
4491 struct userData
*uData
;
4492 struct userNode
*invite
;
4494 uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
4498 if(!(invite
= GetUserH(argv
[1])))
4500 reply("MSG_NICK_UNKNOWN", argv
[1]);
4507 if(GetUserMode(channel
, invite
))
4509 reply("CSMSG_ALREADY_PRESENT", invite
->nick
, channel
->name
);
4517 char *reason
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
4518 send_message(invite
, chanserv
, "CSMSG_INVITING_YOU_REASON", user
->nick
, channel
->name
, reason
);
4521 send_message(invite
, chanserv
, "CSMSG_INVITING_YOU", user
->nick
, channel
->name
);
4524 if (invite
->handle_info
&& invite
->handle_info
->ignores
->used
&& (argc
> 1)) {
4526 for (i
=0; i
< invite
->handle_info
->ignores
->used
; i
++) {
4527 if (user_matches_glob(user
, invite
->handle_info
->ignores
->list
[i
], MATCH_USENICK
)) {
4528 reply("CSMSG_CANNOT_INVITE", argv
[1], channel
->name
);
4534 irc_invite(chanserv
, invite
, channel
);
4536 reply("CSMSG_INVITED_USER", argv
[1], channel
->name
);
4541 static CHANSERV_FUNC(cmd_inviteme
)
4543 if(GetUserMode(channel
, user
))
4545 reply("CSMSG_YOU_ALREADY_PRESENT", channel
->name
);
4548 if(channel
->channel_info
4549 && !check_user_level(channel
, user
, lvlInviteMe
, 1, 0))
4551 reply("CSMSG_LOW_CHANNEL_ACCESS", channel
->name
);
4554 irc_invite(cmd
->parent
->bot
, user
, channel
);
4559 show_suspension_info(struct svccmd
*cmd
, struct userNode
*user
, struct suspended
*suspended
)
4562 char buf1
[INTERVALLEN
], buf2
[INTERVALLEN
];
4564 /* We display things based on two dimensions:
4565 * - Issue time: present or absent
4566 * - Expiration: revoked, expired, expires in future, or indefinite expiration
4567 * (in order of precedence, so something both expired and revoked
4568 * only counts as revoked)
4570 combo
= (suspended
->issued
? 4 : 0)
4571 + (suspended
->revoked
? 3 : suspended
->expires
? ((suspended
->expires
< now
) ? 2 : 1) : 0);
4573 case 0: /* no issue time, indefinite expiration */
4574 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended
->suspender
, suspended
->reason
);
4576 case 1: /* no issue time, expires in future */
4577 intervalString(buf1
, suspended
->expires
-now
, user
->handle_info
);
4578 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended
->suspender
, buf1
, suspended
->reason
);
4580 case 2: /* no issue time, expired */
4581 intervalString(buf1
, now
-suspended
->expires
, user
->handle_info
);
4582 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended
->suspender
, buf1
, suspended
->reason
);
4584 case 3: /* no issue time, revoked */
4585 intervalString(buf1
, now
-suspended
->revoked
, user
->handle_info
);
4586 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended
->suspender
, buf1
, suspended
->reason
);
4588 case 4: /* issue time set, indefinite expiration */
4589 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4590 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1
, suspended
->suspender
, suspended
->reason
);
4592 case 5: /* issue time set, expires in future */
4593 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4594 intervalString(buf2
, suspended
->expires
-now
, user
->handle_info
);
4595 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4597 case 6: /* issue time set, expired */
4598 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4599 intervalString(buf2
, now
-suspended
->expires
, user
->handle_info
);
4600 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4602 case 7: /* issue time set, revoked */
4603 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4604 intervalString(buf2
, now
-suspended
->revoked
, user
->handle_info
);
4605 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4608 log_module(CS_LOG
, LOG_ERROR
, "Invalid combo value %d in show_suspension_info()", combo
);
4614 show_giveownership_info(struct svccmd
*cmd
, struct userNode
*user
, struct giveownership
*giveownership
)
4617 const char *fmt
= "%a %b %d %H:%M %Y";
4618 strftime(buf
, sizeof(buf
), fmt
, localtime(&giveownership
->issued
));
4620 if(giveownership
->staff_issuer
)
4622 if(giveownership
->reason
)
4623 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF_REASON", giveownership
->old_owner
,
4624 giveownership
->target
, giveownership
->target_access
,
4625 giveownership
->staff_issuer
, buf
, giveownership
->reason
);
4627 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF", giveownership
->old_owner
,
4628 giveownership
->target
, giveownership
->target_access
,
4629 giveownership
->staff_issuer
, buf
);
4633 reply("CSMSG_CHANNEL_OWNERSHIP_NORMAL", giveownership
->old_owner
, giveownership
->target
, giveownership
->target_access
, buf
);
4638 static CHANSERV_FUNC(cmd_info
)
4640 char modes
[MAXLEN
], buffer
[INTERVALLEN
];
4641 struct userData
*uData
, *owner
;
4642 struct chanData
*cData
;
4643 struct do_not_register
*dnr
;
4648 cData
= channel
->channel_info
;
4649 reply("CSMSG_CHANNEL_INFO", channel
->name
);
4650 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
4653 uData
= GetChannelUser(cData
, user
->handle_info
);
4654 if(uData
&& (uData
->access
>= UL_OP
/*cData->lvlOpts[lvlGiveOps]*/))
4656 mod_chanmode_format(&cData
->modes
, modes
);
4657 reply("CSMSG_CHANNEL_TOPIC", cData
->topic
);
4658 reply("CSMSG_CHANNEL_MODES", modes
[0] ? modes
: user_find_message(user
, "MSG_NONE"));
4661 for(it
= dict_first(cData
->notes
); it
; it
= iter_next(it
))
4665 note
= iter_data(it
);
4666 if(!note_type_visible_to_user(cData
, note
->type
, user
))
4669 padding
= PADLEN
- 1 - strlen(iter_key(it
));
4670 reply("CSMSG_CHANNEL_NOTE", iter_key(it
), padding
> 0 ? padding
: 1, "", note
->note
);
4673 reply("CSMSG_CHANNEL_MAX", cData
->max
);
4674 for(owner
= cData
->users
; owner
; owner
= owner
->next
)
4675 if(owner
->access
== UL_OWNER
)
4676 reply("CSMSG_CHANNEL_OWNER", owner
->handle
->handle
);
4677 reply("CSMSG_CHANNEL_USERS", cData
->userCount
);
4678 reply("CSMSG_CHANNEL_LAMERS", cData
->banCount
);
4679 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer
, now
- cData
->visited
, user
->handle_info
));
4681 privileged
= IsStaff(user
);
4682 /* if(privileged) */
4683 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer
, now
- cData
->registered
, user
->handle_info
));
4684 if(/*((uData && uData->access >= UL_COOWNER) || privileged) && */cData
->registrar
)
4685 reply("CSMSG_CHANNEL_REGISTRAR", cData
->registrar
);
4687 if(privileged
&& (dnr
= chanserv_is_dnr(channel
->name
, NULL
)))
4688 chanserv_show_dnrs(user
, cmd
, channel
->name
, NULL
);
4690 if(cData
->suspended
&& ((uData
&& (uData
->access
>= UL_COOWNER
)) || IsHelping(user
)))
4692 struct suspended
*suspended
;
4693 reply((IsSuspended(cData
) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel
->name
);
4694 for(suspended
= cData
->suspended
; suspended
; suspended
= suspended
->previous
)
4695 show_suspension_info(cmd
, user
, suspended
);
4697 else if(IsSuspended(cData
))
4699 reply("CSMSG_CHANNEL_SUSPENDED", channel
->name
);
4700 show_suspension_info(cmd
, user
, cData
->suspended
);
4702 if(cData
->giveownership
&& ((uData
&& (uData
->access
>= UL_COOWNER
)) || IsStaff(user
)))
4704 struct giveownership
*giveownership
;
4705 reply("CSMSG_CHANNEL_OWNERSHIP_HISTORY", channel
->name
);
4706 for(giveownership
= cData
->giveownership
; giveownership
; giveownership
= giveownership
->previous
)
4707 show_giveownership_info(cmd
, user
, giveownership
);
4709 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
4710 reply("CSMSG_CHANNEL_END");
4712 reply("CSMSG_CHANNEL_END_CLEAN");
4716 static CHANSERV_FUNC(cmd_netinfo
)
4718 extern time_t boot_time
;
4719 extern unsigned long burst_length
;
4720 char interval
[INTERVALLEN
];
4722 reply("CSMSG_NETWORK_INFO");
4723 reply("CSMSG_NETWORK_SERVERS", dict_size(servers
));
4724 reply("CSMSG_NETWORK_USERS", dict_size(clients
));
4725 reply("CSMSG_NETWORK_OPERS", curr_opers
.used
);
4726 reply("CSMSG_NETWORK_CHANNELS", registered_channels
);
4727 reply("CSMSG_NETWORK_LAMERS", banCount
);
4728 reply("CSMSG_NETWORK_CHANUSERS", userCount
);
4729 reply("CSMSG_SERVICES_UPTIME", intervalString(interval
, time(NULL
) - boot_time
, user
->handle_info
));
4730 reply("CSMSG_BURST_LENGTH", intervalString(interval
, burst_length
, user
->handle_info
));
4735 send_staff_list(struct userNode
*to
, struct userList
*list
, int skip_flags
)
4737 struct helpfile_table table
;
4739 struct userNode
*user
;
4744 table
.flags
= TABLE_REPEAT_ROWS
| TABLE_NO_FREE
| TABLE_NO_HEADERS
;
4745 table
.contents
= alloca(list
->used
*sizeof(*table
.contents
));
4746 for(nn
=0; nn
<list
->used
; nn
++)
4748 user
= list
->list
[nn
];
4749 if(user
->modes
& skip_flags
)
4753 table
.contents
[table
.length
] = alloca(table
.width
*sizeof(**table
.contents
));
4756 nick
= alloca(strlen(user
->nick
)+3);
4757 sprintf(nick
, "(%s)", user
->nick
);
4761 table
.contents
[table
.length
][0] = nick
;
4764 table_send(chanserv
, to
->nick
, 0, NULL
, table
);
4767 static CHANSERV_FUNC(cmd_ircops
)
4769 reply("CSMSG_STAFF_OPERS");
4770 send_staff_list(user
, &curr_opers
, FLAGS_SERVICE
);
4774 static CHANSERV_FUNC(cmd_helpers
)
4776 reply("CSMSG_STAFF_HELPERS");
4777 send_staff_list(user
, &curr_helpers
, FLAGS_OPER
);
4781 static CHANSERV_FUNC(cmd_staff
)
4783 reply("CSMSG_NETWORK_STAFF");
4784 cmd_ircops(CSFUNC_ARGS
);
4785 cmd_helpers(CSFUNC_ARGS
);
4789 static CHANSERV_FUNC(cmd_peek
)
4791 struct modeNode
*mn
;
4792 char modes
[MODELEN
];
4794 struct helpfile_table table
;
4796 irc_make_chanmode(channel
, modes
);
4798 reply("CSMSG_PEEK_INFO", channel
->name
);
4799 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
4801 reply("CSMSG_PEEK_TOPIC", channel
->topic
);
4802 reply("CSMSG_PEEK_MODES", modes
);
4803 reply("CSMSG_PEEK_USERS", channel
->members
.used
);
4807 table
.flags
= TABLE_REPEAT_ROWS
| TABLE_NO_FREE
| TABLE_NO_HEADERS
;
4808 table
.contents
= alloca(channel
->members
.used
*sizeof(*table
.contents
));
4809 for(n
= 0; n
< channel
->members
.used
; n
++)
4811 mn
= channel
->members
.list
[n
];
4812 if(!(mn
->modes
& MODE_CHANOP
) || IsLocal(mn
->user
))
4814 table
.contents
[table
.length
] = alloca(sizeof(**table
.contents
));
4815 table
.contents
[table
.length
][0] = mn
->user
->nick
;
4820 reply("CSMSG_PEEK_OPS");
4821 table_send(chanserv
, user
->nick
, 0, NULL
, table
);
4824 reply("CSMSG_PEEK_NO_OPS");
4825 reply("CSMSG_PEEK_END");
4829 static MODCMD_FUNC(cmd_wipeinfo
)
4831 struct handle_info
*victim
;
4832 struct userData
*ud
, *actor
;
4835 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
4836 if(!(victim
= modcmd_get_handle_info(user
, argv
[1])))
4838 if(!(ud
= GetTrueChannelAccess(channel
->channel_info
, victim
)))
4840 reply("CSMSG_NO_CHAN_USER", argv
[1], channel
->name
);
4843 if((ud
->access
>= actor
->access
) && (ud
!= actor
))
4845 reply("MSG_USER_OUTRANKED", victim
->handle
);
4851 reply("CSMSG_WIPED_INFO_LINE", argv
[1], channel
->name
);
4856 resync_channel(struct chanNode
*channel
)
4858 struct mod_chanmode
*changes
;
4859 struct chanData
*cData
= channel
->channel_info
;
4860 unsigned int ii
, used
;
4862 /* 6 = worst case -ovh+ovh on everyone */
4863 changes
= mod_chanmode_alloc(channel
->members
.used
* 6);
4864 for(ii
= used
= 0; ii
< channel
->members
.used
; ++ii
)
4866 struct modeNode
*mn
= channel
->members
.list
[ii
];
4867 struct userData
*uData
;
4869 if(IsService(mn
->user
))
4873 uData
= GetChannelAccess(cData
, mn
->user
->handle_info
);
4875 /* If the channel is in no-mode mode, de-mode EVERYONE */
4876 if(cData
->chOpts
[chAutomode
] == 'n')
4880 changes
->args
[used
].mode
= MODE_REMOVE
| mn
->modes
;
4881 changes
->args
[used
++].u
.member
= mn
;
4884 else /* Give various userlevels their modes.. */
4886 if(uData
&& uData
->access
>= UL_OP
)
4888 if(!(mn
->modes
& MODE_CHANOP
))
4890 changes
->args
[used
].mode
= MODE_CHANOP
;
4891 changes
->args
[used
++].u
.member
= mn
;
4894 else if(uData
&& uData
->access
>= UL_HALFOP
)
4896 if(mn
->modes
& MODE_CHANOP
)
4898 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_CHANOP
;
4899 changes
->args
[used
++].u
.member
= mn
;
4901 if(!(mn
->modes
& MODE_HALFOP
))
4903 changes
->args
[used
].mode
= MODE_HALFOP
;
4904 changes
->args
[used
++].u
.member
= mn
;
4907 else if(uData
&& uData
->access
>= UL_PEON
)
4909 if(mn
->modes
& MODE_CHANOP
)
4911 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_CHANOP
;
4912 changes
->args
[used
++].u
.member
= mn
;
4914 if(mn
->modes
& MODE_HALFOP
)
4916 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_HALFOP
;
4917 changes
->args
[used
++].u
.member
= mn
;
4919 /* Don't voice peons if were in mode m */
4920 if( cData
->chOpts
[chAutomode
] == 'm')
4922 if(mn
->modes
& MODE_VOICE
)
4924 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_VOICE
;
4925 changes
->args
[used
++].u
.member
= mn
;
4928 /* otherwise, make user they do have voice */
4929 else if(!(mn
->modes
& MODE_VOICE
))
4931 changes
->args
[used
].mode
= MODE_VOICE
;
4932 changes
->args
[used
++].u
.member
= mn
;
4935 else /* They arnt on the userlist.. */
4937 /* If we voice everyone, but they dont.. */
4938 if(cData
->chOpts
[chAutomode
] == 'v')
4940 /* Remove anything except v */
4941 if(mn
->modes
& ~MODE_VOICE
)
4943 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_VOICE
);
4944 changes
->args
[used
++].u
.member
= mn
;
4947 if(!(mn
->modes
& MODE_VOICE
))
4949 changes
->args
[used
].mode
= MODE_VOICE
;
4950 changes
->args
[used
++].u
.member
= mn
;
4953 /* If we hop everyone, but they dont.. */
4954 else if(cData
->chOpts
[chAutomode
] == 'h')
4956 /* Remove anything except h */
4957 if(mn
->modes
& ~MODE_HALFOP
)
4959 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_HALFOP
);
4960 changes
->args
[used
++].u
.member
= mn
;
4963 if(!(mn
->modes
& MODE_HALFOP
))
4965 changes
->args
[used
].mode
= MODE_HALFOP
;
4966 changes
->args
[used
++].u
.member
= mn
;
4969 /* If we op everyone, but they dont.. */
4970 else if(cData
->chOpts
[chAutomode
] == 'o')
4972 /* Remove anything except h */
4973 if(mn
->modes
& ~MODE_CHANOP
)
4975 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_CHANOP
);
4976 changes
->args
[used
++].u
.member
= mn
;
4979 if(!(mn
->modes
& MODE_CHANOP
))
4981 changes
->args
[used
].mode
= MODE_CHANOP
;
4982 changes
->args
[used
++].u
.member
= mn
;
4985 /* they have no excuse for having modes, de-everything them */
4990 changes
->args
[used
].mode
= MODE_REMOVE
| mn
->modes
;
4991 changes
->args
[used
++].u
.member
= mn
;
4997 changes
->argc
= used
;
4998 mod_chanmode_announce(chanserv
, channel
, changes
);
4999 mod_chanmode_free(changes
);
5002 static CHANSERV_FUNC(cmd_resync
)
5004 resync_channel(channel
);
5005 reply("CSMSG_RESYNCED_USERS", channel
->name
);
5009 static CHANSERV_FUNC(cmd_seen
)
5011 struct userData
*uData
;
5012 struct handle_info
*handle
;
5013 char seen
[INTERVALLEN
];
5017 if(!irccasecmp(argv
[1], chanserv
->nick
))
5019 reply("CSMSG_IS_CHANSERV");
5023 if(!(handle
= get_handle_info(argv
[1])))
5025 reply("MSG_HANDLE_UNKNOWN", argv
[1]);
5029 if(!(uData
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
5031 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
5036 reply("CSMSG_USER_PRESENT", handle
->handle
);
5037 else if(uData
->seen
)
5038 reply("CSMSG_USER_SEEN", handle
->handle
, channel
->name
, intervalString(seen
, now
- uData
->seen
, user
->handle_info
));
5040 reply("CSMSG_NEVER_SEEN", handle
->handle
, channel
->name
);
5042 if(!uData
->present
&& HANDLE_FLAGGED(handle
, FROZEN
))
5043 reply("CSMSG_USER_VACATION", handle
->handle
);
5048 static MODCMD_FUNC(cmd_names
)
5050 struct userNode
*targ
;
5051 struct userData
*targData
;
5052 unsigned int ii
, pos
;
5055 for(ii
=pos
=0; ii
<channel
->members
.used
; ++ii
)
5057 targ
= channel
->members
.list
[ii
]->user
;
5058 targData
= GetTrueChannelAccess(channel
->channel_info
, targ
->handle_info
);
5061 if(pos
+ strlen(targ
->nick
) + strlen(targ
->handle_info
->handle
) + 8 > sizeof(buf
))
5064 reply("CSMSG_CHANNEL_NAMES", channel
->name
, buf
);
5068 if(IsUserSuspended(targData
))
5070 pos
+= sprintf(buf
+pos
, "%d:%s(%s)", targData
->access
, targ
->nick
, targ
->handle_info
->handle
);
5073 reply("CSMSG_CHANNEL_NAMES", channel
->name
, buf
);
5074 reply("CSMSG_END_NAMES", channel
->name
);
5079 note_type_visible_to_user(struct chanData
*channel
, struct note_type
*ntype
, struct userNode
*user
)
5081 switch(ntype
->visible_type
)
5083 case NOTE_VIS_ALL
: return 1;
5084 case NOTE_VIS_CHANNEL_USERS
: return !channel
|| !user
|| (user
->handle_info
&& GetChannelUser(channel
, user
->handle_info
));
5085 case NOTE_VIS_PRIVILEGED
: default: return user
&& (IsOper(user
) || IsSupportHelper(user
) || IsNetworkHelper(user
));
5090 note_type_settable_by_user(struct chanNode
*channel
, struct note_type
*ntype
, struct userNode
*user
)
5092 struct userData
*uData
;
5094 switch(ntype
->set_access_type
)
5096 case NOTE_SET_CHANNEL_ACCESS
:
5097 if(!user
->handle_info
)
5099 if(!(uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
)))
5101 return uData
->access
>= ntype
->set_access
.min_ulevel
;
5102 case NOTE_SET_CHANNEL_SETTER
:
5103 return check_user_level(channel
, user
, lvlSetters
, 1, 0);
5104 case NOTE_SET_PRIVILEGED
: default:
5105 return IsHelping(user
) && (user
->handle_info
->opserv_level
>= ntype
->set_access
.min_opserv
);
5109 static CHANSERV_FUNC(cmd_note
)
5111 struct chanData
*cData
;
5113 struct note_type
*ntype
;
5115 cData
= channel
->channel_info
;
5118 reply("CSMSG_NOT_REGISTERED", channel
->name
);
5122 /* If no arguments, show all visible notes for the channel. */
5128 for(count
=0, it
=dict_first(cData
->notes
); it
; it
=iter_next(it
))
5130 note
= iter_data(it
);
5131 if(!note_type_visible_to_user(cData
, note
->type
, user
))
5134 reply("CSMSG_NOTELIST_HEADER", channel
->name
);
5135 reply("CSMSG_NOTE_FORMAT", iter_key(it
), note
->setter
, note
->note
);
5138 reply("CSMSG_NOTELIST_END", channel
->name
);
5140 reply("CSMSG_NOTELIST_EMPTY", channel
->name
);
5142 /* If one argument, show the named note. */
5145 if((note
= dict_find(cData
->notes
, argv
[1], NULL
))
5146 && note_type_visible_to_user(cData
, note
->type
, user
))
5148 reply("CSMSG_NOTE_FORMAT", note
->type
->name
, note
->setter
, note
->note
);
5150 else if((ntype
= dict_find(note_types
, argv
[1], NULL
))
5151 && note_type_visible_to_user(NULL
, ntype
, user
))
5153 reply("CSMSG_NO_SUCH_NOTE", channel
->name
, ntype
->name
);
5158 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
5162 /* Assume they're trying to set a note. */
5166 ntype
= dict_find(note_types
, argv
[1], NULL
);
5169 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
5172 else if(note_type_settable_by_user(channel
, ntype
, user
))
5174 note_text
= unsplit_string(argv
+2, argc
-2, NULL
);
5175 if((note
= dict_find(cData
->notes
, argv
[1], NULL
)))
5176 reply("CSMSG_REPLACED_NOTE", ntype
->name
, channel
->name
, note
->setter
, note
->note
);
5177 chanserv_add_channel_note(cData
, ntype
, user
->handle_info
->handle
, note_text
);
5178 reply("CSMSG_NOTE_SET", ntype
->name
, channel
->name
);
5180 if(ntype
->visible_type
== NOTE_VIS_PRIVILEGED
)
5182 /* The note is viewable to staff only, so return 0
5183 to keep the invocation from getting logged (or
5184 regular users can see it in !events). */
5190 reply("CSMSG_NO_ACCESS");
5197 static CHANSERV_FUNC(cmd_delnote
)
5202 if(!(note
= dict_find(channel
->channel_info
->notes
, argv
[1], NULL
))
5203 || !note_type_settable_by_user(channel
, note
->type
, user
))
5205 reply("CSMSG_NO_SUCH_NOTE", channel
->name
, argv
[1]);
5208 dict_remove(channel
->channel_info
->notes
, note
->type
->name
);
5209 reply("CSMSG_NOTE_REMOVED", argv
[1], channel
->name
);
5213 static CHANSERV_FUNC(cmd_last
)
5219 numoflines
= (argc
> 1) ? atoi(argv
[1]) : 10;
5221 if(numoflines
< 1 || numoflines
> 200)
5223 reply("CSMSG_LAST_INVALID");
5226 ShowLog(user
, channel
, "*", "*", "*", "*", numoflines
);
5230 static CHANSERV_FUNC(cmd_events
)
5232 struct logSearch discrim
;
5233 struct logReport report
;
5234 unsigned int matches
, limit
;
5236 limit
= (argc
> 1) ? atoi(argv
[1]) : 10;
5237 if(limit
< 1 || limit
> 200)
5240 memset(&discrim
, 0, sizeof(discrim
));
5241 discrim
.masks
.bot
= chanserv
;
5242 discrim
.masks
.channel_name
= channel
->name
;
5244 discrim
.masks
.command
= argv
[2];
5245 discrim
.limit
= limit
;
5246 discrim
.max_time
= INT_MAX
;
5247 discrim
.severities
= 1 << LOG_COMMAND
;
5248 report
.reporter
= chanserv
;
5250 reply("CSMSG_EVENT_SEARCH_RESULTS", channel
->name
);
5251 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
5253 matches
= log_entry_search(&discrim
, log_report_entry
, &report
);
5255 reply("MSG_MATCH_COUNT", matches
);
5257 reply("MSG_NO_MATCHES");
5261 static CHANSERV_FUNC(cmd_say
)
5267 msg
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
5268 send_channel_message(channel
, cmd
->parent
->bot
, "%s", msg
);
5270 else if(GetUserH(argv
[1]))
5273 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
5274 send_target_message(5, argv
[1], cmd
->parent
->bot
, "%s", msg
);
5278 reply("MSG_NOT_TARGET_NAME");
5284 static CHANSERV_FUNC(cmd_emote
)
5290 /* CTCP is so annoying. */
5291 msg
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
5292 send_channel_message(channel
, cmd
->parent
->bot
, "\001ACTION %s\001", msg
);
5294 else if(GetUserH(argv
[1]))
5296 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
5297 send_target_message(5, argv
[1], cmd
->parent
->bot
, "\001ACTION %s\001", msg
);
5301 reply("MSG_NOT_TARGET_NAME");
5307 struct channelList
*
5308 chanserv_support_channels(void)
5310 return &chanserv_conf
.support_channels
;
5313 static CHANSERV_FUNC(cmd_expire
)
5315 int channel_count
= registered_channels
;
5316 expire_channels(NULL
);
5317 reply("CSMSG_CHANNELS_EXPIRED", channel_count
- registered_channels
);
5322 chanserv_expire_suspension(void *data
)
5324 struct suspended
*suspended
= data
;
5325 struct chanNode
*channel
;
5327 if(!suspended
->expires
|| (now
< suspended
->expires
))
5328 suspended
->revoked
= now
;
5329 channel
= suspended
->cData
->channel
;
5330 suspended
->cData
->channel
= channel
;
5331 suspended
->cData
->flags
&= ~CHANNEL_SUSPENDED
;
5332 if(!IsOffChannel(suspended
->cData
))
5334 spamserv_cs_suspend(channel
, 0, 0, NULL
);
5335 ss_cs_join_channel(channel
, 1);
5339 static CHANSERV_FUNC(cmd_csuspend
)
5341 struct suspended
*suspended
;
5342 char reason
[MAXLEN
];
5343 time_t expiry
, duration
;
5344 struct userData
*uData
;
5348 if(IsProtected(channel
->channel_info
))
5350 reply("CSMSG_SUSPEND_NODELETE", channel
->name
);
5354 if(argv
[1][0] == '!')
5356 else if(IsSuspended(channel
->channel_info
))
5358 reply("CSMSG_ALREADY_SUSPENDED", channel
->name
);
5359 show_suspension_info(cmd
, user
, channel
->channel_info
->suspended
);
5363 if(!strcmp(argv
[1], "0"))
5365 else if((duration
= ParseInterval(argv
[1])))
5366 expiry
= now
+ duration
;
5369 reply("MSG_INVALID_DURATION", argv
[1]);
5373 unsplit_string(argv
+ 2, argc
- 2, reason
);
5375 suspended
= calloc(1, sizeof(*suspended
));
5376 suspended
->revoked
= 0;
5377 suspended
->issued
= now
;
5378 suspended
->suspender
= strdup(user
->handle_info
->handle
);
5379 suspended
->expires
= expiry
;
5380 suspended
->reason
= strdup(reason
);
5381 suspended
->cData
= channel
->channel_info
;
5382 suspended
->previous
= suspended
->cData
->suspended
;
5383 suspended
->cData
->suspended
= suspended
;
5385 if(suspended
->expires
)
5386 timeq_add(suspended
->expires
, chanserv_expire_suspension
, suspended
);
5388 if(IsSuspended(channel
->channel_info
))
5390 suspended
->previous
->revoked
= now
;
5391 if(suspended
->previous
->expires
)
5392 timeq_del(suspended
->previous
->expires
, chanserv_expire_suspension
, suspended
->previous
, 0);
5393 sprintf(reason
, "%s suspension modified by %s.", channel
->name
, suspended
->suspender
);
5394 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
5398 /* Mark all users in channel as absent. */
5399 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
5408 /* Mark the channel as suspended, then part. */
5409 channel
->channel_info
->flags
|= CHANNEL_SUSPENDED
;
5410 spamserv_cs_suspend(channel
, expiry
, 1, suspended
->reason
);
5411 DelChannelUser(chanserv
, channel
, suspended
->reason
, 0);
5412 reply("CSMSG_SUSPENDED", channel
->name
);
5413 sprintf(reason
, "%s suspended by %s.", channel
->name
, suspended
->suspender
);
5414 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
5419 static CHANSERV_FUNC(cmd_cunsuspend
)
5421 struct suspended
*suspended
;
5422 char message
[MAXLEN
];
5424 if(!IsSuspended(channel
->channel_info
))
5426 reply("CSMSG_NOT_SUSPENDED", channel
->name
);
5430 suspended
= channel
->channel_info
->suspended
;
5432 /* Expire the suspension and join ChanServ to the channel. */
5433 timeq_del(suspended
->expires
, chanserv_expire_suspension
, suspended
, 0);
5434 chanserv_expire_suspension(suspended
);
5435 reply("CSMSG_UNSUSPENDED", channel
->name
);
5436 sprintf(message
, "%s unsuspended by %s.", channel
->name
, user
->handle_info
->handle
);
5437 global_message(MESSAGE_RECIPIENT_OPERS
|MESSAGE_RECIPIENT_HELPERS
, message
);
5441 typedef struct chanservSearch
5449 unsigned long flags
;
5453 typedef void (*channel_search_func
)(struct chanData
*channel
, void *data
);
5456 chanserv_search_create(struct svccmd
*cmd
, struct userNode
*user
, unsigned int argc
, char *argv
[])
5461 search
= malloc(sizeof(struct chanservSearch
));
5462 memset(search
, 0, sizeof(*search
));
5465 for(i
= 0; i
< argc
; i
++)
5467 /* Assume all criteria require arguments. */
5470 reply("MSG_MISSING_PARAMS", argv
[i
]);
5474 if(!irccasecmp(argv
[i
], "name"))
5475 search
->name
= argv
[++i
];
5476 else if(!irccasecmp(argv
[i
], "registrar"))
5477 search
->registrar
= argv
[++i
];
5478 else if(!irccasecmp(argv
[i
], "unvisited"))
5479 search
->unvisited
= ParseInterval(argv
[++i
]);
5480 else if(!irccasecmp(argv
[i
], "registered"))
5481 search
->registered
= ParseInterval(argv
[++i
]);
5482 else if(!irccasecmp(argv
[i
], "flags"))
5485 if(!irccasecmp(argv
[i
], "nodelete"))
5486 search
->flags
|= CHANNEL_NODELETE
;
5487 else if(!irccasecmp(argv
[i
], "suspended"))
5488 search
->flags
|= CHANNEL_SUSPENDED
;
5491 reply("CSMSG_INVALID_CFLAG", argv
[i
]);
5495 else if(!irccasecmp(argv
[i
], "limit"))
5496 search
->limit
= strtoul(argv
[++i
], NULL
, 10);
5499 reply("MSG_INVALID_CRITERIA", argv
[i
]);
5504 if(search
->name
&& !strcmp(search
->name
, "*"))
5506 if(search
->registrar
&& !strcmp(search
->registrar
, "*"))
5507 search
->registrar
= 0;
5516 chanserv_channel_match(struct chanData
*channel
, search_t search
)
5518 const char *name
= channel
->channel
->name
;
5519 if((search
->name
&& !match_ircglob(name
, search
->name
)) ||
5520 (search
->registrar
&& !channel
->registrar
) ||
5521 (search
->registrar
&& !match_ircglob(channel
->registrar
, search
->registrar
)) ||
5522 (search
->unvisited
&& (now
- channel
->visited
) < search
->unvisited
) ||
5523 (search
->registered
&& (now
- channel
->registered
) > search
->registered
) ||
5524 (search
->flags
&& ((search
->flags
& channel
->flags
) != search
->flags
)))
5531 chanserv_channel_search(search_t search
, channel_search_func smf
, void *data
)
5533 struct chanData
*channel
;
5534 unsigned int matches
= 0;
5536 for(channel
= channelList
; channel
&& matches
< search
->limit
; channel
= channel
->next
)
5538 if(!chanserv_channel_match(channel
, search
))
5548 search_count(UNUSED_ARG(struct chanData
*channel
), UNUSED_ARG(void *data
))
5553 search_print(struct chanData
*channel
, void *data
)
5555 send_message_type(4, data
, chanserv
, "%s", channel
->channel
->name
);
5558 static CHANSERV_FUNC(cmd_search
)
5561 unsigned int matches
;
5562 channel_search_func action
;
5566 if(!irccasecmp(argv
[1], "count"))
5567 action
= search_count
;
5568 else if(!irccasecmp(argv
[1], "print"))
5569 action
= search_print
;
5572 reply("CSMSG_ACTION_INVALID", argv
[1]);
5576 search
= chanserv_search_create(cmd
, user
, argc
- 2, argv
+ 2);
5580 if(action
== search_count
)
5581 search
->limit
= INT_MAX
;
5583 if(action
== search_print
)
5585 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
5586 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
5590 matches
= chanserv_channel_search(search
, action
, user
);
5593 reply("MSG_MATCH_COUNT", matches
);
5595 reply("MSG_NO_MATCHES");
5601 static CHANSERV_FUNC(cmd_unvisited
)
5603 struct chanData
*cData
;
5604 time_t interval
= chanserv_conf
.channel_expire_delay
;
5605 char buffer
[INTERVALLEN
];
5606 unsigned int limit
= 25, matches
= 0;
5610 interval
= ParseInterval(argv
[1]);
5612 limit
= atoi(argv
[2]);
5615 intervalString(buffer
, interval
, user
->handle_info
);
5616 reply("CSMSG_UNVISITED_HEADER", limit
, buffer
);
5618 for(cData
= channelList
; cData
&& matches
< limit
; cData
= cData
->next
)
5620 if((now
- cData
->visited
) < interval
)
5623 intervalString(buffer
, now
- cData
->visited
, user
->handle_info
);
5624 reply("CSMSG_UNVISITED_DATA", cData
->channel
->name
, buffer
);
5631 static MODCMD_FUNC(chan_opt_defaulttopic
)
5637 if(!check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
5639 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
5643 topic
= unsplit_string(argv
+1, argc
-1, NULL
);
5645 free(channel
->channel_info
->topic
);
5646 if(topic
[0] == '*' && topic
[1] == 0)
5648 topic
= channel
->channel_info
->topic
= NULL
;
5652 topic
= channel
->channel_info
->topic
= strdup(topic
);
5653 if(channel
->channel_info
->topic_mask
5654 && !match_ircglob(channel
->channel_info
->topic
, channel
->channel_info
->topic_mask
))
5655 reply("CSMSG_TOPIC_MISMATCH", channel
->name
);
5657 SetChannelTopic(channel
, chanserv
, user
, topic
? topic
: "", 1);
5660 if(channel
->channel_info
->topic
)
5661 reply("CSMSG_SET_DEFAULT_TOPIC", channel
->channel_info
->topic
);
5663 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user
, "MSG_NONE"));
5667 static MODCMD_FUNC(chan_opt_topicmask
)
5671 struct chanData
*cData
= channel
->channel_info
;
5674 if(!check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
5676 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
5680 mask
= unsplit_string(argv
+1, argc
-1, NULL
);
5682 if(cData
->topic_mask
)
5683 free(cData
->topic_mask
);
5684 if(mask
[0] == '*' && mask
[1] == 0)
5686 cData
->topic_mask
= 0;
5690 cData
->topic_mask
= strdup(mask
);
5692 reply("CSMSG_MASK_BUT_NO_TOPIC", channel
->name
);
5693 else if(!match_ircglob(cData
->topic
, cData
->topic_mask
))
5694 reply("CSMSG_TOPIC_MISMATCH", channel
->name
);
5698 if(channel
->channel_info
->topic_mask
)
5699 reply("CSMSG_SET_TOPICMASK", channel
->channel_info
->topic_mask
);
5701 reply("CSMSG_SET_TOPICMASK", user_find_message(user
, "MSG_NONE"));
5705 int opt_greeting_common(struct userNode
*user
, struct svccmd
*cmd
, int argc
, char *argv
[], char *name
, char **data
)
5709 char *greeting
= unsplit_string(argv
+1, argc
-1, NULL
);
5713 if(greeting
[0] == '*' && greeting
[1] == 0)
5717 unsigned int length
= strlen(greeting
);
5718 if(length
> chanserv_conf
.greeting_length
)
5720 reply("CSMSG_GREETING_TOO_LONG", length
, chanserv_conf
.greeting_length
);
5723 *data
= strdup(greeting
);
5732 reply(name
, user_find_message(user
, "MSG_NONE"));
5736 static MODCMD_FUNC(chan_opt_greeting
)
5738 return opt_greeting_common(user
, cmd
, argc
, argv
, "CSMSG_SET_GREETING", &channel
->channel_info
->greeting
);
5741 static MODCMD_FUNC(chan_opt_usergreeting
)
5743 return opt_greeting_common(user
, cmd
, argc
, argv
, "CSMSG_SET_USERGREETING", &channel
->channel_info
->user_greeting
);
5746 static MODCMD_FUNC(chan_opt_modes
)
5748 struct mod_chanmode
*new_modes
;
5749 char modes
[MODELEN
];
5753 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0))
5755 reply("CSMSG_NO_ACCESS");
5758 if(argv
[1][0] == '*' && argv
[1][1] == 0)
5760 memset(&channel
->channel_info
->modes
, 0, sizeof(channel
->channel_info
->modes
));
5762 else if(!(new_modes
= mod_chanmode_parse(channel
, argv
+1, argc
-1,MCP_KEY_FREE
|MCP_REGISTERED
, 0)))
5764 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv
+1, argc
-1, NULL
));
5767 else if(new_modes
->argc
> 1)
5769 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv
+1, argc
-1, NULL
));
5770 mod_chanmode_free(new_modes
);
5775 channel
->channel_info
->modes
= *new_modes
;
5776 modcmd_chanmode_announce(new_modes
);
5777 mod_chanmode_free(new_modes
);
5781 mod_chanmode_format(&channel
->channel_info
->modes
, modes
);
5783 reply("CSMSG_SET_MODES", modes
);
5785 reply("CSMSG_SET_MODES", user_find_message(user
, "MSG_NONE"));
5789 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5791 channel_binary_option(char *name
, unsigned long mask
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5793 struct chanData
*cData
= channel
->channel_info
;
5798 /* Set flag according to value. */
5799 if(enabled_string(argv
[1]))
5801 cData
->flags
|= mask
;
5804 else if(disabled_string(argv
[1]))
5806 cData
->flags
&= ~mask
;
5811 reply("MSG_INVALID_BINARY", argv
[1]);
5817 /* Find current option value. */
5818 value
= (cData
->flags
& mask
) ? 1 : 0;
5822 reply(name
, user_find_message(user
, "MSG_ON"));
5824 reply(name
, user_find_message(user
, "MSG_OFF"));
5828 static MODCMD_FUNC(chan_opt_nodelete
)
5830 if((argc
> 1) && (!IsOper(user
) || !user
->handle_info
|| (user
->handle_info
->opserv_level
< chanserv_conf
.nodelete_level
)))
5832 reply("MSG_SETTING_PRIVILEGED", argv
[0]);
5836 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE
);
5839 static MODCMD_FUNC(chan_opt_dynlimit
)
5841 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT
);
5844 static MODCMD_FUNC(chan_opt_offchannel
)
5846 struct chanData
*cData
= channel
->channel_info
;
5851 /* Set flag according to value. */
5852 if(enabled_string(argv
[1]))
5854 if(!IsOffChannel(cData
))
5855 DelChannelUser(chanserv
, channel
, "Going off-channel.", 0);
5856 cData
->flags
|= CHANNEL_OFFCHANNEL
;
5859 else if(disabled_string(argv
[1]))
5861 if(IsOffChannel(cData
))
5863 struct mod_chanmode change
;
5864 mod_chanmode_init(&change
);
5866 change
.args
[0].mode
= MODE_CHANOP
;
5867 change
.args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
5868 mod_chanmode_announce(chanserv
, channel
, &change
);
5870 cData
->flags
&= ~CHANNEL_OFFCHANNEL
;
5875 reply("MSG_INVALID_BINARY", argv
[1]);
5881 /* Find current option value. */
5882 value
= (cData
->flags
& CHANNEL_OFFCHANNEL
) ? 1 : 0;
5886 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user
, "MSG_ON"));
5888 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user
, "MSG_OFF"));
5892 static MODCMD_FUNC(chan_opt_defaults
)
5894 struct userData
*uData
;
5895 struct chanData
*cData
;
5896 const char *confirm
;
5897 enum levelOption lvlOpt
;
5898 enum charOption chOpt
;
5900 cData
= channel
->channel_info
;
5901 uData
= GetChannelUser(cData
, user
->handle_info
);
5902 if(!uData
|| (uData
->access
< UL_OWNER
))
5904 reply("CSMSG_OWNER_DEFAULTS", channel
->name
);
5907 confirm
= make_confirmation_string(uData
);
5908 if((argc
< 2) || strcmp(argv
[1], confirm
))
5910 reply("CSMSG_CONFIRM_DEFAULTS", channel
->name
, confirm
);
5913 cData
->flags
= CHANNEL_DEFAULT_FLAGS
;
5914 cData
->modes
= chanserv_conf
.default_modes
;
5915 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
5916 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
5917 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
5918 cData
->chOpts
[chOpt
] = charOptions
[chOpt
].default_value
;
5919 reply("CSMSG_SETTINGS_DEFAULTED", channel
->name
);
5924 channel_level_option(enum levelOption option
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5926 struct chanData
*cData
= channel
->channel_info
;
5927 struct userData
*uData
;
5928 unsigned short value
;
5932 if(!check_user_level(channel
, user
, option
, 1, 1))
5934 reply("CSMSG_CANNOT_SET");
5937 value
= user_level_from_name(argv
[1], UL_OWNER
+1);
5938 if(!value
&& strcmp(argv
[1], "0"))
5940 reply("CSMSG_INVALID_ACCESS", argv
[1]);
5943 uData
= GetChannelUser(cData
, user
->handle_info
);
5944 if(!uData
|| ((uData
->access
< UL_OWNER
) && (value
> uData
->access
)))
5946 reply("CSMSG_BAD_SETLEVEL");
5952 /* This test only applies to owners, since non-owners
5953 * trying to set an option to above their level get caught
5954 * by the CSMSG_BAD_SETLEVEL test above.
5956 if(value
> uData
->access
)
5958 reply("CSMSG_BAD_SETTERS");
5965 cData
->lvlOpts
[option
] = value
;
5967 reply(levelOptions
[option
].format_name
, cData
->lvlOpts
[option
]);
5971 static MODCMD_FUNC(chan_opt_enfops
)
5973 return channel_level_option(lvlEnfOps
, CSFUNC_ARGS
);
5976 static MODCMD_FUNC(chan_opt_enfhalfops
)
5978 return channel_level_option(lvlEnfHalfOps
, CSFUNC_ARGS
);
5980 static MODCMD_FUNC(chan_opt_enfmodes
)
5982 return channel_level_option(lvlEnfModes
, CSFUNC_ARGS
);
5985 static MODCMD_FUNC(chan_opt_enftopic
)
5987 return channel_level_option(lvlEnfTopic
, CSFUNC_ARGS
);
5990 static MODCMD_FUNC(chan_opt_pubcmd
)
5992 return channel_level_option(lvlPubCmd
, CSFUNC_ARGS
);
5995 static MODCMD_FUNC(chan_opt_setters
)
5997 return channel_level_option(lvlSetters
, CSFUNC_ARGS
);
6000 static MODCMD_FUNC(chan_opt_userinfo
)
6002 return channel_level_option(lvlUserInfo
, CSFUNC_ARGS
);
6005 static MODCMD_FUNC(chan_opt_topicsnarf
)
6007 return channel_level_option(lvlTopicSnarf
, CSFUNC_ARGS
);
6010 static MODCMD_FUNC(chan_opt_inviteme
)
6012 return channel_level_option(lvlInviteMe
, CSFUNC_ARGS
);
6015 /* TODO: Make look like this when no args are
6017 * -X3- -------------------------------
6018 * -X3- BanTimeout: Bans are removed:
6019 * -X3- ----- * indicates current -----
6020 * -X3- 0: [*] Never.
6021 * -X3- 1: [ ] After 10 minutes.
6022 * -X3- 2: [ ] After 2 hours.
6023 * -X3- 3: [ ] After 4 hours.
6024 * -X3- 4: [ ] After 24 hours.
6025 * -X3- 5: [ ] After one week.
6026 * -X3- ------------- End -------------
6029 channel_multiple_option(enum charOption option
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
6031 struct chanData
*cData
= channel
->channel_info
;
6032 int count
= charOptions
[option
].count
, index
;
6036 index
= atoi(argv
[1]);
6038 if(!isdigit(argv
[1][0]) || (index
< 0) || (index
>= count
))
6040 reply("CSMSG_INVALID_NUMERIC", index
);
6041 /* Show possible values. */
6042 for(index
= 0; index
< count
; index
++)
6043 reply(charOptions
[option
].format_name
, index
, user_find_message(user
, charOptions
[option
].values
[index
].format_name
));
6047 cData
->chOpts
[option
] = charOptions
[option
].values
[index
].value
;
6051 /* Find current option value. */
6054 (index
< count
) && (cData
->chOpts
[option
] != charOptions
[option
].values
[index
].value
);
6058 /* Somehow, the option value is corrupt; reset it to the default. */
6059 cData
->chOpts
[option
] = charOptions
[option
].default_value
;
6064 reply(charOptions
[option
].format_name
, index
, user_find_message(user
, charOptions
[option
].values
[index
].format_name
));
6068 static MODCMD_FUNC(chan_opt_automode
)
6070 return channel_multiple_option(chAutomode
, CSFUNC_ARGS
);
6073 static MODCMD_FUNC(chan_opt_protect
)
6075 return channel_multiple_option(chProtect
, CSFUNC_ARGS
);
6078 static MODCMD_FUNC(chan_opt_toys
)
6080 return channel_multiple_option(chToys
, CSFUNC_ARGS
);
6083 static MODCMD_FUNC(chan_opt_ctcpreaction
)
6085 return channel_multiple_option(chCTCPReaction
, CSFUNC_ARGS
);
6088 static MODCMD_FUNC(chan_opt_bantimeout
)
6090 return channel_multiple_option(chBanTimeout
, CSFUNC_ARGS
);
6093 static MODCMD_FUNC(chan_opt_topicrefresh
)
6095 return channel_multiple_option(chTopicRefresh
, CSFUNC_ARGS
);
6098 static MODCMD_FUNC(chan_opt_resync
)
6100 return channel_multiple_option(chResync
, CSFUNC_ARGS
);
6103 static struct svccmd_list set_shows_list
;
6106 handle_svccmd_unbind(struct svccmd
*target
) {
6108 for(ii
=0; ii
<set_shows_list
.used
; ++ii
)
6109 if(target
== set_shows_list
.list
[ii
])
6110 set_shows_list
.used
= 0;
6113 static CHANSERV_FUNC(cmd_set
)
6115 struct svccmd
*subcmd
;
6119 /* Check if we need to (re-)initialize set_shows_list. */
6120 if(!set_shows_list
.used
)
6122 if(!set_shows_list
.size
)
6124 set_shows_list
.size
= chanserv_conf
.set_shows
->used
;
6125 set_shows_list
.list
= calloc(set_shows_list
.size
, sizeof(set_shows_list
.list
[0]));
6127 for(ii
= 0; ii
< chanserv_conf
.set_shows
->used
; ii
++)
6129 const char *name
= chanserv_conf
.set_shows
->list
[ii
];
6130 sprintf(buf
, "%s %s", argv
[0], name
);
6131 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
6134 log_module(CS_LOG
, LOG_ERROR
, "Unable to find set option \"%s\".", name
);
6137 svccmd_list_append(&set_shows_list
, subcmd
);
6143 reply("CSMSG_CHANNEL_OPTIONS", channel
->name
);
6144 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
6146 for(ii
= 0; ii
< set_shows_list
.used
; ii
++)
6148 subcmd
= set_shows_list
.list
[ii
];
6149 subcmd
->command
->func(user
, channel
, 1, argv
+1, subcmd
);
6151 reply("CSMSG_CHANNEL_OPTIONS_END");
6155 sprintf(buf
, "%s %s", argv
[0], argv
[1]);
6156 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
6159 reply("CSMSG_INVALID_OPTION", argv
[1], argv
[0]);
6162 if((argc
> 2) && !check_user_level(channel
, user
, lvlSetters
, 1, 0))
6164 reply("CSMSG_NO_ACCESS");
6168 return subcmd
->command
->func(user
, channel
, argc
- 1, argv
+ 1, subcmd
);
6172 user_binary_option(char *name
, unsigned long mask
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
6174 struct userData
*uData
;
6176 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
6179 reply("CSMSG_NOT_USER", channel
->name
);
6185 /* Just show current option value. */
6187 else if(enabled_string(argv
[1]))
6189 uData
->flags
|= mask
;
6191 else if(disabled_string(argv
[1]))
6193 uData
->flags
&= ~mask
;
6197 reply("MSG_INVALID_BINARY", argv
[1]);
6201 reply(name
, user_find_message(user
, (uData
->flags
& mask
) ? "MSG_ON" : "MSG_OFF"));
6205 static MODCMD_FUNC(user_opt_autoop
)
6207 struct userData
*uData
;
6209 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
6212 reply("CSMSG_NOT_USER", channel
->name
);
6215 if(uData
->access
< UL_OP
/*channel->channel_info->lvlOpts[lvlGiveOps]*/)
6216 return user_binary_option("CSMSG_USET_AUTOVOICE", USER_AUTO_OP
, CSFUNC_ARGS
);
6218 return user_binary_option("CSMSG_USET_AUTOOP", USER_AUTO_OP
, CSFUNC_ARGS
);
6219 /* TODO: add halfops error message? or is the op one generic enough? */
6222 static MODCMD_FUNC(user_opt_autoinvite
)
6224 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE
, CSFUNC_ARGS
);
6227 static MODCMD_FUNC(user_opt_info
)
6229 struct userData
*uData
;
6232 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
6236 /* If they got past the command restrictions (which require access)
6237 * but fail this test, we have some fool with security override on.
6239 reply("CSMSG_NOT_USER", channel
->name
);
6246 infoline
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
6247 if(strlen(infoline
) > chanserv_conf
.max_userinfo_length
)
6249 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf
.max_userinfo_length
);
6252 bp
= strcspn(infoline
, "\001");
6255 reply("CSMSG_BAD_INFOLINE", infoline
[bp
]);
6260 if(infoline
[0] == '*' && infoline
[1] == 0)
6263 uData
->info
= strdup(infoline
);
6266 reply("CSMSG_USET_INFO", uData
->info
);
6268 reply("CSMSG_USET_INFO", user_find_message(user
, "MSG_NONE"));
6272 struct svccmd_list uset_shows_list
;
6274 static CHANSERV_FUNC(cmd_uset
)
6276 struct svccmd
*subcmd
;
6280 /* Check if we need to (re-)initialize uset_shows_list. */
6281 if(!uset_shows_list
.used
)
6285 "AutoOp", "AutoInvite", "Info"
6288 if(!uset_shows_list
.size
)
6290 uset_shows_list
.size
= ArrayLength(options
);
6291 uset_shows_list
.list
= calloc(uset_shows_list
.size
, sizeof(uset_shows_list
.list
[0]));
6293 for(ii
= 0; ii
< ArrayLength(options
); ii
++)
6295 const char *name
= options
[ii
];
6296 sprintf(buf
, "%s %s", argv
[0], name
);
6297 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
6300 log_module(CS_LOG
, LOG_ERROR
, "Unable to find uset option %s.", name
);
6303 svccmd_list_append(&uset_shows_list
, subcmd
);
6309 /* Do this so options are presented in a consistent order. */
6310 reply("CSMSG_USER_OPTIONS");
6311 for(ii
= 0; ii
< uset_shows_list
.used
; ii
++)
6312 uset_shows_list
.list
[ii
]->command
->func(user
, channel
, 1, argv
+1, uset_shows_list
.list
[ii
]);
6316 sprintf(buf
, "%s %s", argv
[0], argv
[1]);
6317 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
6320 reply("CSMSG_INVALID_OPTION", argv
[1], argv
[0]);
6324 return subcmd
->command
->func(user
, channel
, argc
- 1, argv
+ 1, subcmd
);
6327 static CHANSERV_FUNC(cmd_giveownership
)
6329 struct handle_info
*new_owner_hi
;
6330 struct userData
*new_owner
, *curr_user
;
6331 struct chanData
*cData
= channel
->channel_info
;
6332 struct do_not_register
*dnr
;
6333 struct giveownership
*giveownership
;
6334 unsigned int force
, override
;
6335 unsigned short co_access
, new_owner_old_access
;
6336 char reason
[MAXLEN
], transfer_reason
[MAXLEN
];
6339 curr_user
= GetChannelAccess(cData
, user
->handle_info
);
6340 force
= IsHelping(user
) && (argc
> 2) && !irccasecmp(argv
[2], "force");
6342 struct userData
*uData
= _GetChannelUser(channel
->channel_info
, user
->handle_info
, 1, 0);
6343 override
= ((cmd
->effective_flags
& MODCMD_REQUIRE_CHANUSER
)
6344 && (uData
->access
> 500)
6345 && (!(uData
= _GetChannelUser(channel
->channel_info
, user
->handle_info
, 0, 0))
6346 || uData
->access
< 500));
6349 if(!curr_user
|| (curr_user
->access
!= UL_OWNER
))
6351 struct userData
*owner
= NULL
;
6352 for(curr_user
= channel
->channel_info
->users
;
6354 curr_user
= curr_user
->next
)
6356 if(curr_user
->access
!= UL_OWNER
)
6360 reply("CSMSG_MULTIPLE_OWNERS", channel
->name
);
6367 else if (!force
&& (now
< (time_t)(cData
->ownerTransfer
+ chanserv_conf
.giveownership_period
)))
6369 char delay
[INTERVALLEN
];
6370 intervalString(delay
, cData
->ownerTransfer
+ chanserv_conf
.giveownership_period
- now
, user
->handle_info
);
6371 reply("CSMSG_TRANSFER_WAIT", delay
, channel
->name
);
6374 if(!(new_owner_hi
= modcmd_get_handle_info(user
, argv
[1])))
6376 if(new_owner_hi
== user
->handle_info
)
6378 reply("CSMSG_NO_TRANSFER_SELF");
6381 new_owner
= GetChannelAccess(cData
, new_owner_hi
);
6386 new_owner
= add_channel_user(cData
, new_owner_hi
, UL_COOWNER
, 0, NULL
);
6390 reply("CSMSG_NO_CHAN_USER", new_owner_hi
->handle
, channel
->name
);
6394 if((chanserv_get_owned_count(new_owner_hi
) >= chanserv_conf
.max_owned
) && !force
)
6396 reply("CSMSG_OWN_TOO_MANY", new_owner_hi
->handle
, chanserv_conf
.max_owned
);
6399 if((dnr
= chanserv_is_dnr(NULL
, new_owner_hi
)) && !force
) {
6400 if(!IsHelping(user
))
6401 reply("CSMSG_DNR_ACCOUNT", new_owner_hi
->handle
);
6403 chanserv_show_dnrs(user
, cmd
, NULL
, new_owner_hi
->handle
);
6407 new_owner_old_access
= new_owner
->access
;
6408 if(new_owner
->access
>= UL_COOWNER
)
6409 co_access
= new_owner
->access
;
6411 co_access
= UL_COOWNER
;
6412 new_owner
->access
= UL_OWNER
;
6414 curr_user
->access
= co_access
;
6415 cData
->ownerTransfer
= now
;
6417 giveownership
= calloc(1, sizeof(*giveownership
));
6418 giveownership
->issued
= now
;
6419 giveownership
->old_owner
= curr_user
->handle
->handle
;
6420 giveownership
->target
= new_owner_hi
->handle
;
6421 giveownership
->target_access
= new_owner_old_access
;
6424 if(argc
> (2 + force
))
6426 unsplit_string(argv
+ 2 + force
, argc
- 2 - force
, transfer_reason
);
6427 giveownership
->reason
= strdup(transfer_reason
);
6429 giveownership
->staff_issuer
= strdup(user
->handle_info
->handle
);
6432 giveownership
->previous
= channel
->channel_info
->giveownership
;
6433 channel
->channel_info
->giveownership
= giveownership
;
6435 reply("CSMSG_OWNERSHIP_GIVEN", channel
->name
, new_owner_hi
->handle
);
6436 sprintf(reason
, "%s ownership transferred to %s by %s.", channel
->name
, new_owner_hi
->handle
, user
->handle_info
->handle
);
6437 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
6442 chanserv_expire_user_suspension(void *data
)
6444 struct userData
*target
= data
;
6446 target
->expires
= 0;
6447 target
->flags
&= ~USER_SUSPENDED
;
6450 static CHANSERV_FUNC(cmd_suspend
)
6452 struct handle_info
*hi
;
6453 struct userData
*self
, *target
;
6457 if(!(hi
= modcmd_get_handle_info(user
, argv
[1]))) return 0;
6458 self
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
6459 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
6461 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
6464 if(target
->access
>= self
->access
)
6466 reply("MSG_USER_OUTRANKED", hi
->handle
);
6469 if(target
->flags
& USER_SUSPENDED
)
6471 reply("CSMSG_ALREADY_SUSPENDED", hi
->handle
);
6476 target
->present
= 0;
6479 if(!strcmp(argv
[2], "0"))
6483 unsigned int duration
;
6484 if(!(duration
= ParseInterval(argv
[2])))
6486 reply("MSG_INVALID_DURATION", argv
[2]);
6489 expiry
= now
+ duration
;
6492 target
->expires
= expiry
;
6495 timeq_add(target
->expires
, chanserv_expire_user_suspension
, target
);
6497 target
->flags
|= USER_SUSPENDED
;
6498 reply("CSMSG_USER_SUSPENDED", hi
->handle
, channel
->name
);
6502 static CHANSERV_FUNC(cmd_unsuspend
)
6504 struct handle_info
*hi
;
6505 struct userData
*self
, *target
;
6508 if(!(hi
= modcmd_get_handle_info(user
, argv
[1]))) return 0;
6509 self
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
6510 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
6512 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
6515 if(target
->access
>= self
->access
)
6517 reply("MSG_USER_OUTRANKED", hi
->handle
);
6520 if(!(target
->flags
& USER_SUSPENDED
))
6522 reply("CSMSG_NOT_SUSPENDED", hi
->handle
);
6525 target
->flags
&= ~USER_SUSPENDED
;
6526 scan_user_presence(target
, NULL
);
6527 timeq_del(target
->expires
, chanserv_expire_user_suspension
, target
, 0);
6528 reply("CSMSG_USER_UNSUSPENDED", hi
->handle
, channel
->name
);
6532 static MODCMD_FUNC(cmd_deleteme
)
6534 struct handle_info
*hi
;
6535 struct userData
*target
;
6536 const char *confirm_string
;
6537 unsigned short access
;
6540 hi
= user
->handle_info
;
6541 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
6543 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
6546 if(target
->access
== UL_OWNER
)
6548 reply("CSMSG_NO_OWNER_DELETEME", channel
->name
);
6551 confirm_string
= make_confirmation_string(target
);
6552 if((argc
< 2) || strcmp(argv
[1], confirm_string
))
6554 reply("CSMSG_CONFIRM_DELETEME", confirm_string
);
6557 access
= target
->access
;
6558 channel_name
= strdup(channel
->name
);
6559 del_channel_user(target
, 1);
6560 reply("CSMSG_DELETED_YOU", access
, channel_name
);
6566 chanserv_refresh_topics(UNUSED_ARG(void *data
))
6568 unsigned int refresh_num
= (now
- self
->link
) / chanserv_conf
.refresh_period
;
6569 struct chanData
*cData
;
6572 for(cData
= channelList
; cData
; cData
= cData
->next
)
6574 if(IsSuspended(cData
))
6576 opt
= cData
->chOpts
[chTopicRefresh
];
6579 if((refresh_num
- cData
->last_refresh
) < (unsigned int)(1 << (opt
- '1')))
6582 SetChannelTopic(cData
->channel
, chanserv
, chanserv
, cData
->topic
, 1);
6583 cData
->last_refresh
= refresh_num
;
6585 timeq_add(now
+ chanserv_conf
.refresh_period
, chanserv_refresh_topics
, NULL
);
6589 chanserv_auto_resync(UNUSED_ARG(void *data
))
6591 unsigned int refresh_num
= (now
- self
->link
) / chanserv_conf
.refresh_period
;
6592 struct chanData
*cData
;
6595 for(cData
= channelList
; cData
; cData
= cData
->next
)
6597 if(IsSuspended(cData
)) continue;
6598 opt
= cData
->chOpts
[chResync
];
6599 if(opt
== 'n') continue;
6600 if((refresh_num
- cData
->last_resync
) < (unsigned int)(1 << (opt
- '1'))) continue;
6601 resync_channel(cData
->channel
);
6602 cData
->last_resync
= refresh_num
;
6604 timeq_add(now
+ chanserv_conf
.refresh_period
, chanserv_auto_resync
, NULL
);
6607 static CHANSERV_FUNC(cmd_unf
)
6611 char response
[MAXLEN
];
6612 const char *fmt
= user_find_message(user
, "CSMSG_UNF_RESPONSE");
6613 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
6614 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6617 reply("CSMSG_UNF_RESPONSE");
6621 static CHANSERV_FUNC(cmd_ping
)
6625 char response
[MAXLEN
];
6626 const char *fmt
= user_find_message(user
, "CSMSG_PING_RESPONSE");
6627 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
6628 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6631 reply("CSMSG_PING_RESPONSE");
6635 static CHANSERV_FUNC(cmd_wut
)
6639 char response
[MAXLEN
];
6640 const char *fmt
= user_find_message(user
, "CSMSG_WUT_RESPONSE");
6641 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
6642 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6645 reply("CSMSG_WUT_RESPONSE");
6650 static CHANSERV_FUNC(cmd_8ball
)
6652 unsigned int i
, j
, accum
;
6657 for(i
=1; i
<argc
; i
++)
6658 for(j
=0; argv
[i
][j
]; j
++)
6659 accum
= (accum
<< 5) - accum
+ toupper(argv
[i
][j
]);
6660 resp
= chanserv_conf
.eightball
->list
[accum
% chanserv_conf
.eightball
->used
];
6663 char response
[MAXLEN
];
6664 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, resp
);
6665 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6668 send_message_type(4, user
, cmd
->parent
->bot
, "%s", resp
);
6672 #else /* Use cool 8ball instead */
6674 void eightball(char *outcome
, int method
, unsigned int seed
)
6678 #define NUMOFCOLORS 18
6679 char ballcolors
[50][50] = {"blue", "red", "green", "yellow",
6680 "white", "black", "grey", "brown",
6681 "yellow", "pink", "purple", "orange", "teal", "burgandy",
6682 "fuchsia","turquoise","magenta", "cyan"};
6683 #define NUMOFLOCATIONS 50
6684 char balllocations
[50][55] = {
6685 "Locke's house", "Oregon", "California", "Indiana", "Canada",
6686 "Russia", "Japan", "Florida", "the Bahamas", "Hiroshima",
6687 "the Caribbean", "the Everglades", "your head", "your pants", "your school",
6688 "the Statue of Liberty", "Mt. Fugi", "your mother's house", "IRC", "OSU",
6689 "Locke's cat", "the closet", "the washroom", "the lake", "Spain",
6690 "the bathtub", "the toilet", "the sewer", "a horse", "Jupiter",
6691 "Uranus", "Pluto", "a dark place", "your undies", "your shirt",
6692 "your bra", "your hair", "your bed", "the couch", "the wall",
6693 "Reed", "here --> [X]", "your brain", "Italy", "the Netherlands",
6694 "Mars", "my hardware", "the bar", "Neverland Ranch", "Germany" };
6695 #define NUMOFPREPS 15
6696 char ballpreps
[50][50] = {
6697 "Near", "Somewhere near", "In", "In", "In",
6698 "In", "Hiding in", "Under", "Next to", "Over",
6699 "Crying in", "Right beside", "Nowhere near", "North of", "Trying to find"};
6700 #define NUMOFNUMS 34
6701 char ballnums
[50][50] = {
6702 "A hundred", "A thousand", "A few", "42",
6703 "About 1", "About 2", "About 3", "About 4", "About 5", "About 6", "About 7", "About 8", "About 9", "About 10",
6704 "1", "2", "3", "4", "5", "6", "7", "8", "9", "Ten",
6705 "1", "2", "3", "4", "5", "6", "7", "8", "9", "Ten",
6707 #define NUMOFMULTS 8
6708 char ballmults
[50][50] = { " million", " or so", " thousand", "", " or less", " or more", "", ""};
6711 * 0: normal (Not used in x3)
6718 if (method
== 1) /* A Color */
6722 answer
= (rand() % 12); /* Make sure this is the # of entries */
6725 case 0: strcpy(tmp
, "Very bright %s, I'd say.");
6727 case 1: strcpy(tmp
, "Sort of a light %s color.");
6729 case 2: strcpy(tmp
, "Dark and dreary %s.");
6731 case 3: strcpy(tmp
, "Quite a pale shade of %s.");
6733 case 4: strcpy(tmp
, "A gross kind of mucky %s.");
6735 case 5: strcpy(tmp
, "Brilliant whiteish %s.");
6737 case 6: case 7: case 8: case 9: strcpy(tmp
, "%s.");
6739 case 10: strcpy(tmp
, "Solid %s.");
6741 case 11: strcpy(tmp
, "Transparent %s.");
6743 default: strcpy(outcome
, "An invalid random number was generated.");
6746 sprintf(outcome
, tmp
, ballcolors
[rand() % NUMOFCOLORS
]);
6749 else if (method
== 2) /* Location */
6751 sprintf(outcome
, "%s %s.", ballpreps
[rand() % NUMOFPREPS
], balllocations
[rand() % NUMOFLOCATIONS
]);
6753 else if (method
== 3) /* Number of ___ */
6755 sprintf(outcome
, "%s%s.", ballnums
[rand() % NUMOFNUMS
], ballmults
[rand() % NUMOFMULTS
]);
6759 //Debug(DBGWARNING, "Error in 8ball.");
6764 static CHANSERV_FUNC(cmd_8ball
)
6766 char *word1
, *word2
, *word3
;
6767 static char eb
[MAXLEN
];
6768 unsigned int accum
, i
, j
;
6772 for(i
=1; i
<argc
; i
++)
6773 for(j
=0; argv
[i
][j
]; j
++)
6774 accum
= (accum
<< 5) - accum
+ toupper(argv
[i
][j
]);
6776 accum
+= time(NULL
)/3600;
6778 word2
= argc
>2?argv
[2]:"";
6779 word3
= argc
>3?argv
[3]:"";
6782 if((word2
) && strcasecmp(word1
, "what") == 0 && strcasecmp(word2
, "color") == 0)
6783 eightball(eb
, 1, accum
);
6784 else if((word3
) && strcasecmp(word1
, "what's") == 0 && strcasecmp(word2
, "the") == 0 && strcasecmp(word3
, "color") == 0)
6785 eightball(eb
, 1, accum
);
6786 else if((word3
) && strcasecmp(word1
, "whats") == 0 && strcasecmp(word2
, "the") == 0 && strcasecmp(word3
, "color") == 0)
6787 eightball(eb
, 1, accum
);
6788 /*** LOCATION *****/
6793 (strcasecmp(word1
, "where") == 0) &&
6794 (strcasecmp(word2
, "is") == 0)
6798 strcasecmp(word1
, "where's") == 0
6801 eightball(eb
, 2, accum
);
6803 else if((word2
) && strcasecmp(word1
, "how") == 0 && strcasecmp(word2
, "many") == 0)
6804 eightball(eb
, 3, accum
);
6808 /* Generic 8ball question.. so pull from x3.conf srvx style */
6811 resp
= chanserv_conf
.eightball
->list
[accum
% chanserv_conf
.eightball
->used
];
6814 char response
[MAXLEN
];
6815 sprintf(response
, "\002%s\002: %s", user
->nick
, resp
);
6816 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6819 send_message_type(4, user
, cmd
->parent
->bot
, "%s", resp
);
6825 char response
[MAXLEN
];
6826 sprintf(response
, "\002%s\002: %s", user
->nick
, eb
);
6827 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6830 send_message_type(4, user
, cmd
->parent
->bot
, "%s", eb
);
6835 static CHANSERV_FUNC(cmd_d
)
6837 unsigned long sides
, count
, modifier
, ii
, total
;
6838 char response
[MAXLEN
], *sep
;
6842 if((count
= strtoul(argv
[1], &sep
, 10)) < 1)
6852 else if(((sep
[0] == 'd') || (sep
[0] == 'D')) && isdigit(sep
[1])
6853 && (sides
= strtoul(sep
+1, &sep
, 10)) > 1)
6857 else if((sep
[0] == '-') && isdigit(sep
[1]))
6858 modifier
= strtoul(sep
, NULL
, 10);
6859 else if((sep
[0] == '+') && isdigit(sep
[1]))
6860 modifier
= strtoul(sep
+1, NULL
, 10);
6867 reply("CSMSG_BAD_DIE_FORMAT", argv
[1]);
6872 reply("CSMSG_BAD_DICE_COUNT", count
, 10);
6875 for(total
= ii
= 0; ii
< count
; ++ii
)
6876 total
+= (rand() % sides
) + 1;
6879 if((count
> 1) || modifier
)
6881 fmt
= user_find_message(user
, "CSMSG_DICE_ROLL");
6882 sprintf(response
, fmt
, total
, count
, sides
, modifier
);
6886 fmt
= user_find_message(user
, "CSMSG_DIE_ROLL");
6887 sprintf(response
, fmt
, total
, sides
);
6890 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, response
);
6892 send_message_type(4, user
, cmd
->parent
->bot
, "%s", response
);
6896 static CHANSERV_FUNC(cmd_huggle
)
6898 /* CTCP must be via PRIVMSG, never notice */
6900 send_target_message(1, channel
->name
, cmd
->parent
->bot
, "CSMSG_HUGGLES_HIM", user
->nick
);
6902 send_target_message(1, user
->nick
, cmd
->parent
->bot
, "CSMSG_HUGGLES_YOU");
6906 static CHANSERV_FUNC(cmd_calc
)
6908 char response
[MAXLEN
];
6911 do_math(response
, unsplit_string(argv
+ 1, argc
- 1, NULL
));
6914 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, response
);
6916 send_message_type(4, user
, cmd
->parent
->bot
, "%s", response
);
6921 chanserv_adjust_limit(void *data
)
6923 struct mod_chanmode change
;
6924 struct chanData
*cData
= data
;
6925 struct chanNode
*channel
= cData
->channel
;
6928 if(IsSuspended(cData
))
6931 cData
->limitAdjusted
= now
;
6932 limit
= channel
->members
.used
+ chanserv_conf
.adjust_threshold
+ 5;
6933 if(cData
->modes
.modes_set
& MODE_LIMIT
)
6935 if(limit
> cData
->modes
.new_limit
)
6936 limit
= cData
->modes
.new_limit
;
6937 else if(limit
== cData
->modes
.new_limit
)
6941 mod_chanmode_init(&change
);
6942 change
.modes_set
= MODE_LIMIT
;
6943 change
.new_limit
= limit
;
6944 mod_chanmode_announce(chanserv
, channel
, &change
);
6948 handle_new_channel(struct chanNode
*channel
)
6950 struct chanData
*cData
;
6952 if(!(cData
= channel
->channel_info
))
6955 if(cData
->modes
.modes_set
|| cData
->modes
.modes_clear
)
6956 mod_chanmode_announce(chanserv
, cData
->channel
, &cData
->modes
);
6958 if(self
->uplink
&& !self
->uplink
->burst
&& channel
->channel_info
->topic
)
6959 SetChannelTopic(channel
, chanserv
, chanserv
, channel
->channel_info
->topic
, 1);
6962 /* Welcome to my worst nightmare. Warning: Read (or modify)
6963 the code below at your own risk. */
6965 handle_join(struct modeNode
*mNode
)
6967 struct mod_chanmode change
;
6968 struct userNode
*user
= mNode
->user
;
6969 struct chanNode
*channel
= mNode
->channel
;
6970 struct chanData
*cData
;
6971 struct userData
*uData
= NULL
;
6972 struct banData
*bData
;
6973 struct handle_info
*handle
;
6974 unsigned int modes
= 0, info
= 0;
6977 if(IsLocal(user
) || !channel
->channel_info
|| IsSuspended(channel
->channel_info
))
6980 cData
= channel
->channel_info
;
6981 if(channel
->members
.used
> cData
->max
)
6982 cData
->max
= channel
->members
.used
;
6985 /* Check for bans. If they're joining through a ban, one of two
6987 * 1: Join during a netburst, by riding the break. Kick them
6988 * unless they have ops or voice in the channel.
6989 * 2: They're allowed to join through the ban (an invite in
6990 * ircu2.10, or a +e on Hybrid, or something).
6991 * If they're not joining through a ban, and the banlist is not
6992 * full, see if they're on the banlist for the channel. If so,
6995 if(user
->uplink
->burst
&& !mNode
->modes
)
6998 for(ii
= 0; ii
< channel
->banlist
.used
; ii
++)
7000 if(user_matches_glob(user
, channel
->banlist
.list
[ii
]->ban
, MATCH_USENICK
))
7002 /* Riding a netburst. Naughty. */
7003 KickChannelUser(user
, channel
, chanserv
, "User from far side of netsplit should have been banned - bye.");
7010 mod_chanmode_init(&change
);
7012 if(channel
->banlist
.used
< MAXBANS
)
7014 /* Not joining through a ban. */
7015 for(bData
= cData
->bans
;
7016 bData
&& !user_matches_glob(user
, bData
->mask
, MATCH_USENICK
);
7017 bData
= bData
->next
);
7021 char kick_reason
[MAXLEN
];
7022 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
7024 bData
->triggered
= now
;
7025 if(bData
!= cData
->bans
)
7027 /* Shuffle the ban to the head of the list. */
7029 bData
->next
->prev
= bData
->prev
;
7031 bData
->prev
->next
= bData
->next
;
7034 bData
->next
= cData
->bans
;
7037 cData
->bans
->prev
= bData
;
7038 cData
->bans
= bData
;
7041 change
.args
[0].mode
= MODE_BAN
;
7042 change
.args
[0].u
.hostmask
= bData
->mask
;
7043 mod_chanmode_announce(chanserv
, channel
, &change
);
7044 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
7049 /* ChanServ will not modify the limits in join-flooded channels.
7050 It will also skip DynLimit processing when the user (or srvx)
7051 is bursting in, because there are likely more incoming. */
7052 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
)
7053 && !user
->uplink
->burst
7054 && !channel
->join_flooded
7055 && (channel
->limit
- channel
->members
.used
) < chanserv_conf
.adjust_threshold
)
7057 /* The user count has begun "bumping" into the channel limit,
7058 so set a timer to raise the limit a bit. Any previous
7059 timers are removed so three incoming users within the delay
7060 results in one limit change, not three. */
7062 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
7063 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
7066 /* Give automodes exept during join-floods */
7067 if(!channel
->join_flooded
)
7069 if(cData
->chOpts
[chAutomode
] == 'v')
7070 modes
|= MODE_VOICE
;
7071 else if(cData
->chOpts
[chAutomode
] == 'h')
7072 modes
|= MODE_HALFOP
;
7073 else if(cData
->chOpts
[chAutomode
] == 'o')
7074 modes
|= MODE_CHANOP
;
7077 greeting
= cData
->greeting
;
7078 if(user
->handle_info
)
7080 handle
= user
->handle_info
;
7082 if(IsHelper(user
) && !IsHelping(user
))
7085 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
7087 if(channel
== chanserv_conf
.support_channels
.list
[ii
])
7089 HANDLE_SET_FLAG(user
->handle_info
, HELPING
);
7095 uData
= GetTrueChannelAccess(cData
, handle
);
7096 if(uData
&& !IsUserSuspended(uData
))
7098 /* non users getting automodes are handled above. */
7099 if(IsUserAutoOp(uData
) && cData
->chOpts
[chAutomode
] != 'n')
7101 if(uData
->access
>= UL_OP
)
7102 modes
|= MODE_CHANOP
;
7103 else if(uData
->access
>= UL_HALFOP
)
7104 modes
|= MODE_HALFOP
;
7105 else if(uData
->access
>= UL_PEON
&& cData
->chOpts
[chAutomode
] != 'm')
7106 modes
|= MODE_VOICE
;
7108 if(uData
->access
>= UL_PRESENT
)
7109 cData
->visited
= now
;
7110 if(cData
->user_greeting
)
7111 greeting
= cData
->user_greeting
;
7113 && (uData
->access
>= cData
->lvlOpts
[lvlUserInfo
])
7114 && ((now
- uData
->seen
) >= chanserv_conf
.info_delay
)
7122 /* If user joining normally (not during burst), apply op or voice,
7123 * and send greeting/userinfo as appropriate.
7125 if(!user
->uplink
->burst
)
7129 /* -- I'd rather have ops get voice too, if automode is v. -Rubin
7130 if(modes & MODE_CHANOP) {
7131 modes &= ~MODE_HALFOP;
7132 modes &= ~MODE_VOICE;
7135 change
.args
[0].mode
= modes
;
7136 change
.args
[0].u
.member
= mNode
;
7137 mod_chanmode_announce(chanserv
, channel
, &change
);
7140 send_message_type(4, user
, chanserv
, "(%s) %s", channel
->name
, greeting
);
7142 send_target_message(5, channel
->name
, chanserv
, "[%s] %s", user
->nick
, uData
->info
);
7148 handle_auth(struct userNode
*user
, UNUSED_ARG(struct handle_info
*old_handle
))
7150 struct mod_chanmode change
;
7151 struct userData
*channel
;
7152 unsigned int ii
, jj
, i
;
7154 if(!user
->handle_info
)
7157 mod_chanmode_init(&change
);
7159 for(channel
= user
->handle_info
->channels
; channel
; channel
= channel
->u_next
)
7161 struct chanNode
*cn
;
7162 struct modeNode
*mn
;
7163 if(IsUserSuspended(channel
)
7164 || IsSuspended(channel
->channel
)
7165 || !(cn
= channel
->channel
->channel
))
7168 mn
= GetUserMode(cn
, user
);
7171 if(!IsUserSuspended(channel
)
7172 && IsUserAutoInvite(channel
)
7173 && (channel
->access
>= channel
->channel
->lvlOpts
[lvlInviteMe
])
7175 && !user
->uplink
->burst
)
7176 irc_invite(chanserv
, user
, cn
);
7180 if(channel
->access
>= UL_PRESENT
)
7181 channel
->channel
->visited
= now
;
7183 if(IsUserAutoOp(channel
))
7185 if(channel
->access
>= UL_OP
)
7186 change
.args
[0].mode
= MODE_CHANOP
;
7187 else if(channel
->access
>= UL_HALFOP
)
7188 change
.args
[0].mode
= MODE_HALFOP
;
7189 else if(channel
->access
>= UL_PEON
)
7190 change
.args
[0].mode
= MODE_VOICE
;
7192 change
.args
[0].mode
= 0;
7193 change
.args
[0].u
.member
= mn
;
7194 if(change
.args
[0].mode
)
7195 mod_chanmode_announce(chanserv
, cn
, &change
);
7198 channel
->seen
= now
;
7199 channel
->present
= 1;
7202 for(ii
= 0; ii
< user
->channels
.used
; ++ii
)
7204 struct chanNode
*channel
= user
->channels
.list
[ii
]->channel
;
7205 struct banData
*ban
;
7207 if((user
->channels
.list
[ii
]->modes
& (MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
))
7208 || !channel
->channel_info
7209 || IsSuspended(channel
->channel_info
))
7211 for(jj
= 0; jj
< channel
->banlist
.used
; ++jj
)
7212 if(user_matches_glob(user
, channel
->banlist
.list
[jj
]->ban
, MATCH_USENICK
))
7214 if(jj
< channel
->banlist
.used
)
7216 for(ban
= channel
->channel_info
->bans
; ban
; ban
= ban
->next
)
7218 char kick_reason
[MAXLEN
];
7219 if(!user_matches_glob(user
, ban
->mask
,MATCH_USENICK
| MATCH_VISIBLE
))
7221 change
.args
[0].mode
= MODE_BAN
;
7222 change
.args
[0].u
.hostmask
= ban
->mask
;
7223 mod_chanmode_announce(chanserv
, channel
, &change
);
7224 sprintf(kick_reason
, "(%s) %s", ban
->owner
, ban
->reason
);
7225 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
7226 ban
->triggered
= now
;
7231 if(IsSupportHelper(user
))
7233 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
7235 if(GetUserMode(chanserv_conf
.support_channels
.list
[ii
], user
))
7237 HANDLE_SET_FLAG(user
->handle_info
, HELPING
);
7243 if (user
->handle_info
->ignores
->used
) {
7244 for (i
=0; i
< user
->handle_info
->ignores
->used
; i
++) {
7245 irc_silence(user
, user
->handle_info
->ignores
->list
[i
], 1);
7249 if (user
->handle_info
->epithet
)
7250 irc_swhois(chanserv
, user
, user
->handle_info
->epithet
);
7254 handle_part(struct modeNode
*mn
, UNUSED_ARG(const char *reason
))
7256 struct chanData
*cData
;
7257 struct userData
*uData
;
7259 cData
= mn
->channel
->channel_info
;
7260 if(!cData
|| IsSuspended(cData
) || IsLocal(mn
->user
))
7263 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
) && !mn
->channel
->join_flooded
)
7265 /* Allow for a bit of padding so that the limit doesn't
7266 track the user count exactly, which could get annoying. */
7267 if((mn
->channel
->limit
- mn
->channel
->members
.used
) > chanserv_conf
.adjust_threshold
+ 5)
7269 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
7270 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
7274 if((uData
= GetTrueChannelAccess(cData
, mn
->user
->handle_info
)))
7276 scan_user_presence(uData
, mn
->user
);
7280 if(IsHelping(mn
->user
) && IsSupportHelper(mn
->user
))
7282 unsigned int ii
, jj
;
7283 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
7285 for(jj
= 0; jj
< mn
->user
->channels
.used
; ++jj
)
7286 if(mn
->user
->channels
.list
[jj
]->channel
== chanserv_conf
.support_channels
.list
[ii
])
7288 if(jj
< mn
->user
->channels
.used
)
7291 if(ii
== chanserv_conf
.support_channels
.used
)
7292 HANDLE_CLEAR_FLAG(mn
->user
->handle_info
, HELPING
);
7297 handle_kick(struct userNode
*kicker
, struct userNode
*victim
, struct chanNode
*channel
)
7299 struct userData
*uData
;
7301 if(!channel
->channel_info
|| !kicker
|| IsService(kicker
)
7302 || (kicker
== victim
) || IsSuspended(channel
->channel_info
)
7303 || (kicker
->handle_info
&& kicker
->handle_info
== victim
->handle_info
))
7306 if(protect_user(victim
, kicker
, channel
->channel_info
))
7308 const char *reason
= user_find_message(kicker
, "CSMSG_USER_PROTECTED_KICK");
7309 KickChannelUser(kicker
, channel
, chanserv
, reason
);
7312 if((uData
= GetTrueChannelAccess(channel
->channel_info
, victim
->handle_info
)))
7317 handle_topic(struct userNode
*user
, struct chanNode
*channel
, const char *old_topic
)
7319 struct chanData
*cData
;
7321 if(!channel
->channel_info
|| !user
|| IsSuspended(channel
->channel_info
) || IsService(user
))
7324 cData
= channel
->channel_info
;
7325 if(bad_topic(channel
, user
, channel
->topic
))
7326 { /* User doesnt have privs to set topics. Undo it */
7327 send_message(user
, chanserv
, "CSMSG_TOPIC_LOCKED", channel
->name
);
7328 SetChannelTopic(channel
, chanserv
, chanserv
, old_topic
, 1);
7331 /* If there is a topic mask set, and the new topic doesnt match,
7332 * set the topic to mask + new_topic */
7333 if(cData
->topic_mask
&& !match_ircglob(channel
->topic
, cData
->topic_mask
))
7335 char new_topic
[TOPICLEN
+1];
7336 conform_topic(cData
->topic_mask
, channel
->topic
, new_topic
);
7339 SetChannelTopic(channel
, chanserv
, user
, new_topic
, 1);
7340 /* and fall through to topicsnarf code below.. */
7342 else /* Topic couldnt fit into mask, was too long */
7344 SetChannelTopic(channel
, chanserv
, user
, old_topic
, 1);
7345 send_message(user
, chanserv
, "CSMSG_TOPICMASK_CONFLICT1", channel
->name
, cData
->topic_mask
);
7346 send_message(user
, chanserv
, "CSMSG_TOPICMASK_CONFLICT2", TOPICLEN
);
7350 /* With topicsnarf, grab the topic and save it as the default topic. */
7351 if(check_user_level(channel
, user
, lvlTopicSnarf
, 0, 0))
7354 cData
->topic
= strdup(channel
->topic
);
7360 handle_mode(struct chanNode
*channel
, struct userNode
*user
, const struct mod_chanmode
*change
)
7362 struct mod_chanmode
*bounce
= NULL
;
7363 unsigned int bnc
, ii
;
7366 if(!channel
->channel_info
|| IsLocal(user
) || IsSuspended(channel
->channel_info
) || IsService(user
))
7369 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
7370 && mode_lock_violated(&channel
->channel_info
->modes
, change
))
7372 char correct
[MAXLEN
];
7373 bounce
= mod_chanmode_dup(&channel
->channel_info
->modes
, change
->argc
+ 1);
7374 mod_chanmode_format(&channel
->channel_info
->modes
, correct
);
7375 send_message(user
, chanserv
, "CSMSG_MODE_LOCKED", correct
, channel
->name
);
7377 for(ii
= bnc
= 0; ii
< change
->argc
; ++ii
)
7379 if((change
->args
[ii
].mode
& (MODE_REMOVE
|MODE_CHANOP
)) == (MODE_REMOVE
|MODE_CHANOP
))
7381 const struct userNode
*victim
= change
->args
[ii
].u
.member
->user
;
7382 if(!protect_user(victim
, user
, channel
->channel_info
))
7385 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
7388 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_CHANOP
;
7389 bounce
->args
[bnc
].u
.member
= GetUserMode(channel
, user
);
7390 if(bounce
->args
[bnc
].u
.member
)
7394 bounce
->args
[bnc
].mode
= MODE_CHANOP
;
7395 bounce
->args
[bnc
].u
.member
= change
->args
[ii
].u
.member
;
7397 send_message(user
, chanserv
, "CSMSG_USER_PROTECTED", victim
->nick
);
7399 else if(change
->args
[ii
].mode
& MODE_CHANOP
)
7401 const struct userNode
*victim
= change
->args
[ii
].u
.member
->user
;
7402 if(IsService(victim
) || validate_op(NULL
, user
, channel
, (struct userNode
*)victim
))
7405 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
7406 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_CHANOP
;
7407 bounce
->args
[bnc
].u
.member
= change
->args
[ii
].u
.member
;
7410 else if((change
->args
[ii
].mode
& (MODE_REMOVE
| MODE_BAN
)) == MODE_BAN
)
7412 const char *ban
= change
->args
[ii
].u
.hostmask
;
7413 if(!bad_channel_ban(channel
, user
, ban
, NULL
, NULL
))
7416 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
7417 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_BAN
;
7418 bounce
->args
[bnc
].u
.hostmask
= strdup(ban
);
7420 send_message(user
, chanserv
, "CSMSG_MASK_PROTECTED", ban
);
7425 if((bounce
->argc
= bnc
) || bounce
->modes_set
|| bounce
->modes_clear
)
7426 mod_chanmode_announce(chanserv
, channel
, bounce
);
7427 for(ii
= 0; ii
< change
->argc
; ++ii
)
7428 if(bounce
->args
[ii
].mode
== (MODE_REMOVE
| MODE_BAN
))
7429 free((char*)bounce
->args
[ii
].u
.hostmask
);
7430 mod_chanmode_free(bounce
);
7435 handle_nick_change(struct userNode
*user
, UNUSED_ARG(const char *old_nick
))
7437 struct chanNode
*channel
;
7438 struct banData
*bData
;
7439 struct mod_chanmode change
;
7440 unsigned int ii
, jj
;
7441 char kick_reason
[MAXLEN
];
7443 mod_chanmode_init(&change
);
7445 change
.args
[0].mode
= MODE_BAN
;
7446 for(ii
= 0; ii
< user
->channels
.used
; ++ii
)
7448 channel
= user
->channels
.list
[ii
]->channel
;
7449 /* Need not check for bans if they're opped or voiced. */
7450 /* TODO: does this make sense in automode v, h, and o? *
7451 * lets still enforce on voice people anyway, and see how that goes -Rubin */
7452 if(user
->channels
.list
[ii
]->modes
& (MODE_CHANOP
|MODE_HALFOP
/*|MODE_VOICE */))
7454 /* Need not check for bans unless channel registration is active. */
7455 if(!channel
->channel_info
|| IsSuspended(channel
->channel_info
))
7457 /* Look for a matching ban already on the channel. */
7458 for(jj
= 0; jj
< channel
->banlist
.used
; ++jj
)
7459 if(user_matches_glob(user
, channel
->banlist
.list
[jj
]->ban
, MATCH_USENICK
))
7461 /* Need not act if we found one. */
7462 if(jj
< channel
->banlist
.used
)
7464 /* Look for a matching ban in this channel. */
7465 for(bData
= channel
->channel_info
->bans
; bData
; bData
= bData
->next
)
7467 if(!user_matches_glob(user
, bData
->mask
, MATCH_USENICK
| MATCH_VISIBLE
))
7469 change
.args
[0].u
.hostmask
= bData
->mask
;
7470 mod_chanmode_announce(chanserv
, channel
, &change
);
7471 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
7472 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
7473 bData
->triggered
= now
;
7474 break; /* we don't need to check any more bans in the channel */
7479 static void handle_rename(struct handle_info
*handle
, const char *old_handle
)
7481 struct do_not_register
*dnr
= dict_find(handle_dnrs
, old_handle
, NULL
);
7485 dict_remove2(handle_dnrs
, old_handle
, 1);
7486 safestrncpy(dnr
->chan_name
+ 1, handle
->handle
, sizeof(dnr
->chan_name
) - 1);
7487 dict_insert(handle_dnrs
, dnr
->chan_name
+ 1, dnr
);
7492 handle_unreg(UNUSED_ARG(struct userNode
*user
), struct handle_info
*handle
)
7494 struct userNode
*h_user
;
7496 if(handle
->channels
)
7498 for(h_user
= handle
->users
; h_user
; h_user
= h_user
->next_authed
)
7499 send_message(h_user
, chanserv
, "CSMSG_HANDLE_UNREGISTERED");
7501 while(handle
->channels
)
7502 del_channel_user(handle
->channels
, 1);
7507 handle_server_link(UNUSED_ARG(struct server
*server
))
7509 struct chanData
*cData
;
7511 for(cData
= channelList
; cData
; cData
= cData
->next
)
7513 if(!IsSuspended(cData
))
7514 cData
->may_opchan
= 1;
7515 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
)
7516 && !cData
->channel
->join_flooded
7517 && ((cData
->channel
->limit
- cData
->channel
->members
.used
)
7518 < chanserv_conf
.adjust_threshold
))
7520 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
7521 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
7527 chanserv_conf_read(void)
7531 char mode_line
[MAXLEN
], *modes
[MAXNUMPARAMS
];
7532 struct mod_chanmode
*change
;
7533 struct string_list
*strlist
;
7534 struct chanNode
*chan
;
7537 if(!(conf_node
= conf_get_data(CHANSERV_CONF_NAME
, RECDB_OBJECT
)))
7539 log_module(CS_LOG
, LOG_ERROR
, "Invalid config node `%s'.", CHANSERV_CONF_NAME
);
7542 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
7543 UnlockChannel(chanserv_conf
.support_channels
.list
[ii
]);
7544 chanserv_conf
.support_channels
.used
= 0;
7545 if((strlist
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL
, RECDB_STRING_LIST
)))
7547 for(ii
= 0; ii
< strlist
->used
; ++ii
)
7549 const char *str2
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL_MODES
, RECDB_QSTRING
);
7552 chan
= AddChannel(strlist
->list
[ii
], now
, str2
, NULL
, NULL
);
7554 channelList_append(&chanserv_conf
.support_channels
, chan
);
7557 else if((str
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL
, RECDB_QSTRING
)))
7560 str2
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL_MODES
, RECDB_QSTRING
);
7563 chan
= AddChannel(str
, now
, str2
, NULL
, NULL
);
7565 channelList_append(&chanserv_conf
.support_channels
, chan
);
7567 str
= database_get_data(conf_node
, KEY_DB_BACKUP_FREQ
, RECDB_QSTRING
);
7568 chanserv_conf
.db_backup_frequency
= str
? ParseInterval(str
) : 7200;
7569 str
= database_get_data(conf_node
, KEY_INFO_DELAY
, RECDB_QSTRING
);
7570 chanserv_conf
.info_delay
= str
? ParseInterval(str
) : 180;
7571 str
= database_get_data(conf_node
, KEY_MAX_GREETLEN
, RECDB_QSTRING
);
7572 chanserv_conf
.greeting_length
= str
? atoi(str
) : 200;
7573 str
= database_get_data(conf_node
, KEY_ADJUST_THRESHOLD
, RECDB_QSTRING
);
7574 chanserv_conf
.adjust_threshold
= str
? atoi(str
) : 15;
7575 str
= database_get_data(conf_node
, KEY_ADJUST_DELAY
, RECDB_QSTRING
);
7576 chanserv_conf
.adjust_delay
= str
? ParseInterval(str
) : 30;
7577 str
= database_get_data(conf_node
, KEY_CHAN_EXPIRE_FREQ
, RECDB_QSTRING
);
7578 chanserv_conf
.channel_expire_frequency
= str
? ParseInterval(str
) : 86400;
7579 str
= database_get_data(conf_node
, KEY_BAN_TIMEOUT_FREQ
, RECDB_QSTRING
);
7580 chanserv_conf
.ban_timeout_frequency
= str
? ParseInterval(str
) : 600;
7581 str
= database_get_data(conf_node
, KEY_CHAN_EXPIRE_DELAY
, RECDB_QSTRING
);
7582 chanserv_conf
.channel_expire_delay
= str
? ParseInterval(str
) : 86400*30;
7583 str
= database_get_data(conf_node
, KEY_NODELETE_LEVEL
, RECDB_QSTRING
);
7584 chanserv_conf
.nodelete_level
= str
? atoi(str
) : 1;
7585 str
= database_get_data(conf_node
, KEY_MAX_CHAN_USERS
, RECDB_QSTRING
);
7586 chanserv_conf
.max_chan_users
= str
? atoi(str
) : 512;
7587 str
= database_get_data(conf_node
, KEY_MAX_CHAN_BANS
, RECDB_QSTRING
);
7588 chanserv_conf
.max_chan_bans
= str
? atoi(str
) : 512;
7589 str
= database_get_data(conf_node
, KEY_MAX_USERINFO_LENGTH
, RECDB_QSTRING
);
7590 chanserv_conf
.max_userinfo_length
= str
? atoi(str
) : 400;
7591 str
= database_get_data(conf_node
, KEY_NICK
, RECDB_QSTRING
);
7593 NickChange(chanserv
, str
, 0);
7594 str
= database_get_data(conf_node
, KEY_REFRESH_PERIOD
, RECDB_QSTRING
);
7595 chanserv_conf
.refresh_period
= str
? ParseInterval(str
) : 3*60*60;
7596 str
= database_get_data(conf_node
, KEY_GIVEOWNERSHIP_PERIOD
, RECDB_QSTRING
);
7597 chanserv_conf
.giveownership_period
= str
? ParseInterval(str
) : 0;
7598 str
= database_get_data(conf_node
, KEY_CTCP_SHORT_BAN_DURATION
, RECDB_QSTRING
);
7599 chanserv_conf
.ctcp_short_ban_duration
= str
? str
: "3m";
7600 str
= database_get_data(conf_node
, KEY_CTCP_LONG_BAN_DURATION
, RECDB_QSTRING
);
7601 chanserv_conf
.ctcp_long_ban_duration
= str
? str
: "1h";
7602 str
= database_get_data(conf_node
, KEY_MAX_OWNED
, RECDB_QSTRING
);
7603 chanserv_conf
.max_owned
= str
? atoi(str
) : 5;
7604 str
= database_get_data(conf_node
, KEY_IRC_OPERATOR_EPITHET
, RECDB_QSTRING
);
7605 chanserv_conf
.irc_operator_epithet
= str
? str
: "a megalomaniacal power hungry tyrant";
7606 str
= database_get_data(conf_node
, KEY_NETWORK_HELPER_EPITHET
, RECDB_QSTRING
);
7607 chanserv_conf
.network_helper_epithet
= str
? str
: "a wannabe tyrant";
7608 str
= database_get_data(conf_node
, KEY_SUPPORT_HELPER_EPITHET
, RECDB_QSTRING
);
7609 chanserv_conf
.support_helper_epithet
= str
? str
: "a wannabe tyrant";
7610 str
= database_get_data(conf_node
, KEY_GOD_TIMEOUT
, RECDB_QSTRING
);
7611 god_timeout
= str
? ParseInterval(str
) : 60*15;
7612 str
= database_get_data(conf_node
, "default_modes", RECDB_QSTRING
);
7615 safestrncpy(mode_line
, str
, sizeof(mode_line
));
7616 ii
= split_line(mode_line
, 0, ArrayLength(modes
), modes
);
7617 if((change
= mod_chanmode_parse(NULL
, modes
, ii
, MCP_KEY_FREE
, 0))
7618 && (change
->argc
< 2))
7620 chanserv_conf
.default_modes
= *change
;
7621 mod_chanmode_free(change
);
7623 free_string_list(chanserv_conf
.set_shows
);
7624 strlist
= database_get_data(conf_node
, "set_shows", RECDB_STRING_LIST
);
7626 strlist
= string_list_copy(strlist
);
7629 static const char *list
[] = {
7630 /* free form text */
7631 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
7632 /* options based on user level */
7633 "PubCmd", "InviteMe", "UserInfo","EnfOps",
7634 "EnfHalfOps", "EnfModes", "EnfTopic", "TopicSnarf", "Setters",
7635 /* multiple choice options */
7636 "AutoMode", "CtcpReaction", "Protect", "Toys", "TopicRefresh", "Resync",
7637 /* binary options */
7638 "DynLimit", "NoDelete", "BanTimeout",
7643 strlist
= alloc_string_list(ArrayLength(list
)-1);
7644 for(ii
=0; list
[ii
]; ii
++)
7645 string_list_append(strlist
, strdup(list
[ii
]));
7647 chanserv_conf
.set_shows
= strlist
;
7648 /* We don't look things up now, in case the list refers to options
7649 * defined by modules initialized after this point. Just mark the
7650 * function list as invalid, so it will be initialized.
7652 set_shows_list
.used
= 0;
7653 free_string_list(chanserv_conf
.eightball
);
7654 strlist
= database_get_data(conf_node
, KEY_8BALL_RESPONSES
, RECDB_STRING_LIST
);
7657 strlist
= string_list_copy(strlist
);
7661 strlist
= alloc_string_list(4);
7662 string_list_append(strlist
, strdup("Yes."));
7663 string_list_append(strlist
, strdup("No."));
7664 string_list_append(strlist
, strdup("Maybe so."));
7666 chanserv_conf
.eightball
= strlist
;
7667 free_string_list(chanserv_conf
.old_ban_names
);
7668 strlist
= database_get_data(conf_node
, KEY_OLD_BAN_NAMES
, RECDB_STRING_LIST
);
7670 strlist
= string_list_copy(strlist
);
7672 strlist
= alloc_string_list(2);
7673 chanserv_conf
.old_ban_names
= strlist
;
7674 str
= database_get_data(conf_node
, "off_channel", RECDB_QSTRING
);
7675 off_channel
= str
? atoi(str
) : 0;
7679 chanserv_note_type_read(const char *key
, struct record_data
*rd
)
7682 struct note_type
*ntype
;
7685 if(!(obj
= GET_RECORD_OBJECT(rd
)))
7687 log_module(CS_LOG
, LOG_ERROR
, "Invalid note type %s.", key
);
7690 if(!(ntype
= chanserv_create_note_type(key
)))
7692 log_module(CS_LOG
, LOG_ERROR
, "Memory allocation failed for note %s.", key
);
7696 /* Figure out set access */
7697 if((str
= database_get_data(obj
, KEY_NOTE_OPSERV_ACCESS
, RECDB_QSTRING
)))
7699 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
7700 ntype
->set_access
.min_opserv
= strtoul(str
, NULL
, 0);
7702 else if((str
= database_get_data(obj
, KEY_NOTE_CHANNEL_ACCESS
, RECDB_QSTRING
)))
7704 ntype
->set_access_type
= NOTE_SET_CHANNEL_ACCESS
;
7705 ntype
->set_access
.min_ulevel
= strtoul(str
, NULL
, 0);
7707 else if((str
= database_get_data(obj
, KEY_NOTE_SETTER_ACCESS
, RECDB_QSTRING
)))
7709 ntype
->set_access_type
= NOTE_SET_CHANNEL_SETTER
;
7713 log_module(CS_LOG
, LOG_ERROR
, "Could not find access type for note %s; defaulting to OpServ access level 0.", key
);
7714 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
7715 ntype
->set_access
.min_opserv
= 0;
7718 /* Figure out visibility */
7719 if(!(str
= database_get_data(obj
, KEY_NOTE_VISIBILITY
, RECDB_QSTRING
)))
7720 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
7721 else if(!irccasecmp(str
, KEY_NOTE_VIS_PRIVILEGED
))
7722 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
7723 else if(!irccasecmp(str
, KEY_NOTE_VIS_CHANNEL_USERS
))
7724 ntype
->visible_type
= NOTE_VIS_CHANNEL_USERS
;
7725 else if(!irccasecmp(str
, KEY_NOTE_VIS_ALL
))
7726 ntype
->visible_type
= NOTE_VIS_ALL
;
7728 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
7730 str
= database_get_data(obj
, KEY_NOTE_MAX_LENGTH
, RECDB_QSTRING
);
7731 ntype
->max_length
= str
? strtoul(str
, NULL
, 0) : 400;
7735 user_read_helper(const char *key
, struct record_data
*rd
, struct chanData
*chan
)
7737 struct handle_info
*handle
;
7738 struct userData
*uData
;
7739 char *seen
, *inf
, *flags
, *expires
;
7741 unsigned short access
;
7743 if(rd
->type
!= RECDB_OBJECT
|| !dict_size(rd
->d
.object
))
7745 log_module(CS_LOG
, LOG_ERROR
, "Invalid user in %s.", chan
->channel
->name
);
7749 access
= atoi(database_get_data(rd
->d
.object
, KEY_LEVEL
, RECDB_QSTRING
));
7750 if(access
> UL_OWNER
)
7752 log_module(CS_LOG
, LOG_ERROR
, "Invalid access level for %s in %s.", key
, chan
->channel
->name
);
7756 inf
= database_get_data(rd
->d
.object
, KEY_INFO
, RECDB_QSTRING
);
7757 seen
= database_get_data(rd
->d
.object
, KEY_SEEN
, RECDB_QSTRING
);
7758 last_seen
= seen
? (signed)strtoul(seen
, NULL
, 0) : now
;
7759 flags
= database_get_data(rd
->d
.object
, KEY_FLAGS
, RECDB_QSTRING
);
7760 expires
= database_get_data(rd
->d
.object
, KEY_EXPIRES
, RECDB_QSTRING
);
7761 handle
= get_handle_info(key
);
7764 log_module(CS_LOG
, LOG_ERROR
, "Nonexistent account %s in %s.", key
, chan
->channel
->name
);
7768 uData
= add_channel_user(chan
, handle
, access
, last_seen
, inf
);
7769 uData
->flags
= flags
? strtoul(flags
, NULL
, 0) : 0;
7770 uData
->expires
= expires
? strtoul(expires
, NULL
, 0) : 0;
7772 if((uData
->flags
& USER_SUSPENDED
) && uData
->expires
)
7774 if(uData
->expires
> now
)
7775 timeq_add(uData
->expires
, chanserv_expire_user_suspension
, uData
);
7777 uData
->flags
&= ~USER_SUSPENDED
;
7780 /* Upgrade: set autoop to the inverse of noautoop */
7781 if(chanserv_read_version
< 2)
7783 /* if noautoop is true, set autoop false, and vice versa */
7784 if(uData
->flags
& USER_NOAUTO_OP
)
7785 uData
->flags
= uData
->flags
& ~USER_AUTO_OP
;
7787 uData
->flags
= uData
->flags
| USER_AUTO_OP
;
7788 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
);
7794 ban_read_helper(const char *key
, struct record_data
*rd
, struct chanData
*chan
)
7796 struct banData
*bData
;
7797 char *set
, *triggered
, *s_duration
, *s_expires
, *reason
, *owner
;
7798 time_t set_time
, triggered_time
, expires_time
;
7800 if(rd
->type
!= RECDB_OBJECT
|| !dict_size(rd
->d
.object
))
7802 log_module(CS_LOG
, LOG_ERROR
, "Invalid ban in %s.", chan
->channel
->name
);
7806 set
= database_get_data(rd
->d
.object
, KEY_SET
, RECDB_QSTRING
);
7807 triggered
= database_get_data(rd
->d
.object
, KEY_TRIGGERED
, RECDB_QSTRING
);
7808 s_duration
= database_get_data(rd
->d
.object
, KEY_DURATION
, RECDB_QSTRING
);
7809 s_expires
= database_get_data(rd
->d
.object
, KEY_EXPIRES
, RECDB_QSTRING
);
7810 owner
= database_get_data(rd
->d
.object
, KEY_OWNER
, RECDB_QSTRING
);
7811 reason
= database_get_data(rd
->d
.object
, KEY_REASON
, RECDB_QSTRING
);
7812 if (!reason
|| !owner
)
7815 set_time
= set
? (time_t)strtoul(set
, NULL
, 0) : now
;
7816 triggered_time
= triggered
? (time_t)strtoul(triggered
, NULL
, 0) : 0;
7818 expires_time
= (time_t)strtoul(s_expires
, NULL
, 0);
7820 expires_time
= set_time
+ atoi(s_duration
);
7824 if(!reason
|| (expires_time
&& (expires_time
< now
)))
7827 bData
= add_channel_ban(chan
, key
, owner
, set_time
, triggered_time
, expires_time
, reason
);
7830 static struct suspended
*
7831 chanserv_read_suspended(dict_t obj
)
7833 struct suspended
*suspended
= calloc(1, sizeof(*suspended
));
7837 str
= database_get_data(obj
, KEY_EXPIRES
, RECDB_QSTRING
);
7838 suspended
->expires
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7839 str
= database_get_data(obj
, KEY_REVOKED
, RECDB_QSTRING
);
7840 suspended
->revoked
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7841 str
= database_get_data(obj
, KEY_ISSUED
, RECDB_QSTRING
);
7842 suspended
->issued
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7843 suspended
->suspender
= strdup(database_get_data(obj
, KEY_SUSPENDER
, RECDB_QSTRING
));
7844 suspended
->reason
= strdup(database_get_data(obj
, KEY_REASON
, RECDB_QSTRING
));
7845 previous
= database_get_data(obj
, KEY_PREVIOUS
, RECDB_OBJECT
);
7846 suspended
->previous
= previous
? chanserv_read_suspended(previous
) : NULL
;
7850 static struct giveownership
*
7851 chanserv_read_giveownership(dict_t obj
)
7853 struct giveownership
*giveownership
= calloc(1, sizeof(*giveownership
));
7857 str
= database_get_data(obj
, KEY_STAFF_ISSUER
, RECDB_QSTRING
);
7858 giveownership
->staff_issuer
= str
? strdup(str
) : NULL
;
7860 giveownership
->old_owner
= strdup(database_get_data(obj
, KEY_OLD_OWNER
, RECDB_QSTRING
));
7862 giveownership
->target
= strdup(database_get_data(obj
, KEY_TARGET
, RECDB_QSTRING
));
7863 giveownership
->target_access
= atoi(database_get_data(obj
, KEY_TARGET_ACCESS
, RECDB_QSTRING
));
7865 str
= database_get_data(obj
, KEY_REASON
, RECDB_QSTRING
);
7866 giveownership
->reason
= str
? strdup(str
) : NULL
;
7867 str
= database_get_data(obj
, KEY_ISSUED
, RECDB_QSTRING
);
7868 giveownership
->issued
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7870 previous
= database_get_data(obj
, KEY_PREVIOUS
, RECDB_OBJECT
);
7871 giveownership
->previous
= previous
? chanserv_read_giveownership(previous
) : NULL
;
7872 return giveownership
;
7876 chanserv_channel_read(const char *key
, struct record_data
*hir
)
7878 struct suspended
*suspended
;
7879 struct giveownership
*giveownership
;
7880 struct mod_chanmode
*modes
;
7881 struct chanNode
*cNode
;
7882 struct chanData
*cData
;
7883 struct dict
*channel
, *obj
;
7884 char *str
, *argv
[10];
7888 channel
= hir
->d
.object
;
7890 str
= database_get_data(channel
, KEY_REGISTRAR
, RECDB_QSTRING
);
7893 cNode
= AddChannel(key
, now
, NULL
, NULL
, NULL
);
7896 log_module(CS_LOG
, LOG_ERROR
, "Unable to create registered channel %s.", key
);
7899 cData
= register_channel(cNode
, str
);
7902 log_module(CS_LOG
, LOG_ERROR
, "Unable to register channel %s from database.", key
);
7906 if((obj
= database_get_data(channel
, KEY_OPTIONS
, RECDB_OBJECT
)))
7908 enum levelOption lvlOpt
;
7909 enum charOption chOpt
;
7911 if((str
= database_get_data(obj
, KEY_FLAGS
, RECDB_QSTRING
)))
7912 cData
->flags
= atoi(str
);
7914 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
7916 str
= database_get_data(obj
, levelOptions
[lvlOpt
].db_name
, RECDB_QSTRING
);
7918 cData
->lvlOpts
[lvlOpt
] = user_level_from_name(str
, UL_OWNER
+1);
7919 else if(levelOptions
[lvlOpt
].old_flag
)
7921 if(cData
->flags
& levelOptions
[lvlOpt
].old_flag
)
7922 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].flag_value
;
7924 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
7928 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
7930 if(!(str
= database_get_data(obj
, charOptions
[chOpt
].db_name
, RECDB_QSTRING
)))
7932 cData
->chOpts
[chOpt
] = str
[0];
7935 else if((str
= database_get_data(channel
, KEY_FLAGS
, RECDB_QSTRING
)))
7937 enum levelOption lvlOpt
;
7938 enum charOption chOpt
;
7941 cData
->flags
= base64toint(str
, 5);
7942 count
= strlen(str
+= 5);
7943 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
7946 if(levelOptions
[lvlOpt
].old_flag
)
7948 if(cData
->flags
& levelOptions
[lvlOpt
].old_flag
)
7949 lvl
= levelOptions
[lvlOpt
].flag_value
;
7951 lvl
= levelOptions
[lvlOpt
].default_value
;
7953 else switch(((count
<= levelOptions
[lvlOpt
].old_idx
) ? str
: CHANNEL_DEFAULT_OPTIONS
)[levelOptions
[lvlOpt
].old_idx
])
7955 case 'c': lvl
= UL_COOWNER
; break;
7956 case 'm': lvl
= UL_MANAGER
; break;
7957 case 'n': lvl
= UL_OWNER
+1; break;
7958 case 'o': lvl
= UL_OP
; break;
7959 case 'p': lvl
= UL_PEON
; break;
7960 case 'h': lvl
= UL_HALFOP
; break;
7961 case 'w': lvl
= UL_OWNER
; break;
7962 default: lvl
= 0; break;
7964 cData
->lvlOpts
[lvlOpt
] = lvl
;
7966 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
7967 cData
->chOpts
[chOpt
] = ((count
<= charOptions
[chOpt
].old_idx
) ? str
: CHANNEL_DEFAULT_OPTIONS
)[charOptions
[chOpt
].old_idx
];
7970 if((obj
= database_get_data(hir
->d
.object
, KEY_SUSPENDED
, RECDB_OBJECT
)))
7972 suspended
= chanserv_read_suspended(obj
);
7973 cData
->suspended
= suspended
;
7974 suspended
->cData
= cData
;
7975 /* We could use suspended->expires and suspended->revoked to
7976 * set the CHANNEL_SUSPENDED flag, but we don't. */
7978 else if(IsSuspended(cData
) && (str
= database_get_data(hir
->d
.object
, KEY_SUSPENDER
, RECDB_QSTRING
)))
7980 suspended
= calloc(1, sizeof(*suspended
));
7981 suspended
->issued
= 0;
7982 suspended
->revoked
= 0;
7983 suspended
->suspender
= strdup(str
);
7984 str
= database_get_data(hir
->d
.object
, KEY_SUSPEND_EXPIRES
, RECDB_QSTRING
);
7985 suspended
->expires
= str
? atoi(str
) : 0;
7986 str
= database_get_data(hir
->d
.object
, KEY_SUSPEND_REASON
, RECDB_QSTRING
);
7987 suspended
->reason
= strdup(str
? str
: "No reason");
7988 suspended
->previous
= NULL
;
7989 cData
->suspended
= suspended
;
7990 suspended
->cData
= cData
;
7994 cData
->flags
&= ~CHANNEL_SUSPENDED
;
7995 suspended
= NULL
; /* to squelch a warning */
7998 if(IsSuspended(cData
)) {
7999 if(suspended
->expires
> now
)
8000 timeq_add(suspended
->expires
, chanserv_expire_suspension
, suspended
);
8001 else if(suspended
->expires
)
8002 cData
->flags
&= ~CHANNEL_SUSPENDED
;
8005 if((obj
= database_get_data(hir
->d
.object
, KEY_GIVEOWNERSHIP
, RECDB_OBJECT
)))
8007 giveownership
= chanserv_read_giveownership(obj
);
8008 cData
->giveownership
= giveownership
;
8011 if((!off_channel
|| !IsOffChannel(cData
)) && !IsSuspended(cData
)) {
8012 struct mod_chanmode change
;
8013 mod_chanmode_init(&change
);
8015 change
.args
[0].mode
= MODE_CHANOP
;
8016 change
.args
[0].u
.member
= AddChannelUser(chanserv
, cNode
);
8017 mod_chanmode_announce(chanserv
, cNode
, &change
);
8020 str
= database_get_data(channel
, KEY_REGISTERED
, RECDB_QSTRING
);
8021 cData
->registered
= str
? (time_t)strtoul(str
, NULL
, 0) : now
;
8022 str
= database_get_data(channel
, KEY_VISITED
, RECDB_QSTRING
);
8023 cData
->visited
= str
? (time_t)strtoul(str
, NULL
, 0) : now
;
8024 str
= database_get_data(channel
, KEY_OWNER_TRANSFER
, RECDB_QSTRING
);
8025 cData
->ownerTransfer
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
8026 str
= database_get_data(channel
, KEY_MAX
, RECDB_QSTRING
);
8027 cData
->max
= str
? atoi(str
) : 0;
8028 str
= database_get_data(channel
, KEY_GREETING
, RECDB_QSTRING
);
8029 cData
->greeting
= str
? strdup(str
) : NULL
;
8030 str
= database_get_data(channel
, KEY_USER_GREETING
, RECDB_QSTRING
);
8031 cData
->user_greeting
= str
? strdup(str
) : NULL
;
8032 str
= database_get_data(channel
, KEY_TOPIC_MASK
, RECDB_QSTRING
);
8033 cData
->topic_mask
= str
? strdup(str
) : NULL
;
8034 str
= database_get_data(channel
, KEY_TOPIC
, RECDB_QSTRING
);
8035 cData
->topic
= str
? strdup(str
) : NULL
;
8037 if(!IsSuspended(cData
)
8038 && (str
= database_get_data(channel
, KEY_MODES
, RECDB_QSTRING
))
8039 && (argc
= split_line(str
, 0, ArrayLength(argv
), argv
))
8040 && (modes
= mod_chanmode_parse(cNode
, argv
, argc
, MCP_KEY_FREE
, 0))) {
8041 cData
->modes
= *modes
;
8043 cData
->modes
.modes_set
|= MODE_REGISTERED
;
8044 if(cData
->modes
.argc
> 1)
8045 cData
->modes
.argc
= 1;
8046 mod_chanmode_announce(chanserv
, cNode
, &cData
->modes
);
8047 mod_chanmode_free(modes
);
8050 obj
= database_get_data(channel
, KEY_USERS
, RECDB_OBJECT
);
8051 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
8052 user_read_helper(iter_key(it
), iter_data(it
), cData
);
8054 if(!cData
->users
&& !IsProtected(cData
))
8056 log_module(CS_LOG
, LOG_ERROR
, "Channel %s had no users in database, unregistering it.", key
);
8057 unregister_channel(cData
, "has empty user list.");
8061 obj
= database_get_data(channel
, KEY_BANS
, RECDB_OBJECT
);
8062 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
8063 ban_read_helper(iter_key(it
), iter_data(it
), cData
);
8065 obj
= database_get_data(channel
, KEY_NOTES
, RECDB_OBJECT
);
8066 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
8068 struct note_type
*ntype
= dict_find(note_types
, iter_key(it
), NULL
);
8069 struct record_data
*rd
= iter_data(it
);
8070 const char *note
, *setter
;
8072 if(rd
->type
!= RECDB_OBJECT
)
8074 log_module(CS_LOG
, LOG_ERROR
, "Bad record type for note %s in channel %s.", iter_key(it
), key
);
8078 log_module(CS_LOG
, LOG_ERROR
, "Bad note type name %s in channel %s.", iter_key(it
), key
);
8080 else if(!(note
= database_get_data(rd
->d
.object
, KEY_NOTE_NOTE
, RECDB_QSTRING
)))
8082 log_module(CS_LOG
, LOG_ERROR
, "Missing note text for note %s in channel %s.", iter_key(it
), key
);
8086 setter
= database_get_data(rd
->d
.object
, KEY_NOTE_SETTER
, RECDB_QSTRING
);
8087 if(!setter
) setter
= "<unknown>";
8088 chanserv_add_channel_note(cData
, ntype
, setter
, note
);
8096 chanserv_dnr_read(const char *key
, struct record_data
*hir
)
8098 const char *setter
, *reason
, *str
;
8099 struct do_not_register
*dnr
;
8101 setter
= database_get_data(hir
->d
.object
, KEY_DNR_SETTER
, RECDB_QSTRING
);
8104 log_module(CS_LOG
, LOG_ERROR
, "Missing setter for DNR %s.", key
);
8107 reason
= database_get_data(hir
->d
.object
, KEY_DNR_REASON
, RECDB_QSTRING
);
8110 log_module(CS_LOG
, LOG_ERROR
, "Missing reason for DNR %s.", key
);
8113 dnr
= chanserv_add_dnr(key
, setter
, reason
);
8116 str
= database_get_data(hir
->d
.object
, KEY_DNR_SET
, RECDB_QSTRING
);
8118 dnr
->set
= atoi(str
);
8124 chanserv_version_read(struct dict
*section
)
8128 str
= database_get_data(section
, KEY_VERSION_NUMBER
, RECDB_QSTRING
);
8130 chanserv_read_version
= atoi(str
);
8131 log_module(CS_LOG
, LOG_DEBUG
, "Chanserv db version is %d.", chanserv_read_version
);
8135 chanserv_saxdb_read(struct dict
*database
)
8137 struct dict
*section
;
8140 if((section
= database_get_data(database
, KEY_VERSION_CONTROL
, RECDB_OBJECT
)))
8141 chanserv_version_read(section
);
8143 if((section
= database_get_data(database
, KEY_NOTE_TYPES
, RECDB_OBJECT
)))
8144 for(it
= dict_first(section
); it
; it
= iter_next(it
))
8145 chanserv_note_type_read(iter_key(it
), iter_data(it
));
8147 if((section
= database_get_data(database
, KEY_CHANNELS
, RECDB_OBJECT
)))
8148 for(it
= dict_first(section
); it
; it
= iter_next(it
))
8149 chanserv_channel_read(iter_key(it
), iter_data(it
));
8151 if((section
= database_get_data(database
, KEY_DNR
, RECDB_OBJECT
)))
8152 for(it
= dict_first(section
); it
; it
= iter_next(it
))
8153 chanserv_dnr_read(iter_key(it
), iter_data(it
));
8159 chanserv_write_users(struct saxdb_context
*ctx
, struct userData
*uData
)
8161 int high_present
= 0;
8162 saxdb_start_record(ctx
, KEY_USERS
, 1);
8163 for(; uData
; uData
= uData
->next
)
8165 if((uData
->access
>= UL_PRESENT
) && uData
->present
)
8167 saxdb_start_record(ctx
, uData
->handle
->handle
, 0);
8168 saxdb_write_int(ctx
, KEY_LEVEL
, uData
->access
);
8169 saxdb_write_int(ctx
, KEY_SEEN
, uData
->seen
);
8171 saxdb_write_int(ctx
, KEY_FLAGS
, uData
->flags
);
8173 saxdb_write_int(ctx
, KEY_EXPIRES
, uData
->expires
);
8175 saxdb_write_string(ctx
, KEY_INFO
, uData
->info
);
8176 saxdb_end_record(ctx
);
8178 saxdb_end_record(ctx
);
8179 return high_present
;
8183 chanserv_write_bans(struct saxdb_context
*ctx
, struct banData
*bData
)
8187 saxdb_start_record(ctx
, KEY_BANS
, 1);
8188 for(; bData
; bData
= bData
->next
)
8190 saxdb_start_record(ctx
, bData
->mask
, 0);
8191 saxdb_write_int(ctx
, KEY_SET
, bData
->set
);
8192 if(bData
->triggered
)
8193 saxdb_write_int(ctx
, KEY_TRIGGERED
, bData
->triggered
);
8195 saxdb_write_int(ctx
, KEY_EXPIRES
, bData
->expires
);
8197 saxdb_write_string(ctx
, KEY_OWNER
, bData
->owner
);
8199 saxdb_write_string(ctx
, KEY_REASON
, bData
->reason
);
8200 saxdb_end_record(ctx
);
8202 saxdb_end_record(ctx
);
8206 chanserv_write_suspended(struct saxdb_context
*ctx
, const char *name
, struct suspended
*susp
)
8208 saxdb_start_record(ctx
, name
, 0);
8209 saxdb_write_string(ctx
, KEY_SUSPENDER
, susp
->suspender
);
8210 saxdb_write_string(ctx
, KEY_REASON
, susp
->reason
);
8212 saxdb_write_int(ctx
, KEY_ISSUED
, susp
->issued
);
8214 saxdb_write_int(ctx
, KEY_EXPIRES
, susp
->expires
);
8216 saxdb_write_int(ctx
, KEY_REVOKED
, susp
->revoked
);
8218 chanserv_write_suspended(ctx
, KEY_PREVIOUS
, susp
->previous
);
8219 saxdb_end_record(ctx
);
8223 chanserv_write_giveownership(struct saxdb_context
*ctx
, const char *name
, struct giveownership
*giveownership
)
8225 saxdb_start_record(ctx
, name
, 0);
8226 if(giveownership
->staff_issuer
)
8227 saxdb_write_string(ctx
, KEY_STAFF_ISSUER
, giveownership
->staff_issuer
);
8228 if(giveownership
->old_owner
)
8229 saxdb_write_string(ctx
, KEY_OLD_OWNER
, giveownership
->old_owner
);
8230 if(giveownership
->target
)
8231 saxdb_write_string(ctx
, KEY_TARGET
, giveownership
->target
);
8232 if(giveownership
->target_access
)
8233 saxdb_write_int(ctx
, KEY_TARGET_ACCESS
, giveownership
->target_access
);
8234 if(giveownership
->reason
)
8235 saxdb_write_string(ctx
, KEY_REASON
, giveownership
->reason
);
8236 if(giveownership
->issued
)
8237 saxdb_write_int(ctx
, KEY_ISSUED
, giveownership
->issued
);
8238 if(giveownership
->previous
)
8239 chanserv_write_giveownership(ctx
, KEY_PREVIOUS
, giveownership
->previous
);
8240 saxdb_end_record(ctx
);
8244 chanserv_write_channel(struct saxdb_context
*ctx
, struct chanData
*channel
)
8248 enum levelOption lvlOpt
;
8249 enum charOption chOpt
;
8251 saxdb_start_record(ctx
, channel
->channel
->name
, 1);
8253 saxdb_write_int(ctx
, KEY_REGISTERED
, channel
->registered
);
8254 saxdb_write_int(ctx
, KEY_MAX
, channel
->max
);
8256 saxdb_write_string(ctx
, KEY_TOPIC
, channel
->topic
);
8257 if(channel
->registrar
)
8258 saxdb_write_string(ctx
, KEY_REGISTRAR
, channel
->registrar
);
8259 if(channel
->greeting
)
8260 saxdb_write_string(ctx
, KEY_GREETING
, channel
->greeting
);
8261 if(channel
->user_greeting
)
8262 saxdb_write_string(ctx
, KEY_USER_GREETING
, channel
->user_greeting
);
8263 if(channel
->topic_mask
)
8264 saxdb_write_string(ctx
, KEY_TOPIC_MASK
, channel
->topic_mask
);
8265 if(channel
->suspended
)
8266 chanserv_write_suspended(ctx
, "suspended", channel
->suspended
);
8267 if(channel
->giveownership
)
8268 chanserv_write_giveownership(ctx
, "giveownership", channel
->giveownership
);
8270 saxdb_start_record(ctx
, KEY_OPTIONS
, 0);
8271 saxdb_write_int(ctx
, KEY_FLAGS
, channel
->flags
);
8272 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
8273 saxdb_write_int(ctx
, levelOptions
[lvlOpt
].db_name
, channel
->lvlOpts
[lvlOpt
]);
8274 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
8276 buf
[0] = channel
->chOpts
[chOpt
];
8278 saxdb_write_string(ctx
, charOptions
[chOpt
].db_name
, buf
);
8280 saxdb_end_record(ctx
);
8282 if(channel
->modes
.modes_set
|| channel
->modes
.modes_clear
)
8284 mod_chanmode_format(&channel
->modes
, buf
);
8285 saxdb_write_string(ctx
, KEY_MODES
, buf
);
8288 high_present
= chanserv_write_users(ctx
, channel
->users
);
8289 chanserv_write_bans(ctx
, channel
->bans
);
8291 if(dict_size(channel
->notes
))
8295 saxdb_start_record(ctx
, KEY_NOTES
, 1);
8296 for(it
= dict_first(channel
->notes
); it
; it
= iter_next(it
))
8298 struct note
*note
= iter_data(it
);
8299 saxdb_start_record(ctx
, iter_key(it
), 0);
8300 saxdb_write_string(ctx
, KEY_NOTE_SETTER
, note
->setter
);
8301 saxdb_write_string(ctx
, KEY_NOTE_NOTE
, note
->note
);
8302 saxdb_end_record(ctx
);
8304 saxdb_end_record(ctx
);
8307 if(channel
->ownerTransfer
)
8308 saxdb_write_int(ctx
, KEY_OWNER_TRANSFER
, channel
->ownerTransfer
);
8309 saxdb_write_int(ctx
, KEY_VISITED
, high_present
? now
: channel
->visited
);
8310 saxdb_end_record(ctx
);
8314 chanserv_write_note_type(struct saxdb_context
*ctx
, struct note_type
*ntype
)
8318 saxdb_start_record(ctx
, ntype
->name
, 0);
8319 switch(ntype
->set_access_type
)
8321 case NOTE_SET_CHANNEL_ACCESS
:
8322 saxdb_write_int(ctx
, KEY_NOTE_CHANNEL_ACCESS
, ntype
->set_access
.min_ulevel
);
8324 case NOTE_SET_CHANNEL_SETTER
:
8325 saxdb_write_int(ctx
, KEY_NOTE_SETTER_ACCESS
, 1);
8327 case NOTE_SET_PRIVILEGED
: default:
8328 saxdb_write_int(ctx
, KEY_NOTE_OPSERV_ACCESS
, ntype
->set_access
.min_opserv
);
8331 switch(ntype
->visible_type
)
8333 case NOTE_VIS_ALL
: str
= KEY_NOTE_VIS_ALL
; break;
8334 case NOTE_VIS_CHANNEL_USERS
: str
= KEY_NOTE_VIS_CHANNEL_USERS
; break;
8335 case NOTE_VIS_PRIVILEGED
:
8336 default: str
= KEY_NOTE_VIS_PRIVILEGED
; break;
8338 saxdb_write_string(ctx
, KEY_NOTE_VISIBILITY
, str
);
8339 saxdb_write_int(ctx
, KEY_NOTE_MAX_LENGTH
, ntype
->max_length
);
8340 saxdb_end_record(ctx
);
8344 write_dnrs_helper(struct saxdb_context
*ctx
, struct dict
*dnrs
)
8346 struct do_not_register
*dnr
;
8349 for(it
= dict_first(dnrs
); it
; it
= iter_next(it
))
8351 dnr
= iter_data(it
);
8352 saxdb_start_record(ctx
, dnr
->chan_name
, 0);
8354 saxdb_write_int(ctx
, KEY_DNR_SET
, dnr
->set
);
8355 saxdb_write_string(ctx
, KEY_DNR_SETTER
, dnr
->setter
);
8356 saxdb_write_string(ctx
, KEY_DNR_REASON
, dnr
->reason
);
8357 saxdb_end_record(ctx
);
8362 chanserv_saxdb_write(struct saxdb_context
*ctx
)
8365 struct chanData
*channel
;
8367 /* Version Control*/
8368 saxdb_start_record(ctx
, KEY_VERSION_CONTROL
, 1);
8369 saxdb_write_int(ctx
, KEY_VERSION_NUMBER
, CHANSERV_DB_VERSION
);
8370 saxdb_end_record(ctx
);
8373 saxdb_start_record(ctx
, KEY_NOTE_TYPES
, 1);
8374 for(it
= dict_first(note_types
); it
; it
= iter_next(it
))
8375 chanserv_write_note_type(ctx
, iter_data(it
));
8376 saxdb_end_record(ctx
);
8379 saxdb_start_record(ctx
, KEY_DNR
, 1);
8380 write_dnrs_helper(ctx
, handle_dnrs
);
8381 write_dnrs_helper(ctx
, plain_dnrs
);
8382 write_dnrs_helper(ctx
, mask_dnrs
);
8383 saxdb_end_record(ctx
);
8386 saxdb_start_record(ctx
, KEY_CHANNELS
, 1);
8387 for(channel
= channelList
; channel
; channel
= channel
->next
)
8388 chanserv_write_channel(ctx
, channel
);
8389 saxdb_end_record(ctx
);
8395 chanserv_db_cleanup(void) {
8397 unreg_part_func(handle_part
);
8399 unregister_channel(channelList
, "terminating.");
8400 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
8401 UnlockChannel(chanserv_conf
.support_channels
.list
[ii
]);
8402 free(chanserv_conf
.support_channels
.list
);
8403 dict_delete(handle_dnrs
);
8404 dict_delete(plain_dnrs
);
8405 dict_delete(mask_dnrs
);
8406 dict_delete(note_types
);
8407 free_string_list(chanserv_conf
.eightball
);
8408 free_string_list(chanserv_conf
.old_ban_names
);
8409 free_string_list(chanserv_conf
.set_shows
);
8410 free(set_shows_list
.list
);
8411 free(uset_shows_list
.list
);
8414 struct userData
*helper
= helperList
;
8415 helperList
= helperList
->next
;
8420 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
8421 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
8422 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
8425 init_chanserv(const char *nick
)
8427 struct chanNode
*chan
;
8429 CS_LOG
= log_register_type("ChanServ", "file:chanserv.log");
8430 conf_register_reload(chanserv_conf_read
);
8432 reg_server_link_func(handle_server_link
);
8434 reg_new_channel_func(handle_new_channel
);
8435 reg_join_func(handle_join
);
8436 reg_part_func(handle_part
);
8437 reg_kick_func(handle_kick
);
8438 reg_topic_func(handle_topic
);
8439 reg_mode_change_func(handle_mode
);
8440 reg_nick_change_func(handle_nick_change
);
8442 reg_auth_func(handle_auth
);
8443 reg_handle_rename_func(handle_rename
);
8444 reg_unreg_func(handle_unreg
);
8446 handle_dnrs
= dict_new();
8447 dict_set_free_data(handle_dnrs
, free
);
8448 plain_dnrs
= dict_new();
8449 dict_set_free_data(plain_dnrs
, free
);
8450 mask_dnrs
= dict_new();
8451 dict_set_free_data(mask_dnrs
, free
);
8453 reg_svccmd_unbind_func(handle_svccmd_unbind
);
8454 chanserv_module
= module_register("ChanServ", CS_LOG
, "chanserv.help", chanserv_expand_variable
);
8455 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+acceptchan,+helping", NULL
);
8456 DEFINE_COMMAND(noregister
, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+helping", NULL
);
8457 DEFINE_COMMAND(allowregister
, 2, 0, "template", "noregister", NULL
);
8458 DEFINE_COMMAND(move
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "template", "register", NULL
);
8459 DEFINE_COMMAND(csuspend
, 2, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+helping", NULL
);
8460 DEFINE_COMMAND(cunsuspend
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+helping", NULL
);
8461 DEFINE_COMMAND(createnote
, 5, 0, "level", "800", NULL
);
8462 DEFINE_COMMAND(removenote
, 2, 0, "level", "800", NULL
);
8464 DEFINE_COMMAND(pending
, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+helping", NULL
);
8466 DEFINE_COMMAND(unregister
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+loghostmask", NULL
);
8467 DEFINE_COMMAND(merge
, 2, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "access", "owner", NULL
);
8469 DEFINE_COMMAND(adduser
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8470 DEFINE_COMMAND(deluser
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8471 DEFINE_COMMAND(suspend
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8472 DEFINE_COMMAND(unsuspend
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8473 DEFINE_COMMAND(deleteme
, 1, MODCMD_REQUIRE_CHANUSER
, NULL
);
8475 DEFINE_COMMAND(mdelowner
, 2, MODCMD_REQUIRE_CHANUSER
, "flags", "+helping", NULL
);
8476 DEFINE_COMMAND(mdelcoowner
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "owner", NULL
);
8477 DEFINE_COMMAND(mdelmanager
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "coowner", NULL
);
8478 DEFINE_COMMAND(mdelop
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8479 DEFINE_COMMAND(mdelpeon
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8480 DEFINE_COMMAND(mdelhalfop
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8482 DEFINE_COMMAND(trim
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8483 DEFINE_COMMAND(opchan
, 1, MODCMD_REQUIRE_REGCHAN
|MODCMD_NEVER_CSUSPEND
, "access", "1", NULL
);
8484 DEFINE_COMMAND(clvl
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8485 DEFINE_COMMAND(giveownership
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "owner", "flags", "+loghostmask", NULL
);
8487 DEFINE_COMMAND(up
, 1, MODCMD_REQUIRE_CHANUSER
, NULL
);
8488 DEFINE_COMMAND(down
, 1, MODCMD_REQUIRE_REGCHAN
, NULL
);
8489 DEFINE_COMMAND(upall
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
8490 DEFINE_COMMAND(downall
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
8491 DEFINE_COMMAND(hop
, 2, MODCMD_REQUIRE_CHANNEL
, "access", "op", NULL
);
8492 DEFINE_COMMAND(op
, 2, MODCMD_REQUIRE_CHANNEL
, "access", "op", NULL
);
8493 DEFINE_COMMAND(deop
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
8494 DEFINE_COMMAND(dehop
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
8495 DEFINE_COMMAND(voice
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
8496 DEFINE_COMMAND(devoice
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
8498 DEFINE_COMMAND(kickban
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
8499 DEFINE_COMMAND(kick
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
8500 DEFINE_COMMAND(ban
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
8501 DEFINE_COMMAND(unban
, 2, 0, "template", "hop", NULL
);
8502 DEFINE_COMMAND(unbanall
, 1, 0, "template", "hop", NULL
);
8503 DEFINE_COMMAND(unbanme
, 1, MODCMD_REQUIRE_CHANUSER
, "template", "hop", NULL
);
8504 DEFINE_COMMAND(open
, 1, MODCMD_REQUIRE_CHANUSER
, "template", "op", NULL
);
8505 DEFINE_COMMAND(topic
, 1, MODCMD_REQUIRE_REGCHAN
, "template", "hop", "flags", "+never_csuspend", NULL
);
8506 DEFINE_COMMAND(mode
, 1, MODCMD_REQUIRE_REGCHAN
, "template", "op", NULL
);
8507 DEFINE_COMMAND(inviteme
, 1, MODCMD_REQUIRE_CHANNEL
, "access", "1", NULL
);
8508 DEFINE_COMMAND(invite
, 1, MODCMD_REQUIRE_CHANNEL
, "access", "manager", NULL
);
8509 DEFINE_COMMAND(set
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "op", NULL
);
8510 DEFINE_COMMAND(wipeinfo
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8511 DEFINE_COMMAND(resync
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8513 DEFINE_COMMAND(events
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog", "access", "manager", NULL
);
8514 DEFINE_COMMAND(last
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog", "access", "manager", NULL
);
8515 DEFINE_COMMAND(addlamer
, 2, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
8516 DEFINE_COMMAND(addtimedlamer
, 3, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
8518 /* if you change dellamer access, see also places
8519 * like unbanme which have manager hardcoded. */
8520 DEFINE_COMMAND(dellamer
, 2, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
8521 DEFINE_COMMAND(uset
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "1", NULL
);
8523 DEFINE_COMMAND(lamers
, 1, MODCMD_REQUIRE_REGCHAN
, "access", "1", "flags", "+nolog", NULL
);
8525 DEFINE_COMMAND(peek
, 1, MODCMD_REQUIRE_REGCHAN
, "access", "op", "flags", "+nolog", NULL
);
8527 DEFINE_COMMAND(myaccess
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
8528 DEFINE_COMMAND(access
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8529 DEFINE_COMMAND(users
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8530 DEFINE_COMMAND(wlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8531 DEFINE_COMMAND(clist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8532 DEFINE_COMMAND(mlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8533 DEFINE_COMMAND(olist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8534 DEFINE_COMMAND(hlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8535 DEFINE_COMMAND(plist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8536 DEFINE_COMMAND(info
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8537 DEFINE_COMMAND(seen
, 2, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8538 DEFINE_COMMAND(names
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8540 DEFINE_COMMAND(note
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+joinable,+acceptchan", NULL
);
8541 DEFINE_COMMAND(delnote
, 2, MODCMD_REQUIRE_CHANUSER
, NULL
);
8543 DEFINE_COMMAND(netinfo
, 1, 0, "flags", "+nolog", NULL
);
8544 DEFINE_COMMAND(ircops
, 1, 0, "flags", "+nolog", NULL
);
8545 DEFINE_COMMAND(helpers
, 1, 0, "flags", "+nolog", NULL
);
8546 DEFINE_COMMAND(staff
, 1, 0, "flags", "+nolog", NULL
);
8548 DEFINE_COMMAND(say
, 2, 0, "flags", "+oper,+acceptchan", NULL
);
8549 DEFINE_COMMAND(emote
, 2, 0, "flags", "+oper,+acceptchan", NULL
);
8550 DEFINE_COMMAND(expire
, 1, 0, "flags", "+oper", NULL
);
8551 DEFINE_COMMAND(search
, 3, 0, "flags", "+nolog,+helping", NULL
);
8552 DEFINE_COMMAND(unvisited
, 1, 0, "flags", "+nolog,+helping", NULL
);
8554 DEFINE_COMMAND(unf
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8555 DEFINE_COMMAND(ping
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8556 DEFINE_COMMAND(wut
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8557 DEFINE_COMMAND(8ball
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8558 DEFINE_COMMAND(d
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8559 DEFINE_COMMAND(huggle
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8560 DEFINE_COMMAND(calc
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8562 /* Channel options */
8563 DEFINE_CHANNEL_OPTION(defaulttopic
);
8564 DEFINE_CHANNEL_OPTION(topicmask
);
8565 DEFINE_CHANNEL_OPTION(greeting
);
8566 DEFINE_CHANNEL_OPTION(usergreeting
);
8567 DEFINE_CHANNEL_OPTION(modes
);
8568 DEFINE_CHANNEL_OPTION(enfops
);
8569 DEFINE_CHANNEL_OPTION(enfhalfops
);
8570 DEFINE_CHANNEL_OPTION(automode
);
8571 DEFINE_CHANNEL_OPTION(protect
);
8572 DEFINE_CHANNEL_OPTION(enfmodes
);
8573 DEFINE_CHANNEL_OPTION(enftopic
);
8574 DEFINE_CHANNEL_OPTION(pubcmd
);
8575 DEFINE_CHANNEL_OPTION(userinfo
);
8576 DEFINE_CHANNEL_OPTION(dynlimit
);
8577 DEFINE_CHANNEL_OPTION(topicsnarf
);
8578 DEFINE_CHANNEL_OPTION(nodelete
);
8579 DEFINE_CHANNEL_OPTION(toys
);
8580 DEFINE_CHANNEL_OPTION(setters
);
8581 DEFINE_CHANNEL_OPTION(topicrefresh
);
8582 DEFINE_CHANNEL_OPTION(resync
);
8583 DEFINE_CHANNEL_OPTION(ctcpreaction
);
8584 DEFINE_CHANNEL_OPTION(bantimeout
);
8585 DEFINE_CHANNEL_OPTION(inviteme
);
8587 DEFINE_CHANNEL_OPTION(offchannel
);
8588 modcmd_register(chanserv_module
, "set defaults", chan_opt_defaults
, 1, 0, "access", "owner", NULL
);
8590 /* Alias set topic to set defaulttopic for compatibility. */
8591 modcmd_register(chanserv_module
, "set topic", chan_opt_defaulttopic
, 1, 0, NULL
);
8594 DEFINE_USER_OPTION(autoinvite
);
8595 DEFINE_USER_OPTION(info
);
8596 DEFINE_USER_OPTION(autoop
);
8598 /* Alias uset autovoice to uset autoop. */
8599 modcmd_register(chanserv_module
, "uset autovoice", user_opt_autoop
, 1, 0, NULL
);
8601 note_types
= dict_new();
8602 dict_set_free_data(note_types
, chanserv_deref_note_type
);
8605 const char *modes
= conf_get_data("services/chanserv/modes", RECDB_QSTRING
);
8606 chanserv
= AddService(nick
, modes
? modes
: NULL
, "Channel Services", NULL
);
8607 service_register(chanserv
)->trigger
= '!';
8608 reg_chanmsg_func('\001', chanserv
, chanserv_ctcp_check
);
8611 saxdb_register("ChanServ", chanserv_saxdb_read
, chanserv_saxdb_write
);
8613 if(chanserv_conf
.channel_expire_frequency
)
8614 timeq_add(now
+ chanserv_conf
.channel_expire_frequency
, expire_channels
, NULL
);
8616 if(chanserv_conf
.ban_timeout_frequency
)
8617 timeq_add(now
+ chanserv_conf
.ban_timeout_frequency
, expire_bans
, NULL
);
8619 if(chanserv_conf
.refresh_period
)
8621 time_t next_refresh
;
8622 next_refresh
= (now
+ chanserv_conf
.refresh_period
- 1) / chanserv_conf
.refresh_period
* chanserv_conf
.refresh_period
;
8623 timeq_add(next_refresh
, chanserv_refresh_topics
, NULL
);
8624 timeq_add(next_refresh
, chanserv_auto_resync
, NULL
);
8627 if (autojoin_channels
&& chanserv
) {
8628 for (i
= 0; i
< autojoin_channels
->used
; i
++) {
8629 chan
= AddChannel(autojoin_channels
->list
[i
], now
, "+nt", NULL
, NULL
);
8630 AddChannelUser(chanserv
, chan
)->modes
|= MODE_CHANOP
;
8634 reg_exit_func(chanserv_db_cleanup
);
8635 message_register_table(msgtab
);