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