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