92abb8dc0a4af58f542720ba8af1c63763e5875f
[srvx.git] / src / opserv.c
1 /* opserv.c - IRC Operator assistance service
2  * Copyright 2000-2004 srvx Development Team
3  *
4  * This file is part of srvx.
5  *
6  * srvx is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with srvx; if not, write to the Free Software Foundation,
18  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.
19  */
20
21 #include "conf.h"
22 #include "gline.h"
23 #include "global.h"
24 #include "nickserv.h"
25 #include "chanserv.h"
26 #include "modcmd.h"
27 #include "opserv.h"
28 #include "timeq.h"
29 #include "saxdb.h"
30
31 #ifdef HAVE_SYS_TIMES_H
32 #include <sys/times.h>
33 #endif
34 #ifdef HAVE_NETINET_IN_H
35 #include <netinet/in.h>
36 #endif
37 #ifdef HAVE_ARPA_INET_H
38 #include <arpa/inet.h>
39 #endif
40
41 #define OPSERV_CONF_NAME "services/opserv"
42
43 #define KEY_ALERT_CHANNEL "alert_channel"
44 #define KEY_ALERT_CHANNEL_MODES "alert_channel_modes"
45 #define KEY_DEBUG_CHANNEL "debug_channel"
46 #define KEY_DEBUG_CHANNEL_MODES "debug_channel_modes"
47 #define KEY_UNTRUSTED_MAX "untrusted_max"
48 #define KEY_PURGE_LOCK_DELAY "purge_lock_delay"
49 #define KEY_JOIN_FLOOD_MODERATE "join_flood_moderate"
50 #define KEY_JOIN_FLOOD_MODERATE_THRESH "join_flood_moderate_threshold"
51 #define KEY_NICK "nick"
52 #define KEY_JOIN_POLICER "join_policer"
53 #define KEY_NEW_USER_POLICER "new_user_policer"
54 #define KEY_REASON "reason"
55 #define KEY_RESERVES "reserves"
56 #define KEY_IDENT "username" /* for compatibility with 1.0 DBs */
57 #define KEY_HOSTNAME "hostname"
58 #define KEY_DESC "description"
59 #define KEY_BAD_WORDS "bad"
60 #define KEY_EXEMPT_CHANNELS "exempt"
61 #define KEY_SECRET_WORDS "secret"
62 #define KEY_TRUSTED_HOSTS "trusted"
63 #define KEY_OWNER "owner"
64 #define KEY_GAGS "gags"
65 #define KEY_ALERTS "alerts"
66 #define KEY_REACTION "reaction"
67 #define KEY_DISCRIM "discrim"
68 #define KEY_WARN "chanwarn"
69 #define KEY_MAX "max"
70 #define KEY_TIME "time"
71 #define KEY_MAX_CLIENTS "max_clients"
72 #define KEY_LIMIT "limit"
73 #define KEY_EXPIRES "expires"
74 #define KEY_STAFF_AUTH_CHANNEL "staff_auth_channel"
75 #define KEY_STAFF_AUTH_CHANNEL_MODES "staff_auth_channel_modes"
76 #define KEY_STAFF_AUTH_FORCE_OPS "staff_auth_force_opers"
77 #define KEY_CLONE_GLINE_DURATION "clone_gline_duration"
78 #define KEY_BLOCK_GLINE_DURATION "block_gline_duration"
79 #define KEY_ISSUER "issuer"
80 #define KEY_ISSUED "issued"
81 #define KEY_DEVNULL_CLASSES "classes"
82 #define KEY_DEVNULL_NAME "class"
83 #define KEY_DEVNULL_MODE "modes"
84 #define KEY_DEVNULL_MAXCHAN "chanlimit"
85 #define KEY_DEVNULL_MAXSENDQ "sendq"
86
87 #define IDENT_FORMAT            "%s [%s@%s/%s]"
88 #define IDENT_DATA(user)        user->nick, user->ident, user->hostname, irc_ntoa(&user->ip)
89 #define MAX_CHANNELS_WHOIS      50
90 #define OSMSG_PART_REASON       "%s has no reason."
91 #define OSMSG_KICK_REQUESTED    "Kick requested by %s."
92 #define OSMSG_KILL_REQUESTED    "Kill requested by %s."
93 #define OSMSG_GAG_REQUESTED     "Gag requested by %s."
94
95 static const struct message_entry msgtab[] = {
96     { "OSMSG_USER_ACCESS_IS", "$b%s$b (account $b%s$b) has %d access." },
97     { "OSMSG_LEVEL_TOO_LOW", "You lack sufficient access to use this command." },
98     { "OSMSG_NEED_CHANNEL", "You must specify a channel for $b%s$b." },
99     { "OSMSG_INVALID_IRCMASK", "$b%s$b is an invalid IRC hostmask." },
100     { "OSMSG_ADDED_BAN", "I have banned $b%s$b from $b%s$b." },
101     { "OSMSG_NO_GLINE_CMD", "The GLINE command is not bound so you can only block with the default duration." },
102     { "OSMSG_BLOCK_TRUSTED", "$b%s$b is on a trusted ip. If you really want to G-line him, use the GLINE command." },
103     { "OSMSG_BLOCK_OPER" , "G-lining $b%s$b (*@%s) would also hit the IRC operator $b%s$b." },
104     { "OSMSG_GLINE_ISSUED", "G-line issued for $b%s$b." },
105     { "OSMSG_GLINE_REMOVED", "G-line removed for $b%s$b." },
106     { "OSMSG_GLINE_FORCE_REMOVED", "Unknown/expired G-line removed for $b%s$b." },
107     { "OSMSG_GLINES_ONE_REFRESHED", "All G-lines resent to $b%s$b." },
108     { "OSMSG_GLINES_REFRESHED", "All G-lines refreshed." },
109     { "OSMSG_CLEARBANS_DONE", "Cleared all bans from channel $b%s$b." },
110     { "OSMSG_CLEARMODES_DONE", "Cleared all modes from channel $b%s$b." },
111     { "OSMSG_NO_CHANNEL_MODES", "Channel $b%s$b had no modes to clear." },
112     { "OSMSG_DEOP_DONE", "Deopped the requested lusers." },
113     { "OSMSG_DEOPALL_DONE", "Deopped everyone on $b%s$b." },
114     { "OSMSG_NO_DEBUG_CHANNEL", "No debug channel has been configured." },
115     { "OSMSG_INVITE_DONE", "Invited $b%s$b to $b%s$b." },
116     { "OSMSG_ALREADY_THERE", "You are already in $b%s$b." },
117     { "OSMSG_JOIN_DONE", "I have joined $b%s$b." },
118     { "OSMSG_ALREADY_JOINED", "I am already in $b%s$b." },
119     { "OSMSG_NOT_ON_CHANNEL", "$b%s$b does not seem to be on $b%s$b." },
120     { "OSMSG_KICKALL_DONE", "I have cleared out %s." },
121     { "OSMSG_LEAVING", "Leaving $b%s$b." },
122     { "OSMSG_MODE_SET", "I have set the modes for $b%s$b." },
123     { "OSMSG_OP_DONE", "Opped the requested lusers." },
124     { "OSMSG_OPALL_DONE", "Opped everyone on $b%s$b." },
125     { "OSMSG_WHOIS_IDENT", "%s (%s@%s) from %d.%d.%d.%d" },
126     { "OSMSG_WHOIS_NICK", "Nick     : %s" },
127     { "OSMSG_WHOIS_HOST", "Host     : %s@%s" },
128     { "OSMSG_WHOIS_FAKEHOST", "Fakehost : %s" },
129     { "OSMSG_WHOIS_FAKEIDENT", "Fakeident: %s" },
130     { "OSMSG_WHOIS_FAKEIDENTHOST", "Fakehost : %s@%s" },
131     { "OSMSG_WHOIS_IP",   "Real IP  : %s" },
132     { "OSMSG_WHOIS_MODES", "Modes    : +%s " },
133     { "OSMSG_WHOIS_INFO", "Info     : %s" },
134     { "OSMSG_WHOIS_NUMERIC", "Numnick  : %s" },
135     { "OSMSG_WHOIS_SERVER", "Server   : %s" },
136     { "OSMSG_WHOIS_NICK_AGE", "Nick Age : %s" },
137     { "OSMSG_WHOIS_ACCOUNT", "Account  : %s" },
138     { "OSMSG_WHOIS_CHANNELS", "Channels : %s" },
139     { "OSMSG_WHOIS_HIDECHANS", "Channel list omitted for your sanity." },
140     { "OSMSG_UNBAN_DONE", "Ban(s) removed from channel %s." },
141     { "OSMSG_CHANNEL_VOICED", "All users on %s voiced." },
142     { "OSMSG_CHANNEL_DEVOICED", "All voiced users on %s de-voiced." },
143     { "OSMSG_BAD_MODIFIER", "Unknown bad-word modifier $b%s$b." },
144     { "OSMSG_BAD_REDUNDANT", "$b%s$b is already covered by a bad word ($b%s$b)." },
145     { "OSMSG_BAD_GROWING", "Replacing bad word $b%s$b with shorter bad word $b%s$b." },
146     { "OSMSG_BAD_NUKING", " .. and removing redundant bad word $b%s$b." },
147     { "OSMSG_ADDED_BAD", "Added $b%s$b to the bad-word list." },
148     { "OSMSG_REMOVED_BAD", "Removed $b%s$b from the bad-word list." },
149     { "OSMSG_NOT_BAD_WORD", "$b%s$b is not a bad word." },
150     { "OSMSG_ADDED_EXEMPTION", "Added $b%s$b to the bad-word exemption list." },
151     { "OSMSG_ADDED_EXEMPTIONS", "Added %d exception(s) to the bad word list." },
152     { "OSMSG_REMOVED_EXEMPTION", "Removed $b%s$b from the exemption list." },
153     { "OSMSG_NOT_EXEMPT", "$b%s$b is not on the exempt list." },
154     { "OSMSG_ALREADY_TRUSTED", "Host $b%s$b is already trusted (use $bdeltrust$b and then $baddtrust$b to adjust)." },
155     { "OSMSG_NOT_TRUSTED", "Host $b%s$b is not trusted." },
156     { "OSMSG_BAD_IP", "$b%s$b is not a valid IP address" },
157     { "OSMSG_BAD_NUMBER", "$b%s$b is not a number" },
158     { "OSMSG_ADDED_TRUSTED", "Added trusted hosts to the trusted-hosts list." },
159     { "OSMSG_UPDATED_TRUSTED", "Updated trusted host $b%s$b." },
160     { "OSMSG_REMOVED_TRUSTED", "Removed trusted hosts from the trusted-hosts list." },
161     { "OSMSG_CLONE_EXISTS", "Nick $b%s$b is already in use." },
162     { "OSMSG_NOT_A_HOSTMASK", "The hostmask must be in user@host form." },
163     { "OSMSG_BADWORD_LIST", "Bad words: %s" },
164     { "OSMSG_EXEMPTED_LIST", "Exempted channels: %s" },
165     { "OSMSG_GLINE_COUNT", "There are %d glines active on the network." },
166     { "OSMSG_NO_GLINE", "$b%s$b is not a known G-line." },
167     { "OSMSG_LINKS_SERVER", "%s%s (%u clients; %s)" },
168     { "OSMSG_MAX_CLIENTS", "Max clients: %d at %s" },
169     { "OSMSG_NETWORK_INFO", "Total users: %d (%d invisible, %d opers)" },
170     { "OSMSG_RESERVED_LIST", "List of reserved nicks:" },
171     { "OSMSG_TRUSTED_LIST", "List of trusted hosts:" },
172     { "OSMSG_HOST_IS_TRUSTED", "%s (%s; set %s ago by %s; expires %s: %s)" },
173     { "OSMSG_HOST_NOT_TRUSTED", "%s does not have a special trust." },
174     { "OSMSG_UPTIME_STATS", "Uptime: %s (%u lines processed, CPU time %.2fu/%.2fs)" },
175     { "OSMSG_LINE_DUMPED", "Raw line sent." },
176     { "OSMSG_RAW_PARSE_ERROR", "Error parsing raw line (not dumping to uplink)." },
177     { "OSMSG_COLLIDED_NICK", "Now temporarily holding nick $b%s$b." },
178     { "OSMSG_RESERVED_NICK", "Now reserving nick $b%s$b." },
179     { "OSMSG_NICK_UNRESERVED", "Nick $b%s$b is no longer reserved." },
180     { "OSMSG_NOT_RESERVED", "Nick $b%s$b is not reserved." },
181     { "OSMSG_ILLEGAL_REASON", "This channel is illegal." },
182     { "OSMSG_ILLEGAL_KILL_REASON", "Joined an illegal modeless channel - do not repeat." },
183     { "OSMSG_ILLEGAL_CHANNEL", "$b%s$b is an ILLEGAL channel. Do not re-join it." },
184     { "OSMSG_FLOOD_MODERATE", "This channel has been temporarily moderated due to a possible join flood attack detected in this channel; network staff have been notified and will investigate." },
185     { "OSMSG_CLONE_WARNING", "WARNING: You have connected the maximum permitted number of clients from one IP address (clones).  If you connect any more, your host will be temporarily banned from the network." },
186     { "OSMSG_CLONE_ADDED", "Added clone $b%s$b." },
187     { "OSMSG_CLONE_FAILED", "Unable to add user $b%s$b." },
188     { "OSMSG_NOT_A_CLONE", "Har har.  $b%s$b isn't a clone." },
189     { "OSMSG_CLONE_REMOVED", "Removed clone $b%s$b." },
190     { "OSMSG_CLONE_JOINED", "$b%s$b has joined $b%s$b." },
191     { "OSMSG_CLONE_PARTED", "$b%s$b has left $b%s$b." },
192     { "OSMSG_OPS_GIVEN", "I have given ops in $b%s$b to $b%s$b." },
193     { "OSMSG_CLONE_SAID", "$b%s$b has spoken to $b%s$b." },
194     { "OSMSG_UNKNOWN_SUBCOMMAND", "$b%s$b is not a valid subcommand of $b%s$b." },
195     { "OSMSG_UNKNOWN_OPTION", "$b%s$b has not been set." },
196     { "OSMSG_OPTION_IS", "$b%s$b is set to $b%s$b." },
197     { "OSMSG_OPTION_ROOT", "The following keys can be queried:" },
198     { "OSMSG_OPTION_LIST", "$b%s$b contains the following values:" },
199     { "OSMSG_OPTION_KEYS", "$b%s$b contains the following keys:" },
200     { "OSMSG_OPTION_LIST_EMPTY", "Empty list." },
201     { "OSMSG_SET_NOT_SET", "$b%s$b does not exist, and cannot be set." },
202     { "OSMSG_SET_BAD_TYPE", "$b%s$b is not a string, and cannot be set." },
203     { "OSMSG_SET_SUCCESS", "$b%s$b has been set to $b%s$b." },
204     { "OSMSG_SETTIME_SUCCESS", "Set time for servers named like $b%s$b." },
205     { "OSMSG_BAD_ACTION", "Unrecognized trace action $b%s$b." },
206     { "OSMSG_USER_SEARCH_RESULTS", "The following users were found:" },
207     { "OSMSG_CHANNEL_SEARCH_RESULTS", "The following channels were found:" },
208     { "OSMSG_GLINE_SEARCH_RESULTS", "The following glines were found:" },
209     { "OSMSG_LOG_SEARCH_RESULTS", "The following log entries were found:" },
210     { "OSMSG_GSYNC_RUNNING", "Synchronizing glines from %s." },
211     { "OSMSG_GTRACE_FORMAT", "%1$s (issued %2$s ago by %3$s, lastmod %4$s ago, expires %5$s, lifetime %7$s): %6$s" },
212     { "OSMSG_GTRACE_FOREVER", "%1$s (issued %2$s ago by %3$s, lastmod %4$s ago, never expires, lifetime %7$s): %6$s" },
213     { "OSMSG_GTRACE_EXPIRED", "%1$s (issued %2$s ago by %3$s, lastmod %4$s ago, expired %5$s ago, lifetime %7$s): %6$s" },
214     { "OSMSG_GAG_APPLIED", "Gagged $b%s$b, affecting %d users." },
215     { "OSMSG_GAG_ADDED", "Gagged $b%s$b." },
216     { "OSMSG_REDUNDANT_GAG", "Gag $b%s$b is redundant." },
217     { "OSMSG_GAG_NOT_FOUND", "Could not find gag $b%s$b." },
218     { "OSMSG_NO_GAGS", "No gags have been set." },
219     { "OSMSG_UNGAG_APPLIED", "Ungagged $b%s$b, affecting %d users." },
220     { "OSMSG_UNGAG_ADDED", "Ungagged $b%s$b." },
221     { "OSMSG_TIMEQ_INFO", "%u events in timeq; next in %lu seconds." },
222     { "OSMSG_ALERT_EXISTS", "An alert named $b%s$b already exists." },
223     { "OSMSG_UNKNOWN_REACTION", "Unknown alert reaction $b%s$b." },
224     { "OSMSG_ADDED_ALERT", "Added alert named $b%s$b." },
225     { "OSMSG_REMOVED_ALERT", "Removed alert named $b%s$b." },
226     { "OSMSG_NO_SUCH_ALERT", "No alert named $b%s$b could be found." },
227     { "OSMSG_ALERT_IS", "%s (by %s, reaction %s): %s" },
228     { "OSMSG_ALERTS_LIST", "Current $O alerts:" },
229     { "OSMSG_REHASH_COMPLETE", "Completed rehash of configuration database." },
230     { "OSMSG_REHASH_FAILED", "Rehash of configuration database failed, previous configuration is intact." },
231     { "OSMSG_REOPEN_COMPLETE", "Closed and reopened all log files." },
232     { "OSMSG_RECONNECTING", "Reconnecting to my uplink." },
233     { "OSMSG_NUMERIC_COLLIDE", "Numeric %d (%s) is already in use." },
234     { "OSMSG_NAME_COLLIDE", "That name is already in use." },
235     { "OSMSG_SRV_CREATE_FAILED", "Server creation failed -- check log files." },
236     { "OSMSG_SERVER_JUPED", "Added new jupe server %s." },
237     { "OSMSG_SERVER_NOT_JUPE", "That server is not a juped server." },
238     { "OSMSG_SERVER_UNJUPED", "Server jupe removed." },
239     { "OSMSG_WARN_ADDED", "Added channel activity warning for $b%s$b (%s)" },
240     { "OSMSG_WARN_EXISTS", "Channel activity warning for $b%s$b already exists." },
241     { "OSMSG_WARN_DELETED", "Removed channel activity warning for $b%s$b" },
242     { "OSMSG_WARN_NOEXIST", "Channel activity warning for $b%s$b does not exist." },
243     { "OSMSG_WARN_LISTSTART", "Channel activity warnings:" },
244     { "OSMSG_WARN_LISTENTRY", "%s (%s)" },
245     { "OSMSG_WARN_LISTEND", "End of activity warning list." },
246     { "OSMSG_UPLINK_CONNECTING", "Establishing connection with %s (%s:%d)." },
247     { "OSMSG_CURRENT_UPLINK", "$b%s$b is already the current uplink." },
248     { "OSMSG_INVALID_UPLINK", "$b%s$b is not a valid uplink name." },
249     { "OSMSG_UPLINK_DISABLED", "$b%s$b is a disabled or unavailable uplink." },
250     { "OSMSG_UPLINK_START", "Uplink $b%s$b:" },
251     { "OSMSG_UPLINK_ADDRESS", "Address: %s:%d" },
252     { "OSMSG_STUPID_GLINE", "Gline %s?  Now $bthat$b would be smooth." },
253     { "OSMSG_ACCOUNTMASK_AUTHED", "Invalid criteria: it is impossible to match an account mask but not be authed" },
254     { "OSMSG_CHANINFO_HEADER", "%s Information" },
255     { "OSMSG_CHANINFO_TIMESTAMP", "Created on: %a %b %d %H:%M:%S %Y (%s)" },
256     { "OSMSG_CHANINFO_MODES", "Modes: %s" },
257     { "OSMSG_CHANINFO_MODES_BADWORD", "Modes: %s; bad-word channel" },
258     { "OSMSG_CHANINFO_TOPIC", "Topic (set by %%s, %a %b %d %H:%M:%S %Y): %%s" },
259     { "OSMSG_CHANINFO_TOPIC_UNKNOWN", "Topic: (none / not gathered)" },
260     { "OSMSG_CHANINFO_BAN_COUNT", "Bans (%d):" },
261     { "OSMSG_CHANINFO_BAN", "%%s by %%s (%a %b %d %H:%M:%S %Y)" },
262     { "OSMSG_CHANINFO_MANY_USERS", "%d users (\"/msg $S %s %s users\" for the list)" },
263     { "OSMSG_CHANINFO_USER_COUNT", "Users (%d):" },
264     { "OSMSG_CSEARCH_CHANNEL_INFO", "%s [%d users] %s %s" },
265     { "OSMSG_TRACE_MAX_CHANNELS", "You may not use the 'channel' criterion more than %d times." },
266     { "OSMSG_FORCEKICK_LOCAL", "You cannot kick $b%s$b forcefully." },
267     { "OSMSG_SVSNONICK", "$b%s$b is not a valid nick." },
268     { "OSMSG_SVSNICKUSED", "$b%s$b is an already used nickname." },
269     { "OSMSG_SVSNICK", "You have renamed $b%s$b to $b%s$b." },
270     { "OSMSG_SVSJOIN", "$b%s$b joined $b%s$b." },
271     { "OSMSG_SVSPART", "$b%s$b parted $b%s$b." },
272     { "OSMSG_SVSKILL", "$b%s$b killed: $b%s$b." },
273     { "OSMSG_SVSMODE", "You have set mode $b%s$b for $b%s$b." },
274     { "OSMSG_SIMUL", "You have simuled $b%s$b: %s" },
275     { "OSMSG_DEVNULL_USER" , "[%s] %s  %s" },
276     { "OSMSG_DEVNULL_MATCH" , "%d Users found." },
277     { "OSMSG_DEVNULL_CLASS" , "%s is not a valid DevNull class." },
278     { "OSMSG_DEVNULL_ADDED", "Added %s to DevNull list (class: %s)" },
279     { "OSMSG_DEVNULL_DELETED", "Deleted %s from DevNull list (class: %s)" },
280     { "OSMSG_DEVNULL_NOTADDED", "User %s is not listed on DevNull list." },
281     { "OSMSG_DEVNULL_ACTION", "Unrecognized trace action $b%s$b" },
282     { "OSMSG_DEVNULL_FOUND", "DevNull Class %s is already existing." },
283     { "OSMSG_DEVNULL_NOTFOUND", "can't find DevNull class %s." },
284     { "OSMSG_DEVNULL_ADDED", "DevNull Class %s added." },
285     { "OSMSG_DEVNULL_REMOVED", "DevNull Class %s removed." },
286     { "OSMSG_DEVNULL_SET", "Settings for DevNull Class %s" },
287     { "OSMSG_DEVNULL_SET_A", "ChanLimit:        %s" },
288     { "OSMSG_DEVNULL_SET_A_i", "ChanLimit:        %i" },
289     { "OSMSG_DEVNULL_SET_B", "UnlimitTarget:    %s" },
290     { "OSMSG_DEVNULL_SET_C", "Flood:            %s" },
291     { "OSMSG_DEVNULL_SET_E", "ChanHide:         %s" },
292     { "OSMSG_DEVNULL_SET_F", "IdleHide:         %s" },
293     { "OSMSG_DEVNULL_SET_G", "ChServMode:       %s" },
294     { "OSMSG_DEVNULL_SET_H", "XtraOpMode:       %s" },
295     { "OSMSG_DEVNULL_SET_I", "NetServMode:      %s" },
296     { "OSMSG_DEVNULL_SET_J", "SeeIdle:          %s" },
297     { "OSMSG_DEVNULL_SET_K", "ForceIdleHide:    %s" },
298     { "OSMSG_DEVNULL_SET_L", "OverrideCC:       %s" },
299     { "OSMSG_DEVNULL_SET_M", "OverrideNoAmsg:   %s" },
300     { "OSMSG_DEVNULL_SET_N", "MaxSendQ:         %s" },
301     { "OSMSG_DEVNULL_SET_N_i", "MaxSendQ:         %i" },
302     { "OSMSG_DEVNULL_SET_OPME", "OpMe:             %s" },
303     { "OSMSG_DEVNULL_SET_DONE", "Done." },
304     { "OSMSG_DEVNULL_RENAMED", "Devnull class %s renamed to %s" },
305     { "OSMSG_DEVNULL_SET_INVALID", "Invalid Option for setting %s" },
306     { NULL, NULL }
307 };
308
309 #define OPSERV_SYNTAX() svccmd_send_help(user, opserv, cmd)
310
311 typedef int (*discrim_search_func)(struct userNode *match, void *extra);
312
313 struct userNode *opserv;
314
315 static dict_t opserv_chan_warn; /* data is char* */
316 static dict_t opserv_reserved_nick_dict; /* data is struct userNode* */
317 static struct string_list *opserv_bad_words;
318 static dict_t opserv_exempt_channels; /* data is not used */
319 static dict_t opserv_trusted_hosts; /* data is struct trusted_host* */
320 static dict_t opserv_devnull_classes; /* data is struct devnull_class* */
321 static dict_t opserv_hostinfo_dict; /* data is struct opserv_hostinfo* */
322 static dict_t opserv_user_alerts; /* data is struct opserv_user_alert* */
323 static dict_t opserv_nick_based_alerts; /* data is struct opserv_user_alert* */
324 static dict_t opserv_channel_alerts; /* data is struct opserv_user_alert* */
325 static dict_t opserv_account_alerts; /* data is struct opserv_user_alert* */
326 static struct module *opserv_module;
327 static struct log_type *OS_LOG;
328 static unsigned int new_user_flood;
329 const char *devnull_modes = DEVNULL_MODES;
330 static char *level_strings[1001];
331 static char devnull_inverse_modes[256];
332
333 void *devnull_check_priv_func = NULL;
334
335 static struct {
336     struct chanNode *debug_channel;
337     struct chanNode *alert_channel;
338     struct chanNode *staff_auth_channel;
339     int staff_auth_force;
340     struct policer_params *join_policer_params;
341     struct policer new_user_policer;
342     unsigned long untrusted_max;
343     unsigned long clone_gline_duration;
344     unsigned long block_gline_duration;
345     unsigned long purge_lock_delay;
346     unsigned long join_flood_moderate;
347     unsigned long join_flood_moderate_threshold;
348 } opserv_conf;
349
350 struct trusted_host {
351     char *ipaddr;
352     char *issuer;
353     char *reason;
354     unsigned long limit;
355     unsigned long issued;
356     unsigned long expires;
357 };
358
359 struct gag_entry {
360     char *mask;
361     char *owner;
362     char *reason;
363     unsigned long expires;
364     struct gag_entry *next;
365 };
366
367 static struct gag_entry *gagList;
368
369 struct opserv_hostinfo {
370     struct userList clients;
371     struct trusted_host *trusted;
372 };
373
374 static void
375 opserv_free_hostinfo(void *data)
376 {
377     struct opserv_hostinfo *ohi = data;
378     userList_clean(&ohi->clients);
379     free(ohi);
380 }
381
382 #define DISCRIM_MAX_CHANS 20
383
384 typedef struct opservDiscrim {
385     struct chanNode *channels[DISCRIM_MAX_CHANS];
386     unsigned int channel_count;
387     char *mask_nick, *mask_ident, *mask_host, *mask_info, *server, *reason, *notice_target, *accountmask;
388     irc_in_addr_t ip_mask;
389     unsigned long limit;
390     unsigned long min_ts, max_ts;
391     unsigned int min_level, max_level, domain_depth, duration, min_clones, min_channels, max_channels;
392     unsigned char ip_mask_bits;
393     unsigned int match_opers : 1, match_trusted : 1, option_log : 1;
394     unsigned int chan_req_modes[DISCRIM_MAX_CHANS], chan_no_modes[DISCRIM_MAX_CHANS];
395     int authed : 2, info_space : 2;
396 } *discrim_t;
397
398 struct discrim_and_source {
399     discrim_t discrim;
400     struct userNode *source;
401     dict_t dict;
402     unsigned int disp_limit;
403 };
404
405 static discrim_t opserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[], int allow_channel);
406 static unsigned int opserv_discrim_search(discrim_t discrim, discrim_search_func dsf, void *data);
407 static int gag_helper_func(struct userNode *match, void *extra);
408 static int ungag_helper_func(struct userNode *match, void *extra);
409
410 typedef enum {
411     REACT_NOTICE,
412     REACT_KILL,
413     REACT_GLINE
414 } opserv_alert_reaction;
415
416 struct opserv_user_alert {
417     char *owner;
418     char *text_discrim, *split_discrim;
419     discrim_t discrim;
420     opserv_alert_reaction reaction;
421 };
422
423 /* funny type to make it acceptible to dict_set_free_data, far below */
424 static void
425 opserv_free_user_alert(void *data)
426 {
427     struct opserv_user_alert *alert = data;
428     unsigned int i;
429     for(i = 0; i < alert->discrim->channel_count; i++)
430         UnlockChannel(alert->discrim->channels[i]);
431     free(alert->owner);
432     free(alert->text_discrim);
433     free(alert->split_discrim);
434     free(alert->discrim->reason);
435     free(alert->discrim);
436     free(alert);
437 }
438
439 #if defined(GCC_VARMACROS)
440 # define opserv_debug(ARGS...) do { if (opserv_conf.debug_channel) send_channel_message(opserv_conf.debug_channel, opserv, ARGS); } while (0)
441 # define opserv_alert(ARGS...) do { if (opserv_conf.alert_channel) send_channel_message(opserv_conf.alert_channel, opserv, ARGS); } while (0)
442 # define opserv_custom_alert(CHAN, ARGS...) do { if (CHAN) send_target_message(5, (CHAN), opserv, ARGS); else if (opserv_conf.alert_channel) send_channel_message(opserv_conf.alert_channel, opserv, ARGS); } while (0)
443 #elif defined(C99_VARMACROS)
444 # define opserv_debug(...) do { if (opserv_conf.debug_channel) send_channel_message(opserv_conf.debug_channel, opserv, __VA_ARGS__); } while (0)
445 # define opserv_alert(...) do { if (opserv_conf.alert_channel) send_channel_message(opserv_conf.alert_channel, opserv, __VA_ARGS__); } while (0)
446 # define opserv_custom_alert(chan, ...) do { if (chan) send_target_message(5, chan, opserv, __VA_ARGS__); else if (opserv_conf.alert_channel) send_channel_message(opserv_conf.alert_channel, opserv, __VA_ARGS__); } while (0)
447 #endif
448
449 /* A lot of these commands are very similar to what ChanServ can do,
450  * but OpServ can do them even on channels that aren't registered.
451  */
452
453 static MODCMD_FUNC(cmd_access)
454 {
455     struct handle_info *hi;
456     const char *target;
457     unsigned int res;
458
459     target = (argc > 1) ? (const char*)argv[1] : user->nick;
460     if (!irccasecmp(target, "*")) {
461         nickserv_show_oper_accounts(user, cmd);
462         return 1;
463     }
464     if (!(hi = modcmd_get_handle_info(user, target)))
465         return 0;
466     res = (argc > 2) ? oper_try_set_access(user, cmd->parent->bot, hi, strtoul(argv[2], NULL, 0)) : 0;
467     reply("OSMSG_USER_ACCESS_IS", target, hi->handle, hi->opserv_level);
468     return res;
469 }
470
471 static MODCMD_FUNC(cmd_ban)
472 {
473     struct mod_chanmode change;
474     struct userNode *victim;
475
476     mod_chanmode_init(&change);
477     change.argc = 1;
478     change.args[0].mode = MODE_BAN;
479     if (is_ircmask(argv[1]))
480         change.args[0].u.hostmask = strdup(argv[1]);
481     else if ((victim = GetUserH(argv[1])))
482         change.args[0].u.hostmask = generate_hostmask(victim, 0);
483     else {
484         reply("OSMSG_INVALID_IRCMASK", argv[1]);
485         return 0;
486     }
487     modcmd_chanmode_announce(&change);
488     reply("OSMSG_ADDED_BAN", change.args[0].u.hostmask, channel->name);
489     free((char*)change.args[0].u.hostmask);
490     return 1;
491 }
492
493 static MODCMD_FUNC(cmd_chaninfo)
494 {
495     char buffer[MAXLEN];
496     const char *fmt;
497     struct banNode *ban;
498     struct modeNode *moden;
499     struct modeNode **members;
500     time_t feh;
501     unsigned int n;
502     int show_oplevels;
503
504     reply("OSMSG_CHANINFO_HEADER", channel->name);
505     fmt = user_find_message(user, "OSMSG_CHANINFO_TIMESTAMP");
506     feh = channel->timestamp;
507     strftime(buffer, sizeof(buffer), fmt, gmtime(&feh));
508     send_message_type(4, user, cmd->parent->bot, "%s", buffer);
509     irc_make_chanmode(channel, buffer);
510     if (channel->bad_channel)
511         reply("OSMSG_CHANINFO_MODES_BADWORD", buffer);
512     else
513         reply("OSMSG_CHANINFO_MODES", buffer);
514     if (channel->topic_time) {
515         fmt = user_find_message(user, "OSMSG_CHANINFO_TOPIC");
516         feh = channel->topic_time;
517         strftime(buffer, sizeof(buffer), fmt, gmtime(&feh));
518         send_message_type(4, user, cmd->parent->bot, buffer, channel->topic_nick, channel->topic);
519     } else {
520         irc_fetchtopic(cmd->parent->bot, channel->name);
521         reply("OSMSG_CHANINFO_TOPIC_UNKNOWN");
522     }
523     if (channel->banlist.used) {
524         reply("OSMSG_CHANINFO_BAN_COUNT", channel->banlist.used);
525         fmt = user_find_message(user, "OSMSG_CHANINFO_BAN");
526         for (n = 0; n < channel->banlist.used; n++) {
527             ban = channel->banlist.list[n];
528             feh = ban->set;
529             strftime(buffer, sizeof(buffer), fmt, localtime(&feh));
530             send_message_type(4, user, cmd->parent->bot, buffer, ban->ban, ban->who);
531         }
532     }
533     if ((argc < 2) && (channel->members.used >= 50)) {
534         /* early out unless they ask for users */
535         reply("OSMSG_CHANINFO_MANY_USERS", channel->members.used, argv[0], channel->name);
536         return 1;
537     }
538     reply("OSMSG_CHANINFO_USER_COUNT", channel->members.used);
539
540     /* Create and sort the members array. */
541     members = alloca(channel->members.used * sizeof(members[0]));
542     for (n=0; n<channel->members.used; n++)
543         members[n] = channel->members.list[n];
544     qsort(members, channel->members.used, sizeof(members[0]), modeNode_sort);
545
546     /* Display the array. */
547     show_oplevels = (channel->members.used != 0)
548         && (members[0]->modes & MODE_CHANOP)
549         && (members[0]->oplevel < MAXOPLEVEL);
550     for (n=0; n<channel->members.used; n++) {
551         moden = members[n];
552         if (moden->modes & MODE_CHANOP) {
553             if (show_oplevels)
554                 send_message_type(4, user, cmd->parent->bot, " @%s:%d (%s@%s)", moden->user->nick, moden->oplevel, moden->user->ident, moden->user->hostname);
555             else
556                 send_message_type(4, user, cmd->parent->bot, " @%s (%s@%s)", moden->user->nick, moden->user->ident, moden->user->hostname);
557         } else if (moden->modes & MODE_VOICE)
558             send_message_type(4, user, cmd->parent->bot, " +%s (%s@%s)", moden->user->nick, moden->user->ident, moden->user->hostname);
559         else
560             send_message_type(4, user, cmd->parent->bot, "  %s (%s@%s)", moden->user->nick, moden->user->ident, moden->user->hostname);
561     }
562
563     return 1;
564 }
565
566 static MODCMD_FUNC(cmd_warn)
567 {
568     char *reason, *message;
569
570     if (!IsChannelName(argv[1])) {
571         reply("OSMSG_NEED_CHANNEL", argv[0]);
572         return 0;
573     }
574     reason = dict_find(opserv_chan_warn, argv[1], NULL);
575     if (reason) {
576         reply("OSMSG_WARN_EXISTS", argv[1]);
577         return 0;
578     }
579     if (argv[2])
580         reason = strdup(unsplit_string(argv+2, argc-2, NULL));
581     else
582         reason = strdup("No reason");
583     dict_insert(opserv_chan_warn, strdup(argv[1]), reason);
584     reply("OSMSG_WARN_ADDED", argv[1], reason);
585     if (dict_find(channels, argv[1], NULL)) {
586         message = alloca(strlen(reason) + strlen(argv[1]) + 55);
587         sprintf(message, "Channel activity warning for channel %s: %s", argv[1], reason);
588         global_message(MESSAGE_RECIPIENT_OPERS, message);
589     }
590     return 1;
591 }
592
593 static MODCMD_FUNC(cmd_unwarn)
594 {
595     if ((argc < 2) || !IsChannelName(argv[1])) {
596         reply("OSMSG_NEED_CHANNEL", argv[0]);
597         return 0;
598     }
599     if (!dict_remove(opserv_chan_warn, argv[1])) {
600         reply("OSMSG_WARN_NOEXIST", argv[1]);
601         return 0;
602     }
603     reply("OSMSG_WARN_DELETED", argv[1]);
604     return 1;
605 }
606
607 static MODCMD_FUNC(cmd_clearbans)
608 {
609     struct mod_chanmode *change;
610     unsigned int ii;
611
612     change = mod_chanmode_alloc(channel->banlist.used);
613     for (ii=0; ii<channel->banlist.used; ii++) {
614         change->args[ii].mode = MODE_REMOVE | MODE_BAN;
615         change->args[ii].u.hostmask = strdup(channel->banlist.list[ii]->ban);
616     }
617     modcmd_chanmode_announce(change);
618     for (ii=0; ii<change->argc; ++ii)
619         free((char*)change->args[ii].u.hostmask);
620     mod_chanmode_free(change);
621     reply("OSMSG_CLEARBANS_DONE", channel->name);
622     return 1;
623 }
624
625 static MODCMD_FUNC(cmd_clearmodes)
626 {
627     struct mod_chanmode change;
628
629     if (!channel->modes) {
630         reply("OSMSG_NO_CHANNEL_MODES", channel->name);
631         return 0;
632     }
633     mod_chanmode_init(&change);
634     change.modes_clear = channel->modes & ~MODE_REGISTERED;
635     modcmd_chanmode_announce(&change);
636     reply("OSMSG_CLEARMODES_DONE", channel->name);
637     return 1;
638 }
639
640 static MODCMD_FUNC(cmd_deop)
641 {
642     struct mod_chanmode *change;
643     unsigned int arg, count;
644
645     change = mod_chanmode_alloc(argc-1);
646     for (arg = 1, count = 0; arg < argc; ++arg) {
647         struct userNode *victim = GetUserH(argv[arg]);
648         struct modeNode *mn;
649         if (!victim || IsService(victim)
650             || !(mn = GetUserMode(channel, victim))
651             || !(mn->modes & MODE_CHANOP))
652             continue;
653         change->args[count].mode = MODE_REMOVE | MODE_CHANOP;
654         change->args[count++].u.member = mn;
655     }
656     if (count) {
657         change->argc = count;
658         modcmd_chanmode_announce(change);
659     }
660     mod_chanmode_free(change);
661     reply("OSMSG_DEOP_DONE");
662     return 1;
663 }
664
665 static MODCMD_FUNC(cmd_deopall)
666 {
667     struct mod_chanmode *change;
668     unsigned int ii, count;
669
670     change = mod_chanmode_alloc(channel->members.used);
671     for (ii = count = 0; ii < channel->members.used; ++ii) {
672         struct modeNode *mn = channel->members.list[ii];
673         if (IsService(mn->user) || !(mn->modes & MODE_CHANOP))
674             continue;
675         change->args[count].mode = MODE_REMOVE | MODE_CHANOP;
676         change->args[count++].u.member = mn;
677     }
678     if (count) {
679         change->argc = count;
680         modcmd_chanmode_announce(change);
681     }
682     mod_chanmode_free(change);
683     reply("OSMSG_DEOPALL_DONE", channel->name);
684     return 1;
685 }
686
687 static MODCMD_FUNC(cmd_rehash)
688 {
689     extern char *services_config;
690
691     if (conf_read(services_config))
692         reply("OSMSG_REHASH_COMPLETE");
693     else
694         reply("OSMSG_REHASH_FAILED");
695     return 1;
696 }
697
698 static MODCMD_FUNC(cmd_reopen)
699 {
700     log_reopen();
701     reply("OSMSG_REOPEN_COMPLETE");
702     return 1;
703 }
704
705 static MODCMD_FUNC(cmd_reconnect)
706 {
707     reply("OSMSG_RECONNECTING");
708     irc_squit(self, "Reconnecting.", NULL);
709     return 1;
710 }
711
712 static MODCMD_FUNC(cmd_jupe)
713 {
714     extern int force_n2k;
715     struct server *newsrv;
716     unsigned int num;
717     char numeric[COMBO_NUMERIC_LEN+1], srvdesc[SERVERDESCRIPTMAX+1];
718
719     num = atoi(argv[2]);
720     if ((num < 64) && !force_n2k) {
721         inttobase64(numeric, num, 1);
722         inttobase64(numeric+1, 64*64-1, 2);
723     } else {
724         inttobase64(numeric, num, 2);
725         inttobase64(numeric+2, 64*64*64-1, 3);
726     }
727 #ifdef WITH_PROTOCOL_P10
728     if (GetServerN(numeric)) {
729         reply("OSMSG_NUMERIC_COLLIDE", num, numeric);
730         return 0;
731     }
732 #endif
733     if (GetServerH(argv[1])) {
734         reply("OSMSG_NAME_COLLIDE");
735         return 0;
736     }
737     snprintf(srvdesc, sizeof(srvdesc), "JUPE %s", unsplit_string(argv+3, argc-3, NULL));
738     newsrv = AddServer(self, argv[1], 1, now, now, numeric, srvdesc);
739     if (!newsrv) {
740         reply("OSMSG_SRV_CREATE_FAILED");
741         return 0;
742     }
743     irc_server(newsrv);
744     reply("OSMSG_SERVER_JUPED", argv[1]);
745     return 1;
746 }
747
748 static MODCMD_FUNC(cmd_unjupe)
749 {
750     struct server *srv;
751     char *reason;
752
753     srv = GetServerH(argv[1]);
754     if (!srv) {
755         reply("MSG_SERVER_UNKNOWN", argv[1]);
756         return 0;
757     }
758     if (strncmp(srv->description, "JUPE", 4)) {
759         reply("OSMSG_SERVER_NOT_JUPE");
760         return 0;
761     }
762     reason = (argc > 2) ? unsplit_string(argv+2, argc-2, NULL) : "Unjuping server";
763     DelServer(srv, 1, reason);
764     reply("OSMSG_SERVER_UNJUPED");
765     return 1;
766 }
767
768 static MODCMD_FUNC(cmd_jump)
769 {
770     extern struct cManagerNode cManager;
771     void uplink_select(char *name);
772     struct uplinkNode *uplink_find(char *name);
773     struct uplinkNode *uplink;
774     char *target;
775
776     target = unsplit_string(argv+1, argc-1, NULL);
777
778     if (!strcmp(cManager.uplink->name, target)) {
779         reply("OSMSG_CURRENT_UPLINK", cManager.uplink->name);
780         return 0;
781     }
782
783     uplink = uplink_find(target);
784     if (!uplink) {
785         reply("OSMSG_INVALID_UPLINK", target);
786         return 0;
787     }
788     if (uplink->flags & UPLINK_UNAVAILABLE) {
789         reply("OSMSG_UPLINK_DISABLED", uplink->name);
790         return 0;
791     }
792
793     reply("OSMSG_UPLINK_CONNECTING", uplink->name, uplink->host, uplink->port);
794     uplink_select(target);
795     irc_squit(self, "Reconnecting.", NULL);
796     return 1;
797 }
798
799 static MODCMD_FUNC(cmd_die)
800 {
801     char *reason, *text;
802
803     text = unsplit_string(argv+1, argc-1, NULL);
804     reason = alloca(strlen(text) + strlen(user->nick) + 20);
805     sprintf(reason, "Disconnected by %s [%s]", user->nick, text);
806     irc_squit(self, reason, text);
807     quit_services = 1;
808     return 1;
809 }
810
811 static MODCMD_FUNC(cmd_restart)
812 {
813     extern int services_argc;
814     extern char **services_argv;
815     char **restart_argv, *reason, *text;
816
817     text = unsplit_string(argv+1, argc-1, NULL);
818     reason = alloca(strlen(text) + strlen(user->nick) + 17);
819     sprintf(reason, "Restarted by %s [%s]", user->nick, text);
820     irc_squit(self, reason, text);
821
822     /* Append a NULL to the end of argv[]. */
823     restart_argv = (char **)alloca((services_argc + 1) * sizeof(char *));
824     memcpy(restart_argv, services_argv, services_argc * sizeof(char *));
825     restart_argv[services_argc] = NULL;
826
827     call_exit_funcs();
828
829     /* Don't blink. */
830     execv(services_argv[0], restart_argv);
831
832     /* If we're still here, that means something went wrong. Reconnect. */
833     return 1;
834 }
835
836 static struct gline *
837 opserv_block(struct userNode *target, char *src_handle, char *reason, unsigned long duration)
838 {
839     char mask[IRC_NTOP_MAX_SIZE+3] = { '*', '@', '\0' };
840     irc_ntop(mask + 2, sizeof(mask) - 2, &target->ip);
841     if (!reason)
842         snprintf(reason = alloca(MAXLEN), MAXLEN,
843                  "G-line requested by %s.", src_handle);
844     if (!duration)
845         duration = opserv_conf.block_gline_duration;
846     return gline_add(src_handle, mask, duration, reason, now, now, 0, 1);
847 }
848
849 static MODCMD_FUNC(cmd_block)
850 {
851     struct userNode *target;
852     struct gline *gline;
853     char *reason;
854     unsigned long duration = 0;
855     unsigned int offset = 2;
856     unsigned int nn;
857     struct svccmd *gline_cmd;
858
859     target = GetUserH(argv[1]);
860     if (!target) {
861         reply("MSG_NICK_UNKNOWN", argv[1]);
862         return 0;
863     }
864     if (IsService(target)) {
865         reply("MSG_SERVICE_IMMUNE", target->nick);
866         return 0;
867     }
868     if (dict_find(opserv_trusted_hosts, irc_ntoa(&target->ip), NULL)) {
869         reply("OSMSG_BLOCK_TRUSTED", target->nick);
870         return 0;
871     }
872
873     for(nn = 0; nn < curr_opers.used; nn++) {
874         if(memcmp(&curr_opers.list[nn]->ip, &target->ip, sizeof(irc_in_addr_t)) == 0) {
875             reply("OSMSG_BLOCK_OPER", target->nick, irc_ntoa(&target->ip), curr_opers.list[nn]->nick);
876             return 0;
877         }
878     }
879
880     if(argc > 2 && (duration = ParseInterval(argv[2]))) {
881         offset = 3;
882     }
883     if(duration && duration != opserv_conf.block_gline_duration) {
884         /* We require more access when the duration is not the default block duration. */
885         gline_cmd = dict_find(cmd->parent->commands, "gline", NULL);
886         if(!gline_cmd)
887         {
888             reply("OSMSG_NO_GLINE_CMD");
889             return 0;
890         }
891         if(!svccmd_can_invoke(user, cmd->parent->bot, gline_cmd, channel, SVCCMD_NOISY))
892             return 0;
893     }
894     reason = (argc > offset) ? unsplit_string(argv+offset, argc-offset, NULL) : NULL;
895     gline = opserv_block(target, user->handle_info->handle, reason, duration);
896     reply("OSMSG_GLINE_ISSUED", gline->target);
897     return 1;
898 }
899
900 static MODCMD_FUNC(cmd_gline)
901 {
902     unsigned long duration;
903     char *reason;
904     struct gline *gline;
905
906     reason = unsplit_string(argv+3, argc-3, NULL);
907     if (!is_gline(argv[1]) && !IsChannelName(argv[1]) && (argv[1][0] != '&')) {
908         reply("MSG_INVALID_GLINE", argv[1]);
909         return 0;
910     }
911     if (!argv[1][strspn(argv[1], "#&*?@.")] && (strlen(argv[1]) < 10)) {
912         reply("OSMSG_STUPID_GLINE", argv[1]);
913         return 0;
914     }
915     duration = ParseInterval(argv[2]);
916     if (!duration) {
917         reply("MSG_INVALID_DURATION", argv[2]);
918         return 0;
919     }
920     gline = gline_add(user->handle_info->handle, argv[1], duration, reason, now, now, 0, 1);
921     reply("OSMSG_GLINE_ISSUED", gline->target);
922     return 1;
923 }
924
925 static MODCMD_FUNC(cmd_ungline)
926 {
927     if (gline_remove(argv[1], 1))
928         reply("OSMSG_GLINE_REMOVED", argv[1]);
929     else
930         reply("OSMSG_GLINE_FORCE_REMOVED", argv[1]);
931     return 1;
932 }
933
934 static MODCMD_FUNC(cmd_refreshg)
935 {
936     if (argc > 1) {
937         unsigned int count;
938         dict_iterator_t it;
939         struct server *srv;
940
941         for (it=dict_first(servers), count=0; it; it=iter_next(it)) {
942             srv = iter_data(it);
943             if ((srv == self) || !match_ircglob(srv->name, argv[1]))
944                 continue;
945             gline_refresh_server(srv);
946             reply("OSMSG_GLINES_ONE_REFRESHED", srv->name);
947             count++;
948         }
949         if (!count) {
950             reply("MSG_SERVER_UNKNOWN", argv[1]);
951             return 0;
952         }
953     } else {
954         gline_refresh_all();
955         reply("OSMSG_GLINES_REFRESHED");
956     }
957     return 1;
958 }
959
960 static void
961 opserv_ison(struct userNode *tell, struct userNode *target, const char *message)
962 {
963     struct modeNode *mn;
964     unsigned int count, here_len, n, maxlen;
965     char buff[MAXLEN];
966
967     maxlen = tell->handle_info ? tell->handle_info->screen_width : 0;
968     if (!maxlen)
969         maxlen = MAX_LINE_SIZE;
970     for (n=count=0; n<target->channels.used; n++) {
971         mn = target->channels.list[n];
972         here_len = strlen(mn->channel->name);
973         if ((count + here_len + 4) > maxlen) {
974             buff[count] = 0;
975             send_message(tell, opserv, message, buff);
976             count = 0;
977         }
978         if (mn->modes & MODE_CHANOP)
979             buff[count++] = '@';
980         if (mn->modes & MODE_VOICE)
981             buff[count++] = '+';
982         memcpy(buff+count, mn->channel->name, here_len);
983         count += here_len;
984         buff[count++] = ' ';
985     }
986     if (count) {
987         buff[count] = 0;
988         send_message(tell, opserv, message, buff);
989     }
990 }
991
992 static MODCMD_FUNC(cmd_inviteme)
993 {
994     struct userNode *target;
995
996     if (argc < 2) {
997         target = user;
998     } else {
999         target = GetUserH(argv[1]);
1000         if (!target) {
1001             reply("MSG_NICK_UNKNOWN", argv[1]);
1002             return 0;
1003         }
1004     }
1005     if (opserv_conf.debug_channel == NULL) {
1006         reply("OSMSG_NO_DEBUG_CHANNEL");
1007         return 0;
1008     }
1009     if (GetUserMode(opserv_conf.debug_channel, user)) {
1010         reply("OSMSG_ALREADY_THERE", opserv_conf.debug_channel->name);
1011         return 0;
1012     }
1013     irc_invite(cmd->parent->bot, target, opserv_conf.debug_channel);
1014     if (target != user)
1015         reply("OSMSG_INVITE_DONE", target->nick, opserv_conf.debug_channel->name);
1016     return 1;
1017 }
1018
1019 static MODCMD_FUNC(cmd_invite)
1020 {
1021     if (GetUserMode(channel, user)) {
1022         reply("OSMSG_ALREADY_THERE", channel->name);
1023         return 0;
1024     }
1025     irc_invite(cmd->parent->bot, user, channel);
1026     return 1;
1027 }
1028
1029 static MODCMD_FUNC(cmd_join)
1030 {
1031     struct userNode *bot = cmd->parent->bot;
1032
1033     if (!channel) {
1034         if((argc < 2) || !IsChannelName(argv[1]))
1035         {
1036             reply("MSG_NOT_CHANNEL_NAME");
1037             return 0;
1038         }
1039
1040         channel = AddChannel(argv[1], now, NULL, NULL);
1041         AddChannelUser(bot, channel)->modes |= MODE_CHANOP;
1042     } else if (GetUserMode(channel, bot)) {
1043         reply("OSMSG_ALREADY_JOINED", channel->name);
1044         return 0;
1045     } else {
1046         struct mod_chanmode change;
1047         mod_chanmode_init(&change);
1048         change.argc = 1;
1049         change.args[0].mode = MODE_CHANOP;
1050         change.args[0].u.member = AddChannelUser(bot, channel);
1051         modcmd_chanmode_announce(&change);
1052     }
1053
1054     irc_fetchtopic(bot, channel->name);
1055     reply("OSMSG_JOIN_DONE", channel->name);
1056     return 1;
1057 }
1058
1059 static MODCMD_FUNC(cmd_kick)
1060 {
1061     struct userNode *target;
1062     char *reason;
1063
1064     if (argc < 3) {
1065         reason = alloca(strlen(OSMSG_KICK_REQUESTED)+strlen(user->nick)+1);
1066         sprintf(reason, OSMSG_KICK_REQUESTED, user->nick);
1067     } else {
1068         reason = unsplit_string(argv+2, argc-2, NULL);
1069     }
1070     target = GetUserH(argv[1]);
1071     if (!target) {
1072         reply("MSG_NICK_UNKNOWN", argv[1]);
1073         return 0;
1074     }
1075     if (!GetUserMode(channel, target)) {
1076         reply("OSMSG_NOT_ON_CHANNEL", target->nick, channel->name);
1077         return 0;
1078     }
1079     if (IsService(target)) {
1080         reply("MSG_SERVICE_IMMUNE", target->nick);
1081         return 0;
1082     }
1083     KickChannelUser(target, channel, cmd->parent->bot, reason);
1084     return 1;
1085 }
1086
1087 static MODCMD_FUNC(cmd_forcekick)
1088 {
1089     struct userNode *target;
1090     char *reason;
1091
1092     if (argc < 3) {
1093         reason = alloca(strlen(OSMSG_KICK_REQUESTED)+strlen(user->nick)+1);
1094         sprintf(reason, OSMSG_KICK_REQUESTED, user->nick);
1095     } else {
1096         reason = unsplit_string(argv+2, argc-2, NULL);
1097     }
1098     target = GetUserH(argv[1]);
1099     if (!target) {
1100         reply("MSG_NICK_UNKNOWN", argv[1]);
1101         return 0;
1102     }
1103     if (!GetUserMode(channel, target)) {
1104         reply("OSMSG_NOT_ON_CHANNEL", target->nick, channel->name);
1105         return 0;
1106     }
1107     if (IsLocal(target)) {
1108         reply("OSMSG_FORCEKICK_LOCAL", target->nick);
1109         return 0;
1110     }
1111     irc_kick(cmd->parent->bot, target, channel, reason);
1112     return 1;
1113 }
1114
1115 static MODCMD_FUNC(cmd_kickall)
1116 {
1117     unsigned int limit, n, inchan;
1118     struct modeNode *mn;
1119     char *reason;
1120     struct userNode *bot = cmd->parent->bot;
1121
1122     /* ircu doesn't let servers KICK users, so if OpServ's not in the
1123      * channel, we have to join it in temporarily. */
1124     if (!(inchan = GetUserMode(channel, bot) ? 1 : 0)) {
1125         struct mod_chanmode change;
1126         mod_chanmode_init(&change);
1127         change.args[0].mode = MODE_CHANOP;
1128         change.args[0].u.member = AddChannelUser(bot, channel);
1129         modcmd_chanmode_announce(&change);
1130     }
1131     if (argc < 2) {
1132         reason = alloca(strlen(OSMSG_KICK_REQUESTED)+strlen(user->nick)+1);
1133         sprintf(reason, OSMSG_KICK_REQUESTED, user->nick);
1134     } else {
1135         reason = unsplit_string(argv+1, argc-1, NULL);
1136     }
1137     limit = user->handle_info->opserv_level;
1138     for (n=channel->members.used; n>0;) {
1139         mn = channel->members.list[--n];
1140         if (IsService(mn->user)
1141             || (mn->user->handle_info
1142                 && (mn->user->handle_info->opserv_level >= limit))) {
1143             continue;
1144         }
1145         KickChannelUser(mn->user, channel, bot, reason);
1146     }
1147     if (!inchan)
1148         DelChannelUser(bot, channel, "My work here is done", 0);
1149     reply("OSMSG_KICKALL_DONE", channel->name);
1150     return 1;
1151 }
1152
1153 static MODCMD_FUNC(cmd_kickban)
1154 {
1155     struct mod_chanmode change;
1156     struct userNode *target;
1157     char *reason;
1158     char *mask;
1159
1160     if (argc == 2) {
1161         reason = alloca(strlen(OSMSG_KICK_REQUESTED)+strlen(user->nick)+1);
1162         sprintf(reason, OSMSG_KICK_REQUESTED, user->nick);
1163     } else {
1164         reason = unsplit_string(argv+2, argc-2, NULL);
1165     }
1166     target = GetUserH(argv[1]);
1167     if (!target) {
1168         reply("MSG_NICK_UNKNOWN", argv[1]);
1169         return 0;
1170     }
1171     if (!GetUserMode(channel, target)) {
1172         reply("OSMSG_NOT_ON_CHANNEL", target->nick, channel->name);
1173         return 0;
1174     }
1175     mod_chanmode_init(&change);
1176     change.argc = 1;
1177     change.args[0].mode = MODE_BAN;
1178     change.args[0].u.hostmask = mask = generate_hostmask(target, 0);
1179     modcmd_chanmode_announce(&change);
1180     KickChannelUser(target, channel, cmd->parent->bot, reason);
1181     free(mask);
1182     return 1;
1183 }
1184
1185 static MODCMD_FUNC(cmd_kickbanall)
1186 {
1187     struct modeNode *mn;
1188     struct userNode *bot = cmd->parent->bot;
1189     struct mod_chanmode *change;
1190     char *reason;
1191     unsigned int limit, n, inchan;
1192
1193     /* ircu doesn't let servers KICK users, so if OpServ's not in the
1194      * channel, we have to join it in temporarily. */
1195     if (!(inchan = GetUserMode(channel, bot) ? 1 : 0)) {
1196         change = mod_chanmode_alloc(2);
1197         change->args[0].mode = MODE_CHANOP;
1198         change->args[0].u.member = AddChannelUser(bot, channel);
1199         change->args[1].mode = MODE_BAN;
1200         change->args[1].u.hostmask = "*!*@*";
1201     } else {
1202         change = mod_chanmode_alloc(1);
1203         change->args[0].mode = MODE_BAN;
1204         change->args[0].u.hostmask = "*!*@*";
1205     }
1206     modcmd_chanmode_announce(change);
1207     mod_chanmode_free(change);
1208     if (argc < 2) {
1209         reason = alloca(strlen(OSMSG_KICK_REQUESTED)+strlen(user->nick)+1);
1210         sprintf(reason, OSMSG_KICK_REQUESTED, user->nick);
1211     } else {
1212         reason = unsplit_string(argv+1, argc-1, NULL);
1213     }
1214     /* now kick them */
1215     limit = user->handle_info->opserv_level;
1216     for (n=channel->members.used; n>0; ) {
1217         mn = channel->members.list[--n];
1218         if (IsService(mn->user)
1219             || (mn->user->handle_info
1220                 && (mn->user->handle_info->opserv_level >= limit))) {
1221             continue;
1222         }
1223         KickChannelUser(mn->user, channel, bot, reason);
1224     }
1225     if (!inchan)
1226         DelChannelUser(bot, channel, "My work here is done", 0);
1227     reply("OSMSG_KICKALL_DONE", channel->name);
1228     return 1;
1229 }
1230
1231 static MODCMD_FUNC(cmd_part)
1232 {
1233     char *reason;
1234
1235     if (!GetUserMode(channel, cmd->parent->bot)) {
1236         reply("OSMSG_NOT_ON_CHANNEL", cmd->parent->bot->nick, channel->name);
1237         return 0;
1238     }
1239     reason = (argc < 2) ? "Leaving." : unsplit_string(argv+1, argc-1, NULL);
1240     reply("OSMSG_LEAVING", channel->name);
1241     DelChannelUser(cmd->parent->bot, channel, reason, 0);
1242     return 1;
1243 }
1244
1245 static MODCMD_FUNC(cmd_mode)
1246 {
1247     if (!modcmd_chanmode(argv+1, argc-1, MCP_ALLOW_OVB|MCP_KEY_FREE|MC_ANNOUNCE|MCP_OPERMODE)) {
1248         reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
1249         return 0;
1250     }
1251     reply("OSMSG_MODE_SET", channel->name);
1252     return 1;
1253 }
1254
1255 static MODCMD_FUNC(cmd_op)
1256 {
1257     struct mod_chanmode *change;
1258     unsigned int arg, count;
1259
1260     change = mod_chanmode_alloc(argc-1);
1261     for (arg = 1, count = 0; arg < argc; ++arg) {
1262         struct userNode *victim;
1263         struct modeNode *mn;
1264         if (!(victim = GetUserH(argv[arg])))
1265             continue;
1266         if (!(mn =  GetUserMode(channel, victim)))
1267             continue;
1268         if (mn->modes & MODE_CHANOP)
1269             continue;
1270         change->args[count].mode = MODE_CHANOP;
1271         change->args[count++].u.member = mn;
1272     }
1273     if (count) {
1274         change->argc = count;
1275         modcmd_chanmode_announce(change);
1276     }
1277     mod_chanmode_free(change);
1278     reply("OSMSG_OP_DONE");
1279     return 1;
1280 }
1281
1282 static MODCMD_FUNC(cmd_opall)
1283 {
1284     struct mod_chanmode *change;
1285     unsigned int ii, count;
1286
1287     change = mod_chanmode_alloc(channel->members.used);
1288     for (ii = count = 0; ii < channel->members.used; ++ii) {
1289         struct modeNode *mn = channel->members.list[ii];
1290         if (mn->modes & MODE_CHANOP)
1291             continue;
1292         change->args[count].mode = MODE_CHANOP;
1293         change->args[count++].u.member = mn;
1294     }
1295     if (count) {
1296         change->argc = count;
1297         modcmd_chanmode_announce(change);
1298     }
1299     mod_chanmode_free(change);
1300     reply("OSMSG_OPALL_DONE", channel->name);
1301     return 1;
1302 }
1303
1304 static MODCMD_FUNC(cmd_whois)
1305 {
1306     struct userNode *target;
1307     char buffer[128];
1308     int bpos, herelen;
1309
1310 #ifdef WITH_PROTOCOL_P10
1311     if (argv[1][0] == '*')
1312         target = GetUserN(argv[1]+1);
1313     else
1314 #endif
1315     target = GetUserH(argv[1]);
1316     if (!target) {
1317         reply("MSG_NICK_UNKNOWN", argv[1]);
1318         return 0;
1319     }
1320     reply("OSMSG_WHOIS_NICK", target->nick);
1321     reply("OSMSG_WHOIS_HOST", target->ident, target->hostname);
1322     if (IsFakeIdent(target) && IsFakeHost(target))
1323         reply("OSMSG_WHOIS_FAKEIDENTHOST", target->fakeident, target->fakehost);
1324     else if (IsFakeIdent(target))
1325         reply("OSMSG_WHOIS_FAKEIDENT", target->fakeident);
1326     else if (IsFakeHost(target))
1327         reply("OSMSG_WHOIS_FAKEHOST", target->fakehost);
1328     reply("OSMSG_WHOIS_IP", irc_ntoa(&target->ip));
1329     if (target->modes) {
1330         bpos = irc_user_modes(target, buffer, sizeof(buffer));
1331 #define buffer_cat(str) (herelen = strlen(str), memcpy(buffer+bpos, str, herelen), bpos += herelen)
1332         if (IsGagged(target)) buffer_cat(" (gagged)");
1333         if (IsRegistering(target)) buffer_cat(" (registered account)");
1334         buffer[bpos] = 0;
1335         if (bpos > 0)
1336             reply("OSMSG_WHOIS_MODES", buffer);
1337     }
1338     reply("OSMSG_WHOIS_INFO", target->info);
1339 #ifdef WITH_PROTOCOL_P10
1340     reply("OSMSG_WHOIS_NUMERIC", target->numeric);
1341 #endif
1342     reply("OSMSG_WHOIS_SERVER", target->uplink->name);
1343     reply("OSMSG_WHOIS_ACCOUNT", (target->handle_info ? target->handle_info->handle : "Not authenticated"));
1344     intervalString(buffer, now - target->timestamp, user->handle_info);
1345     reply("OSMSG_WHOIS_NICK_AGE", buffer);
1346     if (target->channels.used <= MAX_CHANNELS_WHOIS || HANDLE_FLAGGED(user->handle_info, BOT))
1347         opserv_ison(user, target, "OSMSG_WHOIS_CHANNELS");
1348     else
1349         reply("OSMSG_WHOIS_HIDECHANS");
1350     return 1;
1351 }
1352
1353 static MODCMD_FUNC(cmd_unban)
1354 {
1355     struct mod_chanmode change;
1356     mod_chanmode_init(&change);
1357     change.argc = 1;
1358     change.args[0].mode = MODE_REMOVE | MODE_BAN;
1359     change.args[0].u.hostmask = argv[1];
1360     modcmd_chanmode_announce(&change);
1361     reply("OSMSG_UNBAN_DONE", channel->name);
1362     return 1;
1363 }
1364
1365 static MODCMD_FUNC(cmd_voiceall)
1366 {
1367     struct mod_chanmode *change;
1368     unsigned int ii, count;
1369
1370     change = mod_chanmode_alloc(channel->members.used);
1371     for (ii = count = 0; ii < channel->members.used; ++ii) {
1372         struct modeNode *mn = channel->members.list[ii];
1373         if (mn->modes & (MODE_CHANOP|MODE_VOICE))
1374             continue;
1375         change->args[count].mode = MODE_VOICE;
1376         change->args[count++].u.member = mn;
1377     }
1378     if (count) {
1379         change->argc = count;
1380         modcmd_chanmode_announce(change);
1381     }
1382     mod_chanmode_free(change);
1383     reply("OSMSG_CHANNEL_VOICED", channel->name);
1384     return 1;
1385 }
1386
1387 static MODCMD_FUNC(cmd_devoiceall)
1388 {
1389     struct mod_chanmode *change;
1390     unsigned int ii, count;
1391
1392     change = mod_chanmode_alloc(channel->members.used);
1393     for (ii = count = 0; ii < channel->members.used; ++ii) {
1394         struct modeNode *mn = channel->members.list[ii];
1395         if (!(mn->modes & MODE_VOICE))
1396             continue;
1397         change->args[count].mode = MODE_REMOVE | MODE_VOICE;
1398         change->args[count++].u.member = mn;
1399     }
1400     if (count) {
1401         change->argc = count;
1402         modcmd_chanmode_announce(change);
1403     }
1404     mod_chanmode_free(change);
1405     reply("OSMSG_CHANNEL_DEVOICED", channel->name);
1406     return 1;
1407 }
1408
1409 static MODCMD_FUNC(cmd_stats_bad) {
1410     dict_iterator_t it;
1411     unsigned int ii, end, here_len;
1412     char buffer[400];
1413
1414     /* Show the bad word list.. */
1415     for (ii=end=0; ii<opserv_bad_words->used; ii++) {
1416         here_len = strlen(opserv_bad_words->list[ii]);
1417         if ((end + here_len + 2) > sizeof(buffer)) {
1418             buffer[end] = 0;
1419             reply("OSMSG_BADWORD_LIST", buffer);
1420             end = 0;
1421         }
1422         memcpy(buffer+end, opserv_bad_words->list[ii], here_len);
1423         end += here_len;
1424         buffer[end++] = ' ';
1425     }
1426     buffer[end] = 0;
1427     reply("OSMSG_BADWORD_LIST", buffer);
1428
1429     /* Show the exemption list.. */
1430     for (it=dict_first(opserv_exempt_channels), end=0; it; it=iter_next(it)) {
1431         here_len = strlen(iter_key(it));
1432         if ((end + here_len + 2) > sizeof(buffer)) {
1433             buffer[end] = 0;
1434             reply("OSMSG_EXEMPTED_LIST", buffer);
1435             end = 0;
1436         }
1437         memcpy(buffer+end, iter_key(it), here_len);
1438         end += here_len;
1439         buffer[end++] = ' ';
1440     }
1441     buffer[end] = 0;
1442     reply("OSMSG_EXEMPTED_LIST", buffer);
1443     return 1;
1444 }
1445
1446 static void
1447 trace_links(struct userNode *bot, struct userNode *user, struct server *server, unsigned int depth) {
1448     unsigned int nn, pos;
1449     char buffer[400];
1450
1451     for (nn=1; nn<=depth; nn<<=1) ;
1452     for (pos=0, nn>>=1; nn>1; ) {
1453         nn >>= 1;
1454         buffer[pos++] = (depth & nn) ? ((nn == 1) ? '`' : ' ') : '|';
1455         buffer[pos++] = (nn == 1) ? '-': ' ';
1456     }
1457     buffer[pos] = 0;
1458     send_message(user, bot, "OSMSG_LINKS_SERVER", buffer, server->name, server->clients, server->description);
1459     if (!server->children.used)
1460         return;
1461     for (nn=0; nn<server->children.used-1; nn++) {
1462         trace_links(bot, user, server->children.list[nn], depth<<1);
1463     }
1464     trace_links(bot, user, server->children.list[nn], (depth<<1)|1);
1465 }
1466
1467 static MODCMD_FUNC(cmd_stats_links) {
1468     trace_links(cmd->parent->bot, user, self, 1);
1469     return 1;
1470 }
1471
1472
1473 static MODCMD_FUNC(cmd_stats_max) {
1474     time_t feh;
1475     feh = max_clients_time;
1476     reply("OSMSG_MAX_CLIENTS", max_clients, asctime(localtime(&feh)));
1477     return 1;
1478 }
1479
1480 static MODCMD_FUNC(cmd_stats_network) {
1481     struct helpfile_table tbl;
1482     unsigned int nn, tot_clients;
1483     dict_iterator_t it;
1484
1485     tot_clients = dict_size(clients);
1486     reply("OSMSG_NETWORK_INFO", tot_clients, invis_clients, curr_opers.used);
1487     tbl.length = dict_size(servers)+1;
1488     tbl.width = 3;
1489     tbl.flags = TABLE_NO_FREE;
1490     tbl.contents = calloc(tbl.length, sizeof(*tbl.contents));
1491     tbl.contents[0] = calloc(tbl.width, sizeof(**tbl.contents));
1492     tbl.contents[0][0] = "Server Name";
1493     tbl.contents[0][1] = "Clients";
1494     tbl.contents[0][2] = "Load";
1495     for (it=dict_first(servers), nn=1; it; it=iter_next(it)) {
1496         struct server *server = iter_data(it);
1497         char *buffer = malloc(32);
1498         tbl.contents[nn] = calloc(tbl.width, sizeof(**tbl.contents));
1499         tbl.contents[nn][0] = server->name;
1500         tbl.contents[nn][1] = buffer;
1501         sprintf(buffer, "%u", server->clients);
1502         tbl.contents[nn][2] = buffer + 16;
1503         sprintf(buffer+16, "%3.3g%%", ((double)server->clients/tot_clients)*100);
1504         nn++;
1505     }
1506     table_send(cmd->parent->bot, user->nick, 0, 0, tbl);
1507     for (nn=1; nn<tbl.length; nn++) {
1508         free((char*)tbl.contents[nn][1]);
1509         free(tbl.contents[nn]);
1510     }
1511     free(tbl.contents[0]);
1512     free(tbl.contents);
1513     return 1;
1514 }
1515
1516 static MODCMD_FUNC(cmd_stats_network2) {
1517     struct helpfile_table tbl;
1518     unsigned int nn;
1519     dict_iterator_t it;
1520
1521     tbl.length = dict_size(servers)+1;
1522     tbl.width = 3;
1523     tbl.flags = TABLE_NO_FREE;
1524     tbl.contents = calloc(tbl.length, sizeof(*tbl.contents));
1525     tbl.contents[0] = calloc(tbl.width, sizeof(**tbl.contents));
1526     tbl.contents[0][0] = "Server Name";
1527     tbl.contents[0][1] = "Numeric";
1528     tbl.contents[0][2] = "Link Time";
1529     for (it=dict_first(servers), nn=1; it; it=iter_next(it)) {
1530         struct server *server = iter_data(it);
1531         char *buffer = malloc(64);
1532         int ofs;
1533
1534         tbl.contents[nn] = calloc(tbl.width, sizeof(**tbl.contents));
1535         tbl.contents[nn][0] = server->name;
1536 #ifdef WITH_PROTOCOL_P10
1537         sprintf(buffer, "%s (%ld)", server->numeric, base64toint(server->numeric, strlen(server->numeric)));
1538 #else
1539         buffer[0] = 0;
1540 #endif
1541         tbl.contents[nn][1] = buffer;
1542         ofs = strlen(buffer) + 1;
1543         intervalString(buffer + ofs, now - server->link_time, user->handle_info);
1544         if (server->self_burst)
1545             strcat(buffer + ofs, " Bursting");
1546         tbl.contents[nn][2] = buffer + ofs;
1547         nn++;
1548     }
1549     table_send(cmd->parent->bot, user->nick, 0, 0, tbl);
1550     for (nn=1; nn<tbl.length; nn++) {
1551         free((char*)tbl.contents[nn][1]);
1552         free(tbl.contents[nn]);
1553     }
1554     free(tbl.contents[0]);
1555     free(tbl.contents);
1556     return 1;
1557 }
1558
1559 static MODCMD_FUNC(cmd_stats_reserved) {
1560     dict_iterator_t it;
1561
1562     reply("OSMSG_RESERVED_LIST");
1563     for (it = dict_first(opserv_reserved_nick_dict); it; it = iter_next(it))
1564         send_message_type(4, user, cmd->parent->bot, "%s", iter_key(it));
1565     return 1;
1566 }
1567
1568 static MODCMD_FUNC(cmd_stats_trusted) {
1569     dict_iterator_t it;
1570     struct trusted_host *th;
1571     char length[INTERVALLEN], issued[INTERVALLEN], limit[32];
1572
1573     if (argc > 1) {
1574         th = dict_find(opserv_trusted_hosts, argv[1], NULL);
1575         if (th) {
1576             if (th->issued)
1577                 intervalString(issued, now - th->issued, user->handle_info);
1578             if (th->expires)
1579                 intervalString(length, th->expires - now, user->handle_info);
1580             if (th->limit)
1581                 sprintf(limit, "limit %lu", th->limit);
1582             reply("OSMSG_HOST_IS_TRUSTED",
1583                   th->ipaddr,
1584                   (th->limit ? limit : "no limit"),
1585                   (th->issued ? issued : "some time"),
1586                   (th->issuer ? th->issuer : "<unknown>"),
1587                   (th->expires ? length : "never"),
1588                   (th->reason ? th->reason : "<unknown>"));
1589         } else {
1590             reply("OSMSG_HOST_NOT_TRUSTED", argv[1]);
1591         }
1592     } else {
1593         reply("OSMSG_TRUSTED_LIST");
1594         for (it = dict_first(opserv_trusted_hosts); it; it = iter_next(it)) {
1595             th = iter_data(it);
1596             if (th->issued)
1597                 intervalString(issued, now - th->issued, user->handle_info);
1598             if (th->expires)
1599                 intervalString(length, th->expires - now, user->handle_info);
1600             if (th->limit)
1601                 sprintf(limit, "limit %lu", th->limit);
1602             reply("OSMSG_HOST_IS_TRUSTED", iter_key(it),
1603                   (th->limit ? limit : "no limit"),
1604                   (th->issued ? issued : "some time"),
1605                   (th->issuer ? th->issuer : "<unknown>"),
1606                   (th->expires ? length : "never"),
1607                   (th->reason ? th->reason : "<unknown>"));
1608         }
1609     }
1610     return 1;
1611 }
1612
1613 static MODCMD_FUNC(cmd_stats_uplink) {
1614     extern struct cManagerNode cManager;
1615     struct uplinkNode *uplink;
1616
1617     uplink = cManager.uplink;
1618     reply("OSMSG_UPLINK_START", uplink->name);
1619     reply("OSMSG_UPLINK_ADDRESS", uplink->host, uplink->port);
1620     return 1;
1621 }
1622
1623 static MODCMD_FUNC(cmd_stats_uptime) {
1624     extern int lines_processed;
1625     extern unsigned long boot_time;
1626     double kernel_time;
1627     double user_time;
1628     char uptime[INTERVALLEN];
1629
1630 #if defined(HAVE_TIMES)
1631     static double clocks_per_sec;
1632     struct tms buf;
1633
1634     if (!clocks_per_sec) {
1635 #if defined(HAVE_SYSCONF) && defined(_SC_CLK_TCK)
1636         clocks_per_sec = sysconf(_SC_CLK_TCK);
1637         if (clocks_per_sec <= 0)
1638 #endif
1639         {
1640             log_module(OS_LOG, LOG_ERROR, "Unable to query sysconf(_SC_CLK_TCK), output of 'stats uptime' will be wrong");
1641             clocks_per_sec = CLOCKS_PER_SEC;
1642         }
1643     }
1644     times(&buf);
1645     user_time = buf.tms_utime / clocks_per_sec;
1646     kernel_time = buf.tms_stime / clocks_per_sec;
1647 #elif defined(HAVE_GETPROCESSTIMES)
1648     FILETIME times[4];
1649     LARGE_INTEGER li[2];
1650
1651     GetProcessTimes(GetCurrentProcess(), &times[0], &times[1], &times[2], &times[3]);
1652     li[0].LowPart = times[2].dwLowDateTime;
1653     li[0].HighPart = times[2].dwHighDateTime;
1654     kernel_time = li[0].QuadPart * 1e-7;
1655     li[1].LowPart = times[3].dwLowDateTime;
1656     li[1].HighPart = times[3].dwHighDateTime;
1657     user_time = li[1].QuadPart * 1e-7;
1658 #else
1659     user_time = NAN;
1660     system_time = NAN;
1661 #endif
1662
1663     intervalString(uptime, time(NULL)-boot_time, user->handle_info);
1664     reply("OSMSG_UPTIME_STATS", uptime, lines_processed, user_time, kernel_time);
1665     return 1;
1666 }
1667
1668 static MODCMD_FUNC(cmd_stats_alerts) {
1669     dict_iterator_t it;
1670     struct opserv_user_alert *alert;
1671     const char *reaction;
1672
1673     reply("OSMSG_ALERTS_LIST");
1674     for (it = dict_first(opserv_user_alerts); it; it = iter_next(it)) {
1675         alert = iter_data(it);
1676         switch (alert->reaction) {
1677         case REACT_NOTICE: reaction = "notice"; break;
1678         case REACT_KILL: reaction = "kill"; break;
1679         case REACT_GLINE: reaction = "gline"; break;
1680         default: reaction = "<unknown>"; break;
1681         }
1682         reply("OSMSG_ALERT_IS", iter_key(it), alert->owner, reaction, alert->text_discrim);
1683     }
1684     return 1;
1685 }
1686
1687 static MODCMD_FUNC(cmd_stats_gags) {
1688     struct gag_entry *gag;
1689     struct helpfile_table table;
1690     unsigned int nn;
1691
1692     if (!gagList) {
1693         reply("OSMSG_NO_GAGS");
1694         return 1;
1695     }
1696     for (nn=0, gag=gagList; gag; nn++, gag=gag->next) ;
1697     table.length = nn+1;
1698     table.width = 4;
1699     table.flags = TABLE_NO_FREE;
1700     table.contents = calloc(table.length, sizeof(char**));
1701     table.contents[0] = calloc(table.width, sizeof(char*));
1702     table.contents[0][0] = "Mask";
1703     table.contents[0][1] = "Owner";
1704     table.contents[0][2] = "Expires";
1705     table.contents[0][3] = "Reason";
1706     for (nn=1, gag=gagList; gag; nn++, gag=gag->next) {
1707         char expstr[INTERVALLEN];
1708         if (gag->expires)
1709             intervalString(expstr, gag->expires - now, user->handle_info);
1710         else
1711             strcpy(expstr, "Never");
1712         table.contents[nn] = calloc(table.width, sizeof(char*));
1713         table.contents[nn][0] = gag->mask;
1714         table.contents[nn][1] = gag->owner;
1715         table.contents[nn][2] = strdup(expstr);
1716         table.contents[nn][3] = gag->reason;
1717     }
1718     table_send(cmd->parent->bot, user->nick, 0, NULL, table);
1719     for (nn=1; nn<table.length; nn++) {
1720         free((char*)table.contents[nn][2]);
1721         free(table.contents[nn]);
1722     }
1723     free(table.contents[0]);
1724     free(table.contents);
1725     return 1;
1726 }
1727
1728 static MODCMD_FUNC(cmd_stats_timeq) {
1729     reply("OSMSG_TIMEQ_INFO", timeq_size(), timeq_next()-now);
1730     return 1;
1731 }
1732
1733 static MODCMD_FUNC(cmd_stats_warn) {
1734     dict_iterator_t it;
1735
1736     reply("OSMSG_WARN_LISTSTART");
1737     for (it=dict_first(opserv_chan_warn); it; it=iter_next(it))
1738         reply("OSMSG_WARN_LISTENTRY", iter_key(it), (char*)iter_data(it));
1739     reply("OSMSG_WARN_LISTEND");
1740     return 1;
1741 }
1742
1743 #if defined(WITH_MALLOC_SRVX)
1744 static MODCMD_FUNC(cmd_stats_memory) {
1745     extern unsigned long alloc_count, alloc_size;
1746     send_message_type(MSG_TYPE_NOXLATE, user, cmd->parent->bot,
1747                       "%u allocations totalling %u bytes.",
1748                       alloc_count, alloc_size);
1749     return 1;
1750 }
1751 #elif defined(WITH_MALLOC_SLAB)
1752 static MODCMD_FUNC(cmd_stats_memory) {
1753     extern unsigned long slab_alloc_count, slab_count, slab_alloc_size;
1754     extern unsigned long big_alloc_count, big_alloc_size;
1755     send_message_type(MSG_TYPE_NOXLATE, user, cmd->parent->bot,
1756                       "%u allocations in %u slabs totalling %u bytes.",
1757                       slab_alloc_count, slab_count, slab_alloc_size);
1758     send_message_type(MSG_TYPE_NOXLATE, user, cmd->parent->bot,
1759                       "%u big allocations totalling %u bytes.",
1760                       big_alloc_count, big_alloc_size);
1761     return 1;
1762 }
1763 #endif
1764
1765 static MODCMD_FUNC(cmd_dump)
1766 {
1767     char linedup[MAXLEN], original[MAXLEN];
1768
1769     unsplit_string(argv+1, argc-1, original);
1770     safestrncpy(linedup, original, sizeof(linedup));
1771     /* assume it's only valid IRC if we can parse it */
1772     if (parse_line(linedup, 1)) {
1773         irc_raw(original);
1774         reply("OSMSG_LINE_DUMPED");
1775     } else
1776         reply("OSMSG_RAW_PARSE_ERROR");
1777     return 1;
1778 }
1779
1780 static MODCMD_FUNC(cmd_raw)
1781 {
1782     char linedup[MAXLEN], original[MAXLEN];
1783
1784     unsplit_string(argv+1, argc-1, original);
1785     safestrncpy(linedup, original, sizeof(linedup));
1786     /* Try to parse the line before sending it; if it's too wrong,
1787      * maybe it will core us instead of our uplink. */
1788     parse_line(linedup, 1);
1789     irc_raw(original);
1790     reply("OSMSG_LINE_DUMPED");
1791     return 1;
1792 }
1793
1794 static struct userNode *
1795 opserv_add_reserve(struct svccmd *cmd, struct userNode *user, const char *nick, const char *ident, const char *host, const char *desc)
1796 {
1797     struct userNode *resv = GetUserH(nick);
1798     if (resv) {
1799         if (IsService(resv)) {
1800             reply("MSG_SERVICE_IMMUNE", resv->nick);
1801             return NULL;
1802         }
1803         if (resv->handle_info
1804             && resv->handle_info->opserv_level > user->handle_info->opserv_level) {
1805             reply("OSMSG_LEVEL_TOO_LOW");
1806             return NULL;
1807         }
1808     }
1809     if ((resv = AddLocalUser(nick, ident, host, desc, "+i"))) {
1810         dict_insert(opserv_reserved_nick_dict, resv->nick, resv);
1811     }
1812     return resv;
1813 }
1814
1815 static MODCMD_FUNC(cmd_collide)
1816 {
1817     struct userNode *resv;
1818
1819     resv = opserv_add_reserve(cmd, user, argv[1], argv[2], argv[3], unsplit_string(argv+4, argc-4, NULL));
1820     if (resv) {
1821         reply("OSMSG_COLLIDED_NICK", resv->nick);
1822         return 1;
1823     } else {
1824         reply("OSMSG_CLONE_FAILED", argv[1]);
1825         return 0;
1826     }
1827 }
1828
1829 static MODCMD_FUNC(cmd_reserve)
1830 {
1831     struct userNode *resv;
1832
1833     resv = opserv_add_reserve(cmd, user, argv[1], argv[2], argv[3], unsplit_string(argv+4, argc-4, NULL));
1834     if (resv) {
1835         resv->modes |= FLAGS_PERSISTENT;
1836         reply("OSMSG_RESERVED_NICK", resv->nick);
1837         return 1;
1838     } else {
1839         reply("OSMSG_CLONE_FAILED", argv[1]);
1840         return 0;
1841     }
1842 }
1843
1844 static int
1845 free_reserve(char *nick)
1846 {
1847     struct userNode *resv;
1848     unsigned int rlen;
1849     char *reason;
1850
1851     resv = dict_find(opserv_reserved_nick_dict, nick, NULL);
1852     if (!resv)
1853         return 0;
1854
1855     rlen = strlen(resv->nick)+strlen(OSMSG_PART_REASON);
1856     reason = alloca(rlen);
1857     snprintf(reason, rlen, OSMSG_PART_REASON, resv->nick);
1858     DelUser(resv, NULL, 1, reason);
1859     dict_remove(opserv_reserved_nick_dict, nick);
1860     return 1;
1861 }
1862
1863 static MODCMD_FUNC(cmd_unreserve)
1864 {
1865     if (free_reserve(argv[1]))
1866         reply("OSMSG_NICK_UNRESERVED", argv[1]);
1867     else
1868         reply("OSMSG_NOT_RESERVED", argv[1]);
1869     return 1;
1870 }
1871
1872 static void
1873 opserv_part_channel(void *data)
1874 {
1875     DelChannelUser(opserv, data, "Leaving.", 0);
1876 }
1877
1878 static int alert_check_user(const char *key, void *data, void *extra);
1879
1880 static void
1881 opserv_new_user_check(struct userNode *user)
1882 {
1883     struct opserv_hostinfo *ohi;
1884     struct gag_entry *gag;
1885     char addr[IRC_NTOP_MAX_SIZE];
1886
1887     /* Check to see if we should ignore them entirely. */
1888     if (IsLocal(user) || IsService(user))
1889         return;
1890
1891     /* Check for alerts, and stop if we find one that kills them. */
1892     if (dict_foreach(opserv_user_alerts, alert_check_user, user))
1893         return;
1894
1895     /* Gag them if appropriate. */
1896     for (gag = gagList; gag; gag = gag->next) {
1897         if (user_matches_glob(user, gag->mask, MATCH_USENICK)) {
1898             gag_helper_func(user, NULL);
1899             break;
1900         }
1901     }
1902
1903     /* Add to host info struct */
1904     irc_ntop(addr, sizeof(addr), &user->ip);
1905     if (!(ohi = dict_find(opserv_hostinfo_dict, addr, NULL))) {
1906         ohi = calloc(1, sizeof(*ohi));
1907         dict_insert(opserv_hostinfo_dict, strdup(addr), ohi);
1908         userList_init(&ohi->clients);
1909     }
1910     userList_append(&ohi->clients, user);
1911
1912     /* Only warn of new user floods outside of bursts. */
1913     if (!user->uplink->burst) {
1914         if (!policer_conforms(&opserv_conf.new_user_policer, now, 10)) {
1915             if (!new_user_flood) {
1916                 new_user_flood = 1;
1917                 opserv_alert("Warning: Possible new-user flood.");
1918             }
1919         } else {
1920             new_user_flood = 0;
1921         }
1922     }
1923
1924     /* Only warn or G-line if there's an untrusted max and their IP is sane. */
1925     if (opserv_conf.untrusted_max
1926         && irc_in_addr_is_valid(user->ip)
1927         && !irc_in_addr_is_loopback(user->ip)) {
1928         struct trusted_host *th = dict_find(opserv_trusted_hosts, addr, NULL);
1929         unsigned int limit = th ? th->limit : opserv_conf.untrusted_max;
1930         if (!limit) {
1931             /* 0 means unlimited hosts */
1932         } else if (ohi->clients.used == limit) {
1933             unsigned int nn;
1934             for (nn=0; nn<ohi->clients.used; nn++)
1935                 send_message(ohi->clients.list[nn], opserv, "OSMSG_CLONE_WARNING");
1936         } else if (ohi->clients.used > limit) {
1937             char target[IRC_NTOP_MAX_SIZE + 3] = { '*', '@', '\0' };
1938             strcpy(target + 2, addr);
1939             gline_add(opserv->nick, target, opserv_conf.clone_gline_duration, "AUTO Excessive connections from a single host.", now, now, 0, 1);
1940         }
1941     }
1942 }
1943
1944 static void
1945 opserv_user_cleanup(struct userNode *user, UNUSED_ARG(struct userNode *killer), UNUSED_ARG(const char *why))
1946 {
1947     struct opserv_hostinfo *ohi;
1948     char addr[IRC_NTOP_MAX_SIZE];
1949
1950     if (IsLocal(user)) {
1951         /* Try to remove it from the reserved nick dict without
1952          * calling free_reserve, because that would call DelUser(),
1953          * and we'd loop back to here. */
1954         dict_remove(opserv_reserved_nick_dict, user->nick);
1955         return;
1956     }
1957     irc_ntop(addr, sizeof(addr), &user->ip);
1958     if ((ohi = dict_find(opserv_hostinfo_dict, addr, NULL))) {
1959         userList_remove(&ohi->clients, user);
1960         if (ohi->clients.used == 0)
1961             dict_remove(opserv_hostinfo_dict, addr);
1962     }
1963 }
1964
1965 int
1966 opserv_bad_channel(const char *name)
1967 {
1968     unsigned int found;
1969     int present;
1970
1971     dict_find(opserv_exempt_channels, name, &present);
1972     if (present)
1973         return 0;
1974
1975     if (gline_find(name))
1976         return 1;
1977
1978     for (found=0; found<opserv_bad_words->used; ++found)
1979         if (irccasestr(name, opserv_bad_words->list[found]))
1980             return 1;
1981
1982     return 0;
1983 }
1984
1985 static void
1986 opserv_shutdown_channel(struct chanNode *channel, const char *reason)
1987 {
1988     struct mod_chanmode *change;
1989     unsigned int nn;
1990
1991     change = mod_chanmode_alloc(2);
1992     change->modes_set = MODE_SECRET | MODE_INVITEONLY;
1993     change->args[0].mode = MODE_CHANOP;
1994     change->args[0].u.member = AddChannelUser(opserv, channel);
1995     change->args[1].mode = MODE_BAN;
1996     change->args[1].u.hostmask = "*!*@*";
1997     mod_chanmode_announce(opserv, channel, change);
1998     mod_chanmode_free(change);
1999     for (nn=channel->members.used; nn>0; ) {
2000         struct modeNode *mNode = channel->members.list[--nn];
2001         if (IsService(mNode->user))
2002             continue;
2003         KickChannelUser(mNode->user, channel, opserv, user_find_message(mNode->user, reason));
2004     }
2005     timeq_add(now + opserv_conf.purge_lock_delay, opserv_part_channel, channel);
2006 }
2007
2008 static void
2009 opserv_channel_check(struct chanNode *newchan)
2010 {
2011     char *warning;
2012
2013     if (!newchan->join_policer.params) {
2014         newchan->join_policer.last_req = now;
2015         newchan->join_policer.params = opserv_conf.join_policer_params;
2016     }
2017     if ((warning = dict_find(opserv_chan_warn, newchan->name, NULL))) {
2018         char message[MAXLEN];
2019         snprintf(message, sizeof(message), "Channel activity warning for channel %s: %s", newchan->name, warning);
2020         global_message(MESSAGE_RECIPIENT_OPERS, message);
2021     }
2022
2023     /* Wait until the join check to shut channels down. */
2024     newchan->bad_channel = opserv_bad_channel(newchan->name);
2025 }
2026
2027 static void
2028 opserv_channel_delete(struct chanNode *chan)
2029 {
2030     timeq_del(0, opserv_part_channel, chan, TIMEQ_IGNORE_WHEN);
2031 }
2032
2033 static int
2034 opserv_join_check(struct modeNode *mNode)
2035 {
2036     struct userNode *user = mNode->user;
2037     struct chanNode *channel = mNode->channel;
2038     const char *msg;
2039
2040     if (IsService(user))
2041         return 0;
2042
2043     dict_foreach(opserv_channel_alerts, alert_check_user, user);
2044
2045     if (opserv && channel->bad_channel) {
2046         opserv_debug("Found $b%s$b in bad-word channel $b%s$b; removing the user.", user->nick, channel->name);
2047         if (channel->name[0] != '#')
2048             DelUser(user, opserv, 1, "OSMSG_ILLEGAL_KILL_REASON");
2049         else if (!GetUserMode(channel, opserv))
2050             opserv_shutdown_channel(channel, "OSMSG_ILLEGAL_REASON");
2051         else {
2052             send_message(user, opserv, "OSMSG_ILLEGAL_CHANNEL", channel->name);
2053             msg = user_find_message(user, "OSMSG_ILLEGAL_REASON");
2054             KickChannelUser(user, channel, opserv, msg);
2055         }
2056         return 1;
2057     }
2058
2059     if (user->uplink->burst)
2060         return 0;
2061     if (policer_conforms(&channel->join_policer, now, 1.0)) {
2062         channel->join_flooded = 0;
2063         return 0;
2064     }
2065     if (!channel->join_flooded) {
2066         /* Don't moderate the channel unless it is activated and
2067            the number of users in the channel is over the threshold. */
2068         struct mod_chanmode change;
2069         mod_chanmode_init(&change);
2070         channel->join_flooded = 1;
2071         if (opserv && opserv_conf.join_flood_moderate && (channel->members.used > opserv_conf.join_flood_moderate_threshold)) {
2072             if (!GetUserMode(channel, opserv)) {
2073                 /* If we aren't in the channel, join it. */
2074                 change.args[0].mode = MODE_CHANOP;
2075                 change.args[0].u.member = AddChannelUser(opserv, channel);
2076                 change.argc++;
2077             }
2078             change.modes_set = (MODE_MODERATED | MODE_DELAYJOINS) & ~channel->modes;
2079             if (change.modes_set || change.argc)
2080                 mod_chanmode_announce(opserv, channel, &change);
2081             send_target_message(0, channel->name, opserv, "OSMSG_FLOOD_MODERATE");
2082             opserv_alert("Warning: Possible join flood in %s (currently %d users; channel moderated).", channel->name, channel->members.used);
2083         } else {
2084             opserv_alert("Warning: Possible join flood in %s (currently %d users).", channel->name, channel->members.used);
2085         }
2086     }
2087     log_module(OS_LOG, LOG_INFO, "Join to %s during flood: "IDENT_FORMAT, channel->name, IDENT_DATA(user));
2088     return 0;
2089 }
2090
2091 static int
2092 opserv_add_bad_word(struct svccmd *cmd, struct userNode *user, const char *new_bad) {
2093     unsigned int bad_idx;
2094
2095     for (bad_idx = 0; bad_idx < opserv_bad_words->used; ++bad_idx) {
2096         char *orig_bad = opserv_bad_words->list[bad_idx];
2097         if (irccasestr(new_bad, orig_bad)) {
2098             if (user)
2099                 reply("OSMSG_BAD_REDUNDANT", new_bad, orig_bad);
2100             return 0;
2101         } else if (irccasestr(orig_bad, new_bad)) {
2102             if (user)
2103                 reply("OSMSG_BAD_GROWING", orig_bad, new_bad);
2104             free(orig_bad);
2105             opserv_bad_words->list[bad_idx] = strdup(new_bad);
2106             for (bad_idx++; bad_idx < opserv_bad_words->used; bad_idx++) {
2107                 orig_bad = opserv_bad_words->list[bad_idx];
2108                 if (!irccasestr(orig_bad, new_bad))
2109                     continue;
2110                 if (user)
2111                     reply("OSMSG_BAD_NUKING", orig_bad);
2112                 string_list_delete(opserv_bad_words, bad_idx);
2113                 bad_idx--;
2114             }
2115             return 1;
2116         }
2117     }
2118     string_list_append(opserv_bad_words, strdup(new_bad));
2119     if (user)
2120         reply("OSMSG_ADDED_BAD", new_bad);
2121     return 1;
2122 }
2123
2124 static MODCMD_FUNC(cmd_addbad)
2125 {
2126     unsigned int arg, count;
2127     dict_iterator_t it;
2128     int bad_found, exempt_found;
2129
2130     /* Create the bad word if it doesn't exist. */
2131     bad_found = !opserv_add_bad_word(cmd, user, argv[1]);
2132
2133     /* Look for exception modifiers. */
2134     for (arg=2; arg<argc; arg++) {
2135         if (!irccasecmp(argv[arg], "except")) {
2136             reply("MSG_DEPRECATED_COMMAND", "addbad ... except", "addexempt");
2137             if (++arg > argc) {
2138                 reply("MSG_MISSING_PARAMS", "except");
2139                 break;
2140             }
2141             for (count = 0; (arg < argc) && IsChannelName(argv[arg]); arg++) {
2142                 dict_find(opserv_exempt_channels, argv[arg], &exempt_found);
2143                 if (!exempt_found) {
2144                     dict_insert(opserv_exempt_channels, strdup(argv[arg]), NULL);
2145                     count++;
2146                 }
2147             }
2148             reply("OSMSG_ADDED_EXEMPTIONS", count);
2149         } else {
2150             reply("MSG_DEPRECATED_COMMAND", "addbad (with modifiers)", "addbad");
2151             reply("OSMSG_BAD_MODIFIER", argv[arg]);
2152         }
2153     }
2154
2155     /* Scan for existing channels that match the new bad word. */
2156     if (!bad_found) {
2157         for (it = dict_first(channels); it; it = iter_next(it)) {
2158             struct chanNode *chan = iter_data(it);
2159
2160             if (!opserv_bad_channel(chan->name))
2161                 continue;
2162             chan->bad_channel = 1;
2163             if (chan->name[0] == '#')
2164                 opserv_shutdown_channel(chan, "OSMSG_ILLEGAL_REASON");
2165             else {
2166                 unsigned int nn;
2167                 for (nn = 0; nn < chan->members.used; nn++) {
2168                     struct userNode *victim = chan->members.list[nn]->user;
2169                     DelUser(victim, cmd->parent->bot, 1, "OSMSG_ILLEGAL_KILL_REASON");
2170                 }
2171             }
2172         }
2173     }
2174
2175     return 1;
2176 }
2177
2178 static MODCMD_FUNC(cmd_delbad)
2179 {
2180     dict_iterator_t it;
2181     unsigned int nn;
2182
2183     for (nn=0; nn<opserv_bad_words->used; nn++) {
2184         if (!irccasecmp(opserv_bad_words->list[nn], argv[1])) {
2185             string_list_delete(opserv_bad_words, nn);
2186             for (it = dict_first(channels); it; it = iter_next(it)) {
2187                 channel = iter_data(it);
2188                 if (irccasestr(channel->name, argv[1])
2189                     && !opserv_bad_channel(channel->name)) {
2190                     DelChannelUser(cmd->parent->bot, channel, "Channel name no longer contains a bad word.", 1);
2191                     timeq_del(0, opserv_part_channel, channel, TIMEQ_IGNORE_WHEN);
2192                     channel->bad_channel = 0;
2193                 }
2194             }
2195             reply("OSMSG_REMOVED_BAD", argv[1]);
2196             return 1;
2197         }
2198     }
2199     reply("OSMSG_NOT_BAD_WORD", argv[1]);
2200     return 0;
2201 }
2202
2203 static MODCMD_FUNC(cmd_addexempt)
2204 {
2205     const char *chanName;
2206
2207     if ((argc > 1) && IsChannelName(argv[1])) {
2208         chanName = argv[1];
2209     } else {
2210         reply("MSG_NOT_CHANNEL_NAME");
2211         OPSERV_SYNTAX();
2212         return 0;
2213     }
2214     dict_insert(opserv_exempt_channels, strdup(chanName), NULL);
2215     channel = GetChannel(chanName);
2216     if (channel) {
2217         if (channel->bad_channel) {
2218             DelChannelUser(cmd->parent->bot, channel, "Channel is now exempt from bad-word checking.", 1);
2219             timeq_del(0, opserv_part_channel, channel, TIMEQ_IGNORE_WHEN);
2220         }
2221         channel->bad_channel = 0;
2222     }
2223     reply("OSMSG_ADDED_EXEMPTION", chanName);
2224     return 1;
2225 }
2226
2227 static MODCMD_FUNC(cmd_delexempt)
2228 {
2229     const char *chanName;
2230
2231     if ((argc > 1) && IsChannelName(argv[1])) {
2232         chanName = argv[1];
2233     } else {
2234         reply("MSG_NOT_CHANNEL_NAME");
2235         OPSERV_SYNTAX();
2236         return 0;
2237     }
2238     if (!dict_remove(opserv_exempt_channels, chanName)) {
2239         reply("OSMSG_NOT_EXEMPT", chanName);
2240         return 0;
2241     }
2242     reply("OSMSG_REMOVED_EXEMPTION", chanName);
2243     return 1;
2244 }
2245
2246 static void
2247 opserv_expire_trusted_host(void *data)
2248 {
2249     struct trusted_host *th = data;
2250     dict_remove(opserv_trusted_hosts, th->ipaddr);
2251 }
2252
2253 static void
2254 opserv_add_trusted_host(const char *ipaddr, unsigned int limit, const char *issuer, unsigned long issued, unsigned long expires, const char *reason)
2255 {
2256     struct trusted_host *th;
2257     th = calloc(1, sizeof(*th));
2258     if (!th)
2259         return;
2260     th->ipaddr = strdup(ipaddr);
2261     th->reason = reason ? strdup(reason) : NULL;
2262     th->issuer = issuer ? strdup(issuer) : NULL;
2263     th->issued = issued;
2264     th->limit = limit;
2265     th->expires = expires;
2266     dict_insert(opserv_trusted_hosts, th->ipaddr, th);
2267     if (th->expires)
2268         timeq_add(th->expires, opserv_expire_trusted_host, th);
2269 }
2270
2271 static void
2272 free_trusted_host(void *data)
2273 {
2274     struct trusted_host *th = data;
2275     free(th->ipaddr);
2276     free(th->reason);
2277     free(th->issuer);
2278     free(th);
2279 }
2280
2281 static MODCMD_FUNC(cmd_addtrust)
2282 {
2283     unsigned long interval;
2284     char *reason, *tmp;
2285     irc_in_addr_t tmpaddr;
2286     unsigned int count;
2287
2288     if (dict_find(opserv_trusted_hosts, argv[1], NULL)) {
2289         reply("OSMSG_ALREADY_TRUSTED", argv[1]);
2290         return 0;
2291     }
2292
2293     if (!irc_pton(&tmpaddr, NULL, argv[1])) {
2294         reply("OSMSG_BAD_IP", argv[1]);
2295         return 0;
2296     }
2297
2298     count = strtoul(argv[2], &tmp, 10);
2299     if (*tmp != '\0') {
2300         reply("OSMSG_BAD_NUMBER", argv[2]);
2301         return 0;
2302     }
2303
2304     interval = ParseInterval(argv[3]);
2305     if (!interval && strcmp(argv[3], "0")) {
2306         reply("MSG_INVALID_DURATION", argv[3]);
2307         return 0;
2308     }
2309
2310     reason = unsplit_string(argv+4, argc-4, NULL);
2311     opserv_add_trusted_host(argv[1], count, user->handle_info->handle, now, interval ? (now + interval) : 0, reason);
2312     reply("OSMSG_ADDED_TRUSTED");
2313     return 1;
2314 }
2315
2316 static MODCMD_FUNC(cmd_edittrust)
2317 {
2318     unsigned long interval;
2319     struct trusted_host *th;
2320     char *reason, *tmp;
2321     unsigned int count;
2322
2323     th = dict_find(opserv_trusted_hosts, argv[1], NULL);
2324     if (!th) {
2325         reply("OSMSG_NOT_TRUSTED", argv[1]);
2326         return 0;
2327     }
2328     count = strtoul(argv[2], &tmp, 10);
2329     if (*tmp != '\0') {
2330         reply("OSMSG_BAD_NUMBER", argv[2]);
2331         return 0;
2332     }
2333     interval = ParseInterval(argv[3]);
2334     if (!interval && strcmp(argv[3], "0")) {
2335         reply("MSG_INVALID_DURATION", argv[3]);
2336         return 0;
2337     }
2338     reason = unsplit_string(argv+4, argc-4, NULL);
2339     if (th->expires)
2340         timeq_del(th->expires, opserv_expire_trusted_host, th, 0);
2341
2342     free(th->reason);
2343     th->reason = strdup(reason);
2344     free(th->issuer);
2345     th->issuer = strdup(user->handle_info->handle);
2346     th->issued = now;
2347     th->limit = count;
2348     if (interval) {
2349         th->expires = now + interval;
2350         timeq_add(th->expires, opserv_expire_trusted_host, th);
2351     } else
2352         th->expires = 0;
2353     reply("OSMSG_UPDATED_TRUSTED", th->ipaddr);
2354     return 1;
2355 }
2356
2357 static MODCMD_FUNC(cmd_deltrust)
2358 {
2359     unsigned int n;
2360
2361     for (n=1; n<argc; n++) {
2362         struct trusted_host *th = dict_find(opserv_trusted_hosts, argv[n], NULL);
2363         if (!th)
2364             continue;
2365         if (th->expires)
2366             timeq_del(th->expires, opserv_expire_trusted_host, th, 0);
2367         dict_remove(opserv_trusted_hosts, argv[n]);
2368     }
2369     reply("OSMSG_REMOVED_TRUSTED");
2370     return 1;
2371 }
2372
2373 /* This doesn't use dict_t because it's a little simpler to open-code the
2374  * comparisons (and simpler arg-passing for the ADD subcommand).
2375  */
2376 static MODCMD_FUNC(cmd_clone)
2377 {
2378     int i;
2379     struct userNode *clone;
2380
2381     clone = GetUserH(argv[2]);
2382     if (!irccasecmp(argv[1], "ADD")) {
2383         char *userinfo;
2384         char ident[USERLEN+1];
2385
2386         if (argc < 5) {
2387             reply("MSG_MISSING_PARAMS", argv[1]);
2388             OPSERV_SYNTAX();
2389             return 0;
2390         }
2391         if (clone) {
2392             reply("OSMSG_CLONE_EXISTS", argv[2]);
2393             return 0;
2394         }
2395         userinfo = unsplit_string(argv+4, argc-4, NULL);
2396         for (i=0; argv[3][i] && (i<USERLEN); i++) {
2397             if (argv[3][i] == '@') {
2398                 ident[i++] = 0;
2399                 break;
2400             } else {
2401                 ident[i] = argv[3][i];
2402             }
2403         }
2404         if (!argv[3][i] || (i==USERLEN)) {
2405             reply("OSMSG_NOT_A_HOSTMASK");
2406             return 0;
2407         }
2408         if (!(clone = AddLocalUser(argv[2], ident, argv[3]+i, userinfo, "+i"))) {
2409             reply("OSMSG_CLONE_FAILED", argv[2]);
2410             return 0;
2411         }
2412         reply("OSMSG_CLONE_ADDED", clone->nick);
2413         return 1;
2414     }
2415     if (!clone) {
2416         reply("MSG_NICK_UNKNOWN", argv[2]);
2417         return 0;
2418     }
2419     if (clone->uplink != self || IsService(clone)) {
2420         reply("OSMSG_NOT_A_CLONE", clone->nick);
2421         return 0;
2422     }
2423     if (!irccasecmp(argv[1], "REMOVE")) {
2424         const char *reason;
2425         if (argc > 3) {
2426             reason = unsplit_string(argv+3, argc-3, NULL);
2427         } else {
2428             char *tmp;
2429             tmp = alloca(strlen(clone->nick) + strlen(OSMSG_PART_REASON));
2430             sprintf(tmp, OSMSG_PART_REASON, clone->nick);
2431             reason = tmp;
2432         }
2433         DelUser(clone, NULL, 1, reason);
2434         reply("OSMSG_CLONE_REMOVED", argv[2]);
2435         return 1;
2436     }
2437     if (argc < 4) {
2438         reply("MSG_MISSING_PARAMS", argv[1]);
2439         OPSERV_SYNTAX();
2440         return 0;
2441     }
2442     channel = GetChannel(argv[3]);
2443     if (!irccasecmp(argv[1], "JOIN")) {
2444         if (!channel
2445             && !(channel = AddChannel(argv[3], now, NULL, NULL))) {
2446             reply("MSG_CHANNEL_UNKNOWN", argv[3]);
2447             return 0;
2448         }
2449         AddChannelUser(clone, channel);
2450         reply("OSMSG_CLONE_JOINED", clone->nick, channel->name);
2451         return 1;
2452     }
2453     if (!irccasecmp(argv[1], "PART")) {
2454         if (!channel) {
2455             reply("MSG_CHANNEL_UNKNOWN", argv[3]);
2456             return 0;
2457         }
2458         if (!GetUserMode(channel, clone)) {
2459             reply("OSMSG_NOT_ON_CHANNEL", clone->nick, channel->name);
2460             return 0;
2461         }
2462         reply("OSMSG_CLONE_PARTED", clone->nick, channel->name);
2463         DelChannelUser(clone, channel, "Leaving.", 0);
2464         return 1;
2465     }
2466     if (!irccasecmp(argv[1], "OP")) {
2467         struct mod_chanmode change;
2468         if (!channel) {
2469             reply("MSG_CHANNEL_UNKNOWN", argv[3]);
2470             return 0;
2471         }
2472         mod_chanmode_init(&change);
2473         change.argc = 1;
2474         change.args[0].mode = MODE_CHANOP;
2475         change.args[0].u.member = GetUserMode(channel, clone);
2476         if (!change.args[0].u.member) {
2477             reply("OSMSG_NOT_ON_CHANNEL", clone->nick, channel->name);
2478             return 0;
2479         }
2480         modcmd_chanmode_announce(&change);
2481         reply("OSMSG_OPS_GIVEN", channel->name, clone->nick);
2482         return 1;
2483     }
2484     if (argc < 5) {
2485         reply("MSG_MISSING_PARAMS", argv[1]);
2486         OPSERV_SYNTAX();
2487         return 0;
2488     }
2489     if (!irccasecmp(argv[1], "SAY")) {
2490         char *text = unsplit_string(argv+4, argc-4, NULL);
2491         irc_privmsg(clone, argv[3], text);
2492         reply("OSMSG_CLONE_SAID", clone->nick, argv[3]);
2493         return 1;
2494     }
2495     reply("OSMSG_UNKNOWN_SUBCOMMAND", argv[1], argv[0]);
2496     return 0;
2497 }
2498
2499 static struct helpfile_expansion
2500 opserv_help_expand(const char *variable)
2501 {
2502     extern struct userNode *message_source;
2503     struct helpfile_expansion exp;
2504     struct service *service;
2505     struct svccmd *cmd;
2506     dict_iterator_t it;
2507     int row;
2508     unsigned int level;
2509
2510     if (!(service = service_find(message_source->nick))) {
2511         exp.type = HF_STRING;
2512         exp.value.str = NULL;
2513     } else if (!irccasecmp(variable, "index")) {
2514         exp.type = HF_TABLE;
2515         exp.value.table.length = 1;
2516         exp.value.table.width = 2;
2517         exp.value.table.flags = TABLE_REPEAT_HEADERS | TABLE_REPEAT_ROWS;
2518         exp.value.table.contents = calloc(dict_size(service->commands)+1, sizeof(char**));
2519         exp.value.table.contents[0] = calloc(exp.value.table.width, sizeof(char*));
2520         exp.value.table.contents[0][0] = "Command";
2521         exp.value.table.contents[0][1] = "Level";
2522         for (it=dict_first(service->commands); it; it=iter_next(it)) {
2523             cmd = iter_data(it);
2524             row = exp.value.table.length++;
2525             exp.value.table.contents[row] = calloc(exp.value.table.width, sizeof(char*));
2526             exp.value.table.contents[row][0] = iter_key(it);
2527             level = cmd->min_opserv_level;
2528             if (!level_strings[level]) {
2529                 level_strings[level] = malloc(16);
2530                 snprintf(level_strings[level], 16, "%3d", level);
2531             }
2532             exp.value.table.contents[row][1] = level_strings[level];
2533         }
2534     } else if (!strncasecmp(variable, "level", 5)) {
2535         cmd = dict_find(service->commands, variable+6, NULL);
2536         exp.type = HF_STRING;
2537         if (cmd) {
2538             level = cmd->min_opserv_level;
2539             exp.value.str = malloc(16);
2540             snprintf(exp.value.str, 16, "%3d", level);
2541         } else {
2542             exp.value.str = NULL;
2543         }
2544     } else {
2545         exp.type = HF_STRING;
2546         exp.value.str = NULL;
2547     }
2548     return exp;
2549 }
2550
2551 struct modcmd *
2552 opserv_define_func(const char *name, modcmd_func_t *func, int min_level, int reqchan, int min_argc)
2553 {
2554     char buf[16], *flags = NULL;
2555     unsigned int iflags = 0;
2556     sprintf(buf, "%d", min_level);
2557     switch (reqchan) {
2558     case 1: flags = "+acceptchan"; break;
2559     case 3: flags = "+acceptpluschan"; /* fall through */
2560     case 2: iflags = MODCMD_REQUIRE_CHANNEL; break;
2561     }
2562     if (flags) {
2563         return modcmd_register(opserv_module, name, func, min_argc, iflags, "level", buf, "flags", flags, "flags", "+oper", NULL);
2564     } else {
2565         return modcmd_register(opserv_module, name, func, min_argc, iflags, "level", buf, "flags", "+oper", NULL);
2566     }
2567 }
2568
2569 int add_reserved(const char *key, void *data, void *extra)
2570 {
2571     struct record_data *rd = data;
2572     const char *ident, *hostname, *desc;
2573     struct userNode *reserve;
2574     ident = database_get_data(rd->d.object, KEY_IDENT, RECDB_QSTRING);
2575     if (!ident) {
2576         log_module(OS_LOG, LOG_ERROR, "Missing ident for reserve of %s", key);
2577         return 0;
2578     }
2579     hostname = database_get_data(rd->d.object, KEY_HOSTNAME, RECDB_QSTRING);
2580     if (!hostname) {
2581         log_module(OS_LOG, LOG_ERROR, "Missing hostname for reserve of %s", key);
2582         return 0;
2583     }
2584     desc = database_get_data(rd->d.object, KEY_DESC, RECDB_QSTRING);
2585     if (!desc) {
2586         log_module(OS_LOG, LOG_ERROR, "Missing description for reserve of %s", key);
2587         return 0;
2588     }
2589     if ((reserve = AddLocalUser(key, ident, hostname, desc, "+i"))) {
2590         reserve->modes |= FLAGS_PERSISTENT;
2591         dict_insert(extra, reserve->nick, reserve);
2592     }
2593     return 0;
2594 }
2595
2596 static unsigned int
2597 foreach_matching_user(const char *hostmask, discrim_search_func func, void *extra)
2598 {
2599     discrim_t discrim;
2600     char *dupmask;
2601     unsigned int matched;
2602
2603     if (!self->uplink) return 0;
2604     discrim = calloc(1, sizeof(*discrim));
2605     discrim->limit = dict_size(clients);
2606     discrim->max_level = UINT_MAX;
2607     discrim->max_ts = ULONG_MAX;
2608     discrim->max_channels = INT_MAX;
2609     discrim->authed = -1;
2610     discrim->info_space = -1;
2611     dupmask = strdup(hostmask);
2612     if (split_ircmask(dupmask, &discrim->mask_nick, &discrim->mask_ident, &discrim->mask_host)) {
2613         if (!irc_pton(&discrim->ip_mask, &discrim->ip_mask_bits, discrim->mask_host))
2614             discrim->ip_mask_bits = 0;
2615         matched = opserv_discrim_search(discrim, func, extra);
2616     } else {
2617         log_module(OS_LOG, LOG_ERROR, "Couldn't split IRC mask for gag %s!", hostmask);
2618         matched = 0;
2619     }
2620     free(discrim);
2621     free(dupmask);
2622     return matched;
2623 }
2624
2625 static unsigned int
2626 gag_free(struct gag_entry *gag)
2627 {
2628     unsigned int ungagged;
2629
2630     /* Remove from gag list */
2631     if (gagList == gag) {
2632         gagList = gag->next;
2633     } else {
2634         struct gag_entry *prev;
2635         for (prev = gagList; prev->next != gag; prev = prev->next) ;
2636         prev->next = gag->next;
2637     }
2638
2639     ungagged = foreach_matching_user(gag->mask, ungag_helper_func, NULL);
2640
2641     /* Deallocate storage */
2642     free(gag->reason);
2643     free(gag->owner);
2644     free(gag->mask);
2645     free(gag);
2646
2647     return ungagged;
2648 }
2649
2650 static void
2651 gag_expire(void *data)
2652 {
2653     gag_free(data);
2654 }
2655
2656 unsigned int
2657 gag_create(const char *mask, const char *owner, const char *reason, unsigned long expires)
2658 {
2659     struct gag_entry *gag;
2660
2661     /* Create gag and put it into linked list */
2662     gag = calloc(1, sizeof(*gag));
2663     gag->mask = strdup(mask);
2664     gag->owner = strdup(owner ? owner : "<unknown>");
2665     gag->reason = strdup(reason ? reason : "<unknown>");
2666     gag->expires = expires;
2667     if (gag->expires)
2668         timeq_add(gag->expires, gag_expire, gag);
2669     gag->next = gagList;
2670     gagList = gag;
2671
2672     /* If we're linked, see if who the gag applies to */
2673     return foreach_matching_user(mask, gag_helper_func, gag);
2674 }
2675
2676 static int
2677 add_gag_helper(const char *key, void *data, UNUSED_ARG(void *extra))
2678 {
2679     struct record_data *rd = data;
2680     char *owner, *reason, *expstr;
2681     unsigned long expires;
2682
2683     owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
2684     reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
2685     expstr = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
2686     expires = expstr ? strtoul(expstr, NULL, 0) : 0;
2687     gag_create(key, owner, reason, expires);
2688
2689     return 0;
2690 }
2691
2692 static struct opserv_user_alert *
2693 opserv_add_user_alert(struct userNode *req, const char *name, opserv_alert_reaction reaction, const char *text_discrim)
2694 {
2695     unsigned int wordc;
2696     char *wordv[MAXNUMPARAMS], *discrim_copy;
2697     struct opserv_user_alert *alert;
2698     char *name_dup;
2699
2700     if (dict_find(opserv_user_alerts, name, NULL)) {
2701         send_message(req, opserv, "OSMSG_ALERT_EXISTS", name);
2702         return NULL;
2703     }
2704     alert = malloc(sizeof(*alert));
2705     alert->owner = strdup(req->handle_info ? req->handle_info->handle : req->nick);
2706     alert->text_discrim = strdup(text_discrim);
2707     discrim_copy = strdup(text_discrim); /* save a copy of the discrim */
2708     wordc = split_line(discrim_copy, false, ArrayLength(wordv), wordv);
2709     alert->discrim = opserv_discrim_create(req, wordc, wordv, 0);
2710     if (!alert->discrim) {
2711         free(alert->text_discrim);
2712         free(discrim_copy);
2713         free(alert);
2714         return NULL;
2715     }
2716     alert->split_discrim = discrim_copy;
2717     name_dup = strdup(name);
2718     if (!alert->discrim->reason)
2719         alert->discrim->reason = strdup(name);
2720     alert->reaction = reaction;
2721     dict_insert(opserv_user_alerts, name_dup, alert);
2722     /* Stick the alert into the appropriate additional alert dict(s).
2723      * For channel alerts, we only use channels and min_channels;
2724      * max_channels would have to be checked on /part, which we do not
2725      * yet do, and which seems of questionable value.
2726      */
2727     if (alert->discrim->channel_count || alert->discrim->min_channels)
2728         dict_insert(opserv_channel_alerts, name_dup, alert);
2729     if (alert->discrim->mask_nick)
2730         dict_insert(opserv_nick_based_alerts, name_dup, alert);
2731     if (alert->discrim->accountmask || alert->discrim->authed != -1)
2732         dict_insert(opserv_account_alerts, name_dup, alert);
2733     return alert;
2734 }
2735
2736 static int
2737 add_chan_warn(const char *key, void *data, UNUSED_ARG(void *extra))
2738 {
2739     struct record_data *rd = data;
2740     char *reason = GET_RECORD_QSTRING(rd);
2741
2742     /* i hope this can't happen */
2743     if (!reason)
2744         reason = "No Reason";
2745
2746     dict_insert(opserv_chan_warn, strdup(key), strdup(reason));
2747     return 0;
2748 }
2749
2750 static int
2751 add_user_alert(const char *key, void *data, UNUSED_ARG(void *extra))
2752 {
2753     dict_t alert_dict;
2754     const char *discrim, *react, *owner;
2755     opserv_alert_reaction reaction;
2756     struct opserv_user_alert *alert;
2757
2758     if (!(alert_dict = GET_RECORD_OBJECT((struct record_data *)data))) {
2759         log_module(OS_LOG, LOG_ERROR, "Bad type (not a record) for alert %s.", key);
2760         return 1;
2761     }
2762     discrim = database_get_data(alert_dict, KEY_DISCRIM, RECDB_QSTRING);
2763     react = database_get_data(alert_dict, KEY_REACTION, RECDB_QSTRING);
2764     if (!react || !irccasecmp(react, "notice"))
2765         reaction = REACT_NOTICE;
2766     else if (!irccasecmp(react, "kill"))
2767         reaction = REACT_KILL;
2768     else if (!irccasecmp(react, "gline"))
2769         reaction = REACT_GLINE;
2770     else {
2771         log_module(OS_LOG, LOG_ERROR, "Invalid reaction %s for alert %s.", react, key);
2772         return 0;
2773     }
2774     alert = opserv_add_user_alert(opserv, key, reaction, discrim);
2775     if (!alert) {
2776         log_module(OS_LOG, LOG_ERROR, "Unable to create alert %s from database.", key);
2777         return 0;
2778     }
2779     owner = database_get_data(alert_dict, KEY_OWNER, RECDB_QSTRING);
2780     free(alert->owner);
2781     alert->owner = strdup(owner ? owner : "<unknown>");
2782     return 0;
2783 }
2784
2785 static int
2786 trusted_host_read(const char *host, void *data, UNUSED_ARG(void *extra))
2787 {
2788     struct record_data *rd = data;
2789     const char *limit, *str, *reason, *issuer;
2790     unsigned long issued, expires;
2791
2792     if (rd->type == RECDB_QSTRING) {
2793         /* old style host by itself */
2794         limit = GET_RECORD_QSTRING(rd);
2795         issued = 0;
2796         issuer = NULL;
2797         expires = 0;
2798         reason = NULL;
2799     } else if (rd->type == RECDB_OBJECT) {
2800         dict_t obj = GET_RECORD_OBJECT(rd);
2801         /* new style structure */
2802         limit = database_get_data(obj, KEY_LIMIT, RECDB_QSTRING);
2803         str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
2804         expires = str ? ParseInterval(str) : 0;
2805         reason = database_get_data(obj, KEY_REASON, RECDB_QSTRING);
2806         issuer = database_get_data(obj, KEY_ISSUER, RECDB_QSTRING);
2807         str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
2808         issued = str ? ParseInterval(str) : 0;
2809     } else
2810         return 0;
2811
2812     if (expires && (expires < now))
2813         return 0;
2814     opserv_add_trusted_host(host, (limit ? strtoul(limit, NULL, 0) : 0), issuer, issued, expires, reason);
2815     return 0;
2816 }
2817
2818 static void
2819 opserv_add_devnull_class(const char *name, unsigned long modes, unsigned long maxchan, unsigned long maxsendq)
2820 {
2821     struct devnull_class *th;
2822     th = calloc(1, sizeof(*th));
2823     if (!th)
2824         return;
2825     th->name = strdup(name);
2826     th->modes = modes;
2827     th->maxchan = maxchan;
2828     th->maxsendq = maxsendq;
2829     dict_insert(opserv_devnull_classes, th->name, th);
2830 }
2831
2832 static void
2833 free_devnull_class(void *data)
2834 {
2835     struct devnull_class *th = data;
2836     free(th->name);
2837     free(th);
2838 }
2839
2840 static int
2841 devnull_class_read(const char *name, void *data, UNUSED_ARG(void *extra))
2842 {
2843     struct record_data *rd = data;
2844     const char *nameb = name, *str;
2845     unsigned long modes, maxchan, maxsendq;
2846     unsigned int ii;
2847
2848     if (rd->type == RECDB_OBJECT) {
2849         dict_t obj = GET_RECORD_OBJECT(rd);
2850         /* new style structure */
2851         nameb = database_get_data(obj, KEY_DEVNULL_NAME, RECDB_QSTRING);
2852         str = database_get_data(obj, KEY_DEVNULL_MODE, RECDB_QSTRING);
2853         modes = 0;
2854         if (str) {
2855             for (ii=0; str[ii]; ii++)
2856                 modes |= 1 << (devnull_inverse_modes[(unsigned char)str[ii]] - 1);
2857         }
2858         str = database_get_data(obj, KEY_DEVNULL_MAXCHAN, RECDB_QSTRING);
2859         maxchan = str ? ParseInterval(str) : 0;
2860         str = database_get_data(obj, KEY_DEVNULL_MAXSENDQ, RECDB_QSTRING);
2861         maxsendq = str ? ParseInterval(str) : 0;
2862     } else
2863         return 0;
2864
2865     opserv_add_devnull_class(nameb, modes, maxchan, maxsendq);
2866     return 0;
2867 }
2868
2869 static int
2870 opserv_saxdb_read(struct dict *conf_db)
2871 {
2872     dict_t object;
2873     struct record_data *rd;
2874     dict_iterator_t it;
2875     unsigned int nn;
2876
2877     if ((object = database_get_data(conf_db, KEY_RESERVES, RECDB_OBJECT)))
2878         dict_foreach(object, add_reserved, opserv_reserved_nick_dict);
2879     if ((rd = database_get_path(conf_db, KEY_BAD_WORDS))) {
2880         switch (rd->type) {
2881         case RECDB_STRING_LIST:
2882             /* Add words one by one just in case there are overlaps from an old DB. */
2883             for (nn=0; nn<rd->d.slist->used; ++nn)
2884                 opserv_add_bad_word(NULL, NULL, rd->d.slist->list[nn]);
2885             break;
2886         case RECDB_OBJECT:
2887             for (it=dict_first(rd->d.object); it; it=iter_next(it)) {
2888                 opserv_add_bad_word(NULL, NULL, iter_key(it));
2889                 rd = iter_data(it);
2890                 if (rd->type == RECDB_STRING_LIST)
2891                     for (nn=0; nn<rd->d.slist->used; nn++)
2892                         dict_insert(opserv_exempt_channels, strdup(rd->d.slist->list[nn]), NULL);
2893             }
2894             break;
2895         default:
2896             /* do nothing */;
2897         }
2898     }
2899     if ((rd = database_get_path(conf_db, KEY_EXEMPT_CHANNELS))
2900         && (rd->type == RECDB_STRING_LIST)) {
2901         for (nn=0; nn<rd->d.slist->used; ++nn)
2902             dict_insert(opserv_exempt_channels, strdup(rd->d.slist->list[nn]), NULL);
2903     }
2904     if ((object = database_get_data(conf_db, KEY_MAX_CLIENTS, RECDB_OBJECT))) {
2905         char *str;
2906         if ((str = database_get_data(object, KEY_MAX, RECDB_QSTRING)))
2907             max_clients = atoi(str);
2908         if ((str = database_get_data(object, KEY_TIME, RECDB_QSTRING)))
2909             max_clients_time = atoi(str);
2910     }
2911     if ((object = database_get_data(conf_db, KEY_TRUSTED_HOSTS, RECDB_OBJECT)))
2912         dict_foreach(object, trusted_host_read, opserv_trusted_hosts);
2913     if ((object = database_get_data(conf_db, KEY_DEVNULL_CLASSES, RECDB_OBJECT)))
2914         dict_foreach(object, devnull_class_read, opserv_devnull_classes);
2915     if ((object = database_get_data(conf_db, KEY_GAGS, RECDB_OBJECT)))
2916         dict_foreach(object, add_gag_helper, NULL);
2917     if ((object = database_get_data(conf_db, KEY_ALERTS, RECDB_OBJECT)))
2918         dict_foreach(object, add_user_alert, NULL);
2919     if ((object = database_get_data(conf_db, KEY_WARN, RECDB_OBJECT)))
2920         dict_foreach(object, add_chan_warn, NULL);
2921     return 0;
2922 }
2923
2924 static int
2925 opserv_saxdb_write(struct saxdb_context *ctx)
2926 {
2927     struct string_list *slist;
2928     dict_iterator_t it;
2929
2930     /* reserved nicks */
2931     if (dict_size(opserv_reserved_nick_dict)) {
2932         saxdb_start_record(ctx, KEY_RESERVES, 1);
2933         for (it = dict_first(opserv_reserved_nick_dict); it; it = iter_next(it)) {
2934             struct userNode *user = iter_data(it);
2935             if (!IsPersistent(user)) continue;
2936             saxdb_start_record(ctx, iter_key(it), 0);
2937             saxdb_write_string(ctx, KEY_IDENT, user->ident);
2938             saxdb_write_string(ctx, KEY_HOSTNAME, user->hostname);
2939             saxdb_write_string(ctx, KEY_DESC, user->info);
2940             saxdb_end_record(ctx);
2941         }
2942         saxdb_end_record(ctx);
2943     }
2944     /* bad word set */
2945     if (opserv_bad_words->used) {
2946         saxdb_write_string_list(ctx, KEY_BAD_WORDS, opserv_bad_words);
2947     }
2948     /* insert exempt channel names */
2949     if (dict_size(opserv_exempt_channels)) {
2950         slist = alloc_string_list(dict_size(opserv_exempt_channels));
2951         for (it=dict_first(opserv_exempt_channels); it; it=iter_next(it)) {
2952             string_list_append(slist, strdup(iter_key(it)));
2953         }
2954         saxdb_write_string_list(ctx, KEY_EXEMPT_CHANNELS, slist);
2955         free_string_list(slist);
2956     }
2957     /* trusted hosts takes a little more work */
2958     if (dict_size(opserv_trusted_hosts)) {
2959         saxdb_start_record(ctx, KEY_TRUSTED_HOSTS, 1);
2960         for (it = dict_first(opserv_trusted_hosts); it; it = iter_next(it)) {
2961             struct trusted_host *th = iter_data(it);
2962             saxdb_start_record(ctx, iter_key(it), 0);
2963             if (th->limit) saxdb_write_int(ctx, KEY_LIMIT, th->limit);
2964             if (th->expires) saxdb_write_int(ctx, KEY_EXPIRES, th->expires);
2965             if (th->issued) saxdb_write_int(ctx, KEY_ISSUED, th->issued);
2966             if (th->issuer) saxdb_write_string(ctx, KEY_ISSUER, th->issuer);
2967             if (th->reason) saxdb_write_string(ctx, KEY_REASON, th->reason);
2968             saxdb_end_record(ctx);
2969         }
2970         saxdb_end_record(ctx);
2971     }
2972     /* devnull_classes */
2973     if (dict_size(opserv_devnull_classes)) {
2974         saxdb_start_record(ctx, KEY_DEVNULL_CLASSES, 1);
2975         for (it = dict_first(opserv_devnull_classes); it; it = iter_next(it)) {
2976             struct devnull_class *th = iter_data(it);
2977             saxdb_start_record(ctx, iter_key(it), 0);
2978             if (th->name) saxdb_write_string(ctx, KEY_DEVNULL_NAME, th->name);
2979             if (th->modes) {
2980                 int ii, flen;
2981                 char flags[50];
2982                 for (ii=flen=0; devnull_modes[ii]; ++ii)
2983                     if (th->modes & (1 << ii))
2984                         flags[flen++] = devnull_modes[ii];
2985                 flags[flen] = 0;
2986                 saxdb_write_string(ctx, KEY_DEVNULL_MODE, flags);
2987             }
2988             if (th->maxchan) saxdb_write_int(ctx, KEY_DEVNULL_MAXCHAN, th->maxchan);
2989             if (th->maxsendq) saxdb_write_int(ctx, KEY_DEVNULL_MAXSENDQ, th->maxsendq);
2990             saxdb_end_record(ctx);
2991         }
2992         saxdb_end_record(ctx);
2993     }
2994     /* gags */
2995     if (gagList) {
2996         struct gag_entry *gag;
2997         saxdb_start_record(ctx, KEY_GAGS, 1);
2998         for (gag = gagList; gag; gag = gag->next) {
2999             saxdb_start_record(ctx, gag->mask, 0);
3000             saxdb_write_string(ctx, KEY_OWNER, gag->owner);
3001             saxdb_write_string(ctx, KEY_REASON, gag->reason);
3002             if (gag->expires) saxdb_write_int(ctx, KEY_EXPIRES, gag->expires);
3003             saxdb_end_record(ctx);
3004         }
3005         saxdb_end_record(ctx);
3006     }
3007     /* channel warnings */
3008     if (dict_size(opserv_chan_warn)) {
3009         saxdb_start_record(ctx, KEY_WARN, 0);
3010         for (it = dict_first(opserv_chan_warn); it; it = iter_next(it)) {
3011             saxdb_write_string(ctx, iter_key(it), iter_data(it));
3012         }
3013         saxdb_end_record(ctx);
3014     }
3015     /* alerts */
3016     if (dict_size(opserv_user_alerts)) {
3017         saxdb_start_record(ctx, KEY_ALERTS, 1);
3018         for (it = dict_first(opserv_user_alerts); it; it = iter_next(it)) {
3019             struct opserv_user_alert *alert = iter_data(it);
3020             const char *reaction;
3021             saxdb_start_record(ctx, iter_key(it), 0);
3022             saxdb_write_string(ctx, KEY_DISCRIM, alert->text_discrim);
3023             saxdb_write_string(ctx, KEY_OWNER, alert->owner);
3024             switch (alert->reaction) {
3025             case REACT_NOTICE: reaction = "notice"; break;
3026             case REACT_KILL: reaction = "kill"; break;
3027             case REACT_GLINE: reaction = "gline"; break;
3028             default:
3029                 reaction = NULL;
3030                 log_module(OS_LOG, LOG_ERROR, "Invalid reaction type %d for alert %s (while writing database).", alert->reaction, iter_key(it));
3031                 break;
3032             }
3033             if (reaction) saxdb_write_string(ctx, KEY_REACTION, reaction);
3034             saxdb_end_record(ctx);
3035         }
3036         saxdb_end_record(ctx);
3037     }
3038     /* max clients */
3039     saxdb_start_record(ctx, KEY_MAX_CLIENTS, 0);
3040     saxdb_write_int(ctx, KEY_MAX, max_clients);
3041     saxdb_write_int(ctx, KEY_TIME, max_clients_time);
3042     saxdb_end_record(ctx);
3043     return 0;
3044 }
3045
3046 static int
3047 query_keys_helper(const char *key, UNUSED_ARG(void *data), void *extra)
3048 {
3049     send_message_type(4, extra, opserv, "$b%s$b", key);
3050     return 0;
3051 }
3052
3053 static MODCMD_FUNC(cmd_query)
3054 {
3055     struct record_data *rd;
3056     unsigned int i;
3057     char *nodename;
3058
3059     if (argc < 2) {
3060         reply("OSMSG_OPTION_ROOT");
3061         conf_enum_root(query_keys_helper, user);
3062         return 1;
3063     }
3064
3065     nodename = unsplit_string(argv+1, argc-1, NULL);
3066     if (!(rd = conf_get_node(nodename))) {
3067         reply("OSMSG_UNKNOWN_OPTION", nodename);
3068         return 0;
3069     }
3070
3071     if (rd->type == RECDB_QSTRING)
3072         reply("OSMSG_OPTION_IS", nodename, rd->d.qstring);
3073     else if (rd->type == RECDB_STRING_LIST) {
3074         reply("OSMSG_OPTION_LIST", nodename);
3075         if (rd->d.slist->used)
3076             for (i=0; i<rd->d.slist->used; i++)
3077                 send_message_type(4, user, cmd->parent->bot, "$b%s$b", rd->d.slist->list[i]);
3078         else
3079             reply("OSMSG_OPTION_LIST_EMPTY");
3080     } else if (rd->type == RECDB_OBJECT) {
3081         reply("OSMSG_OPTION_KEYS", nodename);
3082         dict_foreach(rd->d.object, query_keys_helper, user);
3083     }
3084
3085     return 1;
3086 }
3087
3088 static MODCMD_FUNC(cmd_set)
3089 {
3090     struct record_data *rd;
3091
3092     /* I originally wanted to be able to fully manipulate the config
3093        db with this, but i wussed out. feel free to fix this - you'll
3094        need to handle quoted strings which have been split, and likely
3095        invent a syntax for it. -Zoot */
3096
3097     if (!(rd = conf_get_node(argv[1]))) {
3098         reply("OSMSG_SET_NOT_SET", argv[1]);
3099         return 0;
3100     }
3101
3102     if (rd->type != RECDB_QSTRING) {
3103         reply("OSMSG_SET_BAD_TYPE", argv[1]);
3104         return 0;
3105     }
3106
3107     free(rd->d.qstring);
3108     rd->d.qstring = strdup(argv[2]);
3109     conf_call_reload_funcs();
3110     reply("OSMSG_SET_SUCCESS", argv[1], argv[2]);
3111     return 1;
3112 }
3113
3114 static MODCMD_FUNC(cmd_settime)
3115 {
3116     const char *srv_name_mask = "*";
3117     unsigned long new_time = now;
3118
3119     if (argc > 1)
3120         srv_name_mask = argv[1];
3121     if (argc > 2)
3122         new_time = time(NULL);
3123     irc_settime(srv_name_mask, new_time);
3124     reply("OSMSG_SETTIME_SUCCESS", srv_name_mask);
3125     return 1;
3126 }
3127
3128 static discrim_t
3129 opserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[], int allow_channel)
3130 {
3131     unsigned int i, j;
3132     discrim_t discrim;
3133
3134     discrim = calloc(1, sizeof(*discrim));
3135     discrim->limit = 250;
3136     discrim->max_level = UINT_MAX;
3137     discrim->max_ts = ULONG_MAX;
3138     discrim->domain_depth = 2;
3139     discrim->max_channels = INT_MAX;
3140     discrim->authed = -1;
3141     discrim->info_space = -1;
3142
3143     for (i=0; i<argc; i++) {
3144         if (irccasecmp(argv[i], "log") == 0) {
3145             discrim->option_log = 1;
3146             continue;
3147         }
3148         /* Assume all other criteria require arguments. */
3149         if (i == argc - 1) {
3150             send_message(user, opserv, "MSG_MISSING_PARAMS", argv[i]);
3151             goto fail;
3152         }
3153         if (irccasecmp(argv[i], "mask") == 0) {
3154             if (!is_ircmask(argv[++i])) {
3155                 send_message(user, opserv, "OSMSG_INVALID_IRCMASK", argv[i]);
3156                 goto fail;
3157             }
3158             if (!split_ircmask(argv[i],
3159                                &discrim->mask_nick,
3160                                &discrim->mask_ident,
3161                                &discrim->mask_host)) {
3162                 send_message(user, opserv, "OSMSG_INVALID_IRCMASK", argv[i]);
3163                 goto fail;
3164             }
3165         } else if (irccasecmp(argv[i], "nick") == 0) {
3166             discrim->mask_nick = argv[++i];
3167         } else if (irccasecmp(argv[i], "ident") == 0) {
3168             discrim->mask_ident = argv[++i];
3169         } else if (irccasecmp(argv[i], "host") == 0) {
3170             discrim->mask_host = argv[++i];
3171         } else if (irccasecmp(argv[i], "info") == 0) {
3172             discrim->mask_info = argv[++i];
3173         } else if (irccasecmp(argv[i], "server") == 0) {
3174             discrim->server = argv[++i];
3175         } else if (irccasecmp(argv[i], "ip") == 0) {
3176             j = irc_pton(&discrim->ip_mask, &discrim->ip_mask_bits, argv[++i]);
3177             if (!j) {
3178                 send_message(user, opserv, "OSMSG_BAD_IP", argv[i]);
3179                 goto fail;
3180             }
3181         } else if (irccasecmp(argv[i], "account") == 0) {
3182             if (discrim->authed == 0) {
3183                 send_message(user, opserv, "OSMSG_ACCOUNTMASK_AUTHED");
3184                 goto fail;
3185             }
3186             discrim->accountmask = argv[++i];
3187             discrim->authed = 1;
3188         } else if (irccasecmp(argv[i], "authed") == 0) {
3189             i++; /* true_string and false_string are macros! */
3190             if (true_string(argv[i])) {
3191                 discrim->authed = 1;
3192             } else if (false_string(argv[i])) {
3193                 if (discrim->accountmask) {
3194                     send_message(user, opserv, "OSMSG_ACCOUNTMASK_AUTHED");
3195                     goto fail;
3196                 }
3197                 discrim->authed = 0;
3198             } else {
3199                 send_message(user, opserv, "MSG_INVALID_BINARY", argv[i]);
3200                 goto fail;
3201             }
3202         } else if (irccasecmp(argv[i], "info_space") == 0) {
3203             /* XXX: A hack because you can't check explicitly for a space through
3204              * any other means */
3205             i++;
3206             if (true_string(argv[i])) {
3207                 discrim->info_space = 1;
3208             } else if (false_string(argv[i])) {
3209                 discrim->info_space = 0;
3210             } else {
3211                 send_message(user, opserv, "MSG_INVALID_BINARY", argv[i]);
3212                 goto fail;
3213             }
3214         } else if (irccasecmp(argv[i], "duration") == 0) {
3215             discrim->duration = ParseInterval(argv[++i]);
3216         } else if (irccasecmp(argv[i], "channel") == 0) {
3217             if(discrim->channel_count == DISCRIM_MAX_CHANS)
3218             {
3219                 send_message(user, opserv, "OSMSG_TRACE_MAX_CHANNELS", DISCRIM_MAX_CHANS);
3220                 goto fail;
3221             }
3222
3223             for (j=0, i++; ; j++) {
3224                 switch (argv[i][j]) {
3225                 case '#':
3226                     goto find_channel;
3227                 case '-':
3228                     discrim->chan_no_modes[discrim->channel_count]  |= MODE_CHANOP | MODE_VOICE;
3229                     break;
3230                 case '+':
3231                     discrim->chan_req_modes[discrim->channel_count] |= MODE_VOICE;
3232                     discrim->chan_no_modes[discrim->channel_count]  |= MODE_CHANOP;
3233                     break;
3234                 case '@':
3235                     discrim->chan_req_modes[discrim->channel_count] |= MODE_CHANOP;
3236                     break;
3237                 case '\0':
3238                     send_message(user, opserv, "MSG_NOT_CHANNEL_NAME");
3239                     goto fail;
3240                 }
3241             }
3242             find_channel:
3243             discrim->chan_no_modes[discrim->channel_count] &= ~discrim->chan_req_modes[discrim->channel_count];
3244             if (!(discrim->channels[discrim->channel_count] = GetChannel(argv[i]+j))) {
3245                 /* secretly "allow_channel" now means "if a channel name is
3246                  * specified, require that it currently exist" */
3247                 if (allow_channel) {
3248                     send_message(user, opserv, "MSG_CHANNEL_UNKNOWN", argv[i]);
3249                     goto fail;
3250                 } else {
3251                     discrim->channels[discrim->channel_count] = AddChannel(argv[i]+j, now, NULL, NULL);
3252                 }
3253             }
3254             LockChannel(discrim->channels[discrim->channel_count]);
3255             discrim->channel_count++;
3256         } else if (irccasecmp(argv[i], "numchannels") == 0) {
3257             discrim->min_channels = discrim->max_channels = strtoul(argv[++i], NULL, 10);
3258         } else if (irccasecmp(argv[i], "limit") == 0) {
3259             discrim->limit = strtoul(argv[++i], NULL, 10);
3260         } else if (irccasecmp(argv[i], "reason") == 0) {
3261             discrim->reason = strdup(unsplit_string(argv+i+1, argc-i-1, NULL));
3262             i = argc;
3263         } else if (irccasecmp(argv[i], "notice_target") == 0 || irccasecmp(argv[i], "target") == 0) {
3264             if (!IsChannelName(argv[i + 1])) {
3265                 send_message(user, opserv, "MSG_NOT_CHANNEL_NAME");
3266                 goto fail;
3267             }
3268             discrim->notice_target = argv[++i];
3269         } else if (irccasecmp(argv[i], "last") == 0) {
3270             discrim->min_ts = now - ParseInterval(argv[++i]);
3271         } else if ((irccasecmp(argv[i], "linked") == 0)
3272                    || (irccasecmp(argv[i], "nickage") == 0)) {
3273             const char *cmp = argv[++i];
3274             if (cmp[0] == '<') {
3275                 if (cmp[1] == '=') {
3276                     discrim->min_ts = now - ParseInterval(cmp+2);
3277                 } else {
3278                     discrim->min_ts = now - (ParseInterval(cmp+1) - 1);
3279                 }
3280             } else if (cmp[0] == '>') {
3281                 if (cmp[1] == '=') {
3282                     discrim->max_ts = now - ParseInterval(cmp+2);
3283                 } else {
3284                     discrim->max_ts = now - (ParseInterval(cmp+1) - 1);
3285                 }
3286             } else {
3287                 discrim->min_ts = now - ParseInterval(cmp);
3288             }
3289         } else if (irccasecmp(argv[i], "access") == 0) {
3290             const char *cmp = argv[++i];
3291             if (cmp[0] == '<') {
3292                 if (discrim->min_level == 0) discrim->min_level = 1;
3293                 if (cmp[1] == '=') {
3294                     discrim->max_level = strtoul(cmp+2, NULL, 0);
3295                 } else {
3296                     discrim->max_level = strtoul(cmp+1, NULL, 0) - 1;
3297                 }
3298             } else if (cmp[0] == '=') {
3299                 discrim->min_level = discrim->max_level = strtoul(cmp+1, NULL, 0);
3300             } else if (cmp[0] == '>') {
3301                 if (cmp[1] == '=') {
3302                     discrim->min_level = strtoul(cmp+2, NULL, 0);
3303                 } else {
3304                     discrim->min_level = strtoul(cmp+1, NULL, 0) + 1;
3305                 }
3306             } else {
3307                 discrim->min_level = strtoul(cmp, NULL, 0);
3308             }
3309         } else if (irccasecmp(argv[i], "abuse") == 0) {
3310             const char *abuse_what = argv[++i];
3311             if (irccasecmp(abuse_what, "opers") == 0) {
3312                 discrim->match_opers = 1;
3313             } else if (irccasecmp(abuse_what, "trusted") == 0) {
3314                 discrim->match_trusted = 1;
3315             }
3316         } else if (irccasecmp(argv[i], "depth") == 0) {
3317             discrim->domain_depth = strtoul(argv[++i], NULL, 0);
3318         } else if (irccasecmp(argv[i], "clones") == 0) {
3319             discrim->min_clones = strtoul(argv[++i], NULL, 0);
3320         } else {
3321             send_message(user, opserv, "MSG_INVALID_CRITERIA", argv[i]);
3322             goto fail;
3323         }
3324     }
3325
3326     if (discrim->mask_nick && !strcmp(discrim->mask_nick, "*")) {
3327         discrim->mask_nick = 0;
3328     }
3329     if (discrim->mask_ident && !strcmp(discrim->mask_ident, "*")) {
3330         discrim->mask_ident = 0;
3331     }
3332     if (discrim->mask_info && !strcmp(discrim->mask_info, "*")) {
3333         discrim->mask_info = 0;
3334     }
3335     if (discrim->mask_host && !discrim->mask_host[strspn(discrim->mask_host, "*.")]) {
3336         discrim->mask_host = 0;
3337     }
3338     return discrim;
3339   fail:
3340     free(discrim);
3341     return NULL;
3342 }
3343
3344 static int
3345 discrim_match(discrim_t discrim, struct userNode *user)
3346 {
3347     unsigned int level, i;
3348
3349     if ((user->timestamp < discrim->min_ts)
3350         || (user->timestamp > discrim->max_ts)
3351         || (user->channels.used < discrim->min_channels)
3352         || (user->channels.used > discrim->max_channels)
3353         || (discrim->authed == 0 && user->handle_info)
3354         || (discrim->authed == 1 && !user->handle_info)
3355         || (discrim->info_space == 0 && user->info[0] == ' ')
3356         || (discrim->info_space == 1 && user->info[0] != ' ')
3357         || (discrim->mask_nick && !match_ircglob(user->nick, discrim->mask_nick))
3358         || (discrim->mask_ident && !match_ircglob(user->ident, discrim->mask_ident))
3359         || (discrim->mask_host && !match_ircglob(user->hostname, discrim->mask_host))
3360         || (discrim->mask_info && !match_ircglob(user->info, discrim->mask_info))
3361         || (discrim->server && !match_ircglob(user->uplink->name, discrim->server))
3362         || (discrim->accountmask && (!user->handle_info || !match_ircglob(user->handle_info->handle, discrim->accountmask)))
3363         || (discrim->ip_mask_bits && !irc_check_mask(&user->ip, &discrim->ip_mask, discrim->ip_mask_bits))
3364         )
3365         return 0;
3366     for(i = 0; i < discrim->channel_count; i++)
3367         if (!GetUserMode(discrim->channels[i], user))
3368             return 0;
3369     level = user->handle_info ? user->handle_info->opserv_level : 0;
3370     if ((level < discrim->min_level)
3371         || (level > discrim->max_level)) {
3372         return 0;
3373     }
3374     if (discrim->min_clones > 1) {
3375         struct opserv_hostinfo *ohi = dict_find(opserv_hostinfo_dict, irc_ntoa(&user->ip), NULL);
3376         if (!ohi || (ohi->clients.used < discrim->min_clones))
3377             return 0;
3378     }
3379     return 1;
3380 }
3381
3382 static unsigned int
3383 opserv_discrim_search(discrim_t discrim, discrim_search_func dsf, void *data)
3384 {
3385     unsigned int nn, count, match;
3386     struct userList matched;
3387
3388     userList_init(&matched);
3389     /* Try most optimized search methods first */
3390     if (discrim->channel_count)
3391     {
3392         for (nn=0; (nn < discrim->channels[0]->members.used)
3393              && (matched.used < discrim->limit);
3394              nn++) {
3395             struct modeNode *mn = discrim->channels[0]->members.list[nn];
3396
3397             if (((mn->modes & discrim->chan_req_modes[0]) != discrim->chan_req_modes[0])
3398                || ((mn->modes & discrim->chan_no_modes[0]) != 0)) {
3399                 continue;
3400             }
3401
3402             if ((match = discrim_match(discrim, mn->user)))
3403             {
3404                 unsigned int i;
3405
3406                 for (i = 1; i < discrim->channel_count; i++) {
3407                     struct modeNode *mn2 = GetUserMode(discrim->channels[i], mn->user);
3408
3409                     if (((mn2->modes & discrim->chan_req_modes[i]) != discrim->chan_req_modes[i])
3410                         || ((mn2->modes & discrim->chan_no_modes[i]) != 0)) {
3411                         match = 0;
3412                         break;
3413                     }
3414                 }
3415
3416                 if (match)
3417                     userList_append(&matched, mn->user);
3418             }
3419         }
3420     } else if (discrim->ip_mask_bits == 128) {
3421         struct opserv_hostinfo *ohi = dict_find(opserv_hostinfo_dict, irc_ntoa(&discrim->ip_mask), NULL);
3422         if (!ohi) {
3423             userList_clean(&matched);
3424             return 0;
3425         }
3426         for (nn=0; (nn<ohi->clients.used) && (matched.used < discrim->limit); nn++) {
3427             if (discrim_match(discrim, ohi->clients.list[nn])) {
3428                 userList_append(&matched, ohi->clients.list[nn]);
3429             }
3430         }
3431     } else {
3432         dict_iterator_t it;
3433         for (it=dict_first(clients); it && (matched.used < discrim->limit); it=iter_next(it)) {
3434             if (discrim_match(discrim, iter_data(it))) {
3435                 userList_append(&matched, iter_data(it));
3436             }
3437         }
3438     }
3439
3440     if (!matched.used) {
3441         userList_clean(&matched);
3442         return 0;
3443     }
3444
3445     if (discrim->option_log) {
3446         log_module(OS_LOG, LOG_INFO, "Logging matches for search:");
3447     }
3448     for (nn=0; nn<matched.used; nn++) {
3449         struct userNode *user = matched.list[nn];
3450         if (discrim->option_log) {
3451             log_module(OS_LOG, LOG_INFO, "  %s!%s@%s", user->nick, user->ident, user->hostname);
3452         }
3453         if (dsf(user, data)) {
3454             /* If a search function returns true, it ran into a
3455                problem. Stop going through the list. */
3456             break;
3457         }
3458     }
3459     if (discrim->option_log) {
3460         log_module(OS_LOG, LOG_INFO, "End of matching users.");
3461     }
3462     count = matched.used;
3463     userList_clean(&matched);
3464     return count;
3465 }
3466
3467 static int
3468 trace_print_func(struct userNode *match, void *extra)
3469 {
3470     struct discrim_and_source *das = extra;
3471     if (match->handle_info) {
3472         send_message_type(4, das->source, opserv, "%s!%s@%s %s", match->nick, match->ident, match->hostname, match->handle_info->handle);
3473     } else {
3474         send_message_type(4, das->source, opserv, "%s!%s@%s", match->nick, match->ident, match->hostname);
3475     }
3476     return 0;
3477 }
3478
3479 static int
3480 trace_count_func(UNUSED_ARG(struct userNode *match), UNUSED_ARG(void *extra))
3481 {
3482     return 0;
3483 }
3484
3485 static int
3486 is_oper_victim(struct userNode *user, struct userNode *target, int match_opers, int check_ip)
3487 {
3488     unsigned char is_victim;
3489     unsigned int nn;
3490
3491     is_victim = !(IsService(target)
3492                   || (!match_opers && IsOper(target))
3493                   || (target->handle_info
3494                       && target->handle_info->opserv_level > user->handle_info->opserv_level));
3495
3496     /* If we don't need an ip check or want to hit opers or the the "cheap" check already disqualified the target, we are done. */
3497     if (!check_ip || match_opers || !is_victim)
3498         return is_victim;
3499
3500     for(nn = 0; nn < curr_opers.used; nn++) {
3501         if(memcmp(&curr_opers.list[nn]->ip, &target->ip, sizeof(irc_in_addr_t)) == 0)
3502             return 0;
3503     }
3504
3505     return 1;
3506 }
3507
3508 static int
3509 is_trust_victim(struct userNode *target, int match_trusted)
3510 {
3511     return (match_trusted || !dict_find(opserv_trusted_hosts, irc_ntoa(&target->ip), NULL));
3512 }
3513
3514 static int
3515 trace_gline_func(struct userNode *match, void *extra)
3516 {
3517     struct discrim_and_source *das = extra;
3518
3519     if (is_oper_victim(das->source, match, das->discrim->match_opers, 1) && is_trust_victim(match, das->discrim->match_trusted)) {
3520         opserv_block(match, das->source->handle_info->handle, das->discrim->reason, das->discrim->duration);
3521     }
3522
3523     return 0;
3524 }
3525
3526 static int
3527 trace_kill_func(struct userNode *match, void *extra)
3528 {
3529     struct discrim_and_source *das = extra;
3530
3531     if (is_oper_victim(das->source, match, das->discrim->match_opers, 0) && is_trust_victim(match, das->discrim->match_trusted)) {
3532         char *reason;
3533         if (das->discrim->reason) {
3534             reason = das->discrim->reason;
3535         } else {
3536             reason = alloca(strlen(OSMSG_KILL_REQUESTED)+strlen(das->source->nick)+1);
3537             sprintf(reason, OSMSG_KILL_REQUESTED, das->source->nick);
3538         }
3539         DelUser(match, opserv, 1, reason);
3540     }
3541
3542     return 0;
3543 }
3544
3545 static int
3546 is_gagged(char *mask)
3547 {
3548     struct gag_entry *gag;
3549
3550     for (gag = gagList; gag; gag = gag->next) {
3551         if (match_ircglobs(gag->mask, mask)) return 1;
3552     }
3553     return 0;
3554 }
3555
3556 static int
3557 trace_gag_func(struct userNode *match, void *extra)
3558 {
3559     struct discrim_and_source *das = extra;
3560
3561     if (is_oper_victim(das->source, match, das->discrim->match_opers, 1) && is_trust_victim(match, das->discrim->match_trusted)) {
3562         char *reason, *mask;
3563         int masksize;
3564         if (das->discrim->reason) {
3565             reason = das->discrim->reason;
3566         } else {
3567             reason = alloca(strlen(OSMSG_GAG_REQUESTED)+strlen(das->source->nick)+1);
3568             sprintf(reason, OSMSG_GAG_REQUESTED, das->source->nick);
3569         }
3570         masksize = 5+strlen(match->hostname);
3571         mask = alloca(masksize);
3572         snprintf(mask, masksize, "*!*@%s", match->hostname);
3573         if (!is_gagged(mask)) {
3574             gag_create(mask, das->source->handle_info->handle, reason,
3575                        das->discrim->duration ? (now + das->discrim->duration) : 0);
3576         }
3577     }
3578
3579     return 0;
3580 }
3581
3582 static int
3583 trace_domains_func(struct userNode *match, void *extra)
3584 {
3585     struct discrim_and_source *das = extra;
3586     irc_in_addr_t ip;
3587     unsigned long *count;
3588     unsigned int depth;
3589     char *hostname;
3590     char ipmask[IRC_NTOP_MASK_MAX_SIZE];
3591
3592     if (irc_pton(&ip, NULL, match->hostname)) {
3593         if (irc_in_addr_is_ipv4(ip)) {
3594             unsigned long matchip = ntohl(ip.in6_32[3]);
3595             /* raw IP address.. use up to first three octets of IP */
3596             switch (das->discrim->domain_depth) {
3597             default:
3598                 snprintf(ipmask, sizeof(ipmask), "%lu.%lu.%lu.*", (matchip>>24)&255, (matchip>>16)&255, (matchip>>8)&255);
3599                 break;
3600             case 2:
3601                 snprintf(ipmask, sizeof(ipmask), "%lu.%lu.*", (matchip>>24)&255, (matchip>>16)&255);
3602                 break;
3603             case 1:
3604                 snprintf(ipmask, sizeof(ipmask), "%lu.*", (matchip>>24)&255);
3605                 break;
3606             }
3607         } else if (irc_in_addr_is_ipv6(ip)) {
3608             switch (das->discrim->domain_depth) {
3609             case 1:  depth = 16; goto ipv6_pfx;
3610             case 2:  depth = 24; goto ipv6_pfx;
3611             case 3:  depth = 32; goto ipv6_pfx;
3612             default: depth = das->discrim->domain_depth;
3613             ipv6_pfx:
3614                 irc_ntop_mask(ipmask, sizeof(ipmask), &ip, depth);
3615             }
3616         } else safestrncpy(ipmask, match->hostname, sizeof(ipmask));
3617         ipmask[sizeof(ipmask) - 1] = '\0';
3618         hostname = ipmask;
3619     } else {
3620         hostname = match->hostname + strlen(match->hostname);
3621         for (depth=das->discrim->domain_depth;
3622              depth && (hostname > match->hostname);
3623              depth--) {
3624             hostname--;
3625             while ((hostname > match->hostname) && (*hostname != '.')) hostname--;
3626         }
3627         if (*hostname == '.') hostname++; /* advance past last dot we saw */
3628     }
3629     if (!(count = dict_find(das->dict, hostname, NULL))) {
3630         count = calloc(1, sizeof(*count));
3631         dict_insert(das->dict, strdup(hostname), count);
3632     }
3633     (*count)++;
3634     return 0;
3635 }
3636
3637 static int
3638 opserv_show_hostinfo(const char *key, void *data, void *extra)
3639 {
3640     unsigned long *count = data;
3641     struct discrim_and_source *das = extra;
3642
3643     send_message_type(4, das->source, opserv, "%s %lu", key, *count);
3644     return !--das->disp_limit;
3645 }
3646
3647 static MODCMD_FUNC(cmd_trace)
3648 {
3649     struct discrim_and_source das;
3650     discrim_search_func action;
3651     unsigned int matches, i;
3652     struct svccmd *subcmd;
3653     char buf[MAXLEN];
3654
3655     sprintf(buf, "trace %s", argv[1]);
3656     if (!(subcmd = dict_find(cmd->parent->commands, buf, NULL))) {
3657         reply("OSMSG_BAD_ACTION", argv[1]);
3658         return 0;
3659     }
3660     if (!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
3661         return 0;
3662     if (!irccasecmp(argv[1], "print"))
3663         action = trace_print_func;
3664     else if (!irccasecmp(argv[1], "count"))
3665         action = trace_count_func;
3666     else if (!irccasecmp(argv[1], "domains"))
3667         action = trace_domains_func;
3668     else if (!irccasecmp(argv[1], "gline"))
3669         action = trace_gline_func;
3670     else if (!irccasecmp(argv[1], "kill"))
3671         action = trace_kill_func;
3672     else if (!irccasecmp(argv[1], "gag"))
3673         action = trace_gag_func;
3674     else {
3675         reply("OSMSG_BAD_ACTION", argv[1]);
3676         return 0;
3677     }
3678
3679     if (user->handle_info->opserv_level < subcmd->min_opserv_level) {
3680         reply("OSMSG_LEVEL_TOO_LOW");
3681         return 0;
3682     }
3683
3684     das.dict = NULL;
3685     das.source = user;
3686     das.discrim = opserv_discrim_create(user, argc-2, argv+2, 1);
3687     if (!das.discrim)
3688         return 0;
3689
3690     if (action == trace_print_func)
3691         reply("OSMSG_USER_SEARCH_RESULTS");
3692     else if (action == trace_count_func)
3693         das.discrim->limit = INT_MAX;
3694     else if ((action == trace_gline_func) && !das.discrim->duration)
3695         das.discrim->duration = opserv_conf.block_gline_duration;
3696     else if (action == trace_domains_func) {
3697         das.dict = dict_new();
3698         dict_set_free_data(das.dict, free);
3699         dict_set_free_keys(das.dict, free);
3700         das.disp_limit = das.discrim->limit;
3701         das.discrim->limit = INT_MAX;
3702     }
3703     matches = opserv_discrim_search(das.discrim, action, &das);
3704
3705     if (action == trace_domains_func)
3706         dict_foreach(das.dict, opserv_show_hostinfo, &das);
3707
3708     if (matches)
3709         reply("MSG_MATCH_COUNT", matches);
3710     else
3711         reply("MSG_NO_MATCHES");
3712
3713     for (i = 0; i < das.discrim->channel_count; i++)
3714         UnlockChannel(das.discrim->channels[i]);
3715     free(das.discrim->reason);
3716     free(das.discrim);
3717     dict_delete(das.dict);
3718     return 1;
3719 }
3720
3721 typedef void (*cdiscrim_search_func)(struct chanNode *match, void *data);
3722
3723 typedef struct channel_discrim {
3724     char *name, *topic;
3725
3726     unsigned int min_users, max_users;
3727     unsigned long min_ts, max_ts;
3728     unsigned int limit;
3729 } *cdiscrim_t;
3730
3731 static cdiscrim_t opserv_cdiscrim_create(struct userNode *user, unsigned int argc, char *argv[]);
3732 static unsigned int opserv_cdiscrim_search(cdiscrim_t discrim, cdiscrim_search_func dsf, void *data);
3733
3734 static unsigned long
3735 smart_parse_time(const char *str) {
3736     /* If an interval-style string is given, treat as time before now.
3737      * If it's all digits, treat directly as a Unix timestamp. */
3738     return str[strspn(str, "0123456789")] ? (now - ParseInterval(str)) : strtoul(str, NULL, 0);
3739 }
3740
3741 static cdiscrim_t
3742 opserv_cdiscrim_create(struct userNode *user, unsigned int argc, char *argv[])
3743 {
3744     cdiscrim_t discrim;
3745     unsigned int i;
3746
3747     discrim = calloc(1, sizeof(*discrim));
3748     discrim->limit = 25;
3749     discrim->max_users = UINT_MAX;
3750     discrim->max_ts = ULONG_MAX;
3751
3752     for (i = 0; i < argc; i++) {
3753         /* Assume all criteria require arguments. */
3754         if (i == (argc - 1)) {
3755             send_message(user, opserv, "MSG_MISSING_PARAMS", argv[i]);
3756             return NULL;
3757         }
3758
3759         if (!irccasecmp(argv[i], "name"))
3760             discrim->name = argv[++i];
3761         else if (!irccasecmp(argv[i], "topic"))
3762             discrim->topic = argv[++i];
3763         else if (!irccasecmp(argv[i], "users")) {
3764             const char *cmp = argv[++i];
3765             if (cmp[0] == '<') {
3766                 if (cmp[1] == '=')
3767                     discrim->max_users = strtoul(cmp+2, NULL, 0);
3768                 else
3769                     discrim->max_users = strtoul(cmp+1, NULL, 0) - 1;
3770             } else if (cmp[0] == '=') {
3771                 discrim->min_users = discrim->max_users = strtoul(cmp+1, NULL, 0);
3772             } else if (cmp[0] == '>') {
3773                 if (cmp[1] == '=')
3774                     discrim->min_users = strtoul(cmp+2, NULL, 0);
3775                 else
3776                     discrim->min_users = strtoul(cmp+1, NULL, 0) + 1;
3777             } else {
3778                 discrim->min_users = strtoul(cmp, NULL, 0);
3779             }
3780         } else if (!irccasecmp(argv[i], "timestamp")) {
3781             const char *cmp = argv[++i];
3782             if (cmp[0] == '<') {
3783                 if (cmp[1] == '=')
3784                     discrim->max_ts = smart_parse_time(cmp+2);
3785                 else
3786                     discrim->max_ts = smart_parse_time(cmp+1)-1;
3787             } else if (cmp[0] == '=') {
3788                 discrim->min_ts = discrim->max_ts = smart_parse_time(cmp+1);
3789             } else if (cmp[0] == '>') {
3790                 if (cmp[1] == '=')
3791                     discrim->min_ts = smart_parse_time(cmp+2);
3792                 else
3793                     discrim->min_ts = smart_parse_time(cmp+1)+1;
3794             } else {
3795                 discrim->min_ts = smart_parse_time(cmp);
3796             }
3797         } else if (!irccasecmp(argv[i], "limit")) {
3798             discrim->limit = strtoul(argv[++i], NULL, 10);
3799         } else {
3800             send_message(user, opserv, "MSG_INVALID_CRITERIA", argv[i]);
3801             goto fail;
3802         }
3803     }
3804
3805     if (discrim->name && !strcmp(discrim->name, "*"))
3806         discrim->name = 0;
3807     if (discrim->topic && !strcmp(discrim->topic, "*"))
3808         discrim->topic = 0;
3809
3810     return discrim;
3811   fail:
3812     free(discrim);
3813     return NULL;
3814 }
3815
3816 static int
3817 cdiscrim_match(cdiscrim_t discrim, struct chanNode *chan)
3818 {
3819     if ((discrim->name && !match_ircglob(chan->name, discrim->name)) ||
3820         (discrim->topic && !match_ircglob(chan->topic, discrim->topic)) ||
3821         (chan->members.used < discrim->min_users) ||
3822         (chan->members.used > discrim->max_users) ||
3823         (chan->timestamp < discrim->min_ts) ||
3824         (chan->timestamp > discrim->max_ts)) {
3825         return 0;
3826     }
3827     return 1;
3828 }
3829
3830 static unsigned int opserv_cdiscrim_search(cdiscrim_t discrim, cdiscrim_search_func dsf, void *data)
3831 {
3832     unsigned int count = 0;
3833     dict_iterator_t it, next;
3834
3835     for (it = dict_first(channels); it && count < discrim->limit ; it = next) {
3836         struct chanNode *chan = iter_data(it);
3837
3838         /* Hold on to the next channel in case we decide to
3839            add actions that destructively modify the channel. */
3840         next = iter_next(it);
3841         if ((chan->members.used > 0) && cdiscrim_match(discrim, chan)) {
3842             dsf(chan, data);
3843             count++;
3844         }
3845     }
3846
3847     return count;
3848 }
3849
3850 void channel_count(UNUSED_ARG(struct chanNode *channel), UNUSED_ARG(void *data))
3851 {
3852 }
3853
3854 void channel_print(struct chanNode *channel, void *data)
3855 {
3856     char modes[MAXLEN];
3857     irc_make_chanmode(channel, modes);
3858     send_message(data, opserv, "OSMSG_CSEARCH_CHANNEL_INFO", channel->name, channel->members.used, modes, channel->topic);
3859 }
3860
3861 static MODCMD_FUNC(cmd_csearch)
3862 {
3863     cdiscrim_t discrim;
3864     unsigned int matches;
3865     cdiscrim_search_func action;
3866     struct svccmd *subcmd;
3867     char buf[MAXLEN];
3868
3869     if (!irccasecmp(argv[1], "count"))
3870         action = channel_count;
3871     else if (!irccasecmp(argv[1], "print"))
3872         action = channel_print;
3873     else {
3874         reply("OSMSG_BAD_ACTION", argv[1]);
3875         return 0;
3876     }
3877
3878     sprintf(buf, "%s %s", argv[0], argv[0]);
3879     if ((subcmd = dict_find(cmd->parent->commands, buf, NULL))
3880         && !svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY)) {
3881         return 0;
3882     }
3883
3884     discrim = opserv_cdiscrim_create(user, argc - 2, argv + 2);
3885     if (!discrim)
3886         return 0;
3887
3888     if (action == channel_print)
3889         reply("OSMSG_CHANNEL_SEARCH_RESULTS");
3890     else if (action == channel_count)
3891         discrim->limit = INT_MAX;
3892
3893     matches = opserv_cdiscrim_search(discrim, action, user);
3894
3895     if (matches)
3896         reply("MSG_MATCH_COUNT", matches);
3897     else
3898         reply("MSG_NO_MATCHES");
3899
3900     free(discrim);
3901     return 1;
3902 }
3903
3904 static MODCMD_FUNC(cmd_gsync)
3905 {
3906     struct server *src;
3907     if (argc > 1) {
3908         src = GetServerH(argv[1]);
3909         if (!src) {
3910             reply("MSG_SERVER_UNKNOWN", argv[1]);
3911             return 0;
3912         }
3913     } else {
3914         src = self->uplink;
3915     }
3916     irc_stats(cmd->parent->bot, src, 'G');
3917     reply("OSMSG_GSYNC_RUNNING", src->name);
3918     return 1;
3919 }
3920
3921 struct gline_extra {
3922     struct userNode *user;
3923     struct string_list *glines;
3924 };
3925
3926 static void
3927 gtrace_print_func(struct gline *gline, void *extra)
3928 {
3929     struct gline_extra *xtra = extra;
3930     char issued[INTERVALLEN];
3931     char lastmod[INTERVALLEN];
3932     char expires[INTERVALLEN];
3933     char lifetime[INTERVALLEN];
3934
3935     intervalString(issued, now - gline->issued, xtra->user->handle_info);
3936     if (gline->lastmod)
3937         intervalString(lastmod, now - gline->lastmod, xtra->user->handle_info);
3938     else
3939         strcpy(lastmod, "<unknown>");
3940     intervalString(lifetime, gline->lifetime - now, xtra->user->handle_info);
3941     if (!gline->expires) {
3942         send_message(xtra->user, opserv, "OSMSG_GTRACE_FOREVER", gline->target, issued, gline->issuer, lastmod, NULL, gline->reason, lifetime);
3943     } else if (gline->expires < now) {
3944         intervalString(expires, now - gline->expires, xtra->user->handle_info);
3945         send_message(xtra->user, opserv, "OSMSG_GTRACE_EXPIRED", gline->target, issued, gline->issuer, lastmod, expires, gline->reason, lifetime);
3946     } else { /* must be in the future */
3947         intervalString(expires, gline->expires - now, xtra->user->handle_info);
3948         send_message(xtra->user, opserv, "OSMSG_GTRACE_FORMAT", gline->target, issued, gline->issuer, lastmod, expires, gline->reason, lifetime);
3949     }
3950 }
3951
3952 static MODCMD_FUNC(cmd_stats_glines) {
3953     if (argc < 2) {
3954         reply("OSMSG_GLINE_COUNT", gline_count());
3955         return 1;
3956     } else if (argc < 3) {
3957         struct gline_extra extra;
3958         struct gline *gl;
3959
3960         extra.user = user;
3961         gl = gline_find(argv[1]);
3962         if (!gl)
3963             reply("OSMSG_NO_GLINE", argv[1]);
3964         else
3965             gtrace_print_func(gl, &extra);
3966         return 1;
3967     } else return 0;
3968 }
3969
3970 static void
3971 gtrace_count_func(UNUSED_ARG(struct gline *gline), UNUSED_ARG(void *extra))
3972 {
3973 }
3974
3975 static void
3976 gtrace_ungline_func(struct gline *gline, void *extra)
3977 {
3978     struct gline_extra *xtra = extra;
3979     string_list_append(xtra->glines, strdup(gline->target));
3980 }
3981
3982 static MODCMD_FUNC(cmd_gtrace)
3983 {
3984     struct gline_discrim *discrim;
3985     gline_search_func action;
3986     unsigned int matches, nn;
3987     struct gline_extra extra;
3988     struct svccmd *subcmd;
3989     char buf[MAXLEN];
3990
3991     if (!irccasecmp(argv[1], "print"))
3992         action = gtrace_print_func;
3993     else if (!irccasecmp(argv[1], "count"))
3994         action = gtrace_count_func;
3995     else if (!irccasecmp(argv[1], "ungline"))
3996         action = gtrace_ungline_func;
3997     else {
3998         reply("OSMSG_BAD_ACTION", argv[1]);
3999         return 0;
4000     }
4001     sprintf(buf, "%s %s", argv[0], argv[0]);
4002     if ((subcmd = dict_find(cmd->parent->commands, buf, NULL))
4003         && !svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY)) {
4004         return 0;
4005     }
4006
4007     discrim = gline_discrim_create(user, cmd->parent->bot, argc-2, argv+2);
4008     if (!discrim)
4009         return 0;
4010
4011     if (action == gtrace_print_func)
4012         reply("OSMSG_GLINE_SEARCH_RESULTS");
4013     else if (action == gtrace_count_func)
4014         discrim->limit = INT_MAX;
4015
4016     extra.user = user;
4017     extra.glines = alloc_string_list(4);
4018     matches = gline_discrim_search(discrim, action, &extra);
4019
4020     if (action == gtrace_ungline_func)
4021         for (nn=0; nn<extra.glines->used; nn++)
4022             gline_remove(extra.glines->list[nn], 1);
4023     free_string_list(extra.glines);
4024
4025     if (matches)
4026         reply("MSG_MATCH_COUNT", matches);
4027     else
4028         reply("MSG_NO_MATCHES");
4029     free(discrim->alt_target_mask);
4030     free(discrim);
4031     return 1;
4032 }
4033
4034 static int
4035 alert_check_user(const char *key, void *data, void *extra)
4036 {
4037     struct opserv_user_alert *alert = data;
4038     struct userNode *user = extra;
4039
4040     if (!discrim_match(alert->discrim, user))
4041         return 0;
4042
4043     if ((alert->reaction != REACT_NOTICE)
4044         && IsOper(user)
4045         && !alert->discrim->match_opers) {
4046         return 0;
4047     }
4048
4049     if ((alert->reaction != REACT_NOTICE)
4050         && !is_trust_victim(user, alert->discrim->match_trusted)) {
4051         return 0;
4052     }
4053
4054     /* The user matches the alert criteria, so trigger the reaction. */
4055     if (alert->discrim->option_log)
4056         log_module(OS_LOG, LOG_INFO, "Alert %s triggered by user %s!%s@%s (%s).", key, user->nick, user->ident, user->hostname, alert->discrim->reason);
4057
4058     /* Return 1 to halt alert matching, such as when killing the user
4059        that triggered the alert. */
4060     switch (alert->reaction) {
4061     case REACT_KILL:
4062         DelUser(user, opserv, 1, alert->discrim->reason);
4063         return 1;
4064     case REACT_GLINE:
4065         opserv_block(user, alert->owner, alert->discrim->reason, alert->discrim->duration);
4066         return 1;
4067     default:
4068         log_module(OS_LOG, LOG_ERROR, "Invalid reaction type %d for alert %s.", alert->reaction, key);
4069         /* fall through to REACT_NOTICE case */
4070     case REACT_NOTICE:
4071         opserv_custom_alert(alert->discrim->notice_target, "Alert $b%s$b triggered by user $b%s$b!%s@%s (%s).", key, user->nick, user->ident, user->hostname, alert->discrim->reason);
4072         break;
4073     }
4074     return 0;
4075 }
4076
4077 static void
4078 opserv_alert_check_nick(struct userNode *user, UNUSED_ARG(const char *old_nick))
4079 {
4080     struct gag_entry *gag;
4081     dict_foreach(opserv_nick_based_alerts, alert_check_user, user);
4082     /* Gag them if appropriate (and only if). */
4083     user->modes &= ~FLAGS_GAGGED;
4084     for (gag = gagList; gag; gag = gag->next) {
4085         if (user_matches_glob(user, gag->mask, MATCH_USENICK)) {
4086             gag_helper_func(user, NULL);
4087             break;
4088         }
4089     }
4090 }
4091
4092 static void
4093 opserv_staff_alert(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
4094 {
4095     const char *type;
4096
4097     if (!opserv_conf.staff_auth_channel
4098         || user->uplink->burst
4099         || !user->handle_info)
4100         return;
4101     else if (IsBot(user))
4102         return;
4103     else if (user->handle_info->opserv_level)
4104         type = "OPER";
4105     else if (IsNetworkHelper(user))
4106         type = "NETWORK HELPER";
4107     else if (IsSupportHelper(user))
4108         type = "SUPPORT HELPER";
4109     else
4110         return;
4111
4112     if (irc_in_addr_is_valid(user->ip))
4113         send_channel_message(opserv_conf.staff_auth_channel, opserv, IDENT_FORMAT" authed to %s account %s", IDENT_DATA(user), type, user->handle_info->handle);
4114     else
4115         send_channel_message(opserv_conf.staff_auth_channel, opserv, "%s [%s@%s] authed to %s account %s", user->nick, user->ident, user->hostname, type, user->handle_info->handle);
4116 }
4117
4118 static void
4119 opserv_auth_alert(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
4120 {
4121     if (!user->uplink->burst && user->handle_info)
4122         dict_foreach(opserv_account_alerts, alert_check_user, user);
4123 }
4124
4125 static MODCMD_FUNC(cmd_log)
4126 {
4127     struct logSearch *discrim;
4128     unsigned int matches;
4129     struct logReport report;
4130
4131     discrim = log_discrim_create(cmd->parent->bot, user, argc, argv);
4132     if (!discrim)
4133         return 0;
4134
4135     reply("OSMSG_LOG_SEARCH_RESULTS");
4136     report.reporter = opserv;
4137     report.user = user;
4138     matches = log_entry_search(discrim, log_report_entry, &report);
4139
4140     if (matches)
4141         reply("MSG_MATCH_COUNT", matches);
4142     else
4143         reply("MSG_NO_MATCHES");
4144
4145     free(discrim);
4146     return 1;
4147 }
4148
4149 static int
4150 gag_helper_func(struct userNode *match, UNUSED_ARG(void *extra))
4151 {
4152     if (IsOper(match) || IsLocal(match))
4153         return 0;
4154     match->modes |= FLAGS_GAGGED;
4155     return 0;
4156 }
4157
4158 static MODCMD_FUNC(cmd_gag)
4159 {
4160     struct gag_entry *gag;
4161     unsigned int gagged;
4162     unsigned long duration;
4163     char *reason;
4164
4165     reason = unsplit_string(argv + 3, argc - 3, NULL);
4166
4167     if (!is_ircmask(argv[1])) {
4168         reply("OSMSG_INVALID_IRCMASK", argv[1]);
4169         return 0;
4170     }
4171
4172     for (gag = gagList; gag; gag = gag->next)
4173         if (match_ircglobs(gag->mask, argv[1]))
4174             break;
4175
4176     if (gag) {
4177         reply("OSMSG_REDUNDANT_GAG", argv[1]);
4178         return 0;
4179     }
4180
4181     duration = ParseInterval(argv[2]);
4182     gagged = gag_create(argv[1], user->handle_info->handle, reason, (duration?now+duration:0));
4183
4184     if (gagged)
4185         reply("OSMSG_GAG_APPLIED", argv[1], gagged);
4186     else
4187         reply("OSMSG_GAG_ADDED", argv[1]);
4188     return 1;
4189 }
4190
4191 static int
4192 ungag_helper_func(struct userNode *match, UNUSED_ARG(void *extra))
4193 {
4194     match->modes &= ~FLAGS_GAGGED;
4195     return 0;
4196 }
4197
4198 static MODCMD_FUNC(cmd_ungag)
4199 {
4200     struct gag_entry *gag;
4201     unsigned int ungagged;
4202
4203     for (gag = gagList; gag; gag = gag->next)
4204         if (!strcmp(gag->mask, argv[1]))
4205             break;
4206
4207     if (!gag) {
4208         reply("OSMSG_GAG_NOT_FOUND", argv[1]);
4209         return 0;
4210     }
4211
4212     timeq_del(gag->expires, gag_expire, gag, 0);
4213     ungagged = gag_free(gag);
4214
4215     if (ungagged)
4216         reply("OSMSG_UNGAG_APPLIED", argv[1], ungagged);
4217     else
4218         reply("OSMSG_UNGAG_ADDED", argv[1]);
4219     return 1;
4220 }
4221
4222 static MODCMD_FUNC(cmd_addalert)
4223 {
4224     opserv_alert_reaction reaction;
4225     struct svccmd *subcmd;
4226     const char *name;
4227     char buf[MAXLEN];
4228
4229     name = argv[1];
4230     sprintf(buf, "addalert %s", argv[2]);
4231     if (!(subcmd = dict_find(cmd->parent->commands, buf, NULL))) {
4232         reply("OSMSG_UNKNOWN_REACTION", argv[2]);
4233         return 0;
4234     }
4235     if (!irccasecmp(argv[2], "notice"))
4236         reaction = REACT_NOTICE;
4237     else if (!irccasecmp(argv[2], "kill"))
4238         reaction = REACT_KILL;
4239     else if (!irccasecmp(argv[2], "gline"))
4240         reaction = REACT_GLINE;
4241     else {
4242         reply("OSMSG_UNKNOWN_REACTION", argv[2]);
4243         return 0;
4244     }
4245     if (!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY)
4246         || !opserv_add_user_alert(user, name, reaction, unsplit_string(argv + 3, argc - 3, NULL)))
4247         return 0;
4248     reply("OSMSG_ADDED_ALERT", name);
4249     return 1;
4250 }
4251
4252 static MODCMD_FUNC(cmd_delalert)
4253 {
4254     unsigned int i;
4255     for (i=1; i<argc; i++) {
4256         dict_remove(opserv_nick_based_alerts, argv[i]);
4257         dict_remove(opserv_channel_alerts, argv[i]);
4258         dict_remove(opserv_account_alerts, argv[i]);
4259         if (dict_remove(opserv_user_alerts, argv[i]))
4260             reply("OSMSG_REMOVED_ALERT", argv[i]);
4261         else
4262             reply("OSMSG_NO_SUCH_ALERT", argv[i]);
4263     }
4264     return 1;
4265 }
4266
4267 static MODCMD_FUNC(cmd_listdevnull)
4268 {
4269     struct helpfile_table tbl;
4270     unsigned int count = 0, ii = 0;
4271     char *on,*off,*half;
4272     on = "X";
4273     off = "-";
4274     half = "1/2";
4275     
4276     dict_iterator_t it;
4277     for (it = dict_first(opserv_devnull_classes); it; it = iter_next(it)) {
4278         count++;
4279     }
4280     tbl.length = count+1;
4281     tbl.width = 15;
4282     tbl.flags = 0;
4283     tbl.flags = TABLE_NO_FREE;
4284     tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
4285     tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4286     tbl.contents[0][0] = "Name";
4287     tbl.contents[0][1] = "MC";
4288     tbl.contents[0][2] = "UT";
4289     tbl.contents[0][3] = "FL";
4290     tbl.contents[0][4] = "CH";
4291     tbl.contents[0][5] = "IH";
4292     tbl.contents[0][6] = "SI";
4293     tbl.contents[0][7] = "IH²";
4294     tbl.contents[0][8] = "oC";
4295     tbl.contents[0][9] = "oM";
4296     tbl.contents[0][10] = "+k";
4297     tbl.contents[0][11] = "+S";
4298     tbl.contents[0][12] = "+X";
4299     tbl.contents[0][13] = "MaxQ";
4300     tbl.contents[0][14] = "OpMe";
4301     if(!count)
4302     {
4303         table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4304         reply("MSG_NONE");
4305         free(tbl.contents[0]);
4306         free(tbl.contents);
4307         return 0;
4308     }
4309     for (it = dict_first(opserv_devnull_classes); it; it = iter_next(it)) {
4310         struct devnull_class *th = iter_data(it);
4311         tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4312         tbl.contents[ii][0] = th->name;
4313         if(DEVNULL_HAS_PRIV(th, CHANLIMIT)) {
4314             tbl.contents[ii][1] = strtab(th->maxchan);
4315         } else {
4316             tbl.contents[ii][1] = off;
4317         }
4318         if(DEVNULL_HAS_PRIV(th, UNLIMITTARGET)) {
4319             tbl.contents[ii][2] = on;
4320         } else {
4321             tbl.contents[ii][2] = off;
4322         }
4323         if(DEVNULL_HAS_PRIV(th, NOFLOOD)) {
4324             tbl.contents[ii][3] = on;
4325         } else if(DEVNULL_HAS_PRIV(th, HALFFLOOD)) {
4326             tbl.contents[ii][3] = half;
4327         } else {
4328             tbl.contents[ii][3] = off;
4329         }
4330         if(DEVNULL_HAS_PRIV(th, CHANHIDE)) {
4331             tbl.contents[ii][4] = on;
4332         } else {
4333             tbl.contents[ii][4] = off;
4334         }
4335         if(DEVNULL_HAS_PRIV(th, IDLEHIDE)) {
4336             tbl.contents[ii][5] = on;
4337         } else {
4338             tbl.contents[ii][5] = off;
4339         }
4340         if(DEVNULL_HAS_PRIV(th, CHSERVMODE)) {
4341             tbl.contents[ii][6] = on;
4342         } else {
4343             tbl.contents[ii][6] = off;
4344         }
4345         if(DEVNULL_HAS_PRIV(th, XTRAOPMODE)) {
4346             tbl.contents[ii][7] = on;
4347         } else {
4348             tbl.contents[ii][7] = off;
4349         }
4350         if(DEVNULL_HAS_PRIV(th, NETSERVMODE)) {
4351             tbl.contents[ii][8] = on;
4352         } else {
4353             tbl.contents[ii][8] = off;
4354         }
4355         if(DEVNULL_HAS_PRIV(th, SEEIDLE)) {
4356             tbl.contents[ii][9] = on;
4357         } else {
4358             tbl.contents[ii][9] = off;
4359         }
4360         if(DEVNULL_HAS_PRIV(th, FORCEIDLEHIDE)) {
4361             tbl.contents[ii][10] = on;
4362         } else {
4363             tbl.contents[ii][10] = off;
4364         }
4365         if(DEVNULL_HAS_PRIV(th, OVERRIDECC)) {
4366             tbl.contents[ii][11] = on;
4367         } else {
4368             tbl.contents[ii][11] = off;
4369         }
4370         if(DEVNULL_HAS_PRIV(th, OVERRIDENOAMSG)) {
4371             tbl.contents[ii][12] = on;
4372         } else {
4373             tbl.contents[ii][12] = off;
4374         }
4375         if(DEVNULL_HAS_PRIV(th, MAXSENDQ)) {
4376             tbl.contents[ii][13] = on;
4377         } else {
4378             tbl.contents[ii][13] = off;
4379         }
4380         if(DEVNULL_HAS_PRIV(th, OPME)) {
4381             tbl.contents[ii][14] = on;
4382         } else {
4383             tbl.contents[ii][14] = off;
4384         }
4385     }
4386     table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4387     for(ii = 1; ii < tbl.length; ++ii)
4388     {
4389         free(tbl.contents[ii]);
4390     }
4391     free(tbl.contents[0]);
4392     free(tbl.contents);
4393     send_message_type(4, user, opserv,"-");
4394     /*
4395     tbl.contents[0][1] = "MC";
4396     tbl.contents[0][2] = "UT";
4397     tbl.contents[0][3] = "FL";
4398     tbl.contents[0][4] = "CH";
4399     tbl.contents[0][5] = "IH";
4400     tbl.contents[0][6] = "SI";
4401     tbl.contents[0][7] = "IH²";
4402     tbl.contents[0][8] = "oC";
4403     tbl.contents[0][9] = "oM";
4404     tbl.contents[0][10] = "+k";
4405     tbl.contents[0][11] = "+S";
4406     tbl.contents[0][12] = "+X";
4407     tbl.contents[0][13] = "MaxQ";
4408     */
4409     send_message_type(4, user, opserv,"MC = MaxChan");
4410         send_message_type(4, user, opserv,"UT = UnlimitTargets");
4411         send_message_type(4, user, opserv,"FL = Flood (1 = 1/2 flood, X = unlimited)");
4412         send_message_type(4, user, opserv,"CH = Channel Hide (mode +n)");
4413         send_message_type(4, user, opserv,"IH = Idle Hide (mode +I)");
4414         send_message_type(4, user, opserv,"SI = See Idle");
4415         send_message_type(4, user, opserv,"IH² = Idle Hide (override SI)");
4416         send_message_type(4, user, opserv,"oC = Color override (mode +c)");
4417     send_message_type(4, user, opserv,"oM = AMsg override");
4418         send_message_type(4, user, opserv,"+k = ChanServ Mode (mode +k)");
4419     send_message_type(4, user, opserv,"+S = NetServ Mode (mode +S)");
4420     send_message_type(4, user, opserv,"+X = XtraOp Mode (mode +X)");
4421     send_message_type(4, user, opserv,"OpMe = ChanServ opme command");
4422     return 1;
4423 }
4424
4425 static MODCMD_FUNC(cmd_adddevnull)
4426 {
4427
4428     if (dict_find(opserv_devnull_classes, argv[1], NULL)) {
4429         reply("OSMSG_DEVNULL_FOUND", argv[1]);
4430         return 0;
4431     }
4432     
4433     opserv_add_devnull_class(argv[1], 0, 0, 0);
4434     reply("OSMSG_DEVNULL_ADDED",argv[1]);
4435     return 1;
4436 }
4437
4438
4439 static MODCMD_FUNC(cmd_deldevnull)
4440 {
4441     unsigned int n;
4442
4443     for (n=1; n<argc; n++) {
4444         struct devnull_class *th = dict_find(opserv_devnull_classes, argv[n], NULL);
4445         if (!th)
4446             continue;
4447         nickserv_devnull_delete(th->name);
4448         dict_remove(opserv_devnull_classes, argv[n]);
4449         reply("OSMSG_DEVNULL_REMOVED",argv[n]);
4450     }
4451     return 1;
4452 }
4453
4454 static MODCMD_FUNC(cmd_renamedevnull)
4455 {
4456     struct devnull_class *th;
4457     if (dict_find(opserv_devnull_classes, argv[2], NULL)) {
4458         reply("OSMSG_DEVNULL_FOUND", argv[2]);
4459         return 0;
4460     }
4461     if ((th = dict_find(opserv_devnull_classes, argv[1], NULL))) {
4462         opserv_add_devnull_class(argv[2], th->modes, th->maxchan, th->maxsendq);
4463         nickserv_devnull_rename(th->name,argv[2]);
4464         dict_remove(opserv_devnull_classes, argv[1]);
4465         reply("OSMSG_DEVNULL_RENAMED",argv[1],argv[2]);
4466     } else {
4467         reply("OSMSG_DEVNULL_NOTFOUND", argv[1]);
4468         return 0;
4469     }
4470     return 1;
4471 }
4472
4473 static MODCMD_FUNC(cmd_setdevnull)
4474 {
4475     struct devnull_class *th;
4476     unsigned int offset=0;
4477     if ((th = dict_find(opserv_devnull_classes, argv[offset+1], NULL))) {
4478         if (argc > 3) {
4479             unsigned int ii;
4480             for( ii = 0; argv[offset+2][ ii ]; ii++)
4481                 argv[offset+2][ ii ] = toupper( argv[offset+2][ ii ] );
4482             for( ii = 0; argv[offset+3][ ii ]; ii++)
4483                 argv[offset+3][ ii ] = toupper( argv[offset+3][ ii ] );
4484             
4485             if(!strcmp("CHANLIMIT",argv[offset+2])) {
4486                 if (!strcmp("OFF",argv[offset+3])) {
4487                     DEVNULL_CLEAR_PRIV(th, CHANLIMIT);
4488                     reply("OSMSG_DEVNULL_SET_DONE");
4489                 } else {
4490                     DEVNULL_SET_PRIV(th, CHANLIMIT);
4491                     th->maxchan = strtoul(argv[offset+3], NULL, 0);
4492                     reply("OSMSG_DEVNULL_SET_DONE");
4493                 }
4494             } else if(!strcmp("UNLIMITTARGET",argv[offset+2]) || !strcmp("UNLIMITEDTARGET",argv[offset+2])) {
4495                 if (!strcmp("OFF",argv[offset+3]) || !strcmp("0",argv[offset+3])) {
4496                     DEVNULL_CLEAR_PRIV(th, UNLIMITTARGET);
4497                     reply("OSMSG_DEVNULL_SET_DONE");
4498                 } else if (!strcmp("ON",argv[offset+3]) || !strcmp("1",argv[offset+3])) {
4499                     DEVNULL_SET_PRIV(th, UNLIMITTARGET);
4500                     reply("OSMSG_DEVNULL_SET_DONE");
4501                 } else {
4502                     reply("OSMSG_DEVNULL_SET_INVALID", argv[offset+3]);
4503                 }
4504             } else if(!strcmp("FLOOD",argv[offset+2])) {
4505                 if (!strcmp("OFF",argv[offset+3]) || !strcmp("0",argv[offset+3])) {
4506                     DEVNULL_CLEAR_PRIV(th, HALFFLOOD);
4507                     DEVNULL_CLEAR_PRIV(th, NOFLOOD);
4508                     reply("OSMSG_DEVNULL_SET_DONE");
4509                 } else if (!strcmp("ON",argv[offset+3]) || !strcmp("1",argv[offset+3])) {
4510                     DEVNULL_SET_PRIV(th, NOFLOOD);
4511                     reply("OSMSG_DEVNULL_SET_DONE");
4512                 } else if (!strcmp("HALF",argv[offset+3]) || !strcmp("2",argv[offset+3]) || !strcmp("1/2",argv[offset+3])) {
4513                     DEVNULL_SET_PRIV(th, HALFFLOOD);
4514                     DEVNULL_CLEAR_PRIV(th, NOFLOOD);
4515                     reply("OSMSG_DEVNULL_SET_DONE");
4516                 } else {
4517                     reply("OSMSG_DEVNULL_SET_INVALID", argv[offset+3]);
4518                 }
4519             } else if(!strcmp("CHANHIDE",argv[offset+2])) {
4520                 if (!strcmp("OFF",argv[offset+3]) || !strcmp("0",argv[offset+3])) {
4521                     DEVNULL_CLEAR_PRIV(th, CHANHIDE);
4522                     reply("OSMSG_DEVNULL_SET_DONE");
4523                 } else if (!strcmp("ON",argv[offset+3]) || !strcmp("1",argv[offset+3])) {
4524                     DEVNULL_SET_PRIV(th, CHANHIDE);
4525                     reply("OSMSG_DEVNULL_SET_DONE");
4526                 } else {
4527                     reply("OSMSG_DEVNULL_SET_INVALID", argv[offset+3]);
4528                 }
4529             } else if(!strcmp("IDLEHIDE",argv[offset+2])) {
4530                 if (!strcmp("OFF",argv[offset+3]) || !strcmp("0",argv[offset+3])) {
4531                     DEVNULL_CLEAR_PRIV(th, IDLEHIDE);
4532                     reply("OSMSG_DEVNULL_SET_DONE");
4533                 } else if (!strcmp("ON",argv[offset+3]) || !strcmp("1",argv[offset+3])) {
4534                     DEVNULL_SET_PRIV(th, IDLEHIDE);
4535                     reply("OSMSG_DEVNULL_SET_DONE");
4536                 } else {
4537                     reply("OSMSG_DEVNULL_SET_INVALID", argv[offset+3]);
4538                 }
4539             } else if(!strcmp("CHSERVMODE",argv[offset+2])) {
4540                 if (!strcmp("OFF",argv[offset+3]) || !strcmp("0",argv[offset+3])) {
4541                     DEVNULL_CLEAR_PRIV(th, CHSERVMODE);
4542                     reply("OSMSG_DEVNULL_SET_DONE");
4543                 } else if (!strcmp("ON",argv[offset+3]) || !strcmp("1",argv[offset+3])) {
4544                     DEVNULL_SET_PRIV(th, CHSERVMODE);
4545                     reply("OSMSG_DEVNULL_SET_DONE");
4546                 } else {
4547                     reply("OSMSG_DEVNULL_SET_INVALID", argv[offset+3]);
4548                 }
4549             } else if(!strcmp("XTRAOPMODE",argv[offset+2])) {
4550                 if (!strcmp("OFF",argv[offset+3]) || !strcmp("0",argv[offset+3])) {
4551                     DEVNULL_CLEAR_PRIV(th, XTRAOPMODE);
4552                     reply("OSMSG_DEVNULL_SET_DONE");
4553                 } else if (!strcmp("ON",argv[offset+3]) || !strcmp("1",argv[offset+3])) {
4554                     DEVNULL_SET_PRIV(th, XTRAOPMODE);
4555                     reply("OSMSG_DEVNULL_SET_DONE");
4556                 } else {
4557                     reply("OSMSG_DEVNULL_SET_INVALID", argv[offset+3]);
4558                 }
4559             } else if(!strcmp("NETSERVMODE",argv[offset+2])) {
4560                 if (!strcmp("OFF",argv[offset+3]) || !strcmp("0",argv[offset+3])) {
4561                     DEVNULL_CLEAR_PRIV(th, NETSERVMODE);
4562                     reply("OSMSG_DEVNULL_SET_DONE");
4563                 } else if (!strcmp("ON",argv[offset+3]) || !strcmp("1",argv[offset+3])) {
4564                     DEVNULL_SET_PRIV(th, NETSERVMODE);
4565                     reply("OSMSG_DEVNULL_SET_DONE");
4566                 } else {
4567                     reply("OSMSG_DEVNULL_SET_INVALID", argv[offset+3]);
4568                 }
4569             } else if(!strcmp("SEEIDLE",argv[offset+2])) {
4570                 if (!strcmp("OFF",argv[offset+3]) || !strcmp("0",argv[offset+3])) {
4571                     DEVNULL_CLEAR_PRIV(th, SEEIDLE);
4572                     reply("OSMSG_DEVNULL_SET_DONE");
4573                 } else if (!strcmp("ON",argv[offset+3]) || !strcmp("1",argv[offset+3])) {
4574                     DEVNULL_SET_PRIV(th, SEEIDLE);
4575                     reply("OSMSG_DEVNULL_SET_DONE");
4576                 } else {
4577                     reply("OSMSG_DEVNULL_SET_INVALID", argv[offset+3]);
4578                 }
4579             } else if(!strcmp("FORCEIDLEHIDE",argv[offset+2])) {
4580                 if (!strcmp("OFF",argv[offset+3]) || !strcmp("0",argv[offset+3])) {
4581                     DEVNULL_CLEAR_PRIV(th, FORCEIDLEHIDE);
4582                     reply("OSMSG_DEVNULL_SET_DONE");
4583                 } else if (!strcmp("ON",argv[offset+3]) || !strcmp("1",argv[offset+3])) {
4584                     DEVNULL_SET_PRIV(th, FORCEIDLEHIDE);
4585                     reply("OSMSG_DEVNULL_SET_DONE");
4586                 } else {
4587                     reply("OSMSG_DEVNULL_SET_INVALID", argv[offset+3]);
4588                 }
4589             } else if(!strcmp("OVERRIDECC",argv[offset+2])) {
4590                 if (!strcmp("OFF",argv[offset+3]) || !strcmp("0",argv[offset+3])) {
4591                     DEVNULL_CLEAR_PRIV(th, OVERRIDECC);
4592                     reply("OSMSG_DEVNULL_SET_DONE");
4593                 } else if (!strcmp("ON",argv[offset+3]) || !strcmp("1",argv[offset+3])) {
4594                     DEVNULL_SET_PRIV(th, OVERRIDECC);
4595                     reply("OSMSG_DEVNULL_SET_DONE");
4596                 } else {
4597                     reply("OSMSG_DEVNULL_SET_INVALID", argv[offset+3]);
4598                 }
4599             } else if(!strcmp("OVERRIDENOAMSG",argv[offset+2])) {
4600                 if (!strcmp("OFF",argv[offset+3]) || !strcmp("0",argv[offset+3])) {
4601                     DEVNULL_CLEAR_PRIV(th, OVERRIDENOAMSG);
4602                     reply("OSMSG_DEVNULL_SET_DONE");
4603                 } else if (!strcmp("ON",argv[offset+3]) || !strcmp("1",argv[offset+3])) {
4604                     DEVNULL_SET_PRIV(th, OVERRIDENOAMSG);
4605                     reply("OSMSG_DEVNULL_SET_DONE");
4606                 } else {
4607                     reply("OSMSG_DEVNULL_SET_INVALID", argv[offset+3]);
4608                 }
4609             } else if(!strcmp("MAXSENDQ",argv[offset+2])) {
4610                 if (!strcmp("OFF",argv[offset+3]) || !strcmp("0",argv[offset+3])) {
4611                     DEVNULL_CLEAR_PRIV(th, MAXSENDQ);
4612                     reply("OSMSG_DEVNULL_SET_DONE");
4613                 } else {
4614                     DEVNULL_SET_PRIV(th, MAXSENDQ);
4615                     th->maxsendq = strtoul(argv[offset+3], NULL, 0);
4616                     reply("OSMSG_DEVNULL_SET_DONE");
4617                 }
4618             } 
4619             else if(!strcmp("OPME",argv[offset+2])) {
4620                 if (!strcmp("OFF",argv[offset+3]) || !strcmp("0",argv[offset+3])) {
4621                     DEVNULL_CLEAR_PRIV(th, OPME);
4622                     reply("OSMSG_DEVNULL_SET_DONE");
4623                 } else if (!strcmp("ON",argv[offset+3]) || !strcmp("1",argv[offset+3])) {
4624                     DEVNULL_SET_PRIV(th, OPME);
4625                     reply("OSMSG_DEVNULL_SET_DONE");
4626                 } else {
4627                     reply("OSMSG_DEVNULL_SET_INVALID", argv[offset+3]);
4628                 }
4629             } else {
4630                 reply("OSMSG_DEVNULL_SET_INVALID", argv[offset+2]);
4631             }
4632             
4633         } else {
4634             reply("OSMSG_DEVNULL_SET", th->name);
4635             if(DEVNULL_HAS_PRIV(th, CHANLIMIT)) {
4636                 reply("OSMSG_DEVNULL_SET_A_i", th->maxchan);
4637             } else {
4638                 reply("OSMSG_DEVNULL_SET_A", "off");
4639             }
4640             if(DEVNULL_HAS_PRIV(th, UNLIMITTARGET)) {
4641                 reply("OSMSG_DEVNULL_SET_B", "on");
4642             } else {
4643                 reply("OSMSG_DEVNULL_SET_B", "off");
4644             }
4645             if(DEVNULL_HAS_PRIV(th, NOFLOOD)) {
4646                 reply("OSMSG_DEVNULL_SET_C", "on");
4647             } else if(DEVNULL_HAS_PRIV(th, HALFFLOOD)) {
4648                 reply("OSMSG_DEVNULL_SET_C", "half");
4649             } else {
4650                 reply("OSMSG_DEVNULL_SET_C", "off");
4651             }
4652             if(DEVNULL_HAS_PRIV(th, CHANHIDE)) {
4653                 reply("OSMSG_DEVNULL_SET_E", "on");
4654             } else {
4655                 reply("OSMSG_DEVNULL_SET_E", "off");
4656             }
4657             if(DEVNULL_HAS_PRIV(th, IDLEHIDE)) {
4658                 reply("OSMSG_DEVNULL_SET_F", "on");
4659             } else {
4660                 reply("OSMSG_DEVNULL_SET_F", "off");
4661             }
4662             if(DEVNULL_HAS_PRIV(th, CHSERVMODE)) {
4663                 reply("OSMSG_DEVNULL_SET_G", "on");
4664             } else {
4665                 reply("OSMSG_DEVNULL_SET_G", "off");
4666             }
4667             if(DEVNULL_HAS_PRIV(th, XTRAOPMODE)) {
4668                 reply("OSMSG_DEVNULL_SET_H", "on");
4669             } else {
4670                 reply("OSMSG_DEVNULL_SET_H", "off");
4671             }
4672             if(DEVNULL_HAS_PRIV(th, NETSERVMODE)) {
4673                 reply("OSMSG_DEVNULL_SET_I", "on");
4674             } else {
4675                 reply("OSMSG_DEVNULL_SET_I", "off");
4676             }
4677             if(DEVNULL_HAS_PRIV(th, SEEIDLE)) {
4678                 reply("OSMSG_DEVNULL_SET_J", "on");
4679             } else {
4680                 reply("OSMSG_DEVNULL_SET_J", "off");
4681             }
4682             if(DEVNULL_HAS_PRIV(th, FORCEIDLEHIDE)) {
4683                 reply("OSMSG_DEVNULL_SET_K", "on");
4684             } else {
4685                 reply("OSMSG_DEVNULL_SET_K", "off");
4686             }
4687             if(DEVNULL_HAS_PRIV(th, OVERRIDECC)) {
4688                 reply("OSMSG_DEVNULL_SET_L", "on");
4689             } else {
4690                 reply("OSMSG_DEVNULL_SET_L", "off");
4691             }
4692             if(DEVNULL_HAS_PRIV(th, OVERRIDENOAMSG)) {
4693                 reply("OSMSG_DEVNULL_SET_M", "on");
4694             } else {
4695                 reply("OSMSG_DEVNULL_SET_M", "off");
4696             }
4697             if(DEVNULL_HAS_PRIV(th, MAXSENDQ)) {
4698                 reply("OSMSG_DEVNULL_SET_N_i", th->maxsendq);
4699             } else {
4700                 reply("OSMSG_DEVNULL_SET_N", "off");
4701             }
4702             if(DEVNULL_HAS_PRIV(th, OPME)) {
4703                 reply("OSMSG_DEVNULL_SET_OPME", "on");
4704             } else {
4705                 reply("OSMSG_DEVNULL_SET_OPME", "off");
4706             }
4707         }
4708     } else {
4709         reply("OSMSG_DEVNULL_NOTFOUND", argv[offset+1]);
4710         return 0;
4711     }
4712     return 1;
4713 }
4714
4715 int devnull_check_name(const char *name) { 
4716     if (dict_find(opserv_devnull_classes, name, NULL)) {
4717         return 1;
4718     }
4719     return 0;
4720 }
4721
4722 int devnull_user_has_priv(struct handle_info *hi, int devnull_priv) {
4723     if(devnull_check_priv_func) {
4724         int ext_privs = ((struct devnull_class (*)(struct handle_info *))devnull_check_priv_func)(hi, devnull_priv);
4725         if((ext_privs & devnull_priv))
4726             return (ext_privs & devnull_priv);
4727     }
4728     
4729     if(!hi->devnull)
4730         return 0;
4731     struct devnull_class *th;
4732     if(!(th = dict_find(opserv_devnull_classes, hi->devnull, NULL)))
4733         return 0;
4734     
4735     return (th->modes & devnull_priv);
4736 }
4737
4738 struct devnull_class devnull_user_get_class(struct handle_info *hi) {
4739     struct devnull_class th;
4740     th.name = NULL;
4741     th.modes = 0;
4742     
4743     if(hi->devnull) {
4744         struct devnull_class *thp;
4745         if((thp = dict_find(opserv_devnull_classes, hi->devnull, NULL))) {
4746             if(DEVNULL_HAS_PRIV(thp, CHANLIMIT) && (!DEVNULL_HAS_PRIV(&th, CHANLIMIT) || th.maxchan < thp->maxchan))
4747                 th.maxchan = thp->maxchan;
4748             if(DEVNULL_HAS_PRIV(thp, MAXSENDQ) && (!DEVNULL_HAS_PRIV(&th, MAXSENDQ) || th.maxsendq < thp->maxsendq))
4749                 th.maxsendq = thp->maxsendq;
4750             th.modes |= thp->maxsendq;
4751             th.name = thp->name;
4752         }
4753     }
4754     return th;
4755 }
4756
4757 void operpart(struct chanNode *chan, struct userNode *user)
4758 {
4759     if(opserv_conf.alert_channel && opserv_conf.staff_auth_force > 0 &&
4760       !(irccasecmp(chan->name,opserv_conf.alert_channel->name))) {
4761         struct mod_chanmode *change;
4762         change = find_matching_bans(&chan->banlist, user, NULL); //don't join them if they're banned (exceptions from forced join)
4763         if(change)
4764             return;
4765         irc_svsjoin(opserv,user,chan);
4766     }
4767 }
4768
4769 void operadd(struct userNode *user)
4770 {
4771     if(opserv_conf.alert_channel && opserv_conf.staff_auth_force > 0)
4772         irc_svsjoin(opserv,user,opserv_conf.alert_channel);
4773 }
4774
4775 void operdel(struct userNode *user)
4776 {
4777     if(opserv_conf.alert_channel && opserv_conf.staff_auth_force == 2)
4778         irc_kick(opserv, user, opserv_conf.alert_channel, "mode -o");
4779 }
4780
4781 static MODCMD_FUNC(cmd_svsjoin)
4782 {
4783     struct userNode *target;
4784     if(!(target=GetUserH(argv[1]))) {
4785         reply("OSMSG_SVSNONICK", argv[1]);
4786         return 0;
4787     }
4788     if(!IsChannelName(argv[2]))
4789     {
4790         reply("MSG_NOT_CHANNEL_NAME");
4791         return 0;
4792     }
4793     irc_svsjoinchan(opserv,target,argv[2]);
4794     reply("OSMSG_SVSJOIN",target->nick,argv[2]);
4795     return 1;
4796 }
4797
4798 static MODCMD_FUNC(cmd_svspart)
4799 {
4800     struct userNode *target;
4801     if(!(target=GetUserH(argv[1]))) {
4802         reply("OSMSG_SVSNONICK", argv[1]);
4803         return 0;
4804     }
4805     if(!IsChannelName(argv[2]))
4806     {
4807         reply("MSG_NOT_CHANNEL_NAME");
4808         return 0;
4809     }
4810     irc_svspartchan(opserv,target,argv[2]);
4811     reply("OSMSG_SVSPART",target->nick,argv[2]);
4812     return 1;
4813 }
4814
4815 static MODCMD_FUNC(cmd_svskill)
4816 {
4817     struct userNode *target;
4818     if(!(target=GetUserH(argv[1]))) {
4819         reply("OSMSG_SVSNONICK", argv[1]);
4820         return 0;
4821     }
4822     DelUser(target, opserv, 1, argv[2]);
4823     reply("OSMSG_SVSKILL",target->nick,argv[2]);
4824     return 1;
4825 }
4826 static MODCMD_FUNC(cmd_svsnick)
4827 {
4828     struct userNode *target;
4829     if(!(target=GetUserH(argv[1]))) {
4830         reply("OSMSG_SVSNONICK", argv[1]);
4831         return 0;
4832     }
4833     if(GetUserH(argv[2]))
4834     {
4835         reply("OSMSG_SVSNICKUSED",argv[2]);
4836         return 0;
4837     }
4838     irc_svsnick(opserv,target,argv[2]);
4839     reply("OSMSG_SVSNICK",target->nick,argv[2]);
4840     return 1;
4841 }
4842
4843 static MODCMD_FUNC(cmd_svsmode)
4844 {
4845     struct userNode *target;
4846     char *modestr;
4847     if(!(target=GetUserH(argv[1]))) {
4848         reply("OSMSG_SVSNONICK", argv[1]);
4849         return 0;
4850     }
4851     modestr = unsplit_string(argv + 2, argc - 2, NULL);
4852     irc_svsmode(opserv,target,modestr);
4853     reply("OSMSG_SVSMODE",modestr,target->nick);
4854     return 1;
4855 }
4856
4857 static MODCMD_FUNC(cmd_simul)
4858 {
4859     struct userNode *target;
4860     char *line;
4861     if(argc > 2) {
4862                 if(!(target=GetUserH(argv[1]))) {
4863                         reply("OSMSG_SVSNONICK", argv[1]);
4864                         return 0;
4865                 }
4866                 line = unsplit_string(argv + 2, argc - 2, NULL);
4867                 irc_simul(target,line);
4868                 reply("OSMSG_SIMUL",target->nick,line);
4869                 return 1;
4870     }
4871     return 0;
4872 }
4873
4874 static MODCMD_FUNC(cmd_relay)
4875 {
4876     struct userNode *target;
4877     char *line;
4878     if(!(target=GetUserH(argv[1]))) {
4879         reply("OSMSG_SVSNONICK", argv[1]);
4880         return 0;
4881     }
4882     line = unsplit_string(argv + 2, argc - 2, NULL);
4883     char sendline[512];
4884     if(channel)
4885         sprintf(sendline, "relay %s %s :%s",user->nick,channel->name,line);
4886     else
4887         sprintf(sendline, "relay %s query :%s",user->nick,line);
4888     irc_privmsg(opserv,target->numeric,sendline);
4889     return 1;
4890 }
4891
4892 static void
4893 opserv_conf_read(void)
4894 {
4895     struct record_data *rd;
4896     dict_t conf_node, child;
4897     const char *str, *str2;
4898     struct policer_params *pp;
4899     dict_iterator_t it;
4900
4901     rd = conf_get_node(OPSERV_CONF_NAME);
4902     if (!rd || rd->type != RECDB_OBJECT) {
4903         log_module(OS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", OPSERV_CONF_NAME);
4904         return;
4905     }
4906     conf_node = rd->d.object;
4907     str = database_get_data(conf_node, KEY_DEBUG_CHANNEL, RECDB_QSTRING);
4908     if (opserv && str) {
4909         str2 = database_get_data(conf_node, KEY_DEBUG_CHANNEL_MODES, RECDB_QSTRING);
4910         if (!str2)
4911             str2 = "+tinms";
4912         opserv_conf.debug_channel = AddChannel(str, now, str2, NULL);
4913         AddChannelUser(opserv, opserv_conf.debug_channel)->modes |= MODE_CHANOP;
4914     } else {
4915         opserv_conf.debug_channel = NULL;
4916     }
4917     str = database_get_data(conf_node, KEY_ALERT_CHANNEL, RECDB_QSTRING);
4918     if (opserv && str) {
4919         str2 = database_get_data(conf_node, KEY_ALERT_CHANNEL_MODES, RECDB_QSTRING);
4920         if (!str2)
4921             str2 = "+tns";
4922         opserv_conf.alert_channel = AddChannel(str, now, str2, NULL);
4923         AddChannelUser(opserv, opserv_conf.alert_channel)->modes |= MODE_CHANOP;
4924     } else {
4925         opserv_conf.alert_channel = NULL;
4926     }
4927     str = database_get_data(conf_node, KEY_STAFF_AUTH_CHANNEL, RECDB_QSTRING);
4928     if (opserv && str) {
4929         str2 = database_get_data(conf_node, KEY_STAFF_AUTH_CHANNEL_MODES, RECDB_QSTRING);
4930         if (!str2)
4931             str2 = "+timns";
4932         opserv_conf.staff_auth_channel = AddChannel(str, now, str2, NULL);
4933         AddChannelUser(opserv, opserv_conf.staff_auth_channel)->modes |= MODE_CHANOP;
4934         str2 = database_get_data(conf_node, KEY_STAFF_AUTH_FORCE_OPS, RECDB_QSTRING);
4935         opserv_conf.staff_auth_force = str2 ? atoi(str2) : 0;
4936     } else {
4937         opserv_conf.staff_auth_channel = NULL;
4938         opserv_conf.staff_auth_force = 0;
4939     }
4940     str = database_get_data(conf_node, KEY_UNTRUSTED_MAX, RECDB_QSTRING);
4941     opserv_conf.untrusted_max = str ? strtoul(str, NULL, 0) : 5;
4942     str = database_get_data(conf_node, KEY_PURGE_LOCK_DELAY, RECDB_QSTRING);
4943     opserv_conf.purge_lock_delay = str ? strtoul(str, NULL, 0) : 60;
4944     str = database_get_data(conf_node, KEY_JOIN_FLOOD_MODERATE, RECDB_QSTRING);
4945     opserv_conf.join_flood_moderate = str ? strtoul(str, NULL, 0) : 1;
4946     str = database_get_data(conf_node, KEY_JOIN_FLOOD_MODERATE_THRESH, RECDB_QSTRING);
4947     opserv_conf.join_flood_moderate_threshold = str ? strtoul(str, NULL, 0) : 50;
4948     str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
4949     if (opserv && str)
4950         NickChange(opserv, str, 0);
4951     str = database_get_data(conf_node, KEY_CLONE_GLINE_DURATION, RECDB_QSTRING);
4952     opserv_conf.clone_gline_duration = str ? ParseInterval(str) : 3600;
4953     str = database_get_data(conf_node, KEY_BLOCK_GLINE_DURATION, RECDB_QSTRING);
4954     opserv_conf.block_gline_duration = str ? ParseInterval(str) : 3600;
4955
4956     if (!opserv_conf.join_policer_params)
4957         opserv_conf.join_policer_params = policer_params_new();
4958     policer_params_set(opserv_conf.join_policer_params, "size", "20");
4959     policer_params_set(opserv_conf.join_policer_params, "drain-rate", "1");
4960     if ((child = database_get_data(conf_node, KEY_JOIN_POLICER, RECDB_OBJECT)))
4961         dict_foreach(child, set_policer_param, opserv_conf.join_policer_params);
4962
4963     for (it = dict_first(channels); it; it = iter_next(it)) {
4964         struct chanNode *cNode = iter_data(it);
4965         cNode->join_policer.params = opserv_conf.join_policer_params;
4966     }
4967
4968     if (opserv_conf.new_user_policer.params)
4969         pp = opserv_conf.new_user_policer.params;
4970     else
4971         pp = opserv_conf.new_user_policer.params = policer_params_new();
4972     policer_params_set(pp, "size", "200");
4973     policer_params_set(pp, "drain-rate", "3");
4974     if ((child = database_get_data(conf_node, KEY_NEW_USER_POLICER, RECDB_OBJECT)))
4975         dict_foreach(child, set_policer_param, pp);
4976 }
4977
4978 static void
4979 opserv_db_init(void) {
4980     /* set up opserv_trusted_hosts dict */
4981     dict_delete(opserv_trusted_hosts);
4982     opserv_trusted_hosts = dict_new();
4983     dict_set_free_data(opserv_trusted_hosts, free_trusted_host);
4984     /* set up opserv_devnull_classes dict */
4985     dict_delete(opserv_devnull_classes);
4986     opserv_devnull_classes = dict_new();
4987     dict_set_free_data(opserv_devnull_classes, free_devnull_class);
4988     /* set up opserv_chan_warn dict */
4989     dict_delete(opserv_chan_warn);
4990     opserv_chan_warn = dict_new();
4991     dict_set_free_keys(opserv_chan_warn, free);
4992     dict_set_free_data(opserv_chan_warn, free);
4993     /* set up opserv_user_alerts */
4994     dict_delete(opserv_account_alerts);
4995     opserv_account_alerts = dict_new();
4996     dict_delete(opserv_channel_alerts);
4997     opserv_channel_alerts = dict_new();
4998     dict_delete(opserv_nick_based_alerts);
4999     opserv_nick_based_alerts = dict_new();
5000     dict_delete(opserv_user_alerts);
5001     opserv_user_alerts = dict_new();
5002     dict_set_free_keys(opserv_user_alerts, free);
5003     dict_set_free_data(opserv_user_alerts, opserv_free_user_alert);
5004     /* set up opserv_bad_words */
5005     free_string_list(opserv_bad_words);
5006     opserv_bad_words = alloc_string_list(4);
5007     /* and opserv_exempt_channels */
5008     dict_delete(opserv_exempt_channels);
5009     opserv_exempt_channels = dict_new();
5010     dict_set_free_keys(opserv_exempt_channels, free);
5011 }
5012
5013 static void
5014 opserv_db_cleanup(void)
5015 {
5016     unsigned int nn;
5017
5018     dict_delete(opserv_chan_warn);
5019     dict_delete(opserv_reserved_nick_dict);
5020     free_string_list(opserv_bad_words);
5021     dict_delete(opserv_exempt_channels);
5022     dict_delete(opserv_trusted_hosts);
5023     dict_delete(opserv_devnull_classes);
5024     unreg_del_user_func(opserv_user_cleanup);
5025     dict_delete(opserv_hostinfo_dict);
5026     dict_delete(opserv_nick_based_alerts);
5027     dict_delete(opserv_account_alerts);
5028     dict_delete(opserv_channel_alerts);
5029     dict_delete(opserv_user_alerts);
5030     for (nn=0; nn<ArrayLength(level_strings); ++nn)
5031         free(level_strings[nn]);
5032     while (gagList)
5033         gag_free(gagList);
5034     policer_params_delete(opserv_conf.join_policer_params);
5035     policer_params_delete(opserv_conf.new_user_policer.params);
5036 }
5037
5038 void
5039 init_opserv(const char *nick)
5040 {
5041     OS_LOG = log_register_type("OpServ", "file:opserv.log");
5042     if (nick) {
5043         const char *modes = conf_get_data("services/opserv/modes", RECDB_QSTRING);
5044         opserv = AddLocalUser(nick, nick, NULL, "Oper Services", modes);
5045     }
5046     conf_register_reload(opserv_conf_read);
5047
5048     unsigned int i;
5049     /* set up handle_inverse_flags */
5050     memset(devnull_inverse_modes, 0, sizeof(devnull_inverse_modes));
5051     for (i=0; devnull_modes[i]; i++) {
5052         devnull_inverse_modes[(unsigned char)devnull_modes[i]] = i + 1;
5053     }
5054
5055     memset(level_strings, 0, sizeof(level_strings));
5056     opserv_module = module_register("OpServ", OS_LOG, "opserv.help", opserv_help_expand);
5057     opserv_define_func("ACCESS", cmd_access, 0, 0, 0);
5058     opserv_define_func("ADDALERT", cmd_addalert, 800, 0, 4);
5059     opserv_define_func("ADDALERT NOTICE", NULL, 0, 0, 0);
5060     opserv_define_func("ADDALERT GLINE", NULL, 900, 0, 0);
5061     opserv_define_func("ADDALERT KILL", NULL, 900, 0, 0);
5062     opserv_define_func("ADDBAD", cmd_addbad, 800, 0, 2);
5063     opserv_define_func("ADDEXEMPT", cmd_addexempt, 800, 0, 2);
5064     opserv_define_func("ADDTRUST", cmd_addtrust, 800, 0, 5);
5065     opserv_define_func("BAN", cmd_ban, 100, 2, 2);
5066     opserv_define_func("BLOCK", cmd_block, 100, 0, 2);
5067     opserv_define_func("CHANINFO", cmd_chaninfo, 0, 3, 0);
5068     opserv_define_func("CLEARBANS", cmd_clearbans, 300, 2, 0);
5069     opserv_define_func("CLEARMODES", cmd_clearmodes, 400, 2, 0);
5070     opserv_define_func("CLONE", cmd_clone, 999, 0, 3);
5071     opserv_define_func("COLLIDE", cmd_collide, 800, 0, 5);
5072     opserv_define_func("CSEARCH", cmd_csearch, 100, 0, 3);
5073     opserv_define_func("CSEARCH COUNT", cmd_csearch, 0, 0, 0);
5074     opserv_define_func("CSEARCH PRINT", cmd_csearch, 0, 0, 0);
5075     opserv_define_func("DELALERT", cmd_delalert, 800, 0, 2);
5076     opserv_define_func("DELBAD", cmd_delbad, 800, 0, 2);
5077     opserv_define_func("DELEXEMPT", cmd_delexempt, 800, 0, 2);
5078     opserv_define_func("DELTRUST", cmd_deltrust, 800, 0, 2);
5079     opserv_define_func("DEOP", cmd_deop, 100, 2, 2);
5080     opserv_define_func("DEOPALL", cmd_deopall, 400, 2, 0);
5081     opserv_define_func("DEVOICEALL", cmd_devoiceall, 300, 2, 0);
5082     opserv_define_func("DIE", cmd_die, 900, 0, 2);
5083     opserv_define_func("DUMP", cmd_dump, 999, 0, 2);
5084     opserv_define_func("EDITTRUST", cmd_edittrust, 800, 0, 5);
5085     opserv_define_func("GAG", cmd_gag, 600, 0, 4);
5086     opserv_define_func("GLINE", cmd_gline, 600, 0, 4);
5087     opserv_define_func("GSYNC", cmd_gsync, 600, 0, 0);
5088     opserv_define_func("GTRACE", cmd_gtrace, 100, 0, 3);
5089     opserv_define_func("GTRACE COUNT", NULL, 0, 0, 0);
5090     opserv_define_func("GTRACE PRINT", NULL, 0, 0, 0);
5091     opserv_define_func("INVITE", cmd_invite, 100, 2, 0);
5092     opserv_define_func("INVITEME", cmd_inviteme, 100, 0, 0);
5093     opserv_define_func("JOIN", cmd_join, 601, 1, 0);
5094     opserv_define_func("JUMP", cmd_jump, 900, 0, 2);
5095     opserv_define_func("JUPE", cmd_jupe, 900, 0, 4);
5096     opserv_define_func("KICK", cmd_kick, 100, 2, 2);
5097     opserv_define_func("FORCEKICK", cmd_forcekick, 800, 2, 2);
5098     opserv_define_func("KICKALL", cmd_kickall, 400, 2, 0);
5099     opserv_define_func("KICKBAN", cmd_kickban, 100, 2, 2);
5100     opserv_define_func("KICKBANALL", cmd_kickbanall, 450, 2, 0);
5101     opserv_define_func("LOG", cmd_log, 900, 0, 2);
5102     opserv_define_func("MODE", cmd_mode, 100, 2, 2);
5103     opserv_define_func("OP", cmd_op, 100, 2, 2);
5104     opserv_define_func("OPALL", cmd_opall, 400, 2, 0);
5105     opserv_define_func("PART", cmd_part, 601, 2, 0);
5106     opserv_define_func("QUERY", cmd_query, 0, 0, 0);
5107     opserv_define_func("RAW", cmd_raw, 999, 0, 2);
5108     opserv_define_func("RECONNECT", cmd_reconnect, 900, 0, 0);
5109     opserv_define_func("REFRESHG", cmd_refreshg, 600, 0, 0);
5110     opserv_define_func("REHASH", cmd_rehash, 900, 0, 0);
5111     opserv_define_func("REOPEN", cmd_reopen, 900, 0, 0);
5112     opserv_define_func("RESERVE", cmd_reserve, 800, 0, 5);
5113     opserv_define_func("RESTART", cmd_restart, 900, 0, 2);
5114     opserv_define_func("SET", cmd_set, 900, 0, 3);
5115     opserv_define_func("SETTIME", cmd_settime, 901, 0, 0);
5116     opserv_define_func("STATS ALERTS", cmd_stats_alerts, 0, 0, 0);
5117     opserv_define_func("STATS BAD", cmd_stats_bad, 0, 0, 0);
5118     opserv_define_func("STATS GAGS", cmd_stats_gags, 0, 0, 0);
5119     opserv_define_func("STATS GLINES", cmd_stats_glines, 0, 0, 0);
5120     opserv_define_func("STATS LINKS", cmd_stats_links, 0, 0, 0);
5121     opserv_define_func("STATS MAX", cmd_stats_max, 0, 0, 0);
5122     opserv_define_func("STATS NETWORK", cmd_stats_network, 0, 0, 0);
5123     opserv_define_func("STATS NETWORK2", cmd_stats_network2, 0, 0, 0);
5124     opserv_define_func("STATS RESERVED", cmd_stats_reserved, 0, 0, 0);
5125     opserv_define_func("STATS TIMEQ", cmd_stats_timeq, 0, 0, 0);
5126     opserv_define_func("STATS TRUSTED", cmd_stats_trusted, 0, 0, 0);
5127     opserv_define_func("STATS UPLINK", cmd_stats_uplink, 0, 0, 0);
5128     opserv_define_func("STATS UPTIME", cmd_stats_uptime, 0, 0, 0);
5129     opserv_define_func("STATS WARN", cmd_stats_warn, 0, 0, 0);
5130 #if defined(WITH_MALLOC_SRVX) || defined(WITH_MALLOC_SLAB)
5131     opserv_define_func("STATS MEMORY", cmd_stats_memory, 0, 0, 0);
5132 #endif
5133     opserv_define_func("DEVNULL ADD", cmd_adddevnull, 200, 0, 2);
5134     opserv_define_func("DEVNULL DEL", cmd_deldevnull, 200, 0, 2);
5135     opserv_define_func("DEVNULL RENAME", cmd_renamedevnull, 200, 0, 3);
5136     opserv_define_func("DEVNULL SET", cmd_setdevnull, 200, 0, 2);
5137     opserv_define_func("DEVNULL LIST", cmd_listdevnull, 200, 0, 0);
5138     opserv_define_func("SVSJOIN", cmd_svsjoin, 800, 0, 3);
5139     opserv_define_func("SVSPART", cmd_svspart, 800, 0, 3);
5140     opserv_define_func("SVSKILL", cmd_svskill, 800, 0, 3);
5141     opserv_define_func("SVSMODE", cmd_svsmode, 800, 0, 3);
5142     opserv_define_func("SVSNICK", cmd_svsnick, 800, 0, 3);
5143     opserv_define_func("RELAY", cmd_relay, 800, 0, 0);
5144     opserv_define_func("SIMUL", cmd_simul, 999, 0, 3);
5145     opserv_define_func("TRACE", cmd_trace, 100, 0, 3);
5146     opserv_define_func("TRACE PRINT", NULL, 0, 0, 0);
5147     opserv_define_func("TRACE COUNT", NULL, 0, 0, 0);
5148     opserv_define_func("TRACE DOMAINS", NULL, 0, 0, 0);
5149     opserv_define_func("TRACE GLINE", NULL, 600, 0, 0);
5150     opserv_define_func("TRACE GAG", NULL, 600, 0, 0);
5151     opserv_define_func("TRACE KILL", NULL, 600, 0, 0);
5152     opserv_define_func("UNBAN", cmd_unban, 100, 2, 2);
5153     opserv_define_func("UNGAG", cmd_ungag, 600, 0, 2);
5154     opserv_define_func("UNGLINE", cmd_ungline, 600, 0, 2);
5155     modcmd_register(opserv_module, "GTRACE UNGLINE", NULL, 0, 0, "template", "ungline", NULL);
5156     opserv_define_func("UNJUPE", cmd_unjupe, 900, 0, 2);
5157     opserv_define_func("UNRESERVE", cmd_unreserve, 800, 0, 2);
5158     opserv_define_func("UNWARN", cmd_unwarn, 800, 0, 0);
5159     opserv_define_func("VOICEALL", cmd_voiceall, 300, 2, 0);
5160     opserv_define_func("WARN", cmd_warn, 800, 0, 2);
5161     opserv_define_func("WHOIS", cmd_whois, 0, 0, 2);
5162
5163     opserv_reserved_nick_dict = dict_new();
5164     opserv_hostinfo_dict = dict_new();
5165     dict_set_free_keys(opserv_hostinfo_dict, free);
5166     dict_set_free_data(opserv_hostinfo_dict, opserv_free_hostinfo);
5167
5168     reg_new_user_func(opserv_new_user_check);
5169     reg_nick_change_func(opserv_alert_check_nick);
5170     reg_del_user_func(opserv_user_cleanup);
5171     reg_new_channel_func(opserv_channel_check);
5172     reg_del_channel_func(opserv_channel_delete);
5173     reg_join_func(opserv_join_check);
5174     reg_auth_func(opserv_staff_alert);
5175     reg_auth_func(opserv_auth_alert);
5176
5177     opserv_db_init();
5178     saxdb_register("OpServ", opserv_saxdb_read, opserv_saxdb_write);
5179     if (nick)
5180         service_register(opserv)->trigger = '?';
5181
5182     reg_exit_func(opserv_db_cleanup);
5183     message_register_table(msgtab);
5184 }