]> jfr.im git - irc/evilnet/x3.git/blob - src/chanserv.c
Improved and committed the Life4Christ folks aut-oper patch.
[irc/evilnet/x3.git] / src / chanserv.c
1 /* chanserv.c - Channel service bot
2 * Copyright 2000-2004 srvx Development Team
3 *
4 * This file is part of x3.
5 *
6 * srvx is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
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.
15 *
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.
19 */
20
21 #include "chanserv.h"
22 #include "conf.h"
23 #include "global.h"
24 #include "modcmd.h"
25 #include "opserv.h" /* for opserv_bad_channel() */
26 #include "saxdb.h"
27 #include "timeq.h"
28
29 #define CHANSERV_CONF_NAME "services/chanserv"
30
31 /* ChanServ options */
32 #define KEY_SUPPORT_CHANNEL "support_channel"
33 #define KEY_SUPPORT_CHANNEL_MODES "support_channel_modes"
34 #define KEY_DB_BACKUP_FREQ "db_backup_freq"
35 #define KEY_INFO_DELAY "info_delay"
36 #define KEY_MAX_GREETLEN "max_greetlen"
37 #define KEY_ADJUST_THRESHOLD "adjust_threshold"
38 #define KEY_ADJUST_DELAY "adjust_delay"
39 #define KEY_CHAN_EXPIRE_FREQ "chan_expire_freq"
40 #define KEY_CHAN_EXPIRE_DELAY "chan_expire_delay"
41 #define KEY_BAN_TIMEOUT_FREQ "ban_timeout_freq"
42 #define KEY_MAX_CHAN_USERS "max_chan_users"
43 #define KEY_MAX_CHAN_BANS "max_chan_bans"
44 #define KEY_NICK "nick"
45 #define KEY_OLD_CHANSERV_NAME "old_chanserv_name"
46 #define KEY_8BALL_RESPONSES "8ball"
47 #define KEY_OLD_BAN_NAMES "old_ban_names"
48 #define KEY_REFRESH_PERIOD "refresh_period"
49 #define KEY_CTCP_SHORT_BAN_DURATION "ctcp_short_ban_duration"
50 #define KEY_CTCP_LONG_BAN_DURATION "ctcp_long_ban_duration"
51 #define KEY_MAX_OWNED "max_owned"
52 #define KEY_IRC_OPERATOR_EPITHET "irc_operator_epithet"
53 #define KEY_NETWORK_HELPER_EPITHET "network_helper_epithet"
54 #define KEY_SUPPORT_HELPER_EPITHET "support_helper_epithet"
55 #define KEY_NODELETE_LEVEL "nodelete_level"
56 #define KEY_MAX_USERINFO_LENGTH "max_userinfo_length"
57 #define KEY_GIVEOWNERSHIP_PERIOD "giveownership_timeout"
58
59 /* ChanServ database */
60 #define KEY_VERSION_CONTROL "version_control"
61 #define KEY_CHANNELS "channels"
62 #define KEY_NOTE_TYPES "note_types"
63
64 /* version control paramiter */
65 #define KEY_VERSION_NUMBER "version_number"
66
67 /* Note type parameters */
68 #define KEY_NOTE_OPSERV_ACCESS "opserv_access"
69 #define KEY_NOTE_CHANNEL_ACCESS "channel_access"
70 #define KEY_NOTE_SETTER_ACCESS "setter_access"
71 #define KEY_NOTE_VISIBILITY "visibility"
72 #define KEY_NOTE_VIS_PRIVILEGED "privileged"
73 #define KEY_NOTE_VIS_CHANNEL_USERS "channel_users"
74 #define KEY_NOTE_VIS_ALL "all"
75 #define KEY_NOTE_MAX_LENGTH "max_length"
76 #define KEY_NOTE_SETTER "setter"
77 #define KEY_NOTE_NOTE "note"
78
79 /* Do-not-register channels */
80 #define KEY_DNR "dnr"
81 #define KEY_DNR_SET "set"
82 #define KEY_DNR_SETTER "setter"
83 #define KEY_DNR_REASON "reason"
84
85 /* Channel data */
86 #define KEY_REGISTERED "registered"
87 #define KEY_REGISTRAR "registrar"
88 #define KEY_SUSPENDED "suspended"
89 #define KEY_PREVIOUS "previous"
90 #define KEY_SUSPENDER "suspender"
91 #define KEY_ISSUED "issued"
92 #define KEY_REVOKED "revoked"
93 #define KEY_SUSPEND_EXPIRES "suspend_expires"
94 #define KEY_SUSPEND_REASON "suspend_reason"
95 #define KEY_VISITED "visited"
96 #define KEY_TOPIC "topic"
97 #define KEY_GREETING "greeting"
98 #define KEY_USER_GREETING "user_greeting"
99 #define KEY_MODES "modes"
100 #define KEY_FLAGS "flags"
101 #define KEY_OPTIONS "options"
102 #define KEY_USERS "users"
103 #define KEY_BANS "bans" /* for lamers */
104 #define KEY_MAX "max"
105 #define KEY_NOTES "notes"
106 #define KEY_TOPIC_MASK "topic_mask"
107 #define KEY_OWNER_TRANSFER "owner_transfer"
108
109 /* User data */
110 #define KEY_LEVEL "level"
111 #define KEY_INFO "info"
112 #define KEY_SEEN "seen"
113
114 /* Ban data */
115 #define KEY_OWNER "owner"
116 #define KEY_REASON "reason"
117 #define KEY_SET "set"
118 #define KEY_DURATION "duration"
119 #define KEY_EXPIRES "expires"
120 #define KEY_TRIGGERED "triggered"
121
122 #define CHANNEL_DEFAULT_FLAGS (CHANNEL_OFFCHANNEL)
123 #define CHANNEL_DEFAULT_OPTIONS "lmoooanpcnat"
124
125 /* Administrative messages */
126 static const struct message_entry msgtab[] = {
127 { "CSMSG_CHANNELS_EXPIRED", "%i channels expired." },
128
129 /* Channel registration */
130 { "CSMSG_REG_SUCCESS", "You now have ownership of $b%s$b." },
131 { "CSMSG_PROXY_SUCCESS", "%s now has ownership of $b%s$b." },
132 { "CSMSG_ALREADY_REGGED", "$b%s$b is registered to someone else." },
133 { "CSMSG_MUST_BE_OPPED", "You must be a channel operator (+o) in $b%s$b to register it." },
134 { "CSMSG_PROXY_FORBIDDEN", "You may not register a channel for someone else." },
135 { "CSMSG_OWN_TOO_MANY", "%s already owns more than the limit of %d channels. Use FORCE to override." },
136 { "CSMSG_YOU_OWN_TOO_MANY", "You already own more than the limit of %d channels. Ask a staff member for help." },
137 { "CSMSG_ANOTHER_SERVICE", "Another service bot is in that channel already. Ask a staff member for help." },
138
139 /* Do-not-register channels */
140 { "CSMSG_NOT_DNR", "$b%s$b is not a valid channel name or *account." },
141 { "CSMSG_DNR_SEARCH_RESULTS", "$bDo-Not-Register Channels$b" },
142 { "CSMSG_DNR_INFO", "$b%s$b (set by $b%s$b): %s" },
143 { "CSMSG_DNR_INFO_SET", "$b%s$b (set %s ago by $b%s$b): %s" },
144 { "CSMSG_MORE_DNRS", "%d more do-not-register entries skipped." },
145 { "CSMSG_DNR_CHANNEL", "Only network staff may register $b%s$b." },
146 { "CSMSG_DNR_CHANNEL_MOVE", "Only network staff may move $b%s$b." },
147 { "CSMSG_DNR_ACCOUNT", "Only network staff may register channels to $b%s$b." },
148 { "CSMSG_NOREGISTER_CHANNEL", "$b%s$b has been added to the do-not-register list." },
149 { "CSMSG_NO_SUCH_DNR", "$b%s$b is not in the do-not-register list." },
150 { "CSMSG_DNR_REMOVED", "$b%s$b has been removed from the do-not-register list." },
151
152 /* Channel unregistration */
153 { "CSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
154 { "CSMSG_UNREG_NODELETE", "$b%s$b is protected from unregistration." },
155 { "CSMSG_CHAN_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended (%s)." },
156 { "CSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must use 'unregister %s %s'." },
157
158 /* Channel moving */
159 { "CSMSG_MOVE_SUCCESS", "Channel registration has been moved to $b%s$b." },
160 { "CSMSG_MOVE_NODELETE", "$b%s$b is protected from unregistration, and cannot be moved." },
161
162 /* Channel merging */
163 { "CSMSG_MERGE_SUCCESS", "Channel successfully merged into $b%s$b." },
164 { "CSMSG_MERGE_SELF", "Merging cannot be performed if the source and target channels are the same." },
165 { "CSMSG_MERGE_NODELETE", "You may not merge a channel that is marked NoDelete." },
166 { "CSMSG_MERGE_SUSPENDED", "Merging cannot be performed if the source or target channel is suspended." },
167 { "CSMSG_MERGE_NOT_OWNER", "You must be the owner of the target channel (or a helper) to merge into the channel." },
168
169 /* Handle unregistration */
170 { "CSMSG_HANDLE_UNREGISTERED", "As a result of your account unregistration, you have been deleted from all of your channels' userlists." },
171
172 /* Error messages */
173 { "CSMSG_NOT_USER", "You lack access to $b%s$b." },
174 { "CSMSG_NO_CHAN_USER", "%s lacks access to $b%s$b." },
175 { "CSMSG_NO_ACCESS", "You lack sufficient access to use this command." },
176 { "CSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$C$b." },
177 { "CSMSG_MAXIMUM_LAMERS", "This channel has reached the lamer count limit of $b%d$b." },
178 { "CSMSG_MAXIMUM_USERS", "This channel has reached the user count limit of $b%d$b." },
179 { "CSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel, and cannot be registered." },
180 { "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." },
181 { "CSMSG_ALREADY_OPPED", "You are already opped in $b%s$b." },
182 { "CSMSG_ALREADY_HALFOPPED", "You are already halfopped in $b%s$b." },
183 { "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." },
184 { "CSMSG_ALREADY_DOWN", "You are not opped, halfopped, or voiced in $b%s$b." },
185 { "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." },
186 { "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." },
187
188 /* Removing yourself from a channel. */
189 { "CSMSG_NO_OWNER_DELETEME", "You cannot delete your owner access in $b%s$b." },
190 { "CSMSG_CONFIRM_DELETEME", "To really remove yourself, you must use 'deleteme %s'." },
191 { "CSMSG_DELETED_YOU", "Your $b%d$b access has been deleted from $b%s$b." },
192
193 /* User management */
194 { "CSMSG_ADDED_USER", "Added %s to the %s user list with access %s (%d)." },
195 { "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." },
196 { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be greater than maximum (%d)." },
197 { "CSMSG_DELETED_USERS", "Deleted accounts matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list." },
198 { "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." },
199 { "CSMSG_INCORRECT_ACCESS", "%s has access $b%s$b, not %s." },
200 { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with %s access)." },
201 { "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." },
202 { "CSMSG_ADDUSER_PENDING_ALREADY", "He or she is already pending addition to %s once he/she auths with $b$N$b." },
203 { "CSMSG_ADDUSER_PENDING_HEADER", "Users to add to channels pending logins:" }, /* Remove after testing? */
204 { "CSMSG_ADDUSER_PENDING_LIST", "Channel %s user %s" }, /* Remove after testing? */
205 { "CSMSG_ADDUSER_PENDING_FOOTER", "--------- End of pending list ----------" }, /* Remove after testing? */
206 /*{ "CSMSG_ADDUSER_PENDING_NOTINCHAN", "That user is not in %s, and is not auth'd." }, */
207 { "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" },
208 { "CSMSG_CANNOT_TRIM", "You must include a minimum inactivity duration of at least 60 seconds to trim." },
209
210 { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." },
211 { "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." },
212 { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
213 { "CSMSG_TRANSFER_WAIT", "You must wait %s before you can give ownership of $b%s$b to someone else." },
214 { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
215 { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
216
217 /* Ban management */
218 { "CSMSG_LAMER_ADDED", "Added $b%s$b to %s LAMERs." },
219 { "CSMSG_TIMED_LAMER_ADDED", "LAMERed $b%s$b on %s for %s." },
220 { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
221 { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
222 { "CSMSG_REASON_CHANGE", "Reason for LAMER $b%s$b changed." },
223 { "CSMSG_LAMER_EXTENDED", "Extended LAMER for $b%s$b, now expires in %s." },
224 { "CSMSG_BAN_REMOVED", "Matching ban(s) and LAMER(s) in $b%s$b were removed." },
225 { "CSMSG_TRIMMED_LAMERS", "Trimmed $b%d LAMERs$b from the %s LAMER list that were inactive for at least %s." },
226 { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
227 { "CSMSG_REDUNDANT_LAMER", "$b%s$b is already LAMER'd in %s." },
228 { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
229 { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
230 { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
231 { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
232 { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
233 { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban or LAMER found: $b%s$b." },
234 { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
235
236 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
237
238 /* Channel management */
239 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
240 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
241 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
242
243 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
244 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
245 { "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" },
246 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic is at most %d characters and matches the topic mask pattern." },
247 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
248 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
249 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
250
251 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
252 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
253 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
254 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
255 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
256 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
257 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you must use 'set defaults %s'." },
258 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
259 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
260 /*
261 { "CSMSG_BAD_GIVEVOICE", "You cannot change GiveVoice to above GiveHalfOps (%d)." },
262 { "CSMSG_BAD_GIVEHOPS", "You cannot change GiveHalfOps to below GiveOps (%d)." },
263 { "CSMSG_BAD_GIVEOPS", "You cannot change GiveOps to below GiveVoice (%d)." },
264 */
265 { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
266 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
267 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
268 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
269 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
270 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
271 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
272 { "CSMSG_SET_MODES", "$bModes $b %s" },
273 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
274 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s - +l joinflood protection." },
275 { "CSMSG_SET_OFFCHANNEL", "$bOffChannel $b %s" },
276 { "CSMSG_SET_USERINFO", "$bUserInfo $b %d - and above userinfos are shown." },
277 /*
278 { "CSMSG_SET_GIVE_VOICE", "$bGiveVoice $b %d" },
279 { "CSMSG_SET_GIVE_HALFOPS", "$bGiveHalfOps $b %d" },
280 */
281 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" },
282 { "CSMSG_SET_INVITEME", "$bInviteMe $b %d - Userlevel required to invite self." },
283 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d - level and above can op unknown users." },
284 { "CSMSG_SET_ENFHALFOPS", "$bEnfHalfOps $b %d - level and above can hop unknown users." },
285 /*
286 { "CSMSG_SET_GIVE_OPS", "$bGiveOps $b %d" },
287 */
288 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d - and above can change channel modes." },
289 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d - and above can set the topic." },
290 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d - and above can use public commands." },
291 { "CSMSG_SET_SETTERS", "$bSetters $b %d - and above can change these settings." },
292 { "CSMSG_SET_VOICE", "$bvoice $b %d - %s" },
293 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
294 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
295 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
296 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
297 { "CSMSG_SET_BANTIMEOUT", "$bBanTimeout $b %d - %s" },
298 { "CSMSG_USET_AUTOOP", "$bAutoOp $b %s" },
299 { "CSMSG_USET_AUTOVOICE", "$bAutoVoice $b %s" },
300 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
301 { "CSMSG_USET_INFO", "$bInfo $b %s" },
302
303 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
304 { "CSMSG_USER_PROTECTED_KICK", "That user is protected." }, /* No $ or %s replacements! */
305 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
306 { "CSMSG_HOPBY_LOCKED", "You may not halfop users who lack halfop or greater access." },
307 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
308 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
309 { "CSMSG_HALFOPPED_USERS", "Halfopped users in $b%s$b." },
310 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
311 { "CSMSG_DEHALFOPPED_USERS", "DeHalfopped users in $b%s$b." },
312 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
313 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
314 { "CSMSG_VOICE_NONE", "Noone will be auto-voiced" },
315 { "CSMSG_VOICE_PEON", "PEONs will be auto-voiced" },
316 { "CSMSG_VOICE_ALL", "Everyone will be auto-voiced" },
317 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
318 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
319 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
320 { "CSMSG_PROTECT_NONE", "No users will be protected." },
321 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
322 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
323 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
324
325 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
326 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
327 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
328 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
329 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
330
331 { "CSMSG_CTCPREACTION_NONE", "CTCPs are allowed" },
332 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
333 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
334 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
335 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
336
337 { "CSMSG_BANTIMEOUT_NONE", "Bans will not be removed automatically."},
338 { "CSMSG_BANTIMEOUT_10M", "Bans will be removed after 10 minutes."},
339 { "CSMSG_BANTIMEOUT_2H", "Bans will be removed after 2 hours."},
340 { "CSMSG_BANTIMEOUT_4H", "Bans will be removed after 4 hours."},
341 { "CSMSG_BANTIMEOUT_1D", "Bans will be removed after 24 hours."},
342 { "CSMSG_BANTIMEOUT_1W", "Bans will be removed after 1 week."},
343
344 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
345 { "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" },
346 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s." },
347 { "CSMSG_ALREADY_PRESENT", "%s is already in $b%s$b." },
348 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
349 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s to use this command." },
350 { "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
351 { "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
352
353 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
354 { "CSMSG_NO_BANS", "No bans found on $b%s$b." },
355 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
356
357 /* Channel userlist */
358 { "CSMSG_ACCESS_ALL_HEADER", "$b%s Users From Level %s To %s$b" },
359 { "CSMSG_ACCESS_SEARCH_HEADER", "$b%s Users From Level %s To %s Matching %s$b" },
360 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
361 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%s$b (%u) in %s." },
362 { "CSMSG_LAMERS_HEADER", "$bLamers in %s$b" },
363
364 /* Channel note list */
365 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
366 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
367 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
368 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
369 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
370 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
371 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
372 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
373 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
374 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
375 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
376 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
377 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
378 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
379 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
380
381 /* Channel [un]suspension */
382 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
383 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
384 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
385 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
386 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
387 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
388 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
389
390 /* Access information */
391 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
392 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
393 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
394 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
395 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
396 { "CSMSG_USER_HAS_ACCESS", "%s has $b%s$b access (%d) in %s." },
397 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
398 { "CSMSG_HELPER_HAS_ACCESS", "%s has $b%s$b access (%d) in %s and has $bsecurity override$b enabled." },
399 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
400 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
401 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
402
403 /* Seen information */
404 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
405 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
406 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
407 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
408
409 /* Names information */
410 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
411 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
412
413 /* Channel information */
414 { "CSMSG_CHANNEL_INFO", "$bInformation About %s$b" },
415 { "CSMSG_BAR", "----------------------------------------"},
416 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
417 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
418 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
419 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
420 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
421 { "CSMSG_CHANNEL_LAMERS", "$bLamer Count: $b%i" },
422 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
423 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
424 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
425 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
426 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
427 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
428 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
429 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
430 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
431 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
432 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
433 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
434 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
435 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
436 { "CSMSG_CHANNEL_END", "---------------End of Info--------------"},
437
438 { "CSMSG_PEEK_INFO", "$bStatus of %s$b" },
439 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
440 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
441 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d" },
442 { "CSMSG_PEEK_OPS", "$bOps:$b" },
443 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
444 { "CSMSG_PEEK_END", "-------------End of Status--------------" },
445
446 /* Network information */
447 { "CSMSG_NETWORK_INFO", "Network Information:" },
448 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
449 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
450 { "CSMSG_NETWORK_LAMERS", "$bTotal Lamer Count: $b%i" },
451 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
452 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
453 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
454 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
455 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
456
457 /* Staff list */
458 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
459 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
460 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
461
462 /* Channel searches */
463 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
464 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
465 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
466 { "CSMSG_CHANNEL_SEARCH_RESULTS", "$bChannels Found Matching Search$b" },
467
468 /* Channel configuration */
469 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
470 { "CSMSG_CHANNEL_OPTIONS", "$bChannel Options for %s$b" },
471 { "CSMSG_CHANNEL_OPTIONS_END", "-------------End of Options-------------" },
472 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
473
474 /* User settings */
475 { "CSMSG_USER_OPTIONS", "User Options:" },
476 // { "CSMSG_USER_PROTECTED", "That user is protected." },
477
478 /* Toys */
479 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
480 { "CSMSG_PING_RESPONSE", "Pong!" },
481 { "CSMSG_WUT_RESPONSE", "wut" },
482 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
483 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
484 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
485 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
486 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
487 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
488 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
489
490 /* Other things */
491 { "CSMSG_EVENT_SEARCH_RESULTS", "$bChannel Events for %s$b" },
492 { "CSMSG_LAST_INVALID", "Invalid argument. must be 1-200" },
493 { NULL, NULL }
494 };
495
496 /* eject_user and unban_user flags */
497 #define ACTION_KICK 0x0001
498 #define ACTION_BAN 0x0002
499 #define ACTION_ADD_LAMER 0x0004
500 #define ACTION_ADD_TIMED_LAMER 0x0008
501 #define ACTION_UNBAN 0x0010
502 #define ACTION_DEL_LAMER 0x0020
503
504 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
505 #define MODELEN 40 + KEYLEN
506 #define PADLEN 21
507 #define ACCESSLEN 10
508
509 #define CSFUNC_ARGS user, channel, argc, argv, cmd
510
511 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
512 #define CHANSERV_SYNTAX() svccmd_send_help_brief(user, chanserv, cmd)
513 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
514 reply("MSG_MISSING_PARAMS", argv[0]); \
515 CHANSERV_SYNTAX(); \
516 return 0; }
517
518 DECLARE_LIST(dnrList, struct do_not_register *);
519 DEFINE_LIST(dnrList, struct do_not_register *);
520
521 static int eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action);
522
523 struct userNode *chanserv;
524 dict_t note_types;
525 int off_channel;
526 static dict_t plain_dnrs, mask_dnrs, handle_dnrs;
527 static struct log_type *CS_LOG;
528 struct adduserPending* adduser_pendings = NULL;
529 unsigned int adduser_pendings_count = 0;
530
531 static struct
532 {
533 struct channelList support_channels;
534 struct mod_chanmode default_modes;
535
536 unsigned long db_backup_frequency;
537 unsigned long channel_expire_frequency;
538 unsigned long ban_timeout_frequency;
539
540 long info_delay;
541 unsigned int adjust_delay;
542 long channel_expire_delay;
543 unsigned int nodelete_level;
544
545 unsigned int adjust_threshold;
546 int join_flood_threshold;
547
548 unsigned int greeting_length;
549 unsigned int refresh_period;
550 unsigned int giveownership_period;
551
552 unsigned int max_owned;
553 unsigned int max_chan_users;
554 unsigned int max_chan_bans; /* lamers */
555 unsigned int max_userinfo_length;
556
557 struct string_list *set_shows;
558 struct string_list *eightball;
559 struct string_list *old_ban_names;
560
561 const char *ctcp_short_ban_duration;
562 const char *ctcp_long_ban_duration;
563
564 const char *irc_operator_epithet;
565 const char *network_helper_epithet;
566 const char *support_helper_epithet;
567 } chanserv_conf;
568
569 struct listData
570 {
571 struct userNode *user;
572 struct userNode *bot;
573 struct chanNode *channel;
574 const char *search;
575 unsigned short lowest;
576 unsigned short highest;
577 struct userData **users;
578 struct helpfile_table table;
579 };
580
581 enum note_access_type
582 {
583 NOTE_SET_CHANNEL_ACCESS,
584 NOTE_SET_CHANNEL_SETTER,
585 NOTE_SET_PRIVILEGED
586 };
587
588 enum note_visible_type
589 {
590 NOTE_VIS_ALL,
591 NOTE_VIS_CHANNEL_USERS,
592 NOTE_VIS_PRIVILEGED
593 };
594
595 struct note_type
596 {
597 enum note_access_type set_access_type;
598 union {
599 unsigned int min_opserv;
600 unsigned short min_ulevel;
601 } set_access;
602 enum note_visible_type visible_type;
603 unsigned int max_length;
604 unsigned int refs;
605 char name[1];
606 };
607
608 struct note
609 {
610 struct note_type *type;
611 char setter[NICKSERV_HANDLE_LEN+1];
612 char note[1];
613 };
614
615 static unsigned int registered_channels;
616 static unsigned int banCount;
617
618 static const struct {
619 char *name;
620 char *title;
621 unsigned short level;
622 char ch;
623 } accessLevels[] = { /* MUST be orderd less to most! */
624 { "peon", "Peon", UL_PEON, '+' },
625 { "halfop", "HalfOp", UL_HALFOP, '%' },
626 { "op", "Op", UL_OP, '@' },
627 { "manager", "Manager", UL_MANAGER, '%' },
628 { "coowner", "Coowner", UL_COOWNER, '*' },
629 { "owner", "Owner", UL_OWNER, '!' },
630 { "helper", "BUG:", UL_HELPER, 'X' }
631 };
632
633 /* If you change this, change the enum in chanserv.h also, or stack smashing will commence. */
634 static const struct {
635 char *format_name;
636 char *db_name;
637 unsigned short default_value;
638 unsigned int old_idx;
639 unsigned int old_flag;
640 unsigned short flag_value;
641 } levelOptions[] = {
642 // { "CSMSG_SET_GIVE_VOICE", "givevoice", 100, ~0, CHANNEL_VOICE_ALL, 0 },
643 // { "CSMSG_SET_GIVE_HALFOPS", "givehalfops", 150, ~0, CHANNEL_HOP_ALL, 0 },
644 // { "CSMSG_SET_GIVE_OPS", "giveops", 200, 2, 0, 0 }, // these 3 need removed, but causes segs if its still in the db..
645 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
646 { "CSMSG_SET_ENFHALFOPS", "enfhalfops", 300, 1, 0, 0 },
647 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
648 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
649 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
650 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
651 // { "CSMSG_SET_CTCPUSERS", "ctcpusers", 0, 9, 0, 0 },
652 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES, 1 },
653 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE, 200 },
654 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF, 1 }
655 };
656
657 struct charOptionValues {
658 char value;
659 char *format_name;
660 } voiceValues[] = {
661 { 'n', "CSMSG_VOICE_NONE" },
662 { 'p', "CSMSG_VOICE_PEON" },
663 { 'a', "CSMSG_VOICE_ALL" }
664 }, protectValues[] = {
665 { 'a', "CSMSG_PROTECT_ALL" },
666 { 'e', "CSMSG_PROTECT_EQUAL" },
667 { 'l', "CSMSG_PROTECT_LOWER" },
668 { 'n', "CSMSG_PROTECT_NONE" }
669 }, toysValues[] = {
670 { 'd', "CSMSG_TOYS_DISABLED" },
671 { 'n', "CSMSG_TOYS_PRIVATE" },
672 { 'p', "CSMSG_TOYS_PUBLIC" }
673 }, topicRefreshValues[] = {
674 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
675 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
676 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
677 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
678 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
679 }, ctcpReactionValues[] = {
680 { 'n', "CSMSG_CTCPREACTION_NONE" },
681 { 'k', "CSMSG_CTCPREACTION_KICK" },
682 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
683 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
684 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
685 }, banTimeoutValues[] = {
686 { '0', "CSMSG_BANTIMEOUT_NONE" },
687 { '1', "CSMSG_BANTIMEOUT_10M" },
688 { '2', "CSMSG_BANTIMEOUT_2H" },
689 { '3', "CSMSG_BANTIMEOUT_4H" },
690 { '4', "CSMSG_BANTIMEOUT_1D" },
691 { '5', "CSMSG_BANTIMEOUT_1W" }
692 };
693
694 static const struct {
695 char *format_name;
696 char *db_name;
697 char default_value;
698 unsigned int old_idx;
699 unsigned char count;
700 struct charOptionValues *values;
701 } charOptions[] = {
702 { "CSMSG_SET_VOICE", "voice", 'p', 99, ArrayLength(voiceValues), voiceValues },
703 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues), protectValues },
704 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues), toysValues },
705 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues), topicRefreshValues },
706 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 'n', 10, ArrayLength(ctcpReactionValues), ctcpReactionValues },
707 { "CSMSG_SET_BANTIMEOUT", "bantimeout", '0', 11, ArrayLength(banTimeoutValues), banTimeoutValues }
708 };
709
710 struct userData *helperList;
711 struct chanData *channelList;
712 static struct module *chanserv_module;
713 static unsigned int userCount;
714 unsigned int chanserv_read_version = 0; /* db version control */
715
716 #define CHANSERV_DB_VERSION 2
717
718 #define GetChannelUser(channel, handle) _GetChannelUser(channel, handle, 1, 0)
719 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
720 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
721
722 unsigned short
723 user_level_from_name(const char *name, unsigned short clamp_level)
724 {
725 unsigned int level = 0, ii;
726 if(isdigit(name[0]))
727 level = strtoul(name, NULL, 10);
728 else for(ii = 0; (ii < ArrayLength(accessLevels)) && !level; ++ii)
729 if(!irccasecmp(name, accessLevels[ii].name))
730 level = accessLevels[ii].level;
731 if(level > clamp_level)
732 return 0;
733 return level;
734 }
735
736 char *
737 user_level_name_from_level(int level)
738 {
739 unsigned int ii;
740 char* highest;
741
742 highest = "None";
743 if(level >= 1)
744 highest = "Peon";
745 for(ii = 0; (ii < ArrayLength(accessLevels)); ii++)
746 if(level >= accessLevels[ii].level)
747 highest = accessLevels[ii].title;
748 return(highest);
749 }
750
751
752 int
753 parse_level_range(unsigned short *minl, unsigned short *maxl, const char *arg)
754 {
755 char *sep;
756 *minl = strtoul(arg, &sep, 10);
757 if(*sep == '\0')
758 {
759 *maxl = *minl;
760 return 1;
761 }
762 else if(*sep == '-')
763 {
764 *maxl = strtoul(sep+1, &sep, 10);
765 return *sep == '\0';
766 }
767 else
768 return 0;
769 }
770
771 struct userData*
772 _GetChannelUser(struct chanData *channel, struct handle_info *handle, int override, int allow_suspended)
773 {
774 struct userData *uData, **head;
775
776 if(!channel || !handle)
777 return NULL;
778
779 if(override && HANDLE_FLAGGED(handle, HELPING)
780 && ((handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel)))
781 {
782 for(uData = helperList;
783 uData && uData->handle != handle;
784 uData = uData->next);
785
786 if(!uData)
787 {
788 uData = calloc(1, sizeof(struct userData));
789 uData->handle = handle;
790
791 uData->access = UL_HELPER;
792 uData->seen = 0;
793
794 uData->info = NULL;
795
796 uData->prev = NULL;
797 uData->next = helperList;
798 if(helperList)
799 helperList->prev = uData;
800 helperList = uData;
801 }
802
803 head = &helperList;
804 }
805 else
806 {
807 for(uData = channel->users; uData; uData = uData->next)
808 if((uData->handle == handle) && (allow_suspended || !IsUserSuspended(uData)))
809 break;
810
811 head = &(channel->users);
812 }
813
814 if(uData && (uData != *head))
815 {
816 /* Shuffle the user to the head of whatever list he was in. */
817 if(uData->next)
818 uData->next->prev = uData->prev;
819 if(uData->prev)
820 uData->prev->next = uData->next;
821
822 uData->prev = NULL;
823 uData->next = *head;
824
825 if(*head)
826 (**head).prev = uData;
827 *head = uData;
828 }
829
830 return uData;
831 }
832
833 /* Returns non-zero if user has at least the minimum access.
834 * exempt_owner is set when handling !set, so the owner can set things
835 * to/from >500.
836 */
837 int check_user_level(struct chanNode *channel, struct userNode *user, enum levelOption opt, int allow_override, int exempt_owner)
838 {
839 struct userData *uData;
840 struct chanData *cData = channel->channel_info;
841 unsigned short minimum = cData->lvlOpts[opt];
842 if(!minimum)
843 return 1;
844 uData = _GetChannelUser(cData, user->handle_info, allow_override, 0);
845 if(!uData)
846 return 0;
847 if(minimum <= uData->access)
848 return 1;
849 if((minimum > UL_OWNER) && (uData->access == UL_OWNER) && exempt_owner)
850 return 1;
851 return 0;
852 }
853
854 /* Scan for other users authenticated to the same handle
855 still in the channel. If so, keep them listed as present.
856
857 user is optional, if not null, it skips checking that userNode
858 (for the handle_part function) */
859 static void
860 scan_user_presence(struct userData *uData, struct userNode *user)
861 {
862 struct modeNode *mn;
863
864 if(IsSuspended(uData->channel)
865 || IsUserSuspended(uData)
866 || !(mn = find_handle_in_channel(uData->channel->channel, uData->handle, user)))
867 {
868 uData->present = 0;
869 }
870 else
871 {
872 uData->present = 1;
873 uData->seen = now;
874 }
875 }
876
877 static void
878 chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, char *text, UNUSED_ARG(struct userNode *bot))
879 {
880 unsigned int eflags, argc;
881 char *argv[4];
882 static char *bad_ctcp_reason = "CTCPs to this channel are forbidden.";
883
884 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
885 if(!channel->channel_info
886 || IsSuspended(channel->channel_info)
887 || IsService(user)
888 || !ircncasecmp(text, "ACTION ", 7))
889 return;
890 /* We dont punish people we know -Rubin
891 * * Figure out the minimum level needed to CTCP the channel *
892 *
893 * if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
894 * return;
895 */
896 /* If they are a user of the channel, they are exempt */
897 if(_GetChannelUser(channel->channel_info, user->handle_info, 0, 0))
898 return;
899 /* We need to enforce against them; do so. */
900 eflags = 0;
901 argv[0] = text;
902 argv[1] = user->nick;
903 argc = 2;
904 if(GetUserMode(channel, user))
905 eflags |= ACTION_KICK;
906 switch(channel->channel_info->chOpts[chCTCPReaction]) {
907 default: case 'n': return;
908 case 'k':
909 eflags |= ACTION_KICK;
910 break;
911 case 'b':
912 eflags |= ACTION_BAN;
913 break;
914 case 't':
915 eflags |= ACTION_BAN | ACTION_ADD_LAMER | ACTION_ADD_TIMED_LAMER;
916 argv[argc++] = (char*)chanserv_conf.ctcp_short_ban_duration;
917 break;
918 case 'T':
919 eflags |= ACTION_BAN | ACTION_ADD_LAMER | ACTION_ADD_TIMED_LAMER;
920 argv[argc++] = (char*)chanserv_conf.ctcp_long_ban_duration;
921 break;
922 }
923 argv[argc++] = bad_ctcp_reason;
924 eject_user(chanserv, channel, argc, argv, NULL, eflags);
925 }
926
927 struct note_type *
928 chanserv_create_note_type(const char *name)
929 {
930 struct note_type *ntype = calloc(1, sizeof(*ntype) + strlen(name));
931 strcpy(ntype->name, name);
932 ntype->refs = 1;
933 dict_insert(note_types, ntype->name, ntype);
934 return ntype;
935 }
936
937 static void
938 chanserv_deref_note_type(void *data)
939 {
940 struct note_type *ntype = data;
941
942 if(--ntype->refs > 0)
943 return;
944 free(ntype);
945 }
946
947 static void
948 chanserv_flush_note_type(struct note_type *ntype)
949 {
950 struct chanData *cData;
951 for(cData = channelList; cData; cData = cData->next)
952 dict_remove(cData->notes, ntype->name);
953 }
954
955 static void
956 chanserv_truncate_notes(struct note_type *ntype)
957 {
958 struct chanData *cData;
959 struct note *note;
960 unsigned int size = sizeof(*note) + ntype->max_length;
961
962 for(cData = channelList; cData; cData = cData->next) {
963 note = dict_find(cData->notes, ntype->name, NULL);
964 if(!note)
965 continue;
966 if(strlen(note->note) <= ntype->max_length)
967 continue;
968 dict_remove2(cData->notes, ntype->name, 1);
969 note = realloc(note, size);
970 note->note[ntype->max_length] = 0;
971 dict_insert(cData->notes, ntype->name, note);
972 }
973 }
974
975 static int note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user);
976
977 static struct note *
978 chanserv_add_channel_note(struct chanData *channel, struct note_type *type, const char *setter, const char *text)
979 {
980 struct note *note;
981 unsigned int len = strlen(text);
982
983 if(len > type->max_length) len = type->max_length;
984 note = calloc(1, sizeof(*note) + len);
985 note->type = type;
986 strncpy(note->setter, setter, sizeof(note->setter)-1);
987 memcpy(note->note, text, len);
988 note->note[len] = 0;
989 dict_insert(channel->notes, type->name, note);
990 type->refs++;
991 return note;
992 }
993
994 static void
995 chanserv_free_note(void *data)
996 {
997 struct note *note = data;
998
999 chanserv_deref_note_type(note->type);
1000 assert(note->type->refs > 0); /* must use delnote to remove the type */
1001 free(note);
1002 }
1003
1004 static MODCMD_FUNC(cmd_createnote) {
1005 struct note_type *ntype;
1006 unsigned int arg = 1, existed = 0, max_length;
1007
1008 if((ntype = dict_find(note_types, argv[1], NULL)))
1009 existed = 1;
1010 else
1011 ntype = chanserv_create_note_type(argv[arg]);
1012 if(!irccasecmp(argv[++arg], "privileged"))
1013 {
1014 arg++;
1015 ntype->set_access_type = NOTE_SET_PRIVILEGED;
1016 ntype->set_access.min_opserv = strtoul(argv[arg], NULL, 0);
1017 }
1018 else if(!irccasecmp(argv[arg], "channel"))
1019 {
1020 unsigned short ulvl = user_level_from_name(argv[++arg], UL_OWNER);
1021 if(!ulvl)
1022 {
1023 reply("CSMSG_INVALID_ACCESS", argv[arg]);
1024 goto fail;
1025 }
1026 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
1027 ntype->set_access.min_ulevel = ulvl;
1028 }
1029 else if(!irccasecmp(argv[arg], "setter"))
1030 {
1031 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
1032 }
1033 else
1034 {
1035 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
1036 goto fail;
1037 }
1038
1039 if(!irccasecmp(argv[++arg], "privileged"))
1040 ntype->visible_type = NOTE_VIS_PRIVILEGED;
1041 else if(!irccasecmp(argv[arg], "channel_users"))
1042 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
1043 else if(!irccasecmp(argv[arg], "all"))
1044 ntype->visible_type = NOTE_VIS_ALL;
1045 else {
1046 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
1047 goto fail;
1048 }
1049
1050 if((arg+1) >= argc) {
1051 reply("MSG_MISSING_PARAMS", argv[0]);
1052 goto fail;
1053 }
1054 max_length = strtoul(argv[++arg], NULL, 0);
1055 if(max_length < 20 || max_length > 450)
1056 {
1057 reply("CSMSG_BAD_MAX_LENGTH", argv[arg]);
1058 goto fail;
1059 }
1060 if(existed && (max_length < ntype->max_length))
1061 {
1062 ntype->max_length = max_length;
1063 chanserv_truncate_notes(ntype);
1064 }
1065 ntype->max_length = max_length;
1066
1067 if(existed)
1068 reply("CSMSG_NOTE_MODIFIED", ntype->name);
1069 else
1070 reply("CSMSG_NOTE_CREATED", ntype->name);
1071 return 1;
1072
1073 fail:
1074 if(!existed)
1075 dict_remove(note_types, ntype->name);
1076 return 0;
1077 }
1078
1079 static MODCMD_FUNC(cmd_removenote) {
1080 struct note_type *ntype;
1081 int force;
1082
1083 ntype = dict_find(note_types, argv[1], NULL);
1084 force = (argc > 2) && !irccasecmp(argv[2], "force");
1085 if(!ntype)
1086 {
1087 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
1088 return 0;
1089 }
1090 if(ntype->refs > 1)
1091 {
1092 if(!force)
1093 {
1094 reply("CSMSG_NOTE_TYPE_USED", ntype->name);
1095 return 0;
1096 }
1097 chanserv_flush_note_type(ntype);
1098 }
1099 dict_remove(note_types, argv[1]);
1100 reply("CSMSG_NOTE_DELETED", argv[1]);
1101 return 1;
1102 }
1103
1104 static int
1105 mode_lock_violated(const struct mod_chanmode *orig, const struct mod_chanmode *change)
1106 {
1107 if(!orig)
1108 return 0;
1109 if(orig->modes_set & change->modes_clear)
1110 return 1;
1111 if(orig->modes_clear & change->modes_set)
1112 return 1;
1113 if((orig->modes_set & MODE_KEY) && (change->modes_set & MODE_KEY)
1114 && strcmp(orig->new_key, change->new_key))
1115 return 1;
1116 if((orig->modes_set & MODE_LIMIT) && (change->modes_set & MODE_LIMIT)
1117 && (orig->new_limit != change->new_limit))
1118 return 1;
1119 return 0;
1120 }
1121
1122 static char max_length_text[MAXLEN+1][16];
1123
1124 static struct helpfile_expansion
1125 chanserv_expand_variable(const char *variable)
1126 {
1127 struct helpfile_expansion exp;
1128
1129 if(!irccasecmp(variable, "notes"))
1130 {
1131 dict_iterator_t it;
1132 exp.type = HF_TABLE;
1133 exp.value.table.length = 1;
1134 exp.value.table.width = 3;
1135 exp.value.table.flags = 0;
1136 exp.value.table.contents = calloc(dict_size(note_types)+1, sizeof(char**));
1137 exp.value.table.contents[0] = calloc(exp.value.table.width, sizeof(char*));
1138 exp.value.table.contents[0][0] = "Note Type";
1139 exp.value.table.contents[0][1] = "Visibility";
1140 exp.value.table.contents[0][2] = "Max Length";
1141 for(it=dict_first(note_types); it; it=iter_next(it))
1142 {
1143 struct note_type *ntype = iter_data(it);
1144 int row;
1145
1146 if(!note_type_visible_to_user(NULL, ntype, message_dest)) continue;
1147 row = exp.value.table.length++;
1148 exp.value.table.contents[row] = calloc(exp.value.table.width, sizeof(char*));
1149 exp.value.table.contents[row][0] = ntype->name;
1150 exp.value.table.contents[row][1] = (ntype->visible_type == NOTE_VIS_ALL) ? "all" :
1151 (ntype->visible_type == NOTE_VIS_CHANNEL_USERS) ? "chan users" :
1152 "unknown";
1153 if(!max_length_text[ntype->max_length][0])
1154 snprintf(max_length_text[ntype->max_length], sizeof(max_length_text[ntype->max_length]), "%u", ntype->max_length);
1155 exp.value.table.contents[row][2] = max_length_text[ntype->max_length];
1156 }
1157 return exp;
1158 }
1159
1160 exp.type = HF_STRING;
1161 exp.value.str = NULL;
1162 return exp;
1163 }
1164
1165 static struct chanData*
1166 register_channel(struct chanNode *cNode, char *registrar)
1167 {
1168 struct chanData *channel;
1169 enum levelOption lvlOpt;
1170 enum charOption chOpt;
1171
1172 channel = calloc(1, sizeof(struct chanData));
1173
1174 channel->notes = dict_new();
1175 dict_set_free_data(channel->notes, chanserv_free_note);
1176
1177 channel->registrar = strdup(registrar);
1178 channel->registered = now;
1179 channel->visited = now;
1180 channel->limitAdjusted = now;
1181 channel->ownerTransfer = now;
1182 channel->flags = CHANNEL_DEFAULT_FLAGS;
1183 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
1184 channel->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
1185 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
1186 channel->chOpts[chOpt] = charOptions[chOpt].default_value;
1187
1188 channel->prev = NULL;
1189 channel->next = channelList;
1190
1191 if(channelList)
1192 channelList->prev = channel;
1193 channelList = channel;
1194 registered_channels++;
1195
1196 channel->channel = cNode;
1197 LockChannel(cNode);
1198 cNode->channel_info = channel;
1199
1200 return channel;
1201 }
1202
1203 static struct userData*
1204 add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned short access, time_t seen, const char *info)
1205 {
1206 struct userData *ud;
1207
1208 if(access > UL_OWNER)
1209 return NULL;
1210
1211 ud = calloc(1, sizeof(*ud));
1212 ud->channel = channel;
1213 ud->handle = handle;
1214 ud->seen = seen;
1215 ud->access = access;
1216 ud->info = info ? strdup(info) : NULL;
1217
1218 ud->prev = NULL;
1219 ud->next = channel->users;
1220 if(channel->users)
1221 channel->users->prev = ud;
1222 channel->users = ud;
1223
1224 channel->userCount++;
1225 userCount++;
1226
1227 ud->u_prev = NULL;
1228 ud->u_next = ud->handle->channels;
1229 if(ud->u_next)
1230 ud->u_next->u_prev = ud;
1231 ud->handle->channels = ud;
1232
1233 ud->flags = USER_FLAGS_DEFAULT;
1234 return ud;
1235 }
1236
1237 static void unregister_channel(struct chanData *channel, const char *reason);
1238
1239 void
1240 del_channel_user(struct userData *user, int do_gc)
1241 {
1242 struct chanData *channel = user->channel;
1243
1244 channel->userCount--;
1245 userCount--;
1246
1247 if(user->prev)
1248 user->prev->next = user->next;
1249 else
1250 channel->users = user->next;
1251 if(user->next)
1252 user->next->prev = user->prev;
1253
1254 if(user->u_prev)
1255 user->u_prev->u_next = user->u_next;
1256 else
1257 user->handle->channels = user->u_next;
1258 if(user->u_next)
1259 user->u_next->u_prev = user->u_prev;
1260
1261 free(user->info);
1262 free(user);
1263 if(do_gc && !channel->users && !IsProtected(channel))
1264 unregister_channel(channel, "lost all users.");
1265 }
1266
1267 static struct adduserPending*
1268 add_adduser_pending(struct chanNode *channel, struct userNode *user, int level)
1269 {
1270 struct adduserPending *ap;
1271 ap = calloc(1,sizeof(struct adduserPending));
1272 ap->channel = channel;
1273 ap->user = user;
1274 ap->level = level;
1275 ap->created = time(NULL);
1276
1277 /* ap->prev defaults to NULL already.. */
1278 ap->next = adduser_pendings;
1279 if(adduser_pendings)
1280 adduser_pendings->prev = ap;
1281 adduser_pendings = ap;
1282 adduser_pendings_count++;
1283 return(ap);
1284 }
1285
1286 static void
1287 del_adduser_pending(struct adduserPending *ap)
1288 {
1289 if(ap->prev)
1290 ap->prev->next = ap->next;
1291 else
1292 adduser_pendings = ap->next;
1293
1294 if(ap->next)
1295 ap->next->prev = ap->prev;
1296 free(ap);
1297 }
1298
1299 static void expire_adduser_pending();
1300
1301 /* find_adduser_pending(channel, user) will find an arbitrary record
1302 * from user, channel, or user and channel.
1303 * if user or channel are NULL, they will match any records.
1304 */
1305 static struct adduserPending*
1306 find_adduser_pending(struct chanNode *channel, struct userNode *user)
1307 {
1308 struct adduserPending *ap;
1309
1310 expire_adduser_pending(); /* why not here.. */
1311
1312 if(!channel && !user) /* 2 nulls matches all */
1313 return(adduser_pendings);
1314 for(ap = adduser_pendings;ap;ap = ap->next)
1315 {
1316 if((channel == ap->channel && (user == NULL || user == ap->user)) || (user==ap->user && channel==NULL))
1317 return ap;
1318 }
1319 return NULL;
1320 }
1321
1322
1323 /* Remove all pendings for a user or channel
1324 *
1325 * called in nickserv.c DelUser() and proto-* unregister_channel()
1326 */
1327 void
1328 wipe_adduser_pending(struct chanNode *channel, struct userNode *user)
1329 {
1330 struct adduserPending *ap;
1331
1332 /* So this is a bit wastefull, i hate dealing with linked lists.
1333 * if its a problem we'll rewrite it right */
1334 while((ap = find_adduser_pending(channel, user))) {
1335 del_adduser_pending(ap);
1336 }
1337 }
1338
1339 /* Called from nickserv.c cmd_auth after someone auths */
1340 void
1341 process_adduser_pending(struct userNode *user)
1342 {
1343 struct adduserPending *ap;
1344 if(!user->handle_info)
1345 return; /* not associated with an account */
1346 while((ap = find_adduser_pending(NULL, user)))
1347 {
1348 struct userData *actee;
1349 if(GetTrueChannelAccess(ap->channel->channel_info, ap->user->handle_info))
1350 {
1351 /* Already on the userlist. do nothing*/
1352 }
1353 else
1354 {
1355 actee = add_channel_user(ap->channel->channel_info, ap->user->handle_info, ap->level, 0, NULL);
1356 scan_user_presence(actee, NULL);
1357 }
1358 del_adduser_pending(ap);
1359 }
1360 }
1361
1362 static void
1363 expire_adduser_pending()
1364 {
1365 struct adduserPending *ap, *ap_next;
1366 ap = adduser_pendings;
1367 while(ap)
1368 {
1369 if((ap->created + ADDUSER_PENDING_EXPIRE) < time(NULL))
1370 { /* expire it */
1371 ap_next = ap->next; /* save next */
1372 del_adduser_pending(ap); /* free and relink */
1373 ap = ap_next; /* advance */
1374 }
1375 else
1376 ap = ap->next;
1377 }
1378 }
1379
1380 static void expire_ban(void *data);
1381
1382 static struct banData*
1383 add_channel_ban(struct chanData *channel, const char *mask, char *owner, time_t set, time_t triggered, time_t expires, char *reason)
1384 {
1385 struct banData *bd;
1386 unsigned int ii, l1, l2;
1387
1388 if(!mask)
1389 return NULL;
1390
1391 bd = malloc(sizeof(struct banData));
1392
1393 bd->channel = channel;
1394 bd->set = set;
1395 bd->triggered = triggered;
1396 bd->expires = expires;
1397
1398 for(ii = 0; ii < chanserv_conf.old_ban_names->used; ++ii)
1399 {
1400 extern const char *hidden_host_suffix;
1401 const char *old_name = chanserv_conf.old_ban_names->list[ii];
1402 char *new_mask;
1403
1404 l1 = strlen(mask);
1405 l2 = strlen(old_name);
1406 if(l2+2 > l1)
1407 continue;
1408 if(irccasecmp(mask + l1 - l2, old_name))
1409 continue;
1410 new_mask = alloca(MAXLEN);
1411 sprintf(new_mask, "%.*s%s", (int)(l1-l2), mask, hidden_host_suffix);
1412 mask = new_mask;
1413 }
1414 safestrncpy(bd->mask, mask, sizeof(bd->mask));
1415 if(owner)
1416 safestrncpy(bd->owner, owner, sizeof(bd->owner));
1417 bd->reason = strdup(reason);
1418
1419 if(expires)
1420 timeq_add(expires, expire_ban, bd);
1421
1422 bd->prev = NULL;
1423 bd->next = channel->bans; /* lamers */
1424 if(channel->bans)
1425 channel->bans->prev = bd;
1426 channel->bans = bd;
1427 channel->banCount++;
1428 banCount++;
1429
1430 return bd;
1431 }
1432
1433 static void
1434 del_channel_ban(struct banData *ban)
1435 {
1436 ban->channel->banCount--;
1437 banCount--;
1438
1439 if(ban->prev)
1440 ban->prev->next = ban->next;
1441 else
1442 ban->channel->bans = ban->next;
1443
1444 if(ban->next)
1445 ban->next->prev = ban->prev;
1446
1447 if(ban->expires)
1448 timeq_del(0, expire_ban, ban, TIMEQ_IGNORE_WHEN);
1449
1450 if(ban->reason)
1451 free(ban->reason);
1452
1453 free(ban);
1454 }
1455
1456 static void
1457 expire_ban(void *data) /* lamer.. */
1458 {
1459 struct banData *bd = data;
1460 if(!IsSuspended(bd->channel))
1461 {
1462 struct banList bans;
1463 struct mod_chanmode change;
1464 unsigned int ii;
1465 bans = bd->channel->channel->banlist;
1466 mod_chanmode_init(&change);
1467 for(ii=0; ii<bans.used; ii++)
1468 {
1469 if(!strcmp(bans.list[ii]->ban, bd->mask))
1470 {
1471 change.argc = 1;
1472 change.args[0].mode = MODE_REMOVE|MODE_BAN;
1473 change.args[0].u.hostmask = bd->mask;
1474 mod_chanmode_announce(chanserv, bd->channel->channel, &change);
1475 break;
1476 }
1477 }
1478 }
1479 bd->expires = 0;
1480 del_channel_ban(bd);
1481 }
1482
1483 static void chanserv_expire_suspension(void *data);
1484
1485 static void
1486 unregister_channel(struct chanData *channel, const char *reason)
1487 {
1488 struct mod_chanmode change;
1489 char msgbuf[MAXLEN];
1490
1491 /* After channel unregistration, the following must be cleaned
1492 up:
1493 - Channel information.
1494 - Channel users.
1495 - Channel bans. (lamers)
1496 - Channel suspension data.
1497 - adduser_pending data.
1498 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1499 */
1500
1501 if(!channel)
1502 return;
1503
1504 timeq_del(0, NULL, channel, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
1505
1506 if(off_channel > 0)
1507 {
1508 mod_chanmode_init(&change);
1509 change.modes_clear |= MODE_REGISTERED;
1510 mod_chanmode_announce(chanserv, channel->channel, &change);
1511 }
1512
1513 wipe_adduser_pending(channel->channel, NULL);
1514
1515 while(channel->users)
1516 del_channel_user(channel->users, 0);
1517
1518 while(channel->bans)
1519 del_channel_ban(channel->bans);
1520
1521 free(channel->topic);
1522 free(channel->registrar);
1523 free(channel->greeting);
1524 free(channel->user_greeting);
1525 free(channel->topic_mask);
1526
1527 if(channel->prev)
1528 channel->prev->next = channel->next;
1529 else
1530 channelList = channel->next;
1531
1532 if(channel->next)
1533 channel->next->prev = channel->prev;
1534
1535 if(channel->suspended)
1536 {
1537 struct chanNode *cNode = channel->channel;
1538 struct suspended *suspended, *next_suspended;
1539
1540 for(suspended = channel->suspended; suspended; suspended = next_suspended)
1541 {
1542 next_suspended = suspended->previous;
1543 free(suspended->suspender);
1544 free(suspended->reason);
1545 if(suspended->expires)
1546 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
1547 free(suspended);
1548 }
1549
1550 if(cNode)
1551 cNode->channel_info = NULL;
1552 }
1553 channel->channel->channel_info = NULL;
1554
1555 dict_delete(channel->notes);
1556 sprintf(msgbuf, "%s %s", channel->channel->name, reason);
1557 if(!IsSuspended(channel))
1558 DelChannelUser(chanserv, channel->channel, msgbuf, 0);
1559 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, msgbuf);
1560 UnlockChannel(channel->channel);
1561 free(channel);
1562 registered_channels--;
1563 }
1564
1565 static void
1566 expire_channels(UNUSED_ARG(void *data))
1567 {
1568 struct chanData *channel, *next;
1569 struct userData *user;
1570 char delay[INTERVALLEN], reason[INTERVALLEN + 64];
1571
1572 intervalString(delay, chanserv_conf.channel_expire_delay, NULL);
1573 sprintf(reason, "Channel registration automatically expired after %s of disuse.", delay);
1574
1575 for(channel = channelList; channel; channel = next)
1576 {
1577 next = channel->next;
1578
1579 /* See if the channel can be expired. */
1580 if(((now - channel->visited) <= chanserv_conf.channel_expire_delay)
1581 || IsProtected(channel))
1582 continue;
1583
1584 /* Make sure there are no high-ranking users still in the channel. */
1585 for(user=channel->users; user; user=user->next)
1586 if(user->present && (user->access >= UL_PRESENT))
1587 break;
1588 if(user)
1589 continue;
1590
1591 /* Unregister the channel */
1592 log_module(CS_LOG, LOG_INFO, "(%s) Channel registration expired.", channel->channel->name);
1593 unregister_channel(channel, "registration expired.");
1594 }
1595
1596 if(chanserv_conf.channel_expire_frequency)
1597 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
1598 }
1599
1600 static int
1601 protect_user(const struct userNode *victim, const struct userNode *aggressor, struct chanData *channel)
1602 {
1603 char protect = channel->chOpts[chProtect];
1604 struct userData *cs_victim, *cs_aggressor;
1605
1606 /* Don't protect if no one is to be protected, someone is attacking
1607 himself, or if the aggressor is an IRC Operator. */
1608 if(protect == 'n' || victim == aggressor /* Opers dont get special treatment :/ || IsOper(aggressor) */)
1609 return 0;
1610
1611 /* Don't protect if the victim isn't authenticated (because they
1612 can't be a channel user), unless we are to protect non-users
1613 also. */
1614 cs_victim = GetChannelAccess(channel, victim->handle_info);
1615 if(protect != 'a' && !cs_victim)
1616 return 0;
1617
1618 /* Protect if the aggressor isn't a user because at this point,
1619 the aggressor can only be less than or equal to the victim. */
1620 cs_aggressor = GetChannelAccess(channel, aggressor->handle_info);
1621 if(!cs_aggressor)
1622 return 1;
1623
1624 /* If the aggressor was a user, then the victim can't be helped. */
1625 if(!cs_victim)
1626 return 0;
1627
1628 switch(protect)
1629 {
1630 case 'l':
1631 if(cs_victim->access > cs_aggressor->access)
1632 return 1;
1633 break;
1634 case 'a':
1635 case 'e':
1636 if(cs_victim->access >= cs_aggressor->access)
1637 return 1;
1638 break;
1639 }
1640
1641 return 0;
1642 }
1643
1644 static int
1645 validate_op(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1646 {
1647 struct chanData *cData = channel->channel_info;
1648 struct userData *cs_victim;
1649
1650 if((!(cs_victim = GetChannelUser(cData, victim->handle_info))
1651 || (cs_victim->access < UL_OP /* cData->lvlOpts[lvlGiveOps]*/))
1652 && !check_user_level(channel, user, lvlEnfOps, 0, 0))
1653 {
1654 send_message(user, chanserv, "CSMSG_OPBY_LOCKED");
1655 return 0;
1656 }
1657
1658 return 1;
1659 }
1660
1661 static int
1662 validate_halfop(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1663 {
1664 struct chanData *cData = channel->channel_info;
1665 struct userData *cs_victim;
1666
1667 if((!(cs_victim = GetChannelUser(cData, victim->handle_info))
1668 || (cs_victim->access < UL_HALFOP /* cData->lvlOpts[lvlGiveHalfOps] */))
1669 && !check_user_level(channel, user, lvlEnfHalfOps, 0, 0))
1670 {
1671 send_message(user, chanserv, "CSMSG_HOPBY_LOCKED");
1672 return 0;
1673 }
1674
1675 return 1;
1676 }
1677
1678
1679 static int
1680 validate_deop(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1681 {
1682 if(IsService(victim))
1683 {
1684 send_message(user, chanserv, "MSG_SERVICE_IMMUNE", victim->nick);
1685 return 0;
1686 }
1687
1688 if(protect_user(victim, user, channel->channel_info))
1689 {
1690 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
1691 return 0;
1692 }
1693
1694 return 1;
1695 }
1696
1697 static int
1698 validate_dehop(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1699 {
1700 if(IsService(victim))
1701 {
1702 send_message(user, chanserv, "MSG_SERVICE_IMMUNE", victim->nick);
1703 return 0;
1704 }
1705
1706 if(protect_user(victim, user, channel->channel_info))
1707 {
1708 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
1709 return 0;
1710 }
1711
1712 return 1;
1713 }
1714
1715 static struct do_not_register *
1716 chanserv_add_dnr(const char *chan_name, const char *setter, const char *reason)
1717 {
1718 struct do_not_register *dnr = calloc(1, sizeof(*dnr)+strlen(reason));
1719 safestrncpy(dnr->chan_name, chan_name, sizeof(dnr->chan_name));
1720 safestrncpy(dnr->setter, setter, sizeof(dnr->setter));
1721 strcpy(dnr->reason, reason);
1722 dnr->set = now;
1723 if(dnr->chan_name[0] == '*')
1724 dict_insert(handle_dnrs, dnr->chan_name+1, dnr);
1725 else if(strpbrk(dnr->chan_name, "*?"))
1726 dict_insert(mask_dnrs, dnr->chan_name, dnr);
1727 else
1728 dict_insert(plain_dnrs, dnr->chan_name, dnr);
1729 return dnr;
1730 }
1731
1732 static struct dnrList
1733 chanserv_find_dnrs(const char *chan_name, const char *handle)
1734 {
1735 struct dnrList list;
1736 dict_iterator_t it;
1737 struct do_not_register *dnr;
1738
1739 dnrList_init(&list);
1740 if(handle && (dnr = dict_find(handle_dnrs, handle, NULL)))
1741 dnrList_append(&list, dnr);
1742 if(chan_name && (dnr = dict_find(plain_dnrs, chan_name, NULL)))
1743 dnrList_append(&list, dnr);
1744 if(chan_name)
1745 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1746 if(match_ircglob(chan_name, iter_key(it)))
1747 dnrList_append(&list, iter_data(it));
1748 return list;
1749 }
1750
1751 static unsigned int
1752 chanserv_show_dnrs(struct userNode *user, struct svccmd *cmd, const char *chan_name, const char *handle)
1753 {
1754 struct dnrList list;
1755 struct do_not_register *dnr;
1756 unsigned int ii;
1757 char buf[INTERVALLEN];
1758
1759 list = chanserv_find_dnrs(chan_name, handle);
1760 for(ii = 0; (ii < list.used) && (ii < 10); ++ii)
1761 {
1762 dnr = list.list[ii];
1763 if(dnr->set)
1764 {
1765 strftime(buf, sizeof(buf), "%Y %b %d", localtime(&dnr->set));
1766 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, buf, dnr->setter, dnr->reason);
1767 }
1768 else
1769 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1770 }
1771 if(ii < list.used)
1772 reply("CSMSG_MORE_DNRS", list.used - ii);
1773 free(list.list);
1774 return ii;
1775 }
1776
1777 struct do_not_register *
1778 chanserv_is_dnr(const char *chan_name, struct handle_info *handle)
1779 {
1780 struct do_not_register *dnr;
1781 dict_iterator_t it;
1782
1783 if(handle && (dnr = dict_find(handle_dnrs, handle->handle, NULL)))
1784 return dnr;
1785 if(chan_name)
1786 {
1787 if((dnr = dict_find(plain_dnrs, chan_name, NULL)))
1788 return dnr;
1789 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1790 if(match_ircglob(chan_name, iter_key(it)))
1791 return iter_data(it);
1792 }
1793 return NULL;
1794 }
1795
1796 static CHANSERV_FUNC(cmd_noregister)
1797 {
1798 const char *target;
1799 struct do_not_register *dnr;
1800 char buf[INTERVALLEN];
1801 unsigned int matches;
1802
1803 if(argc < 2)
1804 {
1805 dict_iterator_t it;
1806
1807 reply("CSMSG_DNR_SEARCH_RESULTS");
1808 reply("CSMSG_BAR");
1809 matches = 0;
1810 for(it = dict_first(handle_dnrs); it; it = iter_next(it))
1811 {
1812 dnr = iter_data(it);
1813 if(dnr->set)
1814 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
1815 else
1816 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1817 matches++;
1818 }
1819 for(it = dict_first(plain_dnrs); it; it = iter_next(it))
1820 {
1821 dnr = iter_data(it);
1822 if(dnr->set)
1823 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
1824 else
1825 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1826 matches++;
1827 }
1828 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1829 {
1830 dnr = iter_data(it);
1831 if(dnr->set)
1832 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
1833 else
1834 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1835 matches++;
1836 }
1837
1838 if(matches)
1839 reply("MSG_MATCH_COUNT", matches);
1840 else
1841 reply("MSG_NO_MATCHES");
1842 return 0;
1843 }
1844
1845 target = argv[1];
1846
1847 if(!IsChannelName(target) && (*target != '*'))
1848 {
1849 reply("CSMSG_NOT_DNR", target);
1850 return 0;
1851 }
1852
1853 if(argc > 2)
1854 {
1855 const char *reason = unsplit_string(argv + 2, argc - 2, NULL);
1856 if((*target == '*') && !get_handle_info(target + 1))
1857 {
1858 reply("MSG_HANDLE_UNKNOWN", target + 1);
1859 return 0;
1860 }
1861 chanserv_add_dnr(target, user->handle_info->handle, reason);
1862 reply("CSMSG_NOREGISTER_CHANNEL", target);
1863 return 1;
1864 }
1865
1866 reply("CSMSG_DNR_SEARCH_RESULTS");
1867 reply("CSMSG_BAR");
1868 if(*target == '*')
1869 matches = chanserv_show_dnrs(user, cmd, NULL, target + 1);
1870 else
1871 matches = chanserv_show_dnrs(user, cmd, target, NULL);
1872 if(!matches)
1873 reply("MSG_NO_MATCHES");
1874 return 0;
1875 }
1876
1877 static CHANSERV_FUNC(cmd_allowregister)
1878 {
1879 const char *chan_name = argv[1];
1880
1881 if((chan_name[0] == '*') && dict_find(handle_dnrs, chan_name+1, NULL))
1882 {
1883 dict_remove(handle_dnrs, chan_name+1);
1884 reply("CSMSG_DNR_REMOVED", chan_name);
1885 }
1886 else if(dict_find(plain_dnrs, chan_name, NULL))
1887 {
1888 dict_remove(plain_dnrs, chan_name);
1889 reply("CSMSG_DNR_REMOVED", chan_name);
1890 }
1891 else if(dict_find(mask_dnrs, chan_name, NULL))
1892 {
1893 dict_remove(mask_dnrs, chan_name);
1894 reply("CSMSG_DNR_REMOVED", chan_name);
1895 }
1896 else
1897 {
1898 reply("CSMSG_NO_SUCH_DNR", chan_name);
1899 return 0;
1900 }
1901 return 1;
1902 }
1903
1904 unsigned int
1905 chanserv_get_owned_count(struct handle_info *hi)
1906 {
1907 struct userData *cList;
1908 unsigned int owned;
1909
1910 for(owned=0, cList=hi->channels; cList; cList=cList->u_next)
1911 if(cList->access == UL_OWNER)
1912 owned++;
1913 return owned;
1914 }
1915
1916 static CHANSERV_FUNC(cmd_register)
1917 {
1918 struct handle_info *handle;
1919 struct chanData *cData;
1920 struct modeNode *mn;
1921 char reason[MAXLEN];
1922 char *chan_name;
1923 unsigned int new_channel, force=0;
1924 struct do_not_register *dnr;
1925 unsigned int n;
1926
1927
1928 if(channel)
1929 {
1930 if(channel->channel_info)
1931 {
1932 reply("CSMSG_ALREADY_REGGED", channel->name);
1933 return 0;
1934 }
1935
1936 if(channel->bad_channel)
1937 {
1938 reply("CSMSG_ILLEGAL_CHANNEL", channel->name);
1939 return 0;
1940 }
1941
1942 if(!IsHelping(user) && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
1943 {
1944 reply("CSMSG_MUST_BE_OPPED", channel->name);
1945 return 0;
1946 }
1947
1948 new_channel = 0;
1949 chan_name = channel->name;
1950 }
1951 else
1952 {
1953 if(argc < 2)
1954 {
1955 reply("MSG_MISSING_PARAMS", cmd->name);
1956 svccmd_send_help_brief(user, chanserv, cmd);
1957 return 0;
1958 }
1959 if(!IsChannelName(argv[1]))
1960 {
1961 reply("MSG_NOT_CHANNEL_NAME");
1962 return 0;
1963 }
1964
1965 if(opserv_bad_channel(argv[1]))
1966 {
1967 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
1968 return 0;
1969 }
1970
1971 new_channel = 1;
1972 chan_name = argv[1];
1973 }
1974
1975 if(argc >= (new_channel+2))
1976 {
1977 if(!IsHelping(user))
1978 {
1979 reply("CSMSG_PROXY_FORBIDDEN");
1980 return 0;
1981 }
1982
1983 if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
1984 return 0;
1985 force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
1986 dnr = chanserv_is_dnr(chan_name, handle);
1987
1988 /* Check if they are over the limit.. */
1989 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
1990 {
1991 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
1992 return 0;
1993 }
1994
1995 }
1996 else
1997 {
1998 handle = user->handle_info;
1999 dnr = chanserv_is_dnr(chan_name, handle);
2000 /* Check if they are over the limit.. */
2001 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2002 {
2003 reply("CSMSG_YOU_OWN_TOO_MANY", chanserv_conf.max_owned);
2004 return 0;
2005 }
2006 /* Check if another service is in the channel */
2007 if(channel)
2008 for(n = 0; n < channel->members.used; n++)
2009 {
2010 mn = channel->members.list[n];
2011 if((mn && mn->user && (mn->user->modes & FLAGS_SERVICE)) || IsLocal(mn->user))
2012 {
2013 reply("CSMSG_ANOTHER_SERVICE");
2014 return 0;
2015 }
2016 }
2017 }
2018 if(dnr && !force)
2019 {
2020 if(!IsHelping(user))
2021 reply("CSMSG_DNR_CHANNEL", chan_name);
2022 else
2023 chanserv_show_dnrs(user, cmd, chan_name, handle->handle);
2024 return 0;
2025 }
2026
2027 /* now handled above for message specilization *
2028 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2029 {
2030 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
2031 return 0;
2032 }
2033 */
2034
2035 if(new_channel)
2036 channel = AddChannel(argv[1], now, NULL, NULL, NULL);
2037
2038 cData = register_channel(channel, user->handle_info->handle);
2039 scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL), NULL);
2040 cData->modes = chanserv_conf.default_modes;
2041 if(off_channel > 0)
2042 cData->modes.modes_set |= MODE_REGISTERED;
2043 if (IsOffChannel(cData))
2044 {
2045 mod_chanmode_announce(chanserv, channel, &cData->modes);
2046 }
2047 else
2048 {
2049 struct mod_chanmode *change = mod_chanmode_dup(&cData->modes, 1);
2050 change->args[change->argc].mode = MODE_CHANOP;
2051 change->args[change->argc].u.member = AddChannelUser(chanserv, channel);
2052 change->argc++;
2053 mod_chanmode_announce(chanserv, channel, change);
2054 mod_chanmode_free(change);
2055 }
2056
2057 /* Initialize the channel's max user record. */
2058 cData->max = channel->members.used;
2059
2060 if(handle != user->handle_info)
2061 reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
2062 else
2063 reply("CSMSG_REG_SUCCESS", channel->name);
2064
2065 sprintf(reason, "%s registered to %s by %s.", channel->name, handle->handle, user->handle_info->handle);
2066 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2067 return 1;
2068 }
2069
2070 static const char *
2071 make_confirmation_string(struct userData *uData)
2072 {
2073 static char strbuf[16];
2074 char *src;
2075 unsigned int accum;
2076
2077 accum = 0;
2078 for(src = uData->handle->handle; *src; )
2079 accum = accum * 31 + toupper(*src++);
2080 if(uData->channel)
2081 for(src = uData->channel->channel->name; *src; )
2082 accum = accum * 31 + toupper(*src++);
2083 sprintf(strbuf, "%08x", accum);
2084 return strbuf;
2085 }
2086
2087 static CHANSERV_FUNC(cmd_unregister)
2088 {
2089 char *name;
2090 char reason[MAXLEN];
2091 struct chanData *cData;
2092 struct userData *uData;
2093
2094 cData = channel->channel_info;
2095 if(!cData)
2096 {
2097 reply("CSMSG_NOT_REGISTERED", channel->name);
2098 return 0;
2099 }
2100
2101 uData = GetChannelUser(cData, user->handle_info);
2102 if(!uData || (uData->access < UL_OWNER))
2103 {
2104 reply("CSMSG_NO_ACCESS");
2105 return 0;
2106 }
2107
2108 if(IsProtected(cData))
2109 {
2110 reply("CSMSG_UNREG_NODELETE", channel->name);
2111 return 0;
2112 }
2113
2114 if(!IsHelping(user))
2115 {
2116 const char *confirm_string;
2117 if(IsSuspended(cData))
2118 {
2119 reply("CSMSG_CHAN_SUSPENDED", channel->name, cData->suspended->reason);
2120 return 0;
2121 }
2122 confirm_string = make_confirmation_string(uData);
2123 if((argc < 2) || strcmp(argv[1], confirm_string))
2124 {
2125 reply("CSMSG_CONFIRM_UNREG", channel->name, confirm_string);
2126 return 0;
2127 }
2128 }
2129
2130 sprintf(reason, "unregistered by %s.", user->handle_info->handle);
2131 name = strdup(channel->name);
2132 unregister_channel(cData, reason);
2133 reply("CSMSG_UNREG_SUCCESS", name);
2134 free(name);
2135 return 1;
2136 }
2137
2138 static CHANSERV_FUNC(cmd_move)
2139 {
2140 struct mod_chanmode change;
2141 struct chanNode *target;
2142 struct modeNode *mn;
2143 struct userData *uData;
2144 char reason[MAXLEN];
2145 struct do_not_register *dnr;
2146
2147 REQUIRE_PARAMS(2);
2148
2149 if(IsProtected(channel->channel_info))
2150 {
2151 reply("CSMSG_MOVE_NODELETE", channel->name);
2152 return 0;
2153 }
2154
2155 if(!IsChannelName(argv[1]))
2156 {
2157 reply("MSG_NOT_CHANNEL_NAME");
2158 return 0;
2159 }
2160
2161 if(opserv_bad_channel(argv[1]))
2162 {
2163 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2164 return 0;
2165 }
2166
2167 if(!IsHelping(user) || (argc < 3) || irccasecmp(argv[2], "force"))
2168 {
2169 for(uData = channel->channel_info->users; uData; uData = uData->next)
2170 {
2171 if((uData->access == UL_OWNER) && (dnr = chanserv_is_dnr(argv[1], uData->handle)))
2172 {
2173 if(!IsHelping(user))
2174 reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]);
2175 else
2176 chanserv_show_dnrs(user, cmd, argv[1], uData->handle->handle);
2177 return 0;
2178 }
2179 }
2180 }
2181
2182 mod_chanmode_init(&change);
2183 if(!(target = GetChannel(argv[1])))
2184 {
2185 target = AddChannel(argv[1], now, NULL, NULL, NULL);
2186 if(!IsSuspended(channel->channel_info))
2187 AddChannelUser(chanserv, target);
2188 }
2189 else if(target->channel_info)
2190 {
2191 reply("CSMSG_ALREADY_REGGED", target->name);
2192 return 0;
2193 }
2194 else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
2195 && !IsHelping(user))
2196 {
2197 reply("CSMSG_MUST_BE_OPPED", target->name);
2198 return 0;
2199 }
2200 else if(!IsSuspended(channel->channel_info))
2201 {
2202 change.argc = 1;
2203 change.args[0].mode = MODE_CHANOP;
2204 change.args[0].u.member = AddChannelUser(chanserv, target);
2205 mod_chanmode_announce(chanserv, target, &change);
2206 }
2207
2208 if(off_channel > 0)
2209 {
2210 /* Clear MODE_REGISTERED from old channel, add it to new. */
2211 change.argc = 0;
2212 change.modes_clear = MODE_REGISTERED;
2213 mod_chanmode_announce(chanserv, channel, &change);
2214 change.modes_clear = 0;
2215 change.modes_set = MODE_REGISTERED;
2216 mod_chanmode_announce(chanserv, target, &change);
2217 }
2218
2219 /* Move the channel_info to the target channel; it
2220 shouldn't be necessary to clear timeq callbacks
2221 for the old channel. */
2222 target->channel_info = channel->channel_info;
2223 target->channel_info->channel = target;
2224 channel->channel_info = NULL;
2225
2226 reply("CSMSG_MOVE_SUCCESS", target->name);
2227
2228 sprintf(reason, "%s moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
2229 if(!IsSuspended(target->channel_info))
2230 {
2231 char reason2[MAXLEN];
2232 sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
2233 DelChannelUser(chanserv, channel, reason2, 0);
2234 }
2235 UnlockChannel(channel);
2236 LockChannel(target);
2237 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2238 return 1;
2239 }
2240
2241 static void
2242 merge_users(struct chanData *source, struct chanData *target)
2243 {
2244 struct userData *suData, *tuData, *next;
2245 dict_iterator_t it;
2246 dict_t merge;
2247
2248 merge = dict_new();
2249
2250 /* Insert the source's users into the scratch area. */
2251 for(suData = source->users; suData; suData = suData->next)
2252 dict_insert(merge, suData->handle->handle, suData);
2253
2254 /* Iterate through the target's users, looking for
2255 users common to both channels. The lower access is
2256 removed from either the scratch area or target user
2257 list. */
2258 for(tuData = target->users; tuData; tuData = next)
2259 {
2260 struct userData *choice;
2261
2262 next = tuData->next;
2263
2264 /* If a source user exists with the same handle as a target
2265 channel's user, resolve the conflict by removing one. */
2266 suData = dict_find(merge, tuData->handle->handle, NULL);
2267 if(!suData)
2268 continue;
2269
2270 /* Pick the data we want to keep. */
2271 /* If the access is the same, use the later seen time. */
2272 if(suData->access == tuData->access)
2273 choice = (suData->seen > tuData->seen) ? suData : tuData;
2274 else /* Otherwise, keep the higher access level. */
2275 choice = (suData->access > tuData->access) ? suData : tuData;
2276
2277 /* Remove the user that wasn't picked. */
2278 if(choice == tuData)
2279 {
2280 dict_remove(merge, suData->handle->handle);
2281 del_channel_user(suData, 0);
2282 }
2283 else
2284 del_channel_user(tuData, 0);
2285 }
2286
2287 /* Move the remaining users to the target channel. */
2288 for(it = dict_first(merge); it; it = iter_next(it))
2289 {
2290 suData = iter_data(it);
2291
2292 /* Insert the user into the target channel's linked list. */
2293 suData->prev = NULL;
2294 suData->next = target->users;
2295 suData->channel = target;
2296
2297 if(target->users)
2298 target->users->prev = suData;
2299 target->users = suData;
2300
2301 /* Update the user counts for the target channel; the
2302 source counts are left alone. */
2303 target->userCount++;
2304 }
2305
2306 /* Possible to assert (source->users == NULL) here. */
2307 source->users = NULL;
2308 dict_delete(merge);
2309 }
2310
2311 static void
2312 merge_bans(struct chanData *source, struct chanData *target)
2313 {
2314 struct banData *sbData, *tbData, *sNext, *tNext, *tFront;
2315
2316 /* Hold on to the original head of the target ban list
2317 to avoid comparing source bans with source bans. */
2318 tFront = target->bans;
2319
2320 /* Perform a totally expensive O(n*m) merge, ick. */
2321 for(sbData = source->bans; sbData; sbData = sNext)
2322 {
2323 /* Flag to track whether the ban's been moved
2324 to the destination yet. */
2325 int moved = 0;
2326
2327 /* Possible to assert (sbData->prev == NULL) here. */
2328 sNext = sbData->next;
2329
2330 for(tbData = tFront; tbData; tbData = tNext)
2331 {
2332 tNext = tbData->next;
2333
2334 /* Perform two comparisons between each source
2335 and target ban, conflicts are resolved by
2336 keeping the broader ban and copying the later
2337 expiration and triggered time. */
2338 if(match_ircglobs(tbData->mask, sbData->mask))
2339 {
2340 /* There is a broader ban in the target channel that
2341 overrides one in the source channel; remove the
2342 source ban and break. */
2343 if(sbData->expires > tbData->expires)
2344 tbData->expires = sbData->expires;
2345 if(sbData->triggered > tbData->triggered)
2346 tbData->triggered = sbData->triggered;
2347 del_channel_ban(sbData);
2348 break;
2349 }
2350 else if(match_ircglobs(sbData->mask, tbData->mask))
2351 {
2352 /* There is a broader ban in the source channel that
2353 overrides one in the target channel; remove the
2354 target ban, fall through and move the source over. */
2355 if(tbData->expires > sbData->expires)
2356 sbData->expires = tbData->expires;
2357 if(tbData->triggered > sbData->triggered)
2358 sbData->triggered = tbData->triggered;
2359 if(tbData == tFront)
2360 tFront = tNext;
2361 del_channel_ban(tbData);
2362 }
2363
2364 /* Source bans can override multiple target bans, so
2365 we allow a source to run through this loop multiple
2366 times, but we can only move it once. */
2367 if(moved)
2368 continue;
2369 moved = 1;
2370
2371 /* Remove the source ban from the source ban list. */
2372 if(sbData->next)
2373 sbData->next->prev = sbData->prev;
2374
2375 /* Modify the source ban's associated channel. */
2376 sbData->channel = target;
2377
2378 /* Insert the ban into the target channel's linked list. */
2379 sbData->prev = NULL;
2380 sbData->next = target->bans;
2381
2382 if(target->bans)
2383 target->bans->prev = sbData;
2384 target->bans = sbData;
2385
2386 /* Update the user counts for the target channel. */
2387 target->banCount++;
2388 }
2389 }
2390
2391 /* Possible to assert (source->bans == NULL) here. */
2392 source->bans = NULL;
2393 }
2394
2395 static void
2396 merge_data(struct chanData *source, struct chanData *target)
2397 {
2398 if(source->visited > target->visited)
2399 target->visited = source->visited;
2400 }
2401
2402 static void
2403 merge_channel(struct chanData *source, struct chanData *target)
2404 {
2405 merge_users(source, target);
2406 merge_bans(source, target);
2407 merge_data(source, target);
2408 }
2409
2410 static CHANSERV_FUNC(cmd_merge)
2411 {
2412 struct userData *target_user;
2413 struct chanNode *target;
2414 char reason[MAXLEN];
2415
2416 REQUIRE_PARAMS(2);
2417
2418 /* Make sure the target channel exists and is registered to the user
2419 performing the command. */
2420 if(!(target = GetChannel(argv[1])))
2421 {
2422 reply("MSG_INVALID_CHANNEL");
2423 return 0;
2424 }
2425
2426 if(!target->channel_info)
2427 {
2428 reply("CSMSG_NOT_REGISTERED", target->name);
2429 return 0;
2430 }
2431
2432 if(IsProtected(channel->channel_info))
2433 {
2434 reply("CSMSG_MERGE_NODELETE");
2435 return 0;
2436 }
2437
2438 if(IsSuspended(target->channel_info))
2439 {
2440 reply("CSMSG_MERGE_SUSPENDED");
2441 return 0;
2442 }
2443
2444 if(channel == target)
2445 {
2446 reply("CSMSG_MERGE_SELF");
2447 return 0;
2448 }
2449
2450 target_user = GetChannelUser(target->channel_info, user->handle_info);
2451 if(!target_user || (target_user->access < UL_OWNER))
2452 {
2453 reply("CSMSG_MERGE_NOT_OWNER");
2454 return 0;
2455 }
2456
2457 /* Merge the channel structures and associated data. */
2458 merge_channel(channel->channel_info, target->channel_info);
2459 sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
2460 unregister_channel(channel->channel_info, reason);
2461 reply("CSMSG_MERGE_SUCCESS", target->name);
2462 return 1;
2463 }
2464
2465 static CHANSERV_FUNC(cmd_opchan)
2466 {
2467 struct mod_chanmode change;
2468 if(!IsHelping(user) && !channel->channel_info->may_opchan)
2469 {
2470 reply("CSMSG_ALREADY_OPCHANNED", channel->name);
2471 return 0;
2472 }
2473 channel->channel_info->may_opchan = 0;
2474 mod_chanmode_init(&change);
2475 change.argc = 1;
2476 change.args[0].mode = MODE_CHANOP;
2477 change.args[0].u.member = GetUserMode(channel, chanserv);
2478 mod_chanmode_announce(chanserv, channel, &change);
2479 reply("CSMSG_OPCHAN_DONE", channel->name);
2480 return 1;
2481 }
2482
2483 static CHANSERV_FUNC(cmd_adduser)
2484 {
2485 struct userData *actee;
2486 struct userData *actor;
2487 struct handle_info *handle;
2488 unsigned short access;
2489
2490 REQUIRE_PARAMS(3);
2491
2492 if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
2493 {
2494 reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
2495 return 0;
2496 }
2497
2498 access = user_level_from_name(argv[2], UL_OWNER);
2499 if(!access)
2500 {
2501 reply("CSMSG_INVALID_ACCESS", argv[2]);
2502 return 0;
2503 }
2504
2505 actor = GetChannelUser(channel->channel_info, user->handle_info);
2506 if(actor->access <= access)
2507 {
2508 reply("CSMSG_NO_BUMP_ACCESS");
2509 return 0;
2510 }
2511
2512 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2513 {
2514 // 'kevin must first authenticate with AuthServ.' is sent to user
2515 struct userNode *unode;
2516 unode = GetUserH(argv[1]); /* find user struct by nick */
2517 if(unode)
2518 {
2519 if(find_adduser_pending(channel, unode)) {
2520 reply("CSMSG_ADDUSER_PENDING_ALREADY", channel->name);
2521 }
2522 else {
2523 if(IsInChannel(channel, unode)) {
2524 reply("CSMSG_ADDUSER_PENDING");
2525 add_adduser_pending(channel, unode, access);
2526 send_message_type(1,unode, chanserv, "CSMSG_ADDUSER_PENDING_TARGET", user->nick, channel->name);
2527 }
2528 /* this results in user must auth AND not in chan errors. too confusing..
2529 else {
2530 reply("CSMSG_ADDUSER_PENDING_NOTINCHAN", channel->name);
2531 }
2532 */
2533 }
2534 }
2535 return 0;
2536 }
2537
2538 if((actee = GetTrueChannelAccess(channel->channel_info, handle)))
2539 {
2540 reply("CSMSG_USER_EXISTS", handle->handle, channel->name, user_level_name_from_level(actee->access));
2541 return 0;
2542 }
2543
2544 actee = add_channel_user(channel->channel_info, handle, access, 0, NULL);
2545 scan_user_presence(actee, NULL);
2546 reply("CSMSG_ADDED_USER", handle->handle, channel->name, user_level_name_from_level(access), access);
2547 return 1;
2548 }
2549
2550 static CHANSERV_FUNC(cmd_clvl)
2551 {
2552 struct handle_info *handle;
2553 struct userData *victim;
2554 struct userData *actor;
2555 unsigned short new_access;
2556 int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
2557
2558 REQUIRE_PARAMS(3);
2559
2560 actor = GetChannelUser(channel->channel_info, user->handle_info);
2561
2562 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2563 return 0;
2564
2565 if(handle == user->handle_info && !privileged)
2566 {
2567 reply("CSMSG_NO_SELF_CLVL");
2568 return 0;
2569 }
2570
2571 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2572 {
2573 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2574 return 0;
2575 }
2576
2577 if(actor->access <= victim->access && !privileged)
2578 {
2579 reply("MSG_USER_OUTRANKED", handle->handle);
2580 return 0;
2581 }
2582
2583 new_access = user_level_from_name(argv[2], UL_OWNER);
2584
2585 if(!new_access)
2586 {
2587 reply("CSMSG_INVALID_ACCESS", argv[2]);
2588 return 0;
2589 }
2590
2591 if(new_access >= actor->access && !privileged)
2592 {
2593 reply("CSMSG_NO_BUMP_ACCESS");
2594 return 0;
2595 }
2596
2597 victim->access = new_access;
2598 reply("CSMSG_CHANGED_ACCESS", handle->handle, user_level_name_from_level(new_access), new_access, channel->name);
2599 return 1;
2600 }
2601
2602 static CHANSERV_FUNC(cmd_deluser)
2603 {
2604 struct handle_info *handle;
2605 struct userData *victim;
2606 struct userData *actor;
2607 unsigned short access;
2608 char *chan_name;
2609
2610 REQUIRE_PARAMS(2);
2611
2612 actor = GetChannelUser(channel->channel_info, user->handle_info);
2613
2614 if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
2615 return 0;
2616
2617 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2618 {
2619 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2620 return 0;
2621 }
2622
2623 if(argc > 2)
2624 {
2625 access = user_level_from_name(argv[1], UL_OWNER);
2626 if(!access)
2627 {
2628 reply("CSMSG_INVALID_ACCESS", argv[1]);
2629 return 0;
2630 }
2631 if(access != victim->access)
2632 {
2633 reply("CSMSG_INCORRECT_ACCESS", handle->handle, user_level_name_from_level(victim->access), argv[1]);
2634 return 0;
2635 }
2636 }
2637 else
2638 {
2639 access = victim->access;
2640 }
2641
2642 if((actor->access <= victim->access) && !IsHelping(user))
2643 {
2644 reply("MSG_USER_OUTRANKED", victim->handle->handle);
2645 return 0;
2646 }
2647
2648 chan_name = strdup(channel->name);
2649 del_channel_user(victim, 1);
2650 reply("CSMSG_DELETED_USER", handle->handle, access, chan_name);
2651 free(chan_name);
2652 return 1;
2653 }
2654
2655 static int
2656 cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
2657 {
2658 struct userData *actor, *uData, *next;
2659
2660 actor = GetChannelUser(channel->channel_info, user->handle_info);
2661
2662 if(min_access > max_access)
2663 {
2664 reply("CSMSG_BAD_RANGE", min_access, max_access);
2665 return 0;
2666 }
2667
2668 if((actor->access <= max_access) && !IsHelping(user))
2669 {
2670 reply("CSMSG_NO_ACCESS");
2671 return 0;
2672 }
2673
2674 for(uData = channel->channel_info->users; uData; uData = next)
2675 {
2676 next = uData->next;
2677
2678 if((uData->access >= min_access)
2679 && (uData->access <= max_access)
2680 && match_ircglob(uData->handle->handle, mask))
2681 del_channel_user(uData, 1);
2682 }
2683
2684 reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
2685 return 1;
2686 }
2687
2688 static CHANSERV_FUNC(cmd_mdelowner)
2689 {
2690 return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
2691 }
2692
2693 static CHANSERV_FUNC(cmd_mdelcoowner)
2694 {
2695 return cmd_mdel_user(user, channel, UL_COOWNER, UL_COOWNER, argv[1], cmd);
2696 }
2697
2698 static CHANSERV_FUNC(cmd_mdelmanager)
2699 {
2700 return cmd_mdel_user(user, channel, UL_MANAGER, UL_MANAGER, argv[1], cmd);
2701 }
2702
2703 static CHANSERV_FUNC(cmd_mdelop)
2704 {
2705 return cmd_mdel_user(user, channel, UL_OP, UL_OP, argv[1], cmd);
2706 }
2707
2708 static CHANSERV_FUNC(cmd_mdelpeon)
2709 {
2710 return cmd_mdel_user(user, channel, UL_PEON, UL_PEON, argv[1], cmd);
2711 }
2712
2713 static CHANSERV_FUNC(cmd_mdelhalfop)
2714 {
2715 return cmd_mdel_user(user, channel, UL_HALFOP, UL_HALFOP, argv[1], cmd);
2716 }
2717
2718
2719 /* trim_lamers.. */
2720 static int
2721 cmd_trim_bans(struct userNode *user, struct chanNode *channel, unsigned long duration)
2722 {
2723 struct banData *bData, *next;
2724 char interval[INTERVALLEN];
2725 unsigned int count;
2726 time_t limit;
2727
2728 count = 0;
2729 limit = now - duration;
2730 for(bData = channel->channel_info->bans; bData; bData = next)
2731 {
2732 next = bData->next;
2733
2734 if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
2735 continue;
2736
2737 del_channel_ban(bData);
2738 count++;
2739 }
2740
2741 intervalString(interval, duration, user->handle_info);
2742 send_message(user, chanserv, "CSMSG_TRIMMED_LAMERS", count, channel->name, interval);
2743 return 1;
2744 }
2745
2746 static int
2747 cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration)
2748 {
2749 struct userData *actor, *uData, *next;
2750 char interval[INTERVALLEN];
2751 unsigned int count;
2752 time_t limit;
2753
2754 actor = GetChannelUser(channel->channel_info, user->handle_info);
2755 if(min_access > max_access)
2756 {
2757 send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
2758 return 0;
2759 }
2760
2761 if((actor->access <= max_access) && !IsHelping(user))
2762 {
2763 send_message(user, chanserv, "CSMSG_NO_ACCESS");
2764 return 0;
2765 }
2766
2767 count = 0;
2768 limit = now - duration;
2769 for(uData = channel->channel_info->users; uData; uData = next)
2770 {
2771 next = uData->next;
2772
2773 if((uData->seen > limit) || uData->present)
2774 continue;
2775
2776 if(((uData->access >= min_access) && (uData->access <= max_access))
2777 || (!max_access && (uData->access < actor->access)))
2778 {
2779 del_channel_user(uData, 1);
2780 count++;
2781 }
2782 }
2783
2784 if(!max_access)
2785 {
2786 min_access = 1;
2787 max_access = (actor->access > UL_OWNER) ? UL_OWNER : (actor->access - 1);
2788 }
2789 send_message(user, chanserv, "CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration, user->handle_info));
2790 return 1;
2791 }
2792
2793 static CHANSERV_FUNC(cmd_trim)
2794 {
2795 unsigned long duration;
2796 unsigned short min_level, max_level;
2797
2798 REQUIRE_PARAMS(3);
2799
2800 duration = ParseInterval(argv[2]);
2801 if(duration < 60)
2802 {
2803 reply("CSMSG_CANNOT_TRIM");
2804 return 0;
2805 }
2806
2807 if(!irccasecmp(argv[1], "lamers"))
2808 {
2809 cmd_trim_bans(user, channel, duration); /* trim_lamers.. */
2810 return 1;
2811 }
2812 else if(!irccasecmp(argv[1], "users"))
2813 {
2814 cmd_trim_users(user, channel, 0, 0, duration);
2815 return 1;
2816 }
2817 else if(parse_level_range(&min_level, &max_level, argv[1]))
2818 {
2819 cmd_trim_users(user, channel, min_level, max_level, duration);
2820 return 1;
2821 }
2822 else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
2823 {
2824 cmd_trim_users(user, channel, min_level, min_level, duration);
2825 return 1;
2826 }
2827 else
2828 {
2829 reply("CSMSG_INVALID_TRIM", argv[1]);
2830 return 0;
2831 }
2832 }
2833
2834 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
2835 to the user. cmd_all takes advantage of this. */
2836 static CHANSERV_FUNC(cmd_up)
2837 {
2838 struct mod_chanmode change;
2839 struct userData *uData;
2840 const char *errmsg;
2841
2842 mod_chanmode_init(&change);
2843 change.argc = 1;
2844 change.args[0].u.member = GetUserMode(channel, user);
2845 if(!change.args[0].u.member)
2846 {
2847 if(argc)
2848 reply("MSG_CHANNEL_ABSENT", channel->name);
2849 return 0;
2850 }
2851
2852 uData = GetChannelAccess(channel->channel_info, user->handle_info);
2853 if(!uData)
2854 {
2855 if(argc)
2856 reply("CSMSG_GODMODE_UP", argv[0]);
2857 return 0;
2858 }
2859 else if(uData->access >= UL_OP /*channel->channel_info->lvlOpts[lvlGiveOps]*/)
2860 {
2861 change.args[0].mode = MODE_CHANOP;
2862 errmsg = "CSMSG_ALREADY_OPPED";
2863 }
2864 else if(uData->access >= UL_HALFOP /*channel->channel_info->lvlOpts[lvlGiveHalfOps]*/)
2865 {
2866 change.args[0].mode = MODE_HALFOP;
2867 errmsg = "CSMSG_ALREADY_HALFOPPED";
2868 }
2869 else if(uData->access >= UL_PEON && (channel->channel_info->chOpts[chVoice] == 'p' || channel->channel_info->chOpts[chVoice] == 'a'))
2870 {
2871 change.args[0].mode = MODE_VOICE;
2872 errmsg = "CSMSG_ALREADY_VOICED";
2873 }
2874 else
2875 {
2876 if(argc)
2877 reply("CSMSG_NO_ACCESS");
2878 return 0;
2879 }
2880 change.args[0].mode &= ~change.args[0].u.member->modes;
2881 if(!change.args[0].mode)
2882 {
2883 if(argc)
2884 reply(errmsg, channel->name);
2885 return 0;
2886 }
2887 modcmd_chanmode_announce(&change);
2888 return 1;
2889 }
2890
2891 static CHANSERV_FUNC(cmd_down)
2892 {
2893 struct mod_chanmode change;
2894
2895 mod_chanmode_init(&change);
2896 change.argc = 1;
2897 change.args[0].u.member = GetUserMode(channel, user);
2898 if(!change.args[0].u.member)
2899 {
2900 if(argc)
2901 reply("MSG_CHANNEL_ABSENT", channel->name);
2902 return 0;
2903 }
2904
2905 if(!change.args[0].u.member->modes)
2906 {
2907 if(argc)
2908 reply("CSMSG_ALREADY_DOWN", channel->name);
2909 return 0;
2910 }
2911
2912 change.args[0].mode = MODE_REMOVE | change.args[0].u.member->modes;
2913 modcmd_chanmode_announce(&change);
2914 return 1;
2915 }
2916
2917 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)
2918 {
2919 struct userData *cList;
2920
2921 for(cList = user->handle_info->channels; cList; cList = cList->u_next)
2922 {
2923 if(IsSuspended(cList->channel)
2924 || IsUserSuspended(cList)
2925 || !GetUserMode(cList->channel->channel, user))
2926 continue;
2927
2928 mcmd(user, cList->channel->channel, 0, NULL, cmd);
2929 }
2930
2931 return 1;
2932 }
2933
2934 static CHANSERV_FUNC(cmd_upall)
2935 {
2936 return cmd_all(CSFUNC_ARGS, cmd_up);
2937 }
2938
2939 static CHANSERV_FUNC(cmd_downall)
2940 {
2941 return cmd_all(CSFUNC_ARGS, cmd_down);
2942 }
2943
2944 typedef int validate_func_t(struct userNode *user, struct chanNode *channel, struct userNode *victim);
2945 typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
2946
2947 static int
2948 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)
2949 {
2950 unsigned int ii, valid;
2951 struct userNode *victim;
2952 struct mod_chanmode *change;
2953
2954 change = mod_chanmode_alloc(argc - 1);
2955
2956 for(ii=valid=0; ++ii < argc; )
2957 {
2958 if(!(victim = GetUserH(argv[ii])))
2959 continue;
2960 change->args[valid].mode = mode;
2961 change->args[valid].u.member = GetUserMode(channel, victim);
2962 if(!change->args[valid].u.member)
2963 continue;
2964 if(validate && !validate(user, channel, victim))
2965 continue;
2966 valid++;
2967 }
2968
2969 change->argc = valid;
2970 if(valid < (argc-1))
2971 reply("CSMSG_PROCESS_FAILED");
2972 if(valid)
2973 {
2974 modcmd_chanmode_announce(change);
2975 reply(action, channel->name);
2976 }
2977 mod_chanmode_free(change);
2978 return 1;
2979 }
2980
2981 static CHANSERV_FUNC(cmd_op)
2982 {
2983 return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
2984 }
2985
2986 static CHANSERV_FUNC(cmd_hop)
2987 {
2988 return modify_users(CSFUNC_ARGS, validate_halfop, MODE_HALFOP, "CSMSG_HALFOPPED_USERS");
2989 }
2990
2991 static CHANSERV_FUNC(cmd_deop)
2992 {
2993 return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
2994 }
2995
2996 static CHANSERV_FUNC(cmd_dehop)
2997 {
2998 return modify_users(CSFUNC_ARGS, validate_dehop, MODE_REMOVE|MODE_HALFOP, "CSMSG_DEHALFOPPED_USERS");
2999 }
3000
3001 static CHANSERV_FUNC(cmd_voice)
3002 {
3003 return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
3004 }
3005
3006 static CHANSERV_FUNC(cmd_devoice)
3007 {
3008 return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
3009 }
3010
3011 static int
3012 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, int *victimCount, struct modeNode **victims)
3013 {
3014 unsigned int ii;
3015
3016 if(victimCount)
3017 *victimCount = 0;
3018 for(ii=0; ii<channel->members.used; ii++)
3019 {
3020 struct modeNode *mn = channel->members.list[ii];
3021
3022 if(IsService(mn->user))
3023 continue;
3024
3025 if(!user_matches_glob(mn->user, ban, 1))
3026 continue;
3027
3028 if(protect_user(mn->user, user, channel->channel_info))
3029 return 1;
3030
3031 if(victims)
3032 victims[(*victimCount)++] = mn;
3033 }
3034 return 0;
3035 }
3036
3037 static int
3038 eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3039 {
3040 struct userNode *victim;
3041 struct modeNode **victims;
3042 unsigned int offset, n, victimCount, duration = 0;
3043 char *reason = "Bye.", *ban, *name;
3044 char interval[INTERVALLEN];
3045
3046 offset = (action & ACTION_ADD_TIMED_LAMER) ? 3 : 2;
3047 REQUIRE_PARAMS(offset);
3048 if(argc > offset)
3049 {
3050 reason = unsplit_string(argv + offset, argc - offset, NULL);
3051 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
3052 {
3053 /* Truncate the reason to a length of TOPICLEN, as
3054 the ircd does; however, leave room for an ellipsis
3055 and the kicker's nick. */
3056 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
3057 }
3058 }
3059
3060 if((victim = GetUserH(argv[1])))
3061 {
3062 victims = alloca(sizeof(victims[0]));
3063 victims[0] = GetUserMode(channel, victim);
3064 /* XXX: The comparison with ACTION_KICK is just because all
3065 * other actions can work on users outside the channel, and we
3066 * want to allow those (e.g. unbans) in that case. If we add
3067 * some other ejection action for in-channel users, change
3068 * this too. */
3069 victimCount = victims[0] ? 1 : 0;
3070
3071 if(IsService(victim))
3072 {
3073 if(cmd)
3074 reply("MSG_SERVICE_IMMUNE", victim->nick);
3075 return 0;
3076 }
3077
3078 if((action == ACTION_KICK) && !victimCount)
3079 {
3080 if(cmd)
3081 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
3082 return 0;
3083 }
3084
3085 if(protect_user(victim, user, channel->channel_info))
3086 {
3087 // This translates to send_message(user, cmd->parent->bot, ...)
3088 // if user is x3 (ctcp action) cmd is null and segfault.
3089 if(cmd)
3090 reply("CSMSG_USER_PROTECTED", victim->nick);
3091 return 0;
3092 }
3093
3094 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
3095 name = victim->nick;
3096 }
3097 else
3098 {
3099 if(!is_ircmask(argv[1]))
3100 {
3101 if(cmd)
3102 reply("MSG_NICK_UNKNOWN", argv[1]);
3103 return 0;
3104 }
3105
3106 victims = alloca(sizeof(victims[0]) * channel->members.used);
3107
3108 if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
3109 {
3110 if(cmd)
3111 reply("CSMSG_MASK_PROTECTED", argv[1]);
3112 return 0;
3113 }
3114 /* We dont actually want a victem count if were banning a mask manually, IMO -Rubin*/
3115 if(cmd)
3116 victimCount = 0; /* Dont deop etc ppl who match this */
3117
3118 #ifdef entropy_lameness
3119 if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
3120 {
3121 if(cmd)
3122 reply("CSMSG_LAME_MASK", argv[1]);
3123 return 0;
3124 }
3125 #endif
3126
3127 if((action == ACTION_KICK) && (victimCount == 0))
3128 {
3129 if(cmd)
3130 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
3131 return 0;
3132 }
3133
3134 name = ban = strdup(argv[1]);
3135 }
3136
3137 /* Truncate the ban in place if necessary; we must ensure
3138 that 'ban' is a valid ban mask before sanitizing it. */
3139 sanitize_ircmask(ban);
3140
3141 if(action & ACTION_ADD_LAMER)
3142 {
3143 struct banData *bData, *next;
3144
3145 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans) /* ..lamers.. */
3146 {
3147 if(cmd)
3148 reply("CSMSG_MAXIMUM_LAMERS", chanserv_conf.max_chan_bans); /* ..lamers.. */
3149 free(ban);
3150 return 0;
3151 }
3152
3153 if(action & ACTION_ADD_TIMED_LAMER)
3154 {
3155 duration = ParseInterval(argv[2]);
3156
3157 if(duration < 15)
3158 {
3159 if(cmd)
3160 reply("CSMSG_DURATION_TOO_LOW");
3161 free(ban);
3162 return 0;
3163 }
3164 else if(duration > (86400 * 365 * 2))
3165 {
3166 if(cmd)
3167 reply("CSMSG_DURATION_TOO_HIGH");
3168 free(ban);
3169 return 0;
3170 }
3171 }
3172
3173 /* lamers... */
3174 for(bData = channel->channel_info->bans; bData; bData = next)
3175 {
3176 if(match_ircglobs(bData->mask, ban))
3177 {
3178 int exact = !irccasecmp(bData->mask, ban);
3179
3180 /* The ban is redundant; there is already a ban
3181 with the same effect in place. */
3182 if(exact)
3183 {
3184 if(bData->reason)
3185 free(bData->reason);
3186 bData->reason = strdup(reason);
3187 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
3188 if(cmd)
3189 reply("CSMSG_REASON_CHANGE", ban);
3190 if(!bData->expires)
3191 goto post_add_ban;
3192 }
3193 if(exact && bData->expires)
3194 {
3195 int reset = 0;
3196
3197 /* If the ban matches an existing one exactly,
3198 extend the expiration time if the provided
3199 duration is longer. */
3200 if(duration && ((time_t)(now + duration) > bData->expires))
3201 {
3202 bData->expires = now + duration;
3203 reset = 1;
3204 }
3205 else if(!duration)
3206 {
3207 bData->expires = 0;
3208 reset = 1;
3209 }
3210
3211 if(reset)
3212 {
3213 /* Delete the expiration timeq entry and
3214 requeue if necessary. */
3215 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
3216
3217 if(bData->expires)
3218 timeq_add(bData->expires, expire_ban, bData);
3219
3220 if(!cmd)
3221 {
3222 /* automated kickban, dont reply */
3223 }
3224 else if(duration)
3225 reply("CSMSG_LAMER_EXTENDED", ban, intervalString(interval, duration, user->handle_info));
3226 else
3227 reply("CSMSG_LAMER_ADDED", name, channel->name);
3228
3229 goto post_add_ban;
3230 }
3231 }
3232 if(cmd)
3233 reply("CSMSG_REDUNDANT_LAMER", name, channel->name);
3234
3235 free(ban);
3236 return 0;
3237 }
3238
3239 next = bData->next;
3240 if(match_ircglobs(ban, bData->mask))
3241 {
3242 /* The ban we are adding makes previously existing
3243 bans redundant; silently remove them. */
3244 del_channel_ban(bData);
3245 }
3246 }
3247
3248 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);
3249 free(ban);
3250 name = ban = strdup(bData->mask);
3251 }
3252 else if(ban)
3253 {
3254 /* WHAT DOES THIS DO?? -Rubin */
3255 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
3256 {
3257 extern const char *hidden_host_suffix;
3258 const char *old_name = chanserv_conf.old_ban_names->list[n];
3259 char *new_mask;
3260 unsigned int l1, l2;
3261
3262 l1 = strlen(ban);
3263 l2 = strlen(old_name);
3264 if(l2+2 > l1)
3265 continue;
3266 if(irccasecmp(ban + l1 - l2, old_name))
3267 continue;
3268 new_mask = malloc(MAXLEN);
3269 sprintf(new_mask, "%.*s%s", (int)(l1-l2), ban, hidden_host_suffix);
3270 free(ban);
3271 name = ban = new_mask;
3272 }
3273 }
3274
3275 post_add_ban:
3276 if(action & ACTION_BAN)
3277 {
3278 unsigned int exists;
3279 struct mod_chanmode *change;
3280
3281 if(channel->banlist.used >= MAXBANS)
3282 {
3283 if(cmd)
3284 reply("CSMSG_BANLIST_FULL", channel->name);
3285 free(ban);
3286 return 0;
3287 }
3288
3289 exists = ChannelBanExists(channel, ban);
3290 change = mod_chanmode_alloc(victimCount + 1);
3291 for(n = 0; n < victimCount; ++n)
3292 {
3293 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_HALFOP|MODE_VOICE;
3294 change->args[n].u.member = victims[n];
3295 }
3296 if(!exists)
3297 {
3298 change->args[n].mode = MODE_BAN;
3299 change->args[n++].u.hostmask = ban;
3300 }
3301 change->argc = n;
3302 if(cmd)
3303 modcmd_chanmode_announce(change);
3304 else
3305 mod_chanmode_announce(chanserv, channel, change);
3306 mod_chanmode_free(change);
3307
3308 if(exists && (action == ACTION_BAN))
3309 {
3310 if(cmd)
3311 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3312 free(ban);
3313 return 0;
3314 }
3315 }
3316
3317 if(action & ACTION_KICK)
3318 {
3319 char kick_reason[MAXLEN];
3320 sprintf(kick_reason, "(%s) %s", user->nick, reason);
3321
3322 for(n = 0; n < victimCount; n++)
3323 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
3324 }
3325
3326 if(!cmd)
3327 {
3328 /* No response, since it was automated. */
3329 }
3330 else if(action & ACTION_ADD_LAMER)
3331 {
3332 if(duration)
3333 reply("CSMSG_TIMED_LAMER_ADDED", name, channel->name, intervalString(interval, duration, user->handle_info));
3334 else
3335 reply("CSMSG_LAMER_ADDED", name, channel->name);
3336 }
3337 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
3338 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
3339 else if(action & ACTION_BAN)
3340 reply("CSMSG_BAN_DONE", name, channel->name);
3341 else if(action & ACTION_KICK && victimCount)
3342 reply("CSMSG_KICK_DONE", name, channel->name);
3343
3344 free(ban);
3345 return 1;
3346 }
3347
3348 static CHANSERV_FUNC(cmd_kickban)
3349 {
3350 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
3351 }
3352
3353 static CHANSERV_FUNC(cmd_kick)
3354 {
3355 return eject_user(CSFUNC_ARGS, ACTION_KICK);
3356 }
3357
3358 static CHANSERV_FUNC(cmd_ban)
3359 {
3360 return eject_user(CSFUNC_ARGS, ACTION_BAN);
3361 }
3362
3363 static CHANSERV_FUNC(cmd_addlamer)
3364 {
3365 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_LAMER);
3366 }
3367
3368 static CHANSERV_FUNC(cmd_addtimedlamer)
3369 {
3370 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_LAMER | ACTION_ADD_TIMED_LAMER);
3371 }
3372
3373 static struct mod_chanmode *
3374 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
3375 {
3376 struct mod_chanmode *change;
3377 unsigned char *match;
3378 unsigned int ii, count;
3379
3380 match = alloca(bans->used);
3381 if(actee)
3382 {
3383 for(ii = count = 0; ii < bans->used; ++ii)
3384 {
3385 match[ii] = user_matches_glob(actee, bans->list[ii]->ban, 1);
3386 if(match[ii])
3387 count++;
3388 }
3389 }
3390 else
3391 {
3392 for(ii = count = 0; ii < bans->used; ++ii)
3393 {
3394 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
3395 if(match[ii])
3396 count++;
3397 }
3398 }
3399 if(!count)
3400 return NULL;
3401 change = mod_chanmode_alloc(count);
3402 for(ii = count = 0; ii < bans->used; ++ii)
3403 {
3404 if(!match[ii])
3405 continue;
3406 change->args[count].mode = MODE_REMOVE | MODE_BAN;
3407 change->args[count++].u.hostmask = strdup(bans->list[ii]->ban);
3408 }
3409 assert(count == change->argc);
3410 return change;
3411 }
3412
3413 void expire_bans(UNUSED_ARG(void* data)) /* Real bans, not lamers */
3414 {
3415 unsigned int jj, ii, count;
3416 struct banNode *bn;
3417 struct chanData *channel;
3418 time_t bantimeout;
3419 struct mod_chanmode *change;
3420
3421 log_module(CS_LOG, LOG_DEBUG, "Checking for expired bans");
3422 /* Walk through every channel */
3423 for(channel = channelList; channel; channel = channel->next) {
3424 switch(channel->chOpts[chBanTimeout])
3425 {
3426 default: case '0': continue; /* Dont remove bans in this chan */
3427 case '1': bantimeout = now - (10 * 60); break; /* 10 minutes */
3428 case '2': bantimeout = now - (2 * 60 * 60); break; /* 2 hours */
3429 case '3': bantimeout = now - (4 * 60 * 60); break; /* 4 hours */
3430 case '4': bantimeout = now - (24 * 60 * 60); break; /* 24 hours */
3431 case '5': bantimeout = now - (7 * 24 * 60 * 60); break; /* 1 week */
3432 }
3433 count = 0;
3434 /* First find out how many bans were going to unset */
3435 for (jj=0; jj < channel->channel->banlist.used; ++jj) {
3436 if(channel->channel->banlist.list[jj]->set < bantimeout)
3437 count++;
3438 }
3439 if(count > 0) {
3440 /* At least one ban, so setup a removal */
3441 change = mod_chanmode_alloc(count);
3442 ii = 0;
3443 /* Walk over every ban in this channel.. */
3444 for (jj=0; jj < channel->channel->banlist.used; ++jj) {
3445 bn = channel->channel->banlist.list[jj];
3446 if (bn->set < bantimeout) {
3447 log_module(CS_LOG, LOG_DEBUG, "Removing ban %s from %s", bn->ban, channel->channel->name);
3448
3449 /* Add this ban to the mode change */
3450 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3451 change->args[ii].u.hostmask = strdup(bn->ban);
3452 ii++;
3453 /* Pull this ban out of the list */
3454 banList_remove(&(channel->channel->banlist), bn);
3455 jj--;
3456 free(bn);
3457 }
3458 }
3459 /* Send the modes to IRC */
3460 mod_chanmode_announce(chanserv, channel->channel, change);
3461
3462 /* free memory from strdup above */
3463 for(ii = 0; ii < count; ++ii)
3464 free((char*)change->args[ii].u.hostmask);
3465
3466 mod_chanmode_free(change);
3467 }
3468 }
3469 /* Set this function to run again */
3470 if(chanserv_conf.ban_timeout_frequency)
3471 timeq_add(now + chanserv_conf.ban_timeout_frequency, expire_bans, NULL);
3472 }
3473
3474
3475 static int
3476 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3477 {
3478 struct userNode *actee;
3479 char *mask = NULL;
3480 int acted = 0;
3481
3482 REQUIRE_PARAMS(2);
3483
3484 /* may want to allow a comma delimited list of users... */
3485 if(!(actee = GetUserH(argv[1])))
3486 {
3487 if(!is_ircmask(argv[1]))
3488 {
3489 reply("MSG_NICK_UNKNOWN", argv[1]);
3490 return 0;
3491 }
3492
3493 mask = strdup(argv[1]);
3494 }
3495
3496 /* We don't sanitize the mask here because ircu
3497 doesn't do it. */
3498 if(action & ACTION_UNBAN)
3499 {
3500 struct mod_chanmode *change;
3501 change = find_matching_bans(&channel->banlist, actee, mask);
3502 if(change)
3503 {
3504 unsigned int ii;
3505
3506 modcmd_chanmode_announce(change);
3507 for(ii = 0; ii < change->argc; ++ii)
3508 free((char*)change->args[ii].u.hostmask);
3509 mod_chanmode_free(change);
3510 acted = 1;
3511 }
3512 }
3513
3514 if(action & ACTION_DEL_LAMER)
3515 {
3516 struct banData *ban, *next;
3517
3518 ban = channel->channel_info->bans; /* lamers */
3519 while(ban)
3520 {
3521 if(actee)
3522 for( ; ban && !user_matches_glob(actee, ban->mask, 1);
3523 ban = ban->next);
3524 else
3525 for( ; ban && !match_ircglobs(mask, ban->mask);
3526 ban = ban->next);
3527 if(!ban)
3528 break;
3529 next = ban->next;
3530 del_channel_ban(ban);
3531 ban = next;
3532 acted = 1;
3533 }
3534 }
3535
3536 if(!acted)
3537 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3538 else
3539 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3540 if(mask)
3541 free(mask);
3542 return 1;
3543 }
3544
3545 static CHANSERV_FUNC(cmd_unban)
3546 {
3547 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3548 }
3549
3550 static CHANSERV_FUNC(cmd_dellamer)
3551 {
3552 /* it doesn't necessarily have to remove the channel ban - may want
3553 to make that an option. */
3554 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_LAMER);
3555 }
3556
3557 static CHANSERV_FUNC(cmd_unbanme)
3558 {
3559 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3560 long flags = ACTION_UNBAN;
3561
3562 /* remove permanent bans if the user has the proper access. */
3563 if(uData->access >= UL_MANAGER)
3564 flags |= ACTION_DEL_LAMER;
3565
3566 argv[1] = user->nick;
3567 return unban_user(user, channel, 2, argv, cmd, flags);
3568 }
3569
3570 static CHANSERV_FUNC(cmd_unbanall)
3571 {
3572 struct mod_chanmode *change;
3573 unsigned int ii;
3574
3575 if(!channel->banlist.used)
3576 {
3577 reply("CSMSG_NO_BANS", channel->name);
3578 return 0;
3579 }
3580
3581 change = mod_chanmode_alloc(channel->banlist.used);
3582 for(ii=0; ii<channel->banlist.used; ii++)
3583 {
3584 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3585 change->args[ii].u.hostmask = strdup(channel->banlist.list[ii]->ban);
3586 }
3587 modcmd_chanmode_announce(change);
3588 for(ii = 0; ii < change->argc; ++ii)
3589 free((char*)change->args[ii].u.hostmask);
3590 mod_chanmode_free(change);
3591 reply("CSMSG_BANS_REMOVED", channel->name);
3592 return 1;
3593 }
3594
3595 static CHANSERV_FUNC(cmd_open)
3596 {
3597 struct mod_chanmode *change;
3598 unsigned int ii;
3599
3600 change = find_matching_bans(&channel->banlist, user, NULL);
3601 if(!change)
3602 change = mod_chanmode_alloc(0);
3603 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3604 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3605 && channel->channel_info->modes.modes_set)
3606 change->modes_clear &= ~channel->channel_info->modes.modes_set;
3607 modcmd_chanmode_announce(change);
3608 reply("CSMSG_CHANNEL_OPENED", channel->name);
3609 for(ii = 0; ii < change->argc; ++ii)
3610 free((char*)change->args[ii].u.hostmask);
3611 mod_chanmode_free(change);
3612 return 1;
3613 }
3614
3615 static CHANSERV_FUNC(cmd_myaccess)
3616 {
3617 static struct string_buffer sbuf;
3618 struct handle_info *target_handle;
3619 struct userData *uData;
3620
3621 if(argc < 2)
3622 target_handle = user->handle_info;
3623 else if(!IsHelping(user))
3624 {
3625 reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
3626 return 0;
3627 }
3628 else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3629 return 0;
3630
3631 if(!target_handle->channels)
3632 {
3633 reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
3634 return 1;
3635 }
3636
3637 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3638 for(uData = target_handle->channels; uData; uData = uData->u_next)
3639 {
3640 struct chanData *cData = uData->channel;
3641
3642 if(uData->access > UL_OWNER)
3643 continue;
3644 if(IsProtected(cData)
3645 && (target_handle != user->handle_info)
3646 && !GetTrueChannelAccess(cData, user->handle_info))
3647 continue;
3648 sbuf.used = 0;
3649 string_buffer_append_printf(&sbuf, "[%s (%d", cData->channel->name, uData->access);
3650 if(uData->flags == USER_AUTO_OP)
3651 string_buffer_append(&sbuf, ',');
3652 if(IsUserSuspended(uData))
3653 string_buffer_append(&sbuf, 's');
3654 if(IsUserAutoOp(uData))
3655 {
3656 if(uData->access >= UL_OP /*cData->lvlOpts[lvlGiveOps]*/)
3657 string_buffer_append(&sbuf, 'o');
3658 else if(uData->access >= UL_HALFOP /*cData->lvlOpts[lvlGiveHalfOps]*/)
3659 string_buffer_append(&sbuf, 'h');
3660 else if(uData->access >= UL_PEON /*cData->lvlOpts[lvlGiveVoice]*/)
3661 string_buffer_append(&sbuf, 'v');
3662 }
3663 if(IsUserAutoInvite(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe]))
3664 string_buffer_append(&sbuf, 'i');
3665 if(uData->info)
3666 string_buffer_append_printf(&sbuf, ")] %s", uData->info);
3667 else
3668 string_buffer_append_string(&sbuf, ")]");
3669 string_buffer_append(&sbuf, '\0');
3670 send_message_type(4, user, cmd->parent->bot, "%s", sbuf.list);
3671 }
3672
3673 return 1;
3674 }
3675
3676 static CHANSERV_FUNC(cmd_access)
3677 {
3678 struct userNode *target;
3679 struct handle_info *target_handle;
3680 struct userData *uData;
3681 int helping;
3682 char prefix[MAXLEN];
3683
3684 if(argc < 2)
3685 {
3686 target = user;
3687 target_handle = target->handle_info;
3688 }
3689 else if((target = GetUserH(argv[1])))
3690 {
3691 target_handle = target->handle_info;
3692 }
3693 else if(argv[1][0] == '*')
3694 {
3695 if(!(target_handle = get_handle_info(argv[1]+1)))
3696 {
3697 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3698 return 0;
3699 }
3700 }
3701 else
3702 {
3703 reply("MSG_NICK_UNKNOWN", argv[1]);
3704 return 0;
3705 }
3706
3707 assert(target || target_handle);
3708
3709 if(target == chanserv)
3710 {
3711 reply("CSMSG_IS_CHANSERV");
3712 return 1;
3713 }
3714
3715 if(!target_handle)
3716 {
3717 if(IsOper(target))
3718 {
3719 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
3720 return 0;
3721 }
3722 if(target != user)
3723 {
3724 reply("MSG_USER_AUTHENTICATE", target->nick);
3725 return 0;
3726 }
3727 reply("MSG_AUTHENTICATE");
3728 return 0;
3729 }
3730
3731 if(target)
3732 {
3733 const char *epithet = NULL, *type = NULL;
3734 if(IsOper(target))
3735 {
3736 epithet = chanserv_conf.irc_operator_epithet;
3737 type = "IRCOp";
3738 }
3739 else if(IsNetworkHelper(target))
3740 {
3741 epithet = chanserv_conf.network_helper_epithet;
3742 type = "network helper";
3743 }
3744 else if(IsSupportHelper(target))
3745 {
3746 epithet = chanserv_conf.support_helper_epithet;
3747 type = "support helper";
3748 }
3749 if(epithet)
3750 {
3751 if(target_handle->epithet)
3752 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
3753 else if(epithet)
3754 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
3755 }
3756 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
3757 }
3758 else
3759 {
3760 sprintf(prefix, "%s", target_handle->handle);
3761 }
3762
3763 if(!channel->channel_info)
3764 {
3765 reply("CSMSG_NOT_REGISTERED", channel->name);
3766 return 1;
3767 }
3768
3769 helping = HANDLE_FLAGGED(target_handle, HELPING)
3770 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
3771 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
3772 {
3773 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, user_level_name_from_level(uData->access), uData->access, channel->name);
3774 /* To prevent possible information leaks, only show infolines
3775 * if the requestor is in the channel or it's their own
3776 * handle. */
3777 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
3778 {
3779 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
3780 }
3781 /* Likewise, only say it's suspended if the user has active
3782 * access in that channel or it's their own entry. */
3783 if(IsUserSuspended(uData)
3784 && (GetChannelUser(channel->channel_info, user->handle_info)
3785 || (user->handle_info == uData->handle)))
3786 {
3787 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
3788 }
3789 }
3790 else
3791 {
3792 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
3793 }
3794
3795 return 1;
3796 }
3797
3798 /* This is never used...
3799 static void
3800 zoot_list(struct listData *list)
3801 {
3802 struct userData *uData;
3803 unsigned int start, curr, highest, lowest;
3804 struct helpfile_table tmp_table;
3805 const char **temp, *msg;
3806
3807 if(list->table.length == 1)
3808 {
3809 if(list->search)
3810 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);
3811 else
3812 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));
3813 msg = user_find_message(list->user, "MSG_NONE");
3814 send_message_type(4, list->user, list->bot, " %s", msg);
3815 }
3816 tmp_table.width = list->table.width;
3817 tmp_table.flags = list->table.flags;
3818 list->table.contents[0][0] = " ";
3819 highest = list->highest;
3820 if(list->lowest != 0)
3821 lowest = list->lowest;
3822 else if(highest < 100)
3823 lowest = 1;
3824 else
3825 lowest = highest - 100;
3826 for(start = curr = 1; curr < list->table.length; )
3827 {
3828 uData = list->users[curr-1];
3829 list->table.contents[curr++][0] = " ";
3830 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
3831 {
3832 if(list->search)
3833 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);
3834 else
3835 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));
3836 temp = list->table.contents[--start];
3837 list->table.contents[start] = list->table.contents[0];
3838 tmp_table.contents = list->table.contents + start;
3839 tmp_table.length = curr - start;
3840 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
3841 list->table.contents[start] = temp;
3842 start = curr;
3843 highest = lowest - 1;
3844 lowest = (highest < 100) ? 0 : (highest - 99);
3845 }
3846 }
3847 }
3848 */
3849
3850 static void
3851 def_list(struct listData *list)
3852 {
3853 const char *msg;
3854 if(list->search)
3855 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);
3856 else
3857 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));
3858 if(list->table.length == 1)
3859 {
3860 msg = user_find_message(list->user, "MSG_NONE");
3861 send_message_type(4, list->user, list->bot, " %s", msg);
3862 }
3863 else
3864 table_send(list->bot, list->user->nick, 0, NULL, list->table);
3865 }
3866
3867 static int
3868 userData_access_comp(const void *arg_a, const void *arg_b)
3869 {
3870 const struct userData *a = *(struct userData**)arg_a;
3871 const struct userData *b = *(struct userData**)arg_b;
3872 int res;
3873 if(a->access != b->access)
3874 res = b->access - a->access;
3875 else
3876 res = irccasecmp(a->handle->handle, b->handle->handle);
3877 return res;
3878 }
3879
3880 static int
3881 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
3882 {
3883 void (*send_list)(struct listData *);
3884 struct userData *uData;
3885 struct listData lData;
3886 unsigned int matches;
3887 const char **ary;
3888
3889 lData.user = user;
3890 lData.bot = cmd->parent->bot;
3891 lData.channel = channel;
3892 lData.lowest = lowest;
3893 lData.highest = highest;
3894 lData.search = (argc > 1) ? argv[1] : NULL;
3895 send_list = def_list;
3896 /* What does the following line do exactly?? */
3897 /*(void)zoot_list; ** since it doesn't show user levels */
3898
3899 /* this does nothing!! -rubin
3900 if(user->handle_info)
3901 {
3902 switch(user->handle_info->userlist_style)
3903 {
3904 case HI_STYLE_DEF: send_list = def_list; break;
3905 case HI_STYLE_ZOOT: send_list = def_list; break;
3906 }
3907 }
3908 */
3909
3910 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
3911 matches = 0;
3912 for(uData = channel->channel_info->users; uData; uData = uData->next)
3913 {
3914 if((uData->access < lowest)
3915 || (uData->access > highest)
3916 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
3917 continue;
3918 lData.users[matches++] = uData;
3919 }
3920 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
3921
3922 lData.table.length = matches+1;
3923 lData.table.width = 5;
3924 lData.table.flags = TABLE_NO_FREE;
3925 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
3926 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3927 lData.table.contents[0] = ary;
3928 ary[0] = "Access";
3929 ary[1] = "Level";
3930 ary[2] = "Account";
3931 ary[3] = "Last Seen";
3932 ary[4] = "Status";
3933 for(matches = 1; matches < lData.table.length; ++matches)
3934 {
3935 struct userData *uData = lData.users[matches-1];
3936 char seen[INTERVALLEN];
3937
3938 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3939 lData.table.contents[matches] = ary;
3940 /* ary[0] = strtab(uData->access);*/
3941 ary[0] = user_level_name_from_level(uData->access);
3942 ary[1] = strtab(uData->access);
3943 ary[2] = uData->handle->handle;
3944 if(uData->present)
3945 ary[3] = "Here";
3946 else if(!uData->seen)
3947 ary[3] = "Never";
3948 else
3949 ary[3] = intervalString(seen, now - uData->seen, user->handle_info);
3950 ary[3] = strdup(ary[3]);
3951 if(IsUserSuspended(uData))
3952 ary[4] = "Suspended";
3953 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
3954 ary[4] = "Vacation";
3955 else
3956 ary[4] = "Normal";
3957 }
3958 send_list(&lData);
3959 for(matches = 1; matches < lData.table.length; ++matches)
3960 {
3961 free((char*)lData.table.contents[matches][3]);
3962 free(lData.table.contents[matches]);
3963 }
3964 free(lData.table.contents[0]);
3965 free(lData.table.contents);
3966 return 1;
3967 }
3968
3969 /* Remove this now that debugging is over? or improve it for
3970 * users? Would it be better tied into USERS somehow? -Rubin */
3971 static CHANSERV_FUNC(cmd_pending)
3972 {
3973 struct adduserPending *ap;
3974 reply("CSMSG_ADDUSER_PENDING_HEADER");
3975 reply("CSMSG_BAR");
3976 for(ap = adduser_pendings;ap;ap = ap->next)
3977 reply("CSMSG_ADDUSER_PENDING_LIST", ap->channel->name, ap->user->nick);
3978 reply("CSMSG_ADDUSER_PENDING_FOOTER");
3979 return 1;
3980 }
3981
3982 static CHANSERV_FUNC(cmd_users)
3983 {
3984 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
3985 }
3986
3987 static CHANSERV_FUNC(cmd_wlist)
3988 {
3989 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
3990 }
3991
3992 static CHANSERV_FUNC(cmd_clist)
3993 {
3994 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
3995 }
3996
3997 static CHANSERV_FUNC(cmd_mlist)
3998 {
3999 return cmd_list_users(CSFUNC_ARGS, UL_MANAGER, UL_COOWNER-1);
4000 }
4001
4002 static CHANSERV_FUNC(cmd_olist)
4003 {
4004 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MANAGER-1);
4005 }
4006
4007 static CHANSERV_FUNC(cmd_hlist)
4008 {
4009 return cmd_list_users(CSFUNC_ARGS, UL_HALFOP, UL_OP-1);
4010 }
4011
4012 static CHANSERV_FUNC(cmd_plist)
4013 {
4014 return cmd_list_users(CSFUNC_ARGS, 1, UL_HALFOP-1);
4015 }
4016
4017 static CHANSERV_FUNC(cmd_lamers)
4018 {
4019 struct helpfile_table tbl;
4020 unsigned int matches = 0, timed = 0, ii;
4021 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
4022 const char *msg_never, *triggered, *expires;
4023 struct banData *ban, **bans; /* lamers */
4024
4025 if(argc > 1)
4026 search = argv[1];
4027 else
4028 search = NULL;
4029
4030 reply("CSMSG_LAMERS_HEADER", channel->name);
4031 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *)); /* lamers */
4032
4033 /* lamers */
4034 for(ban = channel->channel_info->bans; ban; ban = ban->next)
4035 {
4036 if(search && !match_ircglobs(search, ban->mask))
4037 continue;
4038 bans[matches++] = ban;
4039 if(ban->expires)
4040 timed = 1;
4041 }
4042
4043 tbl.length = matches + 1;
4044 tbl.width = 4 + timed;
4045 tbl.flags = 0;
4046 tbl.flags = TABLE_NO_FREE;
4047 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
4048 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4049 tbl.contents[0][0] = "Mask";
4050 tbl.contents[0][1] = "Set By";
4051 tbl.contents[0][2] = "Triggered";
4052 if(timed)
4053 {
4054 tbl.contents[0][3] = "Expires";
4055 tbl.contents[0][4] = "Reason";
4056 }
4057 else
4058 tbl.contents[0][3] = "Reason";
4059 if(!matches)
4060 {
4061 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4062 reply("MSG_NONE");
4063 free(tbl.contents[0]);
4064 free(tbl.contents);
4065 return 0;
4066 }
4067
4068 msg_never = user_find_message(user, "MSG_NEVER");
4069 for(ii = 0; ii < matches; )
4070 {
4071 ban = bans[ii];
4072
4073 if(!timed)
4074 expires = "";
4075 else if(ban->expires)
4076 expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
4077 else
4078 expires = msg_never;
4079
4080 if(ban->triggered)
4081 triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
4082 else
4083 triggered = msg_never;
4084
4085 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4086 tbl.contents[ii][0] = ban->mask;
4087 tbl.contents[ii][1] = ban->owner;
4088 tbl.contents[ii][2] = strdup(triggered);
4089 if(timed)
4090 {
4091 tbl.contents[ii][3] = strdup(expires);
4092 tbl.contents[ii][4] = ban->reason;
4093 }
4094 else
4095 tbl.contents[ii][3] = ban->reason;
4096 }
4097 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4098 /* reply("MSG_MATCH_COUNT", matches); */
4099 for(ii = 1; ii < tbl.length; ++ii)
4100 {
4101 free((char*)tbl.contents[ii][2]);
4102 if(timed)
4103 free((char*)tbl.contents[ii][3]);
4104 free(tbl.contents[ii]);
4105 }
4106 free(tbl.contents[0]);
4107 free(tbl.contents);
4108 return 1;
4109 }
4110
4111 /* bad_topic
4112 *
4113 * return + if the user does NOT have the right to set the topic, and
4114 * the topic is changed.
4115 */
4116 static int
4117 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
4118 {
4119 struct chanData *cData = channel->channel_info;
4120 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
4121 return 0;
4122 else if(cData->topic)
4123 return irccasecmp(new_topic, cData->topic);
4124 else
4125 return 0;
4126 }
4127
4128 /* conform_topic
4129 *
4130 * Makes a givin topic fit into a givin topic mask and returns
4131 * the results.
4132 *
4133 * topic_mask - the mask to conform to
4134 * topic - the topic to make conform
4135 * new_topic - the pre-allocated char* to put the new topic into
4136 *
4137 * modifies: new_topic
4138 */
4139 void
4140 conform_topic(char* topic_mask, char* topic, char *new_topic)
4141 {
4142 //char *topic_mask = cData->topic_mask;
4143 char tchar;
4144 int pos=0, starpos=-1, dpos=0, len;
4145
4146 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
4147 {
4148 switch(tchar)
4149 {
4150 case '*':
4151 if(starpos != -1)
4152 {
4153 strcpy(new_topic, "");
4154 return;
4155 }
4156 len = strlen(topic);
4157 if((dpos + len) > TOPICLEN)
4158 len = TOPICLEN + 1 - dpos;
4159 memcpy(new_topic+dpos, topic, len);
4160 dpos += len;
4161 starpos = pos;
4162 break;
4163 case '\\': tchar = topic_mask[pos++]; /* and fall through */
4164 default: new_topic[dpos++] = tchar; break;
4165 }
4166 }
4167 if((dpos > TOPICLEN) || tchar)
4168 {
4169 strcpy(new_topic, "");
4170 return;
4171 }
4172 new_topic[dpos] = 0;
4173 return;
4174 }
4175
4176 static CHANSERV_FUNC(cmd_topic)
4177 {
4178 struct chanData *cData;
4179 char *topic;
4180
4181 cData = channel->channel_info;
4182 if(argc < 2)
4183 {
4184 if(cData->topic)
4185 {
4186 SetChannelTopic(channel, chanserv, cData->topic, 1);
4187 reply("CSMSG_TOPIC_SET", cData->topic);
4188 return 1;
4189 }
4190
4191 reply("CSMSG_NO_TOPIC", channel->name);
4192 return 0;
4193 }
4194
4195 topic = unsplit_string(argv + 1, argc - 1, NULL);
4196 /* If they say "!topic *", use an empty topic. */
4197 if((topic[0] == '*') && (topic[1] == 0))
4198 topic[0] = 0;
4199
4200 if(bad_topic(channel, user, topic))
4201 {
4202 reply("CSMSG_TOPIC_LOCKED", channel->name);
4203 return 0;
4204 }
4205 else
4206 {
4207 /* If there is a topicmask set, and the new topic doesnt match, make it */
4208 if(cData->topic_mask && !match_ircglob(topic, cData->topic_mask))
4209 {
4210 char *topic_mask = cData->topic_mask;
4211 char new_topic[TOPICLEN+1];
4212
4213 /* make a new topic fitting mask */
4214 conform_topic(topic_mask, topic, new_topic);
4215 if(!*new_topic)
4216 {
4217 /* Topic couldnt fit into mask, was too long */
4218 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
4219 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
4220 return 0;
4221 }
4222 SetChannelTopic(channel, chanserv, new_topic, 1);
4223 }
4224 else /* No mask set, just set the topic */
4225 SetChannelTopic(channel, chanserv, topic, 1);
4226 }
4227
4228 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
4229 {
4230 /* Grab the topic and save it as the default topic. */
4231 free(cData->topic);
4232 cData->topic = strdup(channel->topic);
4233 }
4234
4235 return 1;
4236 }
4237
4238 static CHANSERV_FUNC(cmd_mode)
4239 {
4240 struct mod_chanmode *change;
4241
4242 if(argc < 2)
4243 {
4244 change = &channel->channel_info->modes;
4245 if(change->modes_set || change->modes_clear) {
4246 modcmd_chanmode_announce(change);
4247 reply("CSMSG_DEFAULTED_MODES", channel->name);
4248 } else
4249 reply("CSMSG_NO_MODES", channel->name);
4250 return 1;
4251 }
4252
4253 change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED);
4254 if(!change)
4255 {
4256 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
4257 return 0;
4258 }
4259
4260 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
4261 && mode_lock_violated(&channel->channel_info->modes, change))
4262 {
4263 char modes[MAXLEN];
4264 mod_chanmode_format(&channel->channel_info->modes, modes);
4265 reply("CSMSG_MODE_LOCKED", modes, channel->name);
4266 return 0;
4267 }
4268
4269 modcmd_chanmode_announce(change);
4270 mod_chanmode_free(change);
4271 reply("CSMSG_MODES_SET", unsplit_string(argv+1, argc-1, NULL));
4272 return 1;
4273 }
4274
4275 static CHANSERV_FUNC(cmd_invite)
4276 {
4277 struct userData *uData;
4278 struct userNode *invite;
4279
4280 uData = GetChannelUser(channel->channel_info, user->handle_info);
4281
4282 if(argc > 1)
4283 {
4284 if(!(invite = GetUserH(argv[1])))
4285 {
4286 reply("MSG_NICK_UNKNOWN", argv[1]);
4287 return 0;
4288 }
4289 }
4290 else
4291 invite = user;
4292
4293 if(GetUserMode(channel, invite))
4294 {
4295 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
4296 return 0;
4297 }
4298
4299 if(user != invite)
4300 {
4301 if(argc > 2)
4302 {
4303 char *reason = unsplit_string(argv + 2, argc - 2, NULL);
4304 send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
4305 }
4306 else
4307 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
4308 }
4309 irc_invite(chanserv, invite, channel);
4310 if(argc > 1)
4311 reply("CSMSG_INVITED_USER", argv[1], channel->name);
4312
4313 return 1;
4314 }
4315
4316 static CHANSERV_FUNC(cmd_inviteme)
4317 {
4318 if(GetUserMode(channel, user))
4319 {
4320 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
4321 return 0;
4322 }
4323 if(channel->channel_info
4324 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
4325 {
4326 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
4327 return 0;
4328 }
4329 irc_invite(cmd->parent->bot, user, channel);
4330 return 1;
4331 }
4332
4333 static void
4334 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
4335 {
4336 unsigned int combo;
4337 char buf1[INTERVALLEN], buf2[INTERVALLEN];
4338
4339 /* We display things based on two dimensions:
4340 * - Issue time: present or absent
4341 * - Expiration: revoked, expired, expires in future, or indefinite expiration
4342 * (in order of precedence, so something both expired and revoked
4343 * only counts as revoked)
4344 */
4345 combo = (suspended->issued ? 4 : 0)
4346 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
4347 switch(combo) {
4348 case 0: /* no issue time, indefinite expiration */
4349 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
4350 break;
4351 case 1: /* no issue time, expires in future */
4352 intervalString(buf1, suspended->expires-now, user->handle_info);
4353 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
4354 break;
4355 case 2: /* no issue time, expired */
4356 intervalString(buf1, now-suspended->expires, user->handle_info);
4357 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
4358 break;
4359 case 3: /* no issue time, revoked */
4360 intervalString(buf1, now-suspended->revoked, user->handle_info);
4361 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
4362 break;
4363 case 4: /* issue time set, indefinite expiration */
4364 intervalString(buf1, now-suspended->issued, user->handle_info);
4365 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
4366 break;
4367 case 5: /* issue time set, expires in future */
4368 intervalString(buf1, now-suspended->issued, user->handle_info);
4369 intervalString(buf2, suspended->expires-now, user->handle_info);
4370 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
4371 break;
4372 case 6: /* issue time set, expired */
4373 intervalString(buf1, now-suspended->issued, user->handle_info);
4374 intervalString(buf2, now-suspended->expires, user->handle_info);
4375 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
4376 break;
4377 case 7: /* issue time set, revoked */
4378 intervalString(buf1, now-suspended->issued, user->handle_info);
4379 intervalString(buf2, now-suspended->revoked, user->handle_info);
4380 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
4381 break;
4382 default:
4383 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
4384 return;
4385 }
4386 }
4387
4388 static CHANSERV_FUNC(cmd_info)
4389 {
4390 char modes[MAXLEN], buffer[INTERVALLEN];
4391 struct userData *uData, *owner;
4392 struct chanData *cData;
4393 struct do_not_register *dnr;
4394 struct note *note;
4395 dict_iterator_t it;
4396 int privileged;
4397
4398 cData = channel->channel_info;
4399 reply("CSMSG_CHANNEL_INFO", channel->name);
4400 reply("CSMSG_BAR");
4401
4402 uData = GetChannelUser(cData, user->handle_info);
4403 if(uData && (uData->access >= UL_OP /*cData->lvlOpts[lvlGiveOps]*/))
4404 {
4405 mod_chanmode_format(&cData->modes, modes);
4406 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
4407 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
4408 }
4409
4410 for(it = dict_first(cData->notes); it; it = iter_next(it))
4411 {
4412 int padding;
4413
4414 note = iter_data(it);
4415 if(!note_type_visible_to_user(cData, note->type, user))
4416 continue;
4417
4418 padding = PADLEN - 1 - strlen(iter_key(it));
4419 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
4420 }
4421
4422 reply("CSMSG_CHANNEL_MAX", cData->max);
4423 for(owner = cData->users; owner; owner = owner->next)
4424 if(owner->access == UL_OWNER)
4425 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
4426 reply("CSMSG_CHANNEL_USERS", cData->userCount);
4427 reply("CSMSG_CHANNEL_LAMERS", cData->banCount);
4428 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
4429 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
4430
4431 privileged = IsStaff(user);
4432 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
4433 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
4434
4435 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
4436 chanserv_show_dnrs(user, cmd, channel->name, NULL);
4437
4438 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
4439 {
4440 struct suspended *suspended;
4441 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
4442 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
4443 show_suspension_info(cmd, user, suspended);
4444 }
4445 else if(IsSuspended(cData))
4446 {
4447 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
4448 show_suspension_info(cmd, user, cData->suspended);
4449 }
4450 reply("CSMSG_CHANNEL_END");
4451 return 1;
4452 }
4453
4454 static CHANSERV_FUNC(cmd_netinfo)
4455 {
4456 extern time_t boot_time;
4457 extern unsigned long burst_length;
4458 char interval[INTERVALLEN];
4459
4460 reply("CSMSG_NETWORK_INFO");
4461 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
4462 reply("CSMSG_NETWORK_USERS", dict_size(clients));
4463 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
4464 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
4465 reply("CSMSG_NETWORK_LAMERS", banCount);
4466 reply("CSMSG_NETWORK_CHANUSERS", userCount);
4467 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
4468 reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
4469 return 1;
4470 }
4471
4472 static void
4473 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
4474 {
4475 struct helpfile_table table;
4476 unsigned int nn;
4477 struct userNode *user;
4478 char *nick;
4479
4480 table.length = 0;
4481 table.width = 1;
4482 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4483 table.contents = alloca(list->used*sizeof(*table.contents));
4484 for(nn=0; nn<list->used; nn++)
4485 {
4486 user = list->list[nn];
4487 if(user->modes & skip_flags)
4488 continue;
4489 if(IsBot(user))
4490 continue;
4491 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
4492 if(IsAway(user))
4493 {
4494 nick = alloca(strlen(user->nick)+3);
4495 sprintf(nick, "(%s)", user->nick);
4496 }
4497 else
4498 nick = user->nick;
4499 table.contents[table.length][0] = nick;
4500 table.length++;
4501 }
4502 table_send(chanserv, to->nick, 0, NULL, table);
4503 }
4504
4505 static CHANSERV_FUNC(cmd_ircops)
4506 {
4507 reply("CSMSG_STAFF_OPERS");
4508 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
4509 return 1;
4510 }
4511
4512 static CHANSERV_FUNC(cmd_helpers)
4513 {
4514 reply("CSMSG_STAFF_HELPERS");
4515 send_staff_list(user, &curr_helpers, FLAGS_OPER);
4516 return 1;
4517 }
4518
4519 static CHANSERV_FUNC(cmd_staff)
4520 {
4521 reply("CSMSG_NETWORK_STAFF");
4522 cmd_ircops(CSFUNC_ARGS);
4523 cmd_helpers(CSFUNC_ARGS);
4524 return 1;
4525 }
4526
4527 static CHANSERV_FUNC(cmd_peek)
4528 {
4529 struct modeNode *mn;
4530 char modes[MODELEN];
4531 unsigned int n;
4532 struct helpfile_table table;
4533
4534 irc_make_chanmode(channel, modes);
4535
4536 reply("CSMSG_PEEK_INFO", channel->name);
4537 reply("CSMSG_BAR");
4538 reply("CSMSG_PEEK_TOPIC", channel->topic);
4539 reply("CSMSG_PEEK_MODES", modes);
4540 reply("CSMSG_PEEK_USERS", channel->members.used);
4541
4542 table.length = 0;
4543 table.width = 1;
4544 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4545 table.contents = alloca(channel->members.used*sizeof(*table.contents));
4546 for(n = 0; n < channel->members.used; n++)
4547 {
4548 mn = channel->members.list[n];
4549 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
4550 continue;
4551 table.contents[table.length] = alloca(sizeof(**table.contents));
4552 table.contents[table.length][0] = mn->user->nick;
4553 table.length++;
4554 }
4555 if(table.length)
4556 {
4557 reply("CSMSG_PEEK_OPS");
4558 table_send(chanserv, user->nick, 0, NULL, table);
4559 }
4560 else
4561 reply("CSMSG_PEEK_NO_OPS");
4562 reply("CSMSG_PEEK_END");
4563 return 1;
4564 }
4565
4566 static MODCMD_FUNC(cmd_wipeinfo)
4567 {
4568 struct handle_info *victim;
4569 struct userData *ud, *actor;
4570
4571 REQUIRE_PARAMS(2);
4572 actor = GetChannelUser(channel->channel_info, user->handle_info);
4573 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4574 return 0;
4575 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4576 {
4577 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4578 return 0;
4579 }
4580 if((ud->access >= actor->access) && (ud != actor))
4581 {
4582 reply("MSG_USER_OUTRANKED", victim->handle);
4583 return 0;
4584 }
4585 if(ud->info)
4586 free(ud->info);
4587 ud->info = NULL;
4588 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4589 return 1;
4590 }
4591
4592 static CHANSERV_FUNC(cmd_resync)
4593 {
4594 struct mod_chanmode *changes;
4595 struct chanData *cData = channel->channel_info;
4596 unsigned int ii, used;
4597
4598 changes = mod_chanmode_alloc(channel->members.used * 2);
4599 for(ii = used = 0; ii < channel->members.used; ++ii)
4600 {
4601 struct modeNode *mn = channel->members.list[ii];
4602 struct userData *uData;
4603
4604 if(IsService(mn->user))
4605 continue;
4606
4607 uData = GetChannelAccess(cData, mn->user->handle_info);
4608 if(uData && uData->access >= UL_OP /* cData->lvlOpts[lvlGiveOps]*/)
4609 {
4610 if(!(mn->modes & MODE_CHANOP))
4611 {
4612 changes->args[used].mode = MODE_CHANOP;
4613 changes->args[used++].u.member = mn;
4614 }
4615 }
4616 else if(uData && uData->access >= UL_HALFOP /*cData->lvlOpts[lvlGiveHalfOps]*/)
4617 {
4618 if(mn->modes & MODE_CHANOP)
4619 {
4620 changes->args[used].mode = MODE_REMOVE | MODE_CHANOP;
4621 changes->args[used++].u.member = mn;
4622 }
4623 if(!(mn->modes & MODE_HALFOP))
4624 {
4625 changes->args[used].mode = MODE_HALFOP;
4626 changes->args[used++].u.member = mn;
4627 }
4628 /* why cant halfops keep voice
4629 if(mn->modes & MODE_VOICE)
4630 {
4631 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4632 changes->args[used++].u.member = mn;
4633 }
4634 */
4635 }
4636 else if(uData && uData->access >= UL_PEON /* cData->lvlOpts[lvlGiveVoice]*/)
4637 {
4638 if(mn->modes & MODE_CHANOP)
4639 {
4640 changes->args[used].mode = MODE_REMOVE | MODE_CHANOP;
4641 changes->args[used++].u.member = mn;
4642 }
4643 if(mn->modes & MODE_HALFOP)
4644 {
4645 changes->args[used].mode = MODE_REMOVE | MODE_HALFOP;
4646 changes->args[used++].u.member = mn;
4647 }
4648 if(!(mn->modes & MODE_VOICE))
4649 {
4650 changes->args[used].mode = MODE_VOICE;
4651 changes->args[used++].u.member = mn;
4652 }
4653 }
4654 else
4655 {
4656 if(mn->modes)
4657 {
4658 changes->args[used].mode = MODE_REMOVE | mn->modes;
4659 changes->args[used++].u.member = mn;
4660 }
4661 }
4662 }
4663 changes->argc = used;
4664 modcmd_chanmode_announce(changes);
4665 mod_chanmode_free(changes);
4666 reply("CSMSG_RESYNCED_USERS", channel->name);
4667 return 1;
4668 }
4669
4670 static CHANSERV_FUNC(cmd_seen)
4671 {
4672 struct userData *uData;
4673 struct handle_info *handle;
4674 char seen[INTERVALLEN];
4675
4676 REQUIRE_PARAMS(2);
4677
4678 if(!irccasecmp(argv[1], chanserv->nick))
4679 {
4680 reply("CSMSG_IS_CHANSERV");
4681 return 1;
4682 }
4683
4684 if(!(handle = get_handle_info(argv[1])))
4685 {
4686 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4687 return 0;
4688 }
4689
4690 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4691 {
4692 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4693 return 0;
4694 }
4695
4696 if(uData->present)
4697 reply("CSMSG_USER_PRESENT", handle->handle);
4698 else if(uData->seen)
4699 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
4700 else
4701 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4702
4703 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4704 reply("CSMSG_USER_VACATION", handle->handle);
4705
4706 return 1;
4707 }
4708
4709 static MODCMD_FUNC(cmd_names)
4710 {
4711 struct userNode *targ;
4712 struct userData *targData;
4713 unsigned int ii, pos;
4714 char buf[400];
4715
4716 for(ii=pos=0; ii<channel->members.used; ++ii)
4717 {
4718 targ = channel->members.list[ii]->user;
4719 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4720 if(!targData)
4721 continue;
4722 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
4723 {
4724 buf[pos] = 0;
4725 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4726 pos = 0;
4727 }
4728 buf[pos++] = ' ';
4729 if(IsUserSuspended(targData))
4730 buf[pos++] = 's';
4731 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4732 }
4733 buf[pos] = 0;
4734 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4735 reply("CSMSG_END_NAMES", channel->name);
4736 return 1;
4737 }
4738
4739 static int
4740 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
4741 {
4742 switch(ntype->visible_type)
4743 {
4744 case NOTE_VIS_ALL: return 1;
4745 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
4746 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
4747 }
4748 }
4749
4750 static int
4751 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
4752 {
4753 struct userData *uData;
4754
4755 switch(ntype->set_access_type)
4756 {
4757 case NOTE_SET_CHANNEL_ACCESS:
4758 if(!user->handle_info)
4759 return 0;
4760 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
4761 return 0;
4762 return uData->access >= ntype->set_access.min_ulevel;
4763 case NOTE_SET_CHANNEL_SETTER:
4764 return check_user_level(channel, user, lvlSetters, 1, 0);
4765 case NOTE_SET_PRIVILEGED: default:
4766 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
4767 }
4768 }
4769
4770 static CHANSERV_FUNC(cmd_note)
4771 {
4772 struct chanData *cData;
4773 struct note *note;
4774 struct note_type *ntype;
4775
4776 cData = channel->channel_info;
4777 if(!cData)
4778 {
4779 reply("CSMSG_NOT_REGISTERED", channel->name);
4780 return 0;
4781 }
4782
4783 /* If no arguments, show all visible notes for the channel. */
4784 if(argc < 2)
4785 {
4786 dict_iterator_t it;
4787 unsigned int count;
4788
4789 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
4790 {
4791 note = iter_data(it);
4792 if(!note_type_visible_to_user(cData, note->type, user))
4793 continue;
4794 if(!count++)
4795 reply("CSMSG_NOTELIST_HEADER", channel->name);
4796 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
4797 }
4798 if(count)
4799 reply("CSMSG_NOTELIST_END", channel->name);
4800 else
4801 reply("CSMSG_NOTELIST_EMPTY", channel->name);
4802 }
4803 /* If one argument, show the named note. */
4804 else if(argc == 2)
4805 {
4806 if((note = dict_find(cData->notes, argv[1], NULL))
4807 && note_type_visible_to_user(cData, note->type, user))
4808 {
4809 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
4810 }
4811 else if((ntype = dict_find(note_types, argv[1], NULL))
4812 && note_type_visible_to_user(NULL, ntype, user))
4813 {
4814 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
4815 return 0;
4816 }
4817 else
4818 {
4819 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4820 return 0;
4821 }
4822 }
4823 /* Assume they're trying to set a note. */
4824 else
4825 {
4826 char *note_text;
4827 ntype = dict_find(note_types, argv[1], NULL);
4828 if(!ntype)
4829 {
4830 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4831 return 0;
4832 }
4833 else if(note_type_settable_by_user(channel, ntype, user))
4834 {
4835 note_text = unsplit_string(argv+2, argc-2, NULL);
4836 if((note = dict_find(cData->notes, argv[1], NULL)))
4837 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
4838 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
4839 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
4840
4841 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
4842 {
4843 /* The note is viewable to staff only, so return 0
4844 to keep the invocation from getting logged (or
4845 regular users can see it in !events). */
4846 return 0;
4847 }
4848 }
4849 else
4850 {
4851 reply("CSMSG_NO_ACCESS");
4852 return 0;
4853 }
4854 }
4855 return 1;
4856 }
4857
4858 static CHANSERV_FUNC(cmd_delnote)
4859 {
4860 struct note *note;
4861
4862 REQUIRE_PARAMS(2);
4863 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
4864 || !note_type_settable_by_user(channel, note->type, user))
4865 {
4866 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
4867 return 0;
4868 }
4869 dict_remove(channel->channel_info->notes, note->type->name);
4870 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
4871 return 1;
4872 }
4873
4874 static CHANSERV_FUNC(cmd_last)
4875 {
4876 int numoflines;
4877
4878 REQUIRE_PARAMS(1);
4879
4880 numoflines = (argc > 1) ? atoi(argv[1]) : 10;
4881
4882 if(numoflines < 1 || numoflines > 200)
4883 {
4884 reply("CSMSG_LAST_INVALID");
4885 return 0;
4886 }
4887 ShowLog(user, channel, "*", "*", "*", "*", numoflines);
4888 return 1;
4889 }
4890
4891 static CHANSERV_FUNC(cmd_events)
4892 {
4893 struct logSearch discrim;
4894 struct logReport report;
4895 unsigned int matches, limit;
4896
4897 limit = (argc > 1) ? atoi(argv[1]) : 10;
4898 if(limit < 1 || limit > 200)
4899 limit = 10;
4900
4901 memset(&discrim, 0, sizeof(discrim));
4902 discrim.masks.bot = chanserv;
4903 discrim.masks.channel_name = channel->name;
4904 if(argc > 2)
4905 discrim.masks.command = argv[2];
4906 discrim.limit = limit;
4907 discrim.max_time = INT_MAX;
4908 discrim.severities = 1 << LOG_COMMAND;
4909 report.reporter = chanserv;
4910 report.user = user;
4911 reply("CSMSG_EVENT_SEARCH_RESULTS", channel->name);
4912 reply("CSMSG_BAR");
4913 matches = log_entry_search(&discrim, log_report_entry, &report);
4914 if(matches)
4915 reply("MSG_MATCH_COUNT", matches);
4916 else
4917 reply("MSG_NO_MATCHES");
4918 return 1;
4919 }
4920
4921 static CHANSERV_FUNC(cmd_say)
4922 {
4923 char *msg;
4924 if(channel)
4925 {
4926 REQUIRE_PARAMS(2);
4927 msg = unsplit_string(argv + 1, argc - 1, NULL);
4928 send_channel_message(channel, cmd->parent->bot, "%s", msg);
4929 }
4930 else if(GetUserH(argv[1]))
4931 {
4932 REQUIRE_PARAMS(3);
4933 msg = unsplit_string(argv + 2, argc - 2, NULL);
4934 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
4935 }
4936 else
4937 {
4938 reply("MSG_NOT_TARGET_NAME");
4939 return 0;
4940 }
4941 return 1;
4942 }
4943
4944 static CHANSERV_FUNC(cmd_emote)
4945 {
4946 char *msg;
4947 assert(argc >= 2);
4948 if(channel)
4949 {
4950 /* CTCP is so annoying. */
4951 msg = unsplit_string(argv + 1, argc - 1, NULL);
4952 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
4953 }
4954 else if(GetUserH(argv[1]))
4955 {
4956 msg = unsplit_string(argv + 2, argc - 2, NULL);
4957 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
4958 }
4959 else
4960 {
4961 reply("MSG_NOT_TARGET_NAME");
4962 return 0;
4963 }
4964 return 1;
4965 }
4966
4967 struct channelList *
4968 chanserv_support_channels(void)
4969 {
4970 return &chanserv_conf.support_channels;
4971 }
4972
4973 static CHANSERV_FUNC(cmd_expire)
4974 {
4975 int channel_count = registered_channels;
4976 expire_channels(NULL);
4977 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
4978 return 1;
4979 }
4980
4981 static void
4982 chanserv_expire_suspension(void *data)
4983 {
4984 struct suspended *suspended = data;
4985 struct chanNode *channel;
4986
4987 if(!suspended->expires || (now < suspended->expires))
4988 suspended->revoked = now;
4989 channel = suspended->cData->channel;
4990 suspended->cData->channel = channel;
4991 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
4992 if(!IsOffChannel(suspended->cData))
4993 {
4994 struct mod_chanmode change;
4995 mod_chanmode_init(&change);
4996 change.argc = 1;
4997 change.args[0].mode = MODE_CHANOP;
4998 change.args[0].u.member = AddChannelUser(chanserv, channel);
4999 mod_chanmode_announce(chanserv, channel, &change);
5000 }
5001 }
5002
5003 static CHANSERV_FUNC(cmd_csuspend)
5004 {
5005 struct suspended *suspended;
5006 char reason[MAXLEN];
5007 time_t expiry, duration;
5008 struct userData *uData;
5009
5010 REQUIRE_PARAMS(3);
5011
5012 if(IsProtected(channel->channel_info))
5013 {
5014 reply("CSMSG_SUSPEND_NODELETE", channel->name);
5015 return 0;
5016 }
5017
5018 if(argv[1][0] == '!')
5019 argv[1]++;
5020 else if(IsSuspended(channel->channel_info))
5021 {
5022 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
5023 show_suspension_info(cmd, user, channel->channel_info->suspended);
5024 return 0;
5025 }
5026
5027 if(!strcmp(argv[1], "0"))
5028 expiry = 0;
5029 else if((duration = ParseInterval(argv[1])))
5030 expiry = now + duration;
5031 else
5032 {
5033 reply("MSG_INVALID_DURATION", argv[1]);
5034 return 0;
5035 }
5036
5037 unsplit_string(argv + 2, argc - 2, reason);
5038
5039 suspended = calloc(1, sizeof(*suspended));
5040 suspended->revoked = 0;
5041 suspended->issued = now;
5042 suspended->suspender = strdup(user->handle_info->handle);
5043 suspended->expires = expiry;
5044 suspended->reason = strdup(reason);
5045 suspended->cData = channel->channel_info;
5046 suspended->previous = suspended->cData->suspended;
5047 suspended->cData->suspended = suspended;
5048
5049 if(suspended->expires)
5050 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
5051
5052 if(IsSuspended(channel->channel_info))
5053 {
5054 suspended->previous->revoked = now;
5055 if(suspended->previous->expires)
5056 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
5057 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
5058 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5059 }
5060 else
5061 {
5062 /* Mark all users in channel as absent. */
5063 for(uData = channel->channel_info->users; uData; uData = uData->next)
5064 {
5065 if(uData->present)
5066 {
5067 uData->seen = now;
5068 uData->present = 0;
5069 }
5070 }
5071
5072 /* Mark the channel as suspended, then part. */
5073 channel->channel_info->flags |= CHANNEL_SUSPENDED;
5074 DelChannelUser(chanserv, channel, suspended->reason, 0);
5075 reply("CSMSG_SUSPENDED", channel->name);
5076 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
5077 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5078 }
5079 return 1;
5080 }
5081
5082 static CHANSERV_FUNC(cmd_cunsuspend)
5083 {
5084 struct suspended *suspended;
5085 char message[MAXLEN];
5086
5087 if(!IsSuspended(channel->channel_info))
5088 {
5089 reply("CSMSG_NOT_SUSPENDED", channel->name);
5090 return 0;
5091 }
5092
5093 suspended = channel->channel_info->suspended;
5094
5095 /* Expire the suspension and join ChanServ to the channel. */
5096 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
5097 chanserv_expire_suspension(suspended);
5098 reply("CSMSG_UNSUSPENDED", channel->name);
5099 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
5100 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
5101 return 1;
5102 }
5103
5104 typedef struct chanservSearch
5105 {
5106 char *name;
5107 char *registrar;
5108
5109 time_t unvisited;
5110 time_t registered;
5111
5112 unsigned long flags;
5113 unsigned int limit;
5114 } *search_t;
5115
5116 typedef void (*channel_search_func)(struct chanData *channel, void *data);
5117
5118 static search_t
5119 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
5120 {
5121 search_t search;
5122 unsigned int i;
5123
5124 search = malloc(sizeof(struct chanservSearch));
5125 memset(search, 0, sizeof(*search));
5126 search->limit = 25;
5127
5128 for(i = 0; i < argc; i++)
5129 {
5130 /* Assume all criteria require arguments. */
5131 if(i == (argc - 1))
5132 {
5133 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
5134 goto fail;
5135 }
5136
5137 if(!irccasecmp(argv[i], "name"))
5138 search->name = argv[++i];
5139 else if(!irccasecmp(argv[i], "registrar"))
5140 search->registrar = argv[++i];
5141 else if(!irccasecmp(argv[i], "unvisited"))
5142 search->unvisited = ParseInterval(argv[++i]);
5143 else if(!irccasecmp(argv[i], "registered"))
5144 search->registered = ParseInterval(argv[++i]);
5145 else if(!irccasecmp(argv[i], "flags"))
5146 {
5147 i++;
5148 if(!irccasecmp(argv[i], "nodelete"))
5149 search->flags |= CHANNEL_NODELETE;
5150 else if(!irccasecmp(argv[i], "suspended"))
5151 search->flags |= CHANNEL_SUSPENDED;
5152 else
5153 {
5154 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
5155 goto fail;
5156 }
5157 }
5158 else if(!irccasecmp(argv[i], "limit"))
5159 search->limit = strtoul(argv[++i], NULL, 10);
5160 else
5161 {
5162 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
5163 goto fail;
5164 }
5165 }
5166
5167 if(search->name && !strcmp(search->name, "*"))
5168 search->name = 0;
5169 if(search->registrar && !strcmp(search->registrar, "*"))
5170 search->registrar = 0;
5171
5172 return search;
5173 fail:
5174 free(search);
5175 return NULL;
5176 }
5177
5178 static int
5179 chanserv_channel_match(struct chanData *channel, search_t search)
5180 {
5181 const char *name = channel->channel->name;
5182 if((search->name && !match_ircglob(name, search->name)) ||
5183 (search->registrar && !channel->registrar) ||
5184 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
5185 (search->unvisited && (now - channel->visited) < search->unvisited) ||
5186 (search->registered && (now - channel->registered) > search->registered) ||
5187 (search->flags && ((search->flags & channel->flags) != search->flags)))
5188 return 0;
5189
5190 return 1;
5191 }
5192
5193 static unsigned int
5194 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
5195 {
5196 struct chanData *channel;
5197 unsigned int matches = 0;
5198
5199 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
5200 {
5201 if(!chanserv_channel_match(channel, search))
5202 continue;
5203 matches++;
5204 smf(channel, data);
5205 }
5206
5207 return matches;
5208 }
5209
5210 static void
5211 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
5212 {
5213 }
5214
5215 static void
5216 search_print(struct chanData *channel, void *data)
5217 {
5218 send_message_type(4, data, chanserv, "%s", channel->channel->name);
5219 }
5220
5221 static CHANSERV_FUNC(cmd_search)
5222 {
5223 search_t search;
5224 unsigned int matches;
5225 channel_search_func action;
5226
5227 REQUIRE_PARAMS(3);
5228
5229 if(!irccasecmp(argv[1], "count"))
5230 action = search_count;
5231 else if(!irccasecmp(argv[1], "print"))
5232 action = search_print;
5233 else
5234 {
5235 reply("CSMSG_ACTION_INVALID", argv[1]);
5236 return 0;
5237 }
5238
5239 search = chanserv_search_create(user, argc - 2, argv + 2);
5240 if(!search)
5241 return 0;
5242
5243 if(action == search_count)
5244 search->limit = INT_MAX;
5245
5246 if(action == search_print)
5247 {
5248 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
5249 reply("CSMSG_BAR");
5250 }
5251
5252 matches = chanserv_channel_search(search, action, user);
5253
5254 if(matches)
5255 reply("MSG_MATCH_COUNT", matches);
5256 else
5257 reply("MSG_NO_MATCHES");
5258
5259 free(search);
5260 return 1;
5261 }
5262
5263 static CHANSERV_FUNC(cmd_unvisited)
5264 {
5265 struct chanData *cData;
5266 time_t interval = chanserv_conf.channel_expire_delay;
5267 char buffer[INTERVALLEN];
5268 unsigned int limit = 25, matches = 0;
5269
5270 if(argc > 1)
5271 {
5272 interval = ParseInterval(argv[1]);
5273 if(argc > 2)
5274 limit = atoi(argv[2]);
5275 }
5276
5277 intervalString(buffer, interval, user->handle_info);
5278 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
5279
5280 for(cData = channelList; cData && matches < limit; cData = cData->next)
5281 {
5282 if((now - cData->visited) < interval)
5283 continue;
5284
5285 intervalString(buffer, now - cData->visited, user->handle_info);
5286 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
5287 matches++;
5288 }
5289
5290 return 1;
5291 }
5292
5293 static MODCMD_FUNC(chan_opt_defaulttopic)
5294 {
5295 if(argc > 1)
5296 {
5297 char *topic;
5298
5299 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5300 {
5301 reply("CSMSG_TOPIC_LOCKED", channel->name);
5302 return 0;
5303 }
5304
5305 topic = unsplit_string(argv+1, argc-1, NULL);
5306
5307 free(channel->channel_info->topic);
5308 if(topic[0] == '*' && topic[1] == 0)
5309 {
5310 topic = channel->channel_info->topic = NULL;
5311 }
5312 else
5313 {
5314 topic = channel->channel_info->topic = strdup(topic);
5315 if(channel->channel_info->topic_mask
5316 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
5317 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5318 }
5319 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
5320 }
5321
5322 if(channel->channel_info->topic)
5323 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
5324 else
5325 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
5326 return 1;
5327 }
5328
5329 static MODCMD_FUNC(chan_opt_topicmask)
5330 {
5331 if(argc > 1)
5332 {
5333 struct chanData *cData = channel->channel_info;
5334 char *mask;
5335
5336 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5337 {
5338 reply("CSMSG_TOPIC_LOCKED", channel->name);
5339 return 0;
5340 }
5341
5342 mask = unsplit_string(argv+1, argc-1, NULL);
5343
5344 if(cData->topic_mask)
5345 free(cData->topic_mask);
5346 if(mask[0] == '*' && mask[1] == 0)
5347 {
5348 cData->topic_mask = 0;
5349 }
5350 else
5351 {
5352 cData->topic_mask = strdup(mask);
5353 if(!cData->topic)
5354 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
5355 else if(!match_ircglob(cData->topic, cData->topic_mask))
5356 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5357 }
5358 }
5359
5360 if(channel->channel_info->topic_mask)
5361 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
5362 else
5363 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
5364 return 1;
5365 }
5366
5367 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
5368 {
5369 if(argc > 1)
5370 {
5371 char *greeting = unsplit_string(argv+1, argc-1, NULL);
5372 char *previous;
5373
5374 previous = *data;
5375 if(greeting[0] == '*' && greeting[1] == 0)
5376 *data = NULL;
5377 else
5378 {
5379 unsigned int length = strlen(greeting);
5380 if(length > chanserv_conf.greeting_length)
5381 {
5382 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
5383 return 0;
5384 }
5385 *data = strdup(greeting);
5386 }
5387 if(previous)
5388 free(previous);
5389 }
5390
5391 if(*data)
5392 reply(name, *data);
5393 else
5394 reply(name, user_find_message(user, "MSG_NONE"));
5395 return 1;
5396 }
5397
5398 static MODCMD_FUNC(chan_opt_greeting)
5399 {
5400 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
5401 }
5402
5403 static MODCMD_FUNC(chan_opt_usergreeting)
5404 {
5405 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
5406 }
5407
5408 static MODCMD_FUNC(chan_opt_modes)
5409 {
5410 struct mod_chanmode *new_modes;
5411 char modes[MODELEN];
5412
5413 if(argc > 1)
5414 {
5415 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
5416 {
5417 reply("CSMSG_NO_ACCESS");
5418 return 0;
5419 }
5420 if(argv[1][0] == '*' && argv[1][1] == 0)
5421 {
5422 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
5423 }
5424 else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED)))
5425 {
5426 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5427 return 0;
5428 }
5429 else if(new_modes->argc > 1)
5430 {
5431 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5432 mod_chanmode_free(new_modes);
5433 return 0;
5434 }
5435 else
5436 {
5437 channel->channel_info->modes = *new_modes;
5438 modcmd_chanmode_announce(new_modes);
5439 mod_chanmode_free(new_modes);
5440 }
5441 }
5442
5443 mod_chanmode_format(&channel->channel_info->modes, modes);
5444 if(modes[0])
5445 reply("CSMSG_SET_MODES", modes);
5446 else
5447 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
5448 return 1;
5449 }
5450
5451 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5452 static int
5453 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5454 {
5455 struct chanData *cData = channel->channel_info;
5456 int value;
5457
5458 if(argc > 1)
5459 {
5460 /* Set flag according to value. */
5461 if(enabled_string(argv[1]))
5462 {
5463 cData->flags |= mask;
5464 value = 1;
5465 }
5466 else if(disabled_string(argv[1]))
5467 {
5468 cData->flags &= ~mask;
5469 value = 0;
5470 }
5471 else
5472 {
5473 reply("MSG_INVALID_BINARY", argv[1]);
5474 return 0;
5475 }
5476 }
5477 else
5478 {
5479 /* Find current option value. */
5480 value = (cData->flags & mask) ? 1 : 0;
5481 }
5482
5483 if(value)
5484 reply(name, user_find_message(user, "MSG_ON"));
5485 else
5486 reply(name, user_find_message(user, "MSG_OFF"));
5487 return 1;
5488 }
5489
5490 static MODCMD_FUNC(chan_opt_nodelete)
5491 {
5492 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
5493 {
5494 reply("MSG_SETTING_PRIVILEGED", argv[0]);
5495 return 0;
5496 }
5497
5498 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
5499 }
5500
5501 static MODCMD_FUNC(chan_opt_dynlimit)
5502 {
5503 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
5504 }
5505
5506 static MODCMD_FUNC(chan_opt_offchannel)
5507 {
5508 struct chanData *cData = channel->channel_info;
5509 int value;
5510
5511 if(argc > 1)
5512 {
5513 /* Set flag according to value. */
5514 if(enabled_string(argv[1]))
5515 {
5516 if(!IsOffChannel(cData))
5517 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
5518 cData->flags |= CHANNEL_OFFCHANNEL;
5519 value = 1;
5520 }
5521 else if(disabled_string(argv[1]))
5522 {
5523 if(IsOffChannel(cData))
5524 {
5525 struct mod_chanmode change;
5526 mod_chanmode_init(&change);
5527 change.argc = 1;
5528 change.args[0].mode = MODE_CHANOP;
5529 change.args[0].u.member = AddChannelUser(chanserv, channel);
5530 mod_chanmode_announce(chanserv, channel, &change);
5531 }
5532 cData->flags &= ~CHANNEL_OFFCHANNEL;
5533 value = 0;
5534 }
5535 else
5536 {
5537 reply("MSG_INVALID_BINARY", argv[1]);
5538 return 0;
5539 }
5540 }
5541 else
5542 {
5543 /* Find current option value. */
5544 value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
5545 }
5546
5547 if(value)
5548 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
5549 else
5550 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
5551 return 1;
5552 }
5553
5554 static MODCMD_FUNC(chan_opt_defaults)
5555 {
5556 struct userData *uData;
5557 struct chanData *cData;
5558 const char *confirm;
5559 enum levelOption lvlOpt;
5560 enum charOption chOpt;
5561
5562 cData = channel->channel_info;
5563 uData = GetChannelUser(cData, user->handle_info);
5564 if(!uData || (uData->access < UL_OWNER))
5565 {
5566 reply("CSMSG_OWNER_DEFAULTS", channel->name);
5567 return 0;
5568 }
5569 confirm = make_confirmation_string(uData);
5570 if((argc < 2) || strcmp(argv[1], confirm))
5571 {
5572 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
5573 return 0;
5574 }
5575 cData->flags = CHANNEL_DEFAULT_FLAGS;
5576 cData->modes = chanserv_conf.default_modes;
5577 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
5578 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
5579 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
5580 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
5581 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
5582 return 1;
5583 }
5584
5585 static int
5586 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5587 {
5588 struct chanData *cData = channel->channel_info;
5589 struct userData *uData;
5590 unsigned short value;
5591
5592 if(argc > 1)
5593 {
5594 if(!check_user_level(channel, user, option, 1, 1))
5595 {
5596 reply("CSMSG_CANNOT_SET");
5597 return 0;
5598 }
5599 value = user_level_from_name(argv[1], UL_OWNER+1);
5600 if(!value && strcmp(argv[1], "0"))
5601 {
5602 reply("CSMSG_INVALID_ACCESS", argv[1]);
5603 return 0;
5604 }
5605 uData = GetChannelUser(cData, user->handle_info);
5606 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
5607 {
5608 reply("CSMSG_BAD_SETLEVEL");
5609 return 0;
5610 }
5611 switch(option)
5612 {
5613 /* removing these level sets..
5614 case lvlGiveVoice:
5615 if(value > cData->lvlOpts[lvlGiveOps])
5616 {
5617 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
5618 return 0;
5619 }
5620 break;
5621 case lvlGiveHalfOps:
5622 if(value < cData->lvlOpts[lvlGiveVoice])
5623 {
5624 reply("CSMSG_BAD_GIVEHOPS", cData->lvlOpts[lvlGiveHalfOps]);
5625 return 0;
5626 }
5627 break;
5628 case lvlGiveOps:
5629 if(value < cData->lvlOpts[lvlGiveVoice])
5630 {
5631 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
5632 return 0;
5633 }
5634 break;
5635 */
5636 case lvlSetters:
5637 /* This test only applies to owners, since non-owners
5638 * trying to set an option to above their level get caught
5639 * by the CSMSG_BAD_SETLEVEL test above.
5640 */
5641 if(value > uData->access)
5642 {
5643 reply("CSMSG_BAD_SETTERS");
5644 return 0;
5645 }
5646 break;
5647 default:
5648 break;
5649 }
5650 cData->lvlOpts[option] = value;
5651 }
5652 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
5653 return argc > 1;
5654 }
5655
5656 static MODCMD_FUNC(chan_opt_enfops)
5657 {
5658 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
5659 }
5660
5661 static MODCMD_FUNC(chan_opt_enfhalfops)
5662 {
5663 return channel_level_option(lvlEnfHalfOps, CSFUNC_ARGS);
5664 }
5665 /*
5666 static MODCMD_FUNC(chan_opt_giveops)
5667 {
5668 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
5669 }
5670
5671 static MODCMD_FUNC(chan_opt_givehalfops)
5672 {
5673 return channel_level_option(lvlGiveHalfOps, CSFUNC_ARGS);
5674 }
5675 */
5676 static MODCMD_FUNC(chan_opt_enfmodes)
5677 {
5678 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
5679 }
5680
5681 static MODCMD_FUNC(chan_opt_enftopic)
5682 {
5683 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
5684 }
5685
5686 static MODCMD_FUNC(chan_opt_pubcmd)
5687 {
5688 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
5689 }
5690
5691 static MODCMD_FUNC(chan_opt_setters)
5692 {
5693 return channel_level_option(lvlSetters, CSFUNC_ARGS);
5694 }
5695
5696 static MODCMD_FUNC(chan_opt_userinfo)
5697 {
5698 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
5699 }
5700
5701 /*
5702 static MODCMD_FUNC(chan_opt_givevoice)
5703 {
5704 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
5705 }
5706 */
5707
5708 static MODCMD_FUNC(chan_opt_topicsnarf)
5709 {
5710 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
5711 }
5712
5713 static MODCMD_FUNC(chan_opt_inviteme)
5714 {
5715 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
5716 }
5717
5718 /* TODO: Make look like this when no args are
5719 * given:
5720 * -X3- -------------------------------
5721 * -X3- BanTimeout: Bans are removed:
5722 * -X3- ----- * indicates current -----
5723 * -X3- 0: [*] Never.
5724 * -X3- 1: [ ] After 10 minutes.
5725 * -X3- 2: [ ] After 2 hours.
5726 * -X3- 3: [ ] After 4 hours.
5727 * -X3- 4: [ ] After 24 hours.
5728 * -X3- 5: [ ] After one week.
5729 * -X3- ------------- End -------------
5730 */
5731 static int
5732 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5733 {
5734 struct chanData *cData = channel->channel_info;
5735 int count = charOptions[option].count, index;
5736
5737 if(argc > 1)
5738 {
5739 index = atoi(argv[1]);
5740
5741 if(!isdigit(argv[1][0]) || (index < 0) || (index >= count))
5742 {
5743 reply("CSMSG_INVALID_NUMERIC", index);
5744 /* Show possible values. */
5745 for(index = 0; index < count; index++)
5746 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5747 return 0;
5748 }
5749
5750 cData->chOpts[option] = charOptions[option].values[index].value;
5751 }
5752 else
5753 {
5754 /* Find current option value. */
5755 find_value:
5756 for(index = 0;
5757 (index < count) && (cData->chOpts[option] != charOptions[option].values[index].value);
5758 index++);
5759 if(index == count)
5760 {
5761 /* Somehow, the option value is corrupt; reset it to the default. */
5762 cData->chOpts[option] = charOptions[option].default_value;
5763 goto find_value;
5764 }
5765 }
5766
5767 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5768 return 1;
5769 }
5770
5771 static MODCMD_FUNC(chan_opt_voice)
5772 {
5773 return channel_multiple_option(chVoice, CSFUNC_ARGS);
5774 }
5775
5776 static MODCMD_FUNC(chan_opt_protect)
5777 {
5778 return channel_multiple_option(chProtect, CSFUNC_ARGS);
5779 }
5780
5781 static MODCMD_FUNC(chan_opt_toys)
5782 {
5783 return channel_multiple_option(chToys, CSFUNC_ARGS);
5784 }
5785
5786 static MODCMD_FUNC(chan_opt_ctcpreaction)
5787 {
5788 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
5789 }
5790
5791 static MODCMD_FUNC(chan_opt_bantimeout)
5792 {
5793 return channel_multiple_option(chBanTimeout, CSFUNC_ARGS);
5794 }
5795
5796 static MODCMD_FUNC(chan_opt_topicrefresh)
5797 {
5798 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
5799 }
5800
5801 static struct svccmd_list set_shows_list;
5802
5803 static void
5804 handle_svccmd_unbind(struct svccmd *target) {
5805 unsigned int ii;
5806 for(ii=0; ii<set_shows_list.used; ++ii)
5807 if(target == set_shows_list.list[ii])
5808 set_shows_list.used = 0;
5809 }
5810
5811 static CHANSERV_FUNC(cmd_set)
5812 {
5813 struct svccmd *subcmd;
5814 char buf[MAXLEN];
5815 unsigned int ii;
5816
5817 /* Check if we need to (re-)initialize set_shows_list. */
5818 if(!set_shows_list.used)
5819 {
5820 if(!set_shows_list.size)
5821 {
5822 set_shows_list.size = chanserv_conf.set_shows->used;
5823 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
5824 }
5825 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
5826 {
5827 const char *name = chanserv_conf.set_shows->list[ii];
5828 sprintf(buf, "%s %s", argv[0], name);
5829 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5830 if(!subcmd)
5831 {
5832 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
5833 continue;
5834 }
5835 svccmd_list_append(&set_shows_list, subcmd);
5836 }
5837 }
5838
5839 if(argc < 2)
5840 {
5841 reply("CSMSG_CHANNEL_OPTIONS", channel->name);
5842 reply("CSMSG_BAR");
5843 for(ii = 0; ii < set_shows_list.used; ii++)
5844 {
5845 subcmd = set_shows_list.list[ii];
5846 subcmd->command->func(user, channel, 1, argv+1, subcmd);
5847 }
5848 reply("CSMSG_CHANNEL_OPTIONS_END");
5849 return 1;
5850 }
5851
5852 sprintf(buf, "%s %s", argv[0], argv[1]);
5853 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5854 if(!subcmd)
5855 {
5856 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5857 return 0;
5858 }
5859 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
5860 {
5861 reply("CSMSG_NO_ACCESS");
5862 return 0;
5863 }
5864
5865 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5866 }
5867
5868 static int
5869 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5870 {
5871 struct userData *uData;
5872
5873 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5874 if(!uData)
5875 {
5876 reply("CSMSG_NOT_USER", channel->name);
5877 return 0;
5878 }
5879
5880 if(argc < 2)
5881 {
5882 /* Just show current option value. */
5883 }
5884 else if(enabled_string(argv[1]))
5885 {
5886 uData->flags |= mask;
5887 }
5888 else if(disabled_string(argv[1]))
5889 {
5890 uData->flags &= ~mask;
5891 }
5892 else
5893 {
5894 reply("MSG_INVALID_BINARY", argv[1]);
5895 return 0;
5896 }
5897
5898 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
5899 return 1;
5900 }
5901
5902 static MODCMD_FUNC(user_opt_autoop)
5903 {
5904 struct userData *uData;
5905
5906 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5907 if(!uData)
5908 {
5909 reply("CSMSG_NOT_USER", channel->name);
5910 return 0;
5911 }
5912 if(uData->access < UL_OP /*channel->channel_info->lvlOpts[lvlGiveOps]*/)
5913 return user_binary_option("CSMSG_USET_AUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
5914 else
5915 return user_binary_option("CSMSG_USET_AUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
5916 /* TODO: add halfops error message? or is the op one generic enough? */
5917 }
5918
5919 static MODCMD_FUNC(user_opt_autoinvite)
5920 {
5921 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
5922 }
5923
5924 static MODCMD_FUNC(user_opt_info)
5925 {
5926 struct userData *uData;
5927 char *infoline;
5928
5929 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5930
5931 if(!uData)
5932 {
5933 /* If they got past the command restrictions (which require access)
5934 * but fail this test, we have some fool with security override on.
5935 */
5936 reply("CSMSG_NOT_USER", channel->name);
5937 return 0;
5938 }
5939
5940 if(argc > 1)
5941 {
5942 size_t bp;
5943 infoline = unsplit_string(argv + 1, argc - 1, NULL);
5944 if(strlen(infoline) > chanserv_conf.max_userinfo_length)
5945 {
5946 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
5947 return 0;
5948 }
5949 bp = strcspn(infoline, "\001");
5950 if(infoline[bp])
5951 {
5952 reply("CSMSG_BAD_INFOLINE", infoline[bp]);
5953 return 0;
5954 }
5955 if(uData->info)
5956 free(uData->info);
5957 if(infoline[0] == '*' && infoline[1] == 0)
5958 uData->info = NULL;
5959 else
5960 uData->info = strdup(infoline);
5961 }
5962 if(uData->info)
5963 reply("CSMSG_USET_INFO", uData->info);
5964 else
5965 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
5966 return 1;
5967 }
5968
5969 struct svccmd_list uset_shows_list;
5970
5971 static CHANSERV_FUNC(cmd_uset)
5972 {
5973 struct svccmd *subcmd;
5974 char buf[MAXLEN];
5975 unsigned int ii;
5976
5977 /* Check if we need to (re-)initialize uset_shows_list. */
5978 if(!uset_shows_list.used)
5979 {
5980 char *options[] =
5981 {
5982 "AutoOp", "AutoInvite", "Info"
5983 };
5984
5985 if(!uset_shows_list.size)
5986 {
5987 uset_shows_list.size = ArrayLength(options);
5988 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
5989 }
5990 for(ii = 0; ii < ArrayLength(options); ii++)
5991 {
5992 const char *name = options[ii];
5993 sprintf(buf, "%s %s", argv[0], name);
5994 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5995 if(!subcmd)
5996 {
5997 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
5998 continue;
5999 }
6000 svccmd_list_append(&uset_shows_list, subcmd);
6001 }
6002 }
6003
6004 if(argc < 2)
6005 {
6006 /* Do this so options are presented in a consistent order. */
6007 reply("CSMSG_USER_OPTIONS");
6008 for(ii = 0; ii < uset_shows_list.used; ii++)
6009 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
6010 return 1;
6011 }
6012
6013 sprintf(buf, "%s %s", argv[0], argv[1]);
6014 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6015 if(!subcmd)
6016 {
6017 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
6018 return 0;
6019 }
6020
6021 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
6022 }
6023
6024 static CHANSERV_FUNC(cmd_giveownership)
6025 {
6026 struct handle_info *new_owner_hi;
6027 struct userData *new_owner, *curr_user;
6028 struct chanData *cData = channel->channel_info;
6029 struct do_not_register *dnr;
6030 unsigned int force;
6031 unsigned short co_access;
6032 char reason[MAXLEN];
6033
6034 REQUIRE_PARAMS(2);
6035 curr_user = GetChannelAccess(cData, user->handle_info);
6036 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
6037 if(!curr_user || (curr_user->access != UL_OWNER))
6038 {
6039 struct userData *owner = NULL;
6040 for(curr_user = channel->channel_info->users;
6041 curr_user;
6042 curr_user = curr_user->next)
6043 {
6044 if(curr_user->access != UL_OWNER)
6045 continue;
6046 if(owner)
6047 {
6048 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
6049 return 0;
6050 }
6051 owner = curr_user;
6052 }
6053 curr_user = owner;
6054 }
6055 else if (!force && (now < (time_t)(cData->ownerTransfer + chanserv_conf.giveownership_period)))
6056 {
6057 char delay[INTERVALLEN];
6058 intervalString(delay, cData->ownerTransfer + chanserv_conf.giveownership_period - now, user->handle_info);
6059 reply("CSMSG_TRANSFER_WAIT", delay, channel->name);
6060 return 0;
6061 }
6062 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
6063 return 0;
6064 if(new_owner_hi == user->handle_info)
6065 {
6066 reply("CSMSG_NO_TRANSFER_SELF");
6067 return 0;
6068 }
6069 new_owner = GetChannelAccess(cData, new_owner_hi);
6070 if(!new_owner)
6071 {
6072 if(force)
6073 {
6074 new_owner = add_channel_user(cData, new_owner_hi, UL_COOWNER, 0, NULL);
6075 }
6076 else
6077 {
6078 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
6079 return 0;
6080 }
6081 }
6082 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
6083 {
6084 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
6085 return 0;
6086 }
6087 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
6088 if(!IsHelping(user))
6089 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
6090 else
6091 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi->handle);
6092 return 0;
6093 }
6094 if(new_owner->access >= UL_COOWNER)
6095 co_access = new_owner->access;
6096 else
6097 co_access = UL_COOWNER;
6098 new_owner->access = UL_OWNER;
6099 if(curr_user)
6100 curr_user->access = co_access;
6101 cData->ownerTransfer = now;
6102 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
6103 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
6104 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
6105 return 1;
6106 }
6107
6108 static CHANSERV_FUNC(cmd_suspend)
6109 {
6110 struct handle_info *hi;
6111 struct userData *self, *target;
6112
6113 REQUIRE_PARAMS(2);
6114 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6115 self = GetChannelUser(channel->channel_info, user->handle_info);
6116 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6117 {
6118 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6119 return 0;
6120 }
6121 if(target->access >= self->access)
6122 {
6123 reply("MSG_USER_OUTRANKED", hi->handle);
6124 return 0;
6125 }
6126 if(target->flags & USER_SUSPENDED)
6127 {
6128 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
6129 return 0;
6130 }
6131 if(target->present)
6132 {
6133 target->present = 0;
6134 target->seen = now;
6135 }
6136 target->flags |= USER_SUSPENDED;
6137 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
6138 return 1;
6139 }
6140
6141 static CHANSERV_FUNC(cmd_unsuspend)
6142 {
6143 struct handle_info *hi;
6144 struct userData *self, *target;
6145
6146 REQUIRE_PARAMS(2);
6147 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6148 self = GetChannelUser(channel->channel_info, user->handle_info);
6149 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6150 {
6151 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6152 return 0;
6153 }
6154 if(target->access >= self->access)
6155 {
6156 reply("MSG_USER_OUTRANKED", hi->handle);
6157 return 0;
6158 }
6159 if(!(target->flags & USER_SUSPENDED))
6160 {
6161 reply("CSMSG_NOT_SUSPENDED", hi->handle);
6162 return 0;
6163 }
6164 target->flags &= ~USER_SUSPENDED;
6165 scan_user_presence(target, NULL);
6166 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
6167 return 1;
6168 }
6169
6170 static MODCMD_FUNC(cmd_deleteme)
6171 {
6172 struct handle_info *hi;
6173 struct userData *target;
6174 const char *confirm_string;
6175 unsigned short access;
6176 char *channel_name;
6177
6178 hi = user->handle_info;
6179 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6180 {
6181 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6182 return 0;
6183 }
6184 if(target->access == UL_OWNER)
6185 {
6186 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
6187 return 0;
6188 }
6189 confirm_string = make_confirmation_string(target);
6190 if((argc < 2) || strcmp(argv[1], confirm_string))
6191 {
6192 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
6193 return 0;
6194 }
6195 access = target->access;
6196 channel_name = strdup(channel->name);
6197 del_channel_user(target, 1);
6198 reply("CSMSG_DELETED_YOU", access, channel_name);
6199 free(channel_name);
6200 return 1;
6201 }
6202
6203 static void
6204 chanserv_refresh_topics(UNUSED_ARG(void *data))
6205 {
6206 unsigned int refresh_num = (now - self->link) / chanserv_conf.refresh_period;
6207 struct chanData *cData;
6208 char opt;
6209
6210 for(cData = channelList; cData; cData = cData->next)
6211 {
6212 if(IsSuspended(cData))
6213 continue;
6214 opt = cData->chOpts[chTopicRefresh];
6215 if(opt == 'n')
6216 continue;
6217 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
6218 continue;
6219 if(cData->topic)
6220 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
6221 cData->last_refresh = refresh_num;
6222 }
6223 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
6224 }
6225
6226 static CHANSERV_FUNC(cmd_unf)
6227 {
6228 if(channel)
6229 {
6230 char response[MAXLEN];
6231 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
6232 sprintf(response, "\ 2%s\ 2: %s", user->nick, fmt);
6233 irc_privmsg(cmd->parent->bot, channel->name, response);
6234 }
6235 else
6236 reply("CSMSG_UNF_RESPONSE");
6237 return 1;
6238 }
6239
6240 static CHANSERV_FUNC(cmd_ping)
6241 {
6242 if(channel)
6243 {
6244 char response[MAXLEN];
6245 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
6246 sprintf(response, "\ 2%s\ 2: %s", user->nick, fmt);
6247 irc_privmsg(cmd->parent->bot, channel->name, response);
6248 }
6249 else
6250 reply("CSMSG_PING_RESPONSE");
6251 return 1;
6252 }
6253
6254 static CHANSERV_FUNC(cmd_wut)
6255 {
6256 if(channel)
6257 {
6258 char response[MAXLEN];
6259 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
6260 sprintf(response, "\ 2%s\ 2: %s", user->nick, fmt);
6261 irc_privmsg(cmd->parent->bot, channel->name, response);
6262 }
6263 else
6264 reply("CSMSG_WUT_RESPONSE");
6265 return 1;
6266 }
6267
6268 #ifdef lame8ball
6269 static CHANSERV_FUNC(cmd_8ball)
6270 {
6271 unsigned int i, j, accum;
6272 const char *resp;
6273
6274 REQUIRE_PARAMS(2);
6275 accum = 0;
6276 for(i=1; i<argc; i++)
6277 for(j=0; argv[i][j]; j++)
6278 accum = (accum << 5) - accum + toupper(argv[i][j]);
6279 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
6280 if(channel)
6281 {
6282 char response[MAXLEN];
6283 sprintf(response, "\ 2%s\ 2: %s", user->nick, resp);
6284 irc_privmsg(cmd->parent->bot, channel->name, response);
6285 }
6286 else
6287 send_message_type(4, user, cmd->parent->bot, "%s", resp);
6288 return 1;
6289 }
6290
6291 #else /* Use cool 8ball instead */
6292
6293 void eightball(char *outcome, int method, unsigned int seed)
6294 {
6295 int answer = 0;
6296
6297 #define NUMOFCOLORS 18
6298 char ballcolors[50][50] = {"blue", "red", "green", "yellow",
6299 "white", "black", "grey", "brown",
6300 "yellow", "pink", "purple", "orange", "teal", "burgandy",
6301 "fuchsia","turquoise","magenta", "cyan"};
6302 #define NUMOFLOCATIONS 50
6303 char balllocations[50][55] = {
6304 "Locke's house", "Oregon", "California", "Indiana", "Canada",
6305 "Russia", "Japan", "Florida", "the Bahamas", "Hiroshima",
6306 "the Caribbean", "the Everglades", "your head", "your pants", "your school",
6307 "the Statue of Liberty", "Mt. Fugi", "your mother's house", "IRC", "OSU",
6308 "Locke's cat", "the closet", "the washroom", "the lake", "Spain",
6309 "the bathtub", "the toilet", "the sewer", "a horse", "Jupiter",
6310 "Uranus", "Pluto", "a dark place", "your undies", "your shirt",
6311 "your bra", "your hair", "your bed", "the couch", "the wall",
6312 "Reed", "here --> [X]", "your brain", "Italy", "the Netherlands",
6313 "Mars", "my hardware", "the bar", "Neverland Ranch", "Germany" };
6314 #define NUMOFPREPS 15
6315 char ballpreps[50][50] = {
6316 "Near", "Somewhere near", "In", "In", "In",
6317 "In", "Hiding in", "Under", "Next to", "Over",
6318 "Crying in", "Right beside", "Nowhere near", "North of", "Trying to find"};
6319 #define NUMOFNUMS 34
6320 char ballnums[50][50] = {
6321 "A hundred", "A thousand", "A few", "42",
6322 "About 1", "About 2", "About 3", "About 4", "About 5", "About 6", "About 7", "About 8", "About 9", "About 10",
6323 "1", "2", "3", "4", "5", "6", "7", "8", "9", "Ten",
6324 "1", "2", "3", "4", "5", "6", "7", "8", "9", "Ten",
6325 };
6326 #define NUMOFMULTS 8
6327 char ballmults[50][50] = { " million", " or so", " thousand", "", " or less", " or more", "", ""};
6328
6329 /* Method:
6330 * 0: normal (Not used in x3)
6331 * 1: color
6332 * 2: where is
6333 * 3: how many
6334 */
6335
6336 srand(seed);
6337 if (method == 1) /* A Color */
6338 {
6339 char tmp[MAXLEN];
6340
6341 answer = (rand() % 12); /* Make sure this is the # of entries */
6342 switch(answer)
6343 {
6344 case 0: strcpy(tmp, "Very bright %s, I'd say.");
6345 break;
6346 case 1: strcpy(tmp, "Sort of a light %s color.");
6347 break;
6348 case 2: strcpy(tmp, "Dark and dreary %s.");
6349 break;
6350 case 3: strcpy(tmp, "Quite a pale shade of %s.");
6351 break;
6352 case 4: strcpy(tmp, "A gross kind of mucky %s.");
6353 break;
6354 case 5: strcpy(tmp, "Brilliant whiteish %s.");
6355 break;
6356 case 6: case 7: case 8: case 9: strcpy(tmp, "%s.");
6357 break;
6358 case 10: strcpy(tmp, "Solid %s.");
6359 break;
6360 case 11: strcpy(tmp, "Transparent %s.");
6361 break;
6362 default: strcpy(outcome, "An invalid random number was generated.");
6363 return;
6364 }
6365 sprintf(outcome, tmp, ballcolors[rand() % NUMOFCOLORS]);
6366 return;
6367 }
6368 else if (method == 2) /* Location */
6369 {
6370 sprintf(outcome, "%s %s.", ballpreps[rand() % NUMOFPREPS], balllocations[rand() % NUMOFLOCATIONS]);
6371 }
6372 else if (method == 3) /* Number of ___ */
6373 {
6374 sprintf(outcome, "%s%s.", ballnums[rand() % NUMOFNUMS], ballmults[rand() % NUMOFMULTS]);
6375 }
6376 else
6377 {
6378 //Debug(DBGWARNING, "Error in 8ball.");
6379 }
6380 return;
6381 }
6382
6383 static CHANSERV_FUNC(cmd_8ball)
6384 {
6385 char *word1, *word2, *word3;
6386 static char eb[MAXLEN];
6387 unsigned int accum, i, j;
6388
6389 REQUIRE_PARAMS(2);
6390 accum = 0;
6391 for(i=1; i<argc; i++)
6392 for(j=0; argv[i][j]; j++)
6393 accum = (accum << 5) - accum + toupper(argv[i][j]);
6394
6395 accum += time(NULL)/3600;
6396 word1 = argv[1];
6397 word2 = argc>2?argv[2]:"";
6398 word3 = argc>3?argv[3]:"";
6399
6400 /*** COLOR *****/
6401 if((word2) && strcasecmp(word1, "what") == 0 && strcasecmp(word2, "color") == 0)
6402 eightball(eb, 1, accum);
6403 else if((word3) && strcasecmp(word1, "what's") == 0 && strcasecmp(word2, "the") == 0 && strcasecmp(word3, "color") == 0)
6404 eightball(eb, 1, accum);
6405 else if((word3) && strcasecmp(word1, "whats") == 0 && strcasecmp(word2, "the") == 0 && strcasecmp(word3, "color") == 0)
6406 eightball(eb, 1, accum);
6407 /*** LOCATION *****/
6408 else if(
6409 (
6410 word2 &&
6411 (
6412 (strcasecmp(word1, "where") == 0) &&
6413 (strcasecmp(word2, "is") == 0)
6414 )
6415 ) ||
6416 (
6417 strcasecmp(word1, "where's") == 0
6418 )
6419 )
6420 eightball(eb, 2, accum);
6421 /*** NUMBER *****/
6422 else if((word2) && strcasecmp(word1, "how") == 0 && strcasecmp(word2, "many") == 0)
6423 eightball(eb, 3, accum);
6424 /*** GENERIC *****/
6425 else
6426 {
6427 /* Generic 8ball question.. so pull from x3.conf srvx style */
6428 const char *resp;
6429
6430 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
6431 if(channel)
6432 {
6433 char response[MAXLEN];
6434 sprintf(response, "\002%s\002: %s", user->nick, resp);
6435 irc_privmsg(cmd->parent->bot, channel->name, response);
6436 }
6437 else
6438 send_message_type(4, user, cmd->parent->bot, "%s", resp);
6439 return 1;
6440 }
6441
6442 if(channel)
6443 {
6444 char response[MAXLEN];
6445 sprintf(response, "\002%s\002: %s", user->nick, eb);
6446 irc_privmsg(cmd->parent->bot, channel->name, response);
6447 }
6448 else
6449 send_message_type(4, user, cmd->parent->bot, "%s", eb);
6450 return 1;
6451 }
6452 #endif
6453
6454 static CHANSERV_FUNC(cmd_d)
6455 {
6456 unsigned long sides, count, modifier, ii, total;
6457 char response[MAXLEN], *sep;
6458 const char *fmt;
6459
6460 REQUIRE_PARAMS(2);
6461 if((count = strtoul(argv[1], &sep, 10)) < 1)
6462 goto no_dice;
6463 if(sep[0] == 0)
6464 {
6465 if(count == 1)
6466 goto no_dice;
6467 sides = count;
6468 count = 1;
6469 modifier = 0;
6470 }
6471 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
6472 && (sides = strtoul(sep+1, &sep, 10)) > 1)
6473 {
6474 if(sep[0] == 0)
6475 modifier = 0;
6476 else if((sep[0] == '-') && isdigit(sep[1]))
6477 modifier = strtoul(sep, NULL, 10);
6478 else if((sep[0] == '+') && isdigit(sep[1]))
6479 modifier = strtoul(sep+1, NULL, 10);
6480 else
6481 goto no_dice;
6482 }
6483 else
6484 {
6485 no_dice:
6486 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
6487 return 0;
6488 }
6489 if(count > 10)
6490 {
6491 reply("CSMSG_BAD_DICE_COUNT", count, 10);
6492 return 0;
6493 }
6494 for(total = ii = 0; ii < count; ++ii)
6495 total += (rand() % sides) + 1;
6496 total += modifier;
6497
6498 if((count > 1) || modifier)
6499 {
6500 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
6501 sprintf(response, fmt, total, count, sides, modifier);
6502 }
6503 else
6504 {
6505 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
6506 sprintf(response, fmt, total, sides);
6507 }
6508 if(channel)
6509 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
6510 else
6511 send_message_type(4, user, cmd->parent->bot, "%s", response);
6512 return 1;
6513 }
6514
6515 static CHANSERV_FUNC(cmd_huggle)
6516 {
6517 /* CTCP must be via PRIVMSG, never notice */
6518 if(channel)
6519 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
6520 else
6521 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
6522 return 1;
6523 }
6524
6525 static CHANSERV_FUNC(cmd_calc)
6526 {
6527 char response[MAXLEN];
6528
6529 REQUIRE_PARAMS(2);
6530 do_math(response, unsplit_string(argv + 1, argc - 1, NULL));
6531
6532 if(channel)
6533 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
6534 else
6535 send_message_type(4, user, cmd->parent->bot, "%s", response);
6536 return 1;
6537 }
6538
6539 static void
6540 chanserv_adjust_limit(void *data)
6541 {
6542 struct mod_chanmode change;
6543 struct chanData *cData = data;
6544 struct chanNode *channel = cData->channel;
6545 unsigned int limit;
6546
6547 if(IsSuspended(cData))
6548 return;
6549
6550 cData->limitAdjusted = now;
6551 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
6552 if(cData->modes.modes_set & MODE_LIMIT)
6553 {
6554 if(limit > cData->modes.new_limit)
6555 limit = cData->modes.new_limit;
6556 else if(limit == cData->modes.new_limit)
6557 return;
6558 }
6559
6560 mod_chanmode_init(&change);
6561 change.modes_set = MODE_LIMIT;
6562 change.new_limit = limit;
6563 mod_chanmode_announce(chanserv, channel, &change);
6564 }
6565
6566 static void
6567 handle_new_channel(struct chanNode *channel)
6568 {
6569 struct chanData *cData;
6570
6571 if(!(cData = channel->channel_info))
6572 return;
6573
6574 if(cData->modes.modes_set || cData->modes.modes_clear)
6575 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
6576
6577 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
6578 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
6579 }
6580
6581 /* Welcome to my worst nightmare. Warning: Read (or modify)
6582 the code below at your own risk. */
6583 static int
6584 handle_join(struct modeNode *mNode)
6585 {
6586 struct mod_chanmode change;
6587 struct userNode *user = mNode->user;
6588 struct chanNode *channel = mNode->channel;
6589 struct chanData *cData;
6590 struct userData *uData = NULL;
6591 struct banData *bData;
6592 struct handle_info *handle;
6593 unsigned int modes = 0, info = 0;
6594 char *greeting;
6595
6596 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
6597 return 0;
6598
6599 cData = channel->channel_info;
6600 if(channel->members.used > cData->max)
6601 cData->max = channel->members.used;
6602
6603 /* Check for bans. If they're joining through a ban, one of two
6604 * cases applies:
6605 * 1: Join during a netburst, by riding the break. Kick them
6606 * unless they have ops or voice in the channel.
6607 * 2: They're allowed to join through the ban (an invite in
6608 * ircu2.10, or a +e on Hybrid, or something).
6609 * If they're not joining through a ban, and the banlist is not
6610 * full, see if they're on the banlist for the channel. If so,
6611 * kickban them.
6612 */
6613 /* This is really, really stupid. not all banned people are kicked.
6614 * sometimes we like to leave them unkicked.
6615 * I tried to explain this to the srvx developers and
6616 * got insulted.. hence one reason for this fork.
6617 *
6618 if(user->uplink->burst && !mNode->modes)
6619 {
6620 unsigned int ii;
6621 for(ii = 0; ii < channel->banlist.used; ii++)
6622 {
6623 if(user_matches_glob(user, channel->banlist.list[ii]->ban, 1))
6624 {
6625 ** Riding a netburst. Naughty. **
6626 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
6627 return 1;
6628 }
6629 }
6630 }
6631 */
6632
6633 mod_chanmode_init(&change);
6634 change.argc = 1;
6635 if(channel->banlist.used < MAXBANS)
6636 {
6637 /* Not joining through a ban. */
6638 for(bData = cData->bans;
6639 bData && !user_matches_glob(user, bData->mask, 1);
6640 bData = bData->next);
6641
6642 if(bData)
6643 {
6644 char kick_reason[MAXLEN];
6645 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6646
6647 bData->triggered = now;
6648 if(bData != cData->bans)
6649 {
6650 /* Shuffle the ban to the head of the list. */
6651 if(bData->next)
6652 bData->next->prev = bData->prev;
6653 if(bData->prev)
6654 bData->prev->next = bData->next;
6655
6656 bData->prev = NULL;
6657 bData->next = cData->bans;
6658
6659 if(cData->bans)
6660 cData->bans->prev = bData;
6661 cData->bans = bData;
6662 }
6663
6664 change.args[0].mode = MODE_BAN;
6665 change.args[0].u.hostmask = bData->mask;
6666 mod_chanmode_announce(chanserv, channel, &change);
6667 KickChannelUser(user, channel, chanserv, kick_reason);
6668 return 1;
6669 }
6670 }
6671
6672 /* ChanServ will not modify the limits in join-flooded channels.
6673 It will also skip DynLimit processing when the user (or srvx)
6674 is bursting in, because there are likely more incoming. */
6675 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6676 && !user->uplink->burst
6677 && !channel->join_flooded
6678 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
6679 {
6680 /* The user count has begun "bumping" into the channel limit,
6681 so set a timer to raise the limit a bit. Any previous
6682 timers are removed so three incoming users within the delay
6683 results in one limit change, not three. */
6684
6685 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6686 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6687 }
6688
6689 if(channel->join_flooded)
6690 {
6691 /* don't automatically give non users ops or voice during a join flood */
6692 }
6693 /* EVERYONE is to get voice */
6694 else if(cData->chOpts[chVoice] == 'a')
6695 modes |= MODE_VOICE;
6696
6697 greeting = cData->greeting;
6698 if(user->handle_info)
6699 {
6700 handle = user->handle_info;
6701
6702 if(IsHelper(user) && !IsHelping(user))
6703 {
6704 unsigned int ii;
6705 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6706 {
6707 if(channel == chanserv_conf.support_channels.list[ii])
6708 {
6709 HANDLE_SET_FLAG(user->handle_info, HELPING);
6710 break;
6711 }
6712 }
6713 }
6714
6715 uData = GetTrueChannelAccess(cData, handle);
6716 if(uData && !IsUserSuspended(uData))
6717 {
6718 /* non users getting voice are handled above. */
6719 if(IsUserAutoOp(uData))
6720 {
6721 if(uData->access >= UL_OP /*cData->lvlOpts[lvlGiveOps]*/)
6722 modes |= MODE_CHANOP;
6723 if(uData->access >= UL_HALFOP /*cData->lvlOpts[lvlGiveHalfOps]*/)
6724 modes |= MODE_HALFOP;
6725 else if(uData->access >= UL_PEON && cData->chOpts[chVoice] == 'p')
6726 modes |= MODE_VOICE;
6727 }
6728 if(uData->access >= UL_PRESENT)
6729 cData->visited = now;
6730 if(cData->user_greeting)
6731 greeting = cData->user_greeting;
6732 if(uData->info
6733 && (uData->access >= cData->lvlOpts[lvlUserInfo])
6734 && ((now - uData->seen) >= chanserv_conf.info_delay)
6735 && !uData->present)
6736 info = 1;
6737 uData->seen = now;
6738 uData->present = 1;
6739 }
6740 }
6741 if(!user->uplink->burst)
6742 {
6743 if(modes)
6744 {
6745 if(modes & MODE_CHANOP) {
6746 modes &= ~MODE_HALFOP;
6747 modes &= ~MODE_VOICE;
6748 }
6749 change.args[0].mode = modes;
6750 change.args[0].u.member = mNode;
6751 mod_chanmode_announce(chanserv, channel, &change);
6752 }
6753 if(greeting && !user->uplink->burst)
6754 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
6755 if(uData && info)
6756 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
6757 }
6758 return 0;
6759 }
6760
6761 static void
6762 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
6763 {
6764 struct mod_chanmode change;
6765 struct userData *channel;
6766 unsigned int ii, jj;
6767
6768 if(!user->handle_info)
6769 return;
6770
6771 mod_chanmode_init(&change);
6772 change.argc = 1;
6773 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
6774 {
6775 struct chanNode *cn;
6776 struct modeNode *mn;
6777 if(IsUserSuspended(channel)
6778 || IsSuspended(channel->channel)
6779 || !(cn = channel->channel->channel))
6780 continue;
6781
6782 mn = GetUserMode(cn, user);
6783 if(!mn)
6784 {
6785 if(!IsUserSuspended(channel)
6786 && IsUserAutoInvite(channel)
6787 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
6788 && !self->burst
6789 && !user->uplink->burst)
6790 irc_invite(chanserv, user, cn);
6791 continue;
6792 }
6793
6794 if(channel->access >= UL_PRESENT)
6795 channel->channel->visited = now;
6796
6797 if(IsUserAutoOp(channel))
6798 {
6799 if(channel->access >= UL_OP /* cn->channel_info->lvlOpts[lvlGiveOps] */)
6800 change.args[0].mode = MODE_CHANOP;
6801 else if(channel->access >= UL_HALFOP /* cn->channel_info->lvlOpts[lvlGiveHalfOps]*/)
6802 change.args[0].mode = MODE_HALFOP;
6803 else if(channel->access >= UL_PEON /* cn->channel_info->lvlOpts[lvlGiveVoice]*/)
6804 change.args[0].mode = MODE_VOICE;
6805 else
6806 change.args[0].mode = 0;
6807 change.args[0].u.member = mn;
6808 if(change.args[0].mode)
6809 mod_chanmode_announce(chanserv, cn, &change);
6810 }
6811
6812 channel->seen = now;
6813 channel->present = 1;
6814 }
6815
6816 for(ii = 0; ii < user->channels.used; ++ii)
6817 {
6818 struct chanNode *channel = user->channels.list[ii]->channel;
6819 struct banData *ban;
6820
6821 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_HALFOP|MODE_VOICE))
6822 || !channel->channel_info
6823 || IsSuspended(channel->channel_info))
6824 continue;
6825 for(jj = 0; jj < channel->banlist.used; ++jj)
6826 if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
6827 break;
6828 if(jj < channel->banlist.used)
6829 continue;
6830 for(ban = channel->channel_info->bans; ban; ban = ban->next)
6831 {
6832 char kick_reason[MAXLEN];
6833 if(!user_matches_glob(user, ban->mask, 1))
6834 continue;
6835 change.args[0].mode = MODE_BAN;
6836 change.args[0].u.hostmask = ban->mask;
6837 mod_chanmode_announce(chanserv, channel, &change);
6838 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
6839 KickChannelUser(user, channel, chanserv, kick_reason);
6840 ban->triggered = now;
6841 break;
6842 }
6843 }
6844
6845 if(IsSupportHelper(user))
6846 {
6847 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6848 {
6849 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
6850 {
6851 HANDLE_SET_FLAG(user->handle_info, HELPING);
6852 break;
6853 }
6854 }
6855 }
6856 }
6857
6858 static void
6859 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
6860 {
6861 struct chanData *cData;
6862 struct userData *uData;
6863
6864 cData = mn->channel->channel_info;
6865 if(!cData || IsSuspended(cData) || IsLocal(mn->user))
6866 return;
6867
6868 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
6869 {
6870 /* Allow for a bit of padding so that the limit doesn't
6871 track the user count exactly, which could get annoying. */
6872 if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
6873 {
6874 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6875 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6876 }
6877 }
6878
6879 if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
6880 {
6881 scan_user_presence(uData, mn->user);
6882 uData->seen = now;
6883 }
6884
6885 if(IsHelping(mn->user) && IsSupportHelper(mn->user))
6886 {
6887 unsigned int ii, jj;
6888 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6889 {
6890 for(jj = 0; jj < mn->user->channels.used; ++jj)
6891 if(mn->user->channels.list[jj]->channel == chanserv_conf.support_channels.list[ii])
6892 break;
6893 if(jj < mn->user->channels.used)
6894 break;
6895 }
6896 if(ii == chanserv_conf.support_channels.used)
6897 HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
6898 }
6899 }
6900
6901 static void
6902 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
6903 {
6904 struct userData *uData;
6905
6906 if(!channel->channel_info || !kicker || IsService(kicker)
6907 || (kicker == victim) || IsSuspended(channel->channel_info)
6908 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
6909 return;
6910
6911 if(protect_user(victim, kicker, channel->channel_info))
6912 {
6913 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED_KICK");
6914 KickChannelUser(kicker, channel, chanserv, reason);
6915 }
6916
6917 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
6918 uData->seen = now;
6919 }
6920
6921 static int
6922 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
6923 {
6924 struct chanData *cData;
6925
6926 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
6927 return 0;
6928
6929 cData = channel->channel_info;
6930 if(bad_topic(channel, user, channel->topic))
6931 { /* User doesnt have privs to set topics. Undo it */
6932 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
6933 SetChannelTopic(channel, chanserv, old_topic, 1);
6934 return 1;
6935 }
6936 /* If there is a topic mask set, and the new topic doesnt match,
6937 * set the topic to mask + new_topic */
6938 if(cData->topic_mask && !match_ircglob(channel->topic, cData->topic_mask))
6939 {
6940 char new_topic[TOPICLEN+1];
6941 conform_topic(cData->topic_mask, channel->topic, new_topic);
6942 if(*new_topic)
6943 {
6944 SetChannelTopic(channel, chanserv, new_topic, 1);
6945 /* and fall through to topicsnarf code below.. */
6946 }
6947 else /* Topic couldnt fit into mask, was too long */
6948 {
6949 SetChannelTopic(channel, chanserv, old_topic, 1);
6950 send_message(user, chanserv, "CSMSG_TOPICMASK_CONFLICT1", channel->name, cData->topic_mask);
6951 send_message(user, chanserv, "CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
6952 return 1;
6953 }
6954 }
6955 /* With topicsnarf, grab the topic and save it as the default topic. */
6956 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
6957 {
6958 free(cData->topic);
6959 cData->topic = strdup(channel->topic);
6960 }
6961 return 0;
6962 }
6963
6964 static void
6965 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
6966 {
6967 struct mod_chanmode *bounce = NULL;
6968 unsigned int bnc, ii;
6969 char deopped = 0;
6970
6971 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
6972 return;
6973
6974 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
6975 && mode_lock_violated(&channel->channel_info->modes, change))
6976 {
6977 char correct[MAXLEN];
6978 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
6979 mod_chanmode_format(&channel->channel_info->modes, correct);
6980 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
6981 }
6982 for(ii = bnc = 0; ii < change->argc; ++ii)
6983 {
6984 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
6985 {
6986 const struct userNode *victim = change->args[ii].u.member->user;
6987 if(!protect_user(victim, user, channel->channel_info))
6988 continue;
6989 if(!bounce)
6990 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6991 if(!deopped)
6992 {
6993 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6994 bounce->args[bnc].u.member = GetUserMode(channel, user);
6995 if(bounce->args[bnc].u.member)
6996 bnc++;
6997 deopped = 1;
6998 }
6999 bounce->args[bnc].mode = MODE_CHANOP;
7000 bounce->args[bnc].u.member = change->args[ii].u.member;
7001 bnc++;
7002 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
7003 }
7004 else if(change->args[ii].mode & MODE_CHANOP)
7005 {
7006 const struct userNode *victim = change->args[ii].u.member->user;
7007 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
7008 continue;
7009 if(!bounce)
7010 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7011 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
7012 bounce->args[bnc].u.member = change->args[ii].u.member;
7013 bnc++;
7014 }
7015 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
7016 {
7017 const char *ban = change->args[ii].u.hostmask;
7018 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
7019 continue;
7020 if(!bounce)
7021 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7022 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
7023 bounce->args[bnc].u.hostmask = strdup(ban);
7024 bnc++;
7025 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
7026 }
7027 }
7028 if(bounce)
7029 {
7030 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
7031 mod_chanmode_announce(chanserv, channel, bounce);
7032 for(ii = 0; ii < change->argc; ++ii)
7033 if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
7034 free((char*)bounce->args[ii].u.hostmask);
7035 mod_chanmode_free(bounce);
7036 }
7037 }
7038
7039 static void
7040 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
7041 {
7042 struct chanNode *channel;
7043 struct banData *bData;
7044 struct mod_chanmode change;
7045 unsigned int ii, jj;
7046 char kick_reason[MAXLEN];
7047
7048 mod_chanmode_init(&change);
7049 change.argc = 1;
7050 change.args[0].mode = MODE_BAN;
7051 for(ii = 0; ii < user->channels.used; ++ii)
7052 {
7053 channel = user->channels.list[ii]->channel;
7054 /* Need not check for bans if they're opped or voiced. */
7055 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_HALFOP|MODE_VOICE))
7056 continue;
7057 /* Need not check for bans unless channel registration is active. */
7058 if(!channel->channel_info || IsSuspended(channel->channel_info))
7059 continue;
7060 /* Look for a matching ban already on the channel. */
7061 for(jj = 0; jj < channel->banlist.used; ++jj)
7062 if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
7063 break;
7064 /* Need not act if we found one. */
7065 if(jj < channel->banlist.used)
7066 continue;
7067 /* Look for a matching ban in this channel. */
7068 for(bData = channel->channel_info->bans; bData; bData = bData->next)
7069 {
7070 if(!user_matches_glob(user, bData->mask, 1))
7071 continue;
7072 change.args[0].u.hostmask = bData->mask;
7073 mod_chanmode_announce(chanserv, channel, &change);
7074 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
7075 KickChannelUser(user, channel, chanserv, kick_reason);
7076 bData->triggered = now;
7077 break; /* we don't need to check any more bans in the channel */
7078 }
7079 }
7080 }
7081
7082 static void handle_rename(struct handle_info *handle, const char *old_handle)
7083 {
7084 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
7085
7086 if(dnr)
7087 {
7088 dict_remove2(handle_dnrs, old_handle, 1);
7089 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
7090 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
7091 }
7092 }
7093
7094 static void
7095 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
7096 {
7097 struct userNode *h_user;
7098
7099 if(handle->channels)
7100 {
7101 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
7102 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
7103
7104 while(handle->channels)
7105 del_channel_user(handle->channels, 1);
7106 }
7107 }
7108
7109 static void
7110 handle_server_link(UNUSED_ARG(struct server *server))
7111 {
7112 struct chanData *cData;
7113
7114 for(cData = channelList; cData; cData = cData->next)
7115 {
7116 if(!IsSuspended(cData))
7117 cData->may_opchan = 1;
7118 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
7119 && !cData->channel->join_flooded
7120 && ((cData->channel->limit - cData->channel->members.used)
7121 < chanserv_conf.adjust_threshold))
7122 {
7123 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7124 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7125 }
7126 }
7127 }
7128
7129 static void
7130 chanserv_conf_read(void)
7131 {
7132 dict_t conf_node;
7133 const char *str;
7134 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
7135 struct mod_chanmode *change;
7136 struct string_list *strlist;
7137 struct chanNode *chan;
7138 unsigned int ii;
7139
7140 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
7141 {
7142 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
7143 return;
7144 }
7145 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7146 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7147 chanserv_conf.support_channels.used = 0;
7148 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
7149 {
7150 for(ii = 0; ii < strlist->used; ++ii)
7151 {
7152 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7153 if(!str2)
7154 str2 = "+nt";
7155 chan = AddChannel(strlist->list[ii], now, str2, NULL, NULL);
7156 LockChannel(chan);
7157 channelList_append(&chanserv_conf.support_channels, chan);
7158 }
7159 }
7160 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
7161 {
7162 const char *str2;
7163 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7164 if(!str2)
7165 str2 = "+nt";
7166 chan = AddChannel(str, now, str2, NULL, NULL);
7167 LockChannel(chan);
7168 channelList_append(&chanserv_conf.support_channels, chan);
7169 }
7170 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
7171 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
7172 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
7173 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
7174 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
7175 chanserv_conf.greeting_length = str ? atoi(str) : 200;
7176 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
7177 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
7178 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
7179 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
7180 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
7181 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
7182 str = database_get_data(conf_node, KEY_BAN_TIMEOUT_FREQ, RECDB_QSTRING);
7183 chanserv_conf.ban_timeout_frequency = str ? ParseInterval(str) : 600;
7184 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
7185 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
7186 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
7187 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
7188 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
7189 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
7190 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
7191 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
7192 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
7193 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
7194 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
7195 if(chanserv && str)
7196 NickChange(chanserv, str, 0);
7197 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
7198 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
7199 str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
7200 chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
7201 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
7202 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
7203 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
7204 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
7205 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
7206 chanserv_conf.max_owned = str ? atoi(str) : 5;
7207 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
7208 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
7209 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
7210 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
7211 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
7212 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
7213 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
7214 if(!str)
7215 str = "+nt";
7216 safestrncpy(mode_line, str, sizeof(mode_line));
7217 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
7218 if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE)) && (change->argc < 2))
7219 {
7220 chanserv_conf.default_modes = *change;
7221 mod_chanmode_free(change);
7222 }
7223 free_string_list(chanserv_conf.set_shows);
7224 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
7225 if(strlist)
7226 strlist = string_list_copy(strlist);
7227 else
7228 {
7229 static const char *list[] = {
7230 /* free form text */
7231 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
7232 /* options based on user level */
7233 "PubCmd", "InviteMe", "UserInfo",/* "GiveVoice", "GiveHalfOps", "GiveOps", */ "EnfOps",
7234 "EnfHalfOps", "EnfModes", "EnfTopic", "TopicSnarf", "Setters", /*"CtcpUsers", */
7235 /* multiple choice options */
7236 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
7237 /* binary options */
7238 "DynLimit", "NoDelete", "BanTimeout",
7239 /* delimiter */
7240 NULL
7241 };
7242 unsigned int ii;
7243 strlist = alloc_string_list(ArrayLength(list)-1);
7244 for(ii=0; list[ii]; ii++)
7245 string_list_append(strlist, strdup(list[ii]));
7246 }
7247 chanserv_conf.set_shows = strlist;
7248 /* We don't look things up now, in case the list refers to options
7249 * defined by modules initialized after this point. Just mark the
7250 * function list as invalid, so it will be initialized.
7251 */
7252 set_shows_list.used = 0;
7253 free_string_list(chanserv_conf.eightball);
7254 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
7255 if(strlist)
7256 {
7257 strlist = string_list_copy(strlist);
7258 }
7259 else
7260 {
7261 strlist = alloc_string_list(4);
7262 string_list_append(strlist, strdup("Yes."));
7263 string_list_append(strlist, strdup("No."));
7264 string_list_append(strlist, strdup("Maybe so."));
7265 }
7266 chanserv_conf.eightball = strlist;
7267 free_string_list(chanserv_conf.old_ban_names);
7268 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
7269 if(strlist)
7270 strlist = string_list_copy(strlist);
7271 else
7272 strlist = alloc_string_list(2);
7273 chanserv_conf.old_ban_names = strlist;
7274 str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
7275 off_channel = str ? atoi(str) : 0;
7276 }
7277
7278 static void
7279 chanserv_note_type_read(const char *key, struct record_data *rd)
7280 {
7281 dict_t obj;
7282 struct note_type *ntype;
7283 const char *str;
7284
7285 if(!(obj = GET_RECORD_OBJECT(rd)))
7286 {
7287 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
7288 return;
7289 }
7290 if(!(ntype = chanserv_create_note_type(key)))
7291 {
7292 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
7293 return;
7294 }
7295
7296 /* Figure out set access */
7297 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
7298 {
7299 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7300 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
7301 }
7302 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
7303 {
7304 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
7305 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
7306 }
7307 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
7308 {
7309 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
7310 }
7311 else
7312 {
7313 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
7314 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7315 ntype->set_access.min_opserv = 0;
7316 }
7317
7318 /* Figure out visibility */
7319 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
7320 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7321 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
7322 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7323 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
7324 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
7325 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
7326 ntype->visible_type = NOTE_VIS_ALL;
7327 else
7328 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7329
7330 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
7331 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
7332 }
7333
7334 static void
7335 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7336 {
7337 struct handle_info *handle;
7338 struct userData *uData;
7339 char *seen, *inf, *flags;
7340 time_t last_seen;
7341 unsigned short access;
7342
7343 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7344 {
7345 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
7346 return;
7347 }
7348
7349 access = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
7350 if(access > UL_OWNER)
7351 {
7352 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
7353 return;
7354 }
7355
7356 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
7357 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
7358 last_seen = seen ? (signed)strtoul(seen, NULL, 0) : now;
7359 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
7360 handle = get_handle_info(key);
7361 if(!handle)
7362 {
7363 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
7364 return;
7365 }
7366
7367 uData = add_channel_user(chan, handle, access, last_seen, inf);
7368 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
7369
7370 /* Upgrade: set autoop to the inverse of noautoop */
7371 if(chanserv_read_version < 2)
7372 {
7373 /* if noautoop is true, set autoop false, and vice versa */
7374 if(uData->flags & USER_NOAUTO_OP)
7375 uData->flags = uData->flags & ~USER_AUTO_OP;
7376 else
7377 uData->flags = uData->flags | USER_AUTO_OP;
7378 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);
7379 }
7380
7381 }
7382
7383 static void
7384 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7385 {
7386 struct banData *bData;
7387 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
7388 time_t set_time, triggered_time, expires_time;
7389
7390 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7391 {
7392 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
7393 return;
7394 }
7395
7396 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
7397 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
7398 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
7399 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
7400 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
7401 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
7402 if (!reason || !owner)
7403 return;
7404
7405 set_time = set ? (time_t)strtoul(set, NULL, 0) : now;
7406 triggered_time = triggered ? (time_t)strtoul(triggered, NULL, 0) : 0;
7407 if(s_expires)
7408 expires_time = (time_t)strtoul(s_expires, NULL, 0);
7409 else if(s_duration)
7410 expires_time = set_time + atoi(s_duration);
7411 else
7412 expires_time = 0;
7413
7414 if(!reason || (expires_time && (expires_time < now)))
7415 return;
7416
7417 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
7418 }
7419
7420 static struct suspended *
7421 chanserv_read_suspended(dict_t obj)
7422 {
7423 struct suspended *suspended = calloc(1, sizeof(*suspended));
7424 char *str;
7425 dict_t previous;
7426
7427 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
7428 suspended->expires = str ? (time_t)strtoul(str, NULL, 0) : 0;
7429 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
7430 suspended->revoked = str ? (time_t)strtoul(str, NULL, 0) : 0;
7431 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
7432 suspended->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
7433 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
7434 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
7435 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
7436 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
7437 return suspended;
7438 }
7439
7440 static int
7441 chanserv_channel_read(const char *key, struct record_data *hir)
7442 {
7443 struct suspended *suspended;
7444 struct mod_chanmode *modes;
7445 struct chanNode *cNode;
7446 struct chanData *cData;
7447 struct dict *channel, *obj;
7448 char *str, *argv[10];
7449 dict_iterator_t it;
7450 unsigned int argc;
7451
7452 channel = hir->d.object;
7453
7454 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
7455 if(!str)
7456 str = "<unknown>";
7457 cNode = AddChannel(key, now, NULL, NULL, NULL);
7458 if(!cNode)
7459 {
7460 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
7461 return 0;
7462 }
7463 cData = register_channel(cNode, str);
7464 if(!cData)
7465 {
7466 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
7467 return 0;
7468 }
7469
7470 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
7471 {
7472 enum levelOption lvlOpt;
7473 enum charOption chOpt;
7474
7475 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
7476 cData->flags = atoi(str);
7477
7478 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7479 {
7480 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
7481 if(str)
7482 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
7483 else if(levelOptions[lvlOpt].old_flag)
7484 {
7485 if(cData->flags & levelOptions[lvlOpt].old_flag)
7486 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
7487 else
7488 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
7489 }
7490 }
7491
7492 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7493 {
7494 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
7495 continue;
7496 cData->chOpts[chOpt] = str[0];
7497 }
7498 }
7499 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
7500 {
7501 enum levelOption lvlOpt;
7502 enum charOption chOpt;
7503 unsigned int count;
7504
7505 cData->flags = base64toint(str, 5);
7506 count = strlen(str += 5);
7507 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7508 {
7509 unsigned short lvl;
7510 if(levelOptions[lvlOpt].old_flag)
7511 {
7512 if(cData->flags & levelOptions[lvlOpt].old_flag)
7513 lvl = levelOptions[lvlOpt].flag_value;
7514 else
7515 lvl = levelOptions[lvlOpt].default_value;
7516 }
7517 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
7518 {
7519 case 'c': lvl = UL_COOWNER; break;
7520 case 'm': lvl = UL_MANAGER; break;
7521 case 'n': lvl = UL_OWNER+1; break;
7522 case 'o': lvl = UL_OP; break;
7523 case 'p': lvl = UL_PEON; break;
7524 case 'h': lvl = UL_HALFOP; break;
7525 case 'w': lvl = UL_OWNER; break;
7526 default: lvl = 0; break;
7527 }
7528 cData->lvlOpts[lvlOpt] = lvl;
7529 }
7530 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7531 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
7532 }
7533
7534 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
7535 {
7536 suspended = chanserv_read_suspended(obj);
7537 cData->suspended = suspended;
7538 suspended->cData = cData;
7539 /* We could use suspended->expires and suspended->revoked to
7540 * set the CHANNEL_SUSPENDED flag, but we don't. */
7541 }
7542 else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
7543 {
7544 suspended = calloc(1, sizeof(*suspended));
7545 suspended->issued = 0;
7546 suspended->revoked = 0;
7547 suspended->suspender = strdup(str);
7548 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
7549 suspended->expires = str ? atoi(str) : 0;
7550 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
7551 suspended->reason = strdup(str ? str : "No reason");
7552 suspended->previous = NULL;
7553 cData->suspended = suspended;
7554 suspended->cData = cData;
7555 }
7556 else
7557 {
7558 cData->flags &= ~CHANNEL_SUSPENDED;
7559 suspended = NULL; /* to squelch a warning */
7560 }
7561
7562 if(IsSuspended(cData)) {
7563 if(suspended->expires > now)
7564 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
7565 else if(suspended->expires)
7566 cData->flags &= ~CHANNEL_SUSPENDED;
7567 }
7568
7569 if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
7570 struct mod_chanmode change;
7571 mod_chanmode_init(&change);
7572 change.argc = 1;
7573 change.args[0].mode = MODE_CHANOP;
7574 change.args[0].u.member = AddChannelUser(chanserv, cNode);
7575 mod_chanmode_announce(chanserv, cNode, &change);
7576 }
7577
7578 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
7579 cData->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
7580 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
7581 cData->visited = str ? (time_t)strtoul(str, NULL, 0) : now;
7582 str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
7583 cData->ownerTransfer = str ? (time_t)strtoul(str, NULL, 0) : 0;
7584 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
7585 cData->max = str ? atoi(str) : 0;
7586 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
7587 cData->greeting = str ? strdup(str) : NULL;
7588 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
7589 cData->user_greeting = str ? strdup(str) : NULL;
7590 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
7591 cData->topic_mask = str ? strdup(str) : NULL;
7592 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
7593 cData->topic = str ? strdup(str) : NULL;
7594
7595 if(!IsSuspended(cData)
7596 && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
7597 && (argc = split_line(str, 0, ArrayLength(argv), argv))
7598 && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE))) {
7599 cData->modes = *modes;
7600 if(off_channel > 0)
7601 cData->modes.modes_set |= MODE_REGISTERED;
7602 if(cData->modes.argc > 1)
7603 cData->modes.argc = 1;
7604 mod_chanmode_announce(chanserv, cNode, &cData->modes);
7605 mod_chanmode_free(modes);
7606 }
7607
7608 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
7609 for(it = dict_first(obj); it; it = iter_next(it))
7610 user_read_helper(iter_key(it), iter_data(it), cData);
7611
7612 if(!cData->users && !IsProtected(cData))
7613 {
7614 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
7615 unregister_channel(cData, "has empty user list.");
7616 return 0;
7617 }
7618
7619 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
7620 for(it = dict_first(obj); it; it = iter_next(it))
7621 ban_read_helper(iter_key(it), iter_data(it), cData);
7622
7623 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
7624 for(it = dict_first(obj); it; it = iter_next(it))
7625 {
7626 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
7627 struct record_data *rd = iter_data(it);
7628 const char *note, *setter;
7629
7630 if(rd->type != RECDB_OBJECT)
7631 {
7632 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
7633 }
7634 else if(!ntype)
7635 {
7636 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
7637 }
7638 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
7639 {
7640 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
7641 }
7642 else
7643 {
7644 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
7645 if(!setter) setter = "<unknown>";
7646 chanserv_add_channel_note(cData, ntype, setter, note);
7647 }
7648 }
7649
7650 return 0;
7651 }
7652
7653 static void
7654 chanserv_dnr_read(const char *key, struct record_data *hir)
7655 {
7656 const char *setter, *reason, *str;
7657 struct do_not_register *dnr;
7658
7659 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
7660 if(!setter)
7661 {
7662 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
7663 return;
7664 }
7665 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
7666 if(!reason)
7667 {
7668 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
7669 return;
7670 }
7671 dnr = chanserv_add_dnr(key, setter, reason);
7672 if(!dnr)
7673 return;
7674 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
7675 if(str)
7676 dnr->set = atoi(str);
7677 else
7678 dnr->set = 0;
7679 }
7680
7681 static void
7682 chanserv_version_read(struct dict *section)
7683 {
7684 /* global var.. */
7685 char *str;
7686 str = database_get_data(section, KEY_VERSION_NUMBER, RECDB_QSTRING);
7687 if(str)
7688 chanserv_read_version = atoi(str);
7689 log_module(CS_LOG, LOG_DEBUG, "Chanserv db version is %d.", chanserv_read_version);
7690 }
7691
7692 static int
7693 chanserv_saxdb_read(struct dict *database)
7694 {
7695 struct dict *section;
7696 dict_iterator_t it;
7697
7698 if((section = database_get_data(database, KEY_VERSION_CONTROL, RECDB_OBJECT)))
7699 chanserv_version_read(section);
7700
7701 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
7702 for(it = dict_first(section); it; it = iter_next(it))
7703 chanserv_note_type_read(iter_key(it), iter_data(it));
7704
7705 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
7706 for(it = dict_first(section); it; it = iter_next(it))
7707 chanserv_channel_read(iter_key(it), iter_data(it));
7708
7709 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
7710 for(it = dict_first(section); it; it = iter_next(it))
7711 chanserv_dnr_read(iter_key(it), iter_data(it));
7712
7713 return 0;
7714 }
7715
7716 static int
7717 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
7718 {
7719 int high_present = 0;
7720 saxdb_start_record(ctx, KEY_USERS, 1);
7721 for(; uData; uData = uData->next)
7722 {
7723 if((uData->access >= UL_PRESENT) && uData->present)
7724 high_present = 1;
7725 saxdb_start_record(ctx, uData->handle->handle, 0);
7726 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
7727 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
7728 if(uData->flags)
7729 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
7730 if(uData->info)
7731 saxdb_write_string(ctx, KEY_INFO, uData->info);
7732 saxdb_end_record(ctx);
7733 }
7734 saxdb_end_record(ctx);
7735 return high_present;
7736 }
7737
7738 static void
7739 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
7740 {
7741 if(!bData)
7742 return;
7743 saxdb_start_record(ctx, KEY_BANS, 1);
7744 for(; bData; bData = bData->next)
7745 {
7746 saxdb_start_record(ctx, bData->mask, 0);
7747 saxdb_write_int(ctx, KEY_SET, bData->set);
7748 if(bData->triggered)
7749 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
7750 if(bData->expires)
7751 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
7752 if(bData->owner[0])
7753 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
7754 if(bData->reason)
7755 saxdb_write_string(ctx, KEY_REASON, bData->reason);
7756 saxdb_end_record(ctx);
7757 }
7758 saxdb_end_record(ctx);
7759 }
7760
7761 static void
7762 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
7763 {
7764 saxdb_start_record(ctx, name, 0);
7765 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
7766 saxdb_write_string(ctx, KEY_REASON, susp->reason);
7767 if(susp->issued)
7768 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
7769 if(susp->expires)
7770 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
7771 if(susp->revoked)
7772 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
7773 if(susp->previous)
7774 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
7775 saxdb_end_record(ctx);
7776 }
7777
7778 static void
7779 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
7780 {
7781 char buf[MAXLEN];
7782 int high_present;
7783 enum levelOption lvlOpt;
7784 enum charOption chOpt;
7785
7786 saxdb_start_record(ctx, channel->channel->name, 1);
7787
7788 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
7789 saxdb_write_int(ctx, KEY_MAX, channel->max);
7790 if(channel->topic)
7791 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
7792 if(channel->registrar)
7793 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
7794 if(channel->greeting)
7795 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
7796 if(channel->user_greeting)
7797 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
7798 if(channel->topic_mask)
7799 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
7800 if(channel->suspended)
7801 chanserv_write_suspended(ctx, "suspended", channel->suspended);
7802
7803 saxdb_start_record(ctx, KEY_OPTIONS, 0);
7804 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
7805 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7806 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
7807 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7808 {
7809 buf[0] = channel->chOpts[chOpt];
7810 buf[1] = '\0';
7811 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
7812 }
7813 saxdb_end_record(ctx);
7814
7815 if(channel->modes.modes_set || channel->modes.modes_clear)
7816 {
7817 mod_chanmode_format(&channel->modes, buf);
7818 saxdb_write_string(ctx, KEY_MODES, buf);
7819 }
7820
7821 high_present = chanserv_write_users(ctx, channel->users);
7822 chanserv_write_bans(ctx, channel->bans);
7823
7824 if(dict_size(channel->notes))
7825 {
7826 dict_iterator_t it;
7827
7828 saxdb_start_record(ctx, KEY_NOTES, 1);
7829 for(it = dict_first(channel->notes); it; it = iter_next(it))
7830 {
7831 struct note *note = iter_data(it);
7832 saxdb_start_record(ctx, iter_key(it), 0);
7833 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
7834 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
7835 saxdb_end_record(ctx);
7836 }
7837 saxdb_end_record(ctx);
7838 }
7839
7840 if(channel->ownerTransfer)
7841 saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
7842 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
7843 saxdb_end_record(ctx);
7844 }
7845
7846 static void
7847 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
7848 {
7849 const char *str;
7850
7851 saxdb_start_record(ctx, ntype->name, 0);
7852 switch(ntype->set_access_type)
7853 {
7854 case NOTE_SET_CHANNEL_ACCESS:
7855 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
7856 break;
7857 case NOTE_SET_CHANNEL_SETTER:
7858 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
7859 break;
7860 case NOTE_SET_PRIVILEGED: default:
7861 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
7862 break;
7863 }
7864 switch(ntype->visible_type)
7865 {
7866 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
7867 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
7868 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
7869 }
7870 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
7871 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
7872 saxdb_end_record(ctx);
7873 }
7874
7875 static void
7876 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
7877 {
7878 struct do_not_register *dnr;
7879 dict_iterator_t it;
7880
7881 for(it = dict_first(dnrs); it; it = iter_next(it))
7882 {
7883 dnr = iter_data(it);
7884 saxdb_start_record(ctx, dnr->chan_name, 0);
7885 if(dnr->set)
7886 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
7887 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
7888 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
7889 saxdb_end_record(ctx);
7890 }
7891 }
7892
7893 static int
7894 chanserv_saxdb_write(struct saxdb_context *ctx)
7895 {
7896 dict_iterator_t it;
7897 struct chanData *channel;
7898
7899 /* Version Control*/
7900 saxdb_start_record(ctx, KEY_VERSION_CONTROL, 1);
7901 saxdb_write_int(ctx, KEY_VERSION_NUMBER, CHANSERV_DB_VERSION);
7902 saxdb_end_record(ctx);
7903
7904 /* Notes */
7905 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
7906 for(it = dict_first(note_types); it; it = iter_next(it))
7907 chanserv_write_note_type(ctx, iter_data(it));
7908 saxdb_end_record(ctx);
7909
7910 /* DNRs */
7911 saxdb_start_record(ctx, KEY_DNR, 1);
7912 write_dnrs_helper(ctx, handle_dnrs);
7913 write_dnrs_helper(ctx, plain_dnrs);
7914 write_dnrs_helper(ctx, mask_dnrs);
7915 saxdb_end_record(ctx);
7916
7917 /* Channels */
7918 saxdb_start_record(ctx, KEY_CHANNELS, 1);
7919 for(channel = channelList; channel; channel = channel->next)
7920 chanserv_write_channel(ctx, channel);
7921 saxdb_end_record(ctx);
7922
7923 return 0;
7924 }
7925
7926 static void
7927 chanserv_db_cleanup(void) {
7928 unsigned int ii;
7929 unreg_part_func(handle_part);
7930 while(channelList)
7931 unregister_channel(channelList, "terminating.");
7932 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7933 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7934 free(chanserv_conf.support_channels.list);
7935 dict_delete(handle_dnrs);
7936 dict_delete(plain_dnrs);
7937 dict_delete(mask_dnrs);
7938 dict_delete(note_types);
7939 free_string_list(chanserv_conf.eightball);
7940 free_string_list(chanserv_conf.old_ban_names);
7941 free_string_list(chanserv_conf.set_shows);
7942 free(set_shows_list.list);
7943 free(uset_shows_list.list);
7944 while(helperList)
7945 {
7946 struct userData *helper = helperList;
7947 helperList = helperList->next;
7948 free(helper);
7949 }
7950 }
7951
7952 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
7953 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
7954 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
7955
7956 void
7957 init_chanserv(const char *nick)
7958 {
7959 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
7960 conf_register_reload(chanserv_conf_read);
7961
7962 reg_server_link_func(handle_server_link);
7963
7964 reg_new_channel_func(handle_new_channel);
7965 reg_join_func(handle_join);
7966 reg_part_func(handle_part);
7967 reg_kick_func(handle_kick);
7968 reg_topic_func(handle_topic);
7969 reg_mode_change_func(handle_mode);
7970 reg_nick_change_func(handle_nick_change);
7971
7972 reg_auth_func(handle_auth);
7973 reg_handle_rename_func(handle_rename);
7974 reg_unreg_func(handle_unreg);
7975
7976 handle_dnrs = dict_new();
7977 dict_set_free_data(handle_dnrs, free);
7978 plain_dnrs = dict_new();
7979 dict_set_free_data(plain_dnrs, free);
7980 mask_dnrs = dict_new();
7981 dict_set_free_data(mask_dnrs, free);
7982
7983 reg_svccmd_unbind_func(handle_svccmd_unbind);
7984 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
7985 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
7986 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
7987 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
7988 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
7989 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7990 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7991 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
7992 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
7993
7994 DEFINE_COMMAND(pending, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
7995
7996 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
7997 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
7998
7999 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
8000 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
8001 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
8002 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
8003 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
8004
8005 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
8006 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
8007 DEFINE_COMMAND(mdelmanager, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
8008 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
8009 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
8010 DEFINE_COMMAND(mdelhalfop, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
8011
8012 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
8013 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
8014 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
8015 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
8016
8017 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
8018 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
8019 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8020 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8021 DEFINE_COMMAND(hop, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
8022 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
8023 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8024 DEFINE_COMMAND(dehop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8025 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8026 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8027
8028 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "hop", NULL);
8029 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "hop", NULL);
8030 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "hop", NULL);
8031 DEFINE_COMMAND(unban, 2, 0, "template", "hop", NULL);
8032 DEFINE_COMMAND(unbanall, 1, 0, "template", "hop", NULL);
8033 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "hop", NULL);
8034 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
8035 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "hop", "flags", "+never_csuspend", NULL);
8036 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8037 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
8038 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "manager", NULL);
8039 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
8040 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
8041 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
8042
8043 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "manager", NULL);
8044 DEFINE_COMMAND(last, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "manager", NULL);
8045 DEFINE_COMMAND(addlamer, 2, MODCMD_REQUIRE_REGCHAN, "access", "manager", NULL);
8046 DEFINE_COMMAND(addtimedlamer, 3, MODCMD_REQUIRE_REGCHAN, "access", "manager", NULL);
8047
8048 /* if you change dellamer access, see also places
8049 * like unbanme which have manager hardcoded. */
8050 DEFINE_COMMAND(dellamer, 2, MODCMD_REQUIRE_REGCHAN, "access", "manager", NULL);
8051 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
8052
8053 DEFINE_COMMAND(lamers, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
8054
8055 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
8056
8057 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
8058 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8059 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8060 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8061 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8062 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8063 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8064 DEFINE_COMMAND(hlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8065 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8066 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8067 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8068 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8069
8070 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
8071 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
8072
8073 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
8074 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
8075 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
8076 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
8077
8078 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
8079 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
8080 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
8081 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
8082 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
8083
8084 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8085 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8086 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8087 DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8088 DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8089 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8090 DEFINE_COMMAND(calc, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8091
8092 /* Channel options */
8093 DEFINE_CHANNEL_OPTION(defaulttopic);
8094 DEFINE_CHANNEL_OPTION(topicmask);
8095 DEFINE_CHANNEL_OPTION(greeting);
8096 DEFINE_CHANNEL_OPTION(usergreeting);
8097 DEFINE_CHANNEL_OPTION(modes);
8098 DEFINE_CHANNEL_OPTION(enfops);
8099 DEFINE_CHANNEL_OPTION(enfhalfops);
8100 /*DEFINE_CHANNEL_OPTION(giveops);
8101 DEFINE_CHANNEL_OPTION(givehalfops);
8102 */
8103 DEFINE_CHANNEL_OPTION(voice);
8104 DEFINE_CHANNEL_OPTION(protect);
8105 DEFINE_CHANNEL_OPTION(enfmodes);
8106 DEFINE_CHANNEL_OPTION(enftopic);
8107 DEFINE_CHANNEL_OPTION(pubcmd);
8108 /*DEFINE_CHANNEL_OPTION(givevoice);
8109 */
8110 DEFINE_CHANNEL_OPTION(userinfo);
8111 DEFINE_CHANNEL_OPTION(dynlimit);
8112 DEFINE_CHANNEL_OPTION(topicsnarf);
8113 DEFINE_CHANNEL_OPTION(nodelete);
8114 DEFINE_CHANNEL_OPTION(toys);
8115 DEFINE_CHANNEL_OPTION(setters);
8116 DEFINE_CHANNEL_OPTION(topicrefresh);
8117 DEFINE_CHANNEL_OPTION(ctcpreaction);
8118 DEFINE_CHANNEL_OPTION(bantimeout);
8119 DEFINE_CHANNEL_OPTION(inviteme);
8120 if(off_channel > 1)
8121 DEFINE_CHANNEL_OPTION(offchannel);
8122 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
8123
8124 /* Alias set topic to set defaulttopic for compatibility. */
8125 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
8126
8127 /* User options */
8128 DEFINE_USER_OPTION(autoinvite);
8129 DEFINE_USER_OPTION(info);
8130 DEFINE_USER_OPTION(autoop);
8131
8132 /* Alias uset autovoice to uset autoop. */
8133 modcmd_register(chanserv_module, "uset autovoice", user_opt_autoop, 1, 0, NULL);
8134
8135 note_types = dict_new();
8136 dict_set_free_data(note_types, chanserv_deref_note_type);
8137 if(nick)
8138 {
8139 const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
8140 chanserv = AddService(nick, modes ? modes : NULL, "Channel Services", NULL);
8141 service_register(chanserv)->trigger = '!';
8142 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
8143 }
8144
8145 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
8146
8147 if(chanserv_conf.channel_expire_frequency)
8148 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
8149
8150 if(chanserv_conf.ban_timeout_frequency)
8151 timeq_add(now + chanserv_conf.ban_timeout_frequency, expire_bans, NULL);
8152
8153 if(chanserv_conf.refresh_period)
8154 {
8155 time_t next_refresh;
8156 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
8157 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
8158 }
8159
8160 reg_exit_func(chanserv_db_cleanup);
8161 message_register_table(msgtab);
8162 }