]> jfr.im git - irc/evilnet/x3.git/blame - src/opserv.c
cleanup and fixes for spamserv and mod-track
[irc/evilnet/x3.git] / src / opserv.c
CommitLineData
d76ed9a9 1/* opserv.c - IRC Operator assistance service
2 * Copyright 2000-2004 srvx Development Team
3 *
83ff05c3 4 * This file is part of x3.
d76ed9a9 5 *
d0f04f71 6 * x3 is free software; you can redistribute it and/or modify
d76ed9a9 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
5aa400d2 21#include "chanserv.h"
d76ed9a9 22#include "conf.h"
47956fc5 23#include "common.h"
d76ed9a9 24#include "gline.h"
25#include "global.h"
26#include "nickserv.h"
27#include "modcmd.h"
ec311f39 28#include "modules.h"
89d871d8 29#include "proto.h"
d76ed9a9 30#include "opserv.h"
31#include "timeq.h"
32#include "saxdb.h"
d914d1cb 33#include "shun.h"
d76ed9a9 34
35305a49 35#include <tre/regex.h>
5b1166fd 36
d76ed9a9 37#ifdef HAVE_SYS_TIMES_H
38#include <sys/times.h>
39#endif
40#ifdef HAVE_NETINET_IN_H
41#include <netinet/in.h>
42#endif
43#ifdef HAVE_ARPA_INET_H
44#include <arpa/inet.h>
45#endif
5b1166fd 46
d76ed9a9 47
48#define OPSERV_CONF_NAME "services/opserv"
49
50#define KEY_ALERT_CHANNEL "alert_channel"
51#define KEY_ALERT_CHANNEL_MODES "alert_channel_modes"
52#define KEY_DEBUG_CHANNEL "debug_channel"
53#define KEY_DEBUG_CHANNEL_MODES "debug_channel_modes"
54#define KEY_UNTRUSTED_MAX "untrusted_max"
55#define KEY_PURGE_LOCK_DELAY "purge_lock_delay"
56#define KEY_JOIN_FLOOD_MODERATE "join_flood_moderate"
57#define KEY_JOIN_FLOOD_MODERATE_THRESH "join_flood_moderate_threshold"
58#define KEY_NICK "nick"
59#define KEY_JOIN_POLICER "join_policer"
60#define KEY_NEW_USER_POLICER "new_user_policer"
7637f48f 61#define KEY_AUTOJOIN_CHANNELS "autojoin_channels"
d76ed9a9 62#define KEY_REASON "reason"
63#define KEY_RESERVES "reserves"
64#define KEY_IDENT "username" /* for compatibility with 1.0 DBs */
65#define KEY_HOSTNAME "hostname"
66#define KEY_DESC "description"
67#define KEY_BAD_WORDS "bad"
68#define KEY_EXEMPT_CHANNELS "exempt"
69#define KEY_SECRET_WORDS "secret"
70#define KEY_TRUSTED_HOSTS "trusted"
71#define KEY_OWNER "owner"
72#define KEY_GAGS "gags"
73#define KEY_ALERTS "alerts"
74#define KEY_REACTION "reaction"
75#define KEY_DISCRIM "discrim"
76#define KEY_WARN "chanwarn"
77#define KEY_MAX "max"
78#define KEY_TIME "time"
79#define KEY_MAX_CLIENTS "max_clients"
80#define KEY_LIMIT "limit"
81#define KEY_EXPIRES "expires"
82#define KEY_STAFF_AUTH_CHANNEL "staff_auth_channel"
83#define KEY_STAFF_AUTH_CHANNEL_MODES "staff_auth_channel_modes"
84#define KEY_CLONE_GLINE_DURATION "clone_gline_duration"
85#define KEY_BLOCK_GLINE_DURATION "block_gline_duration"
d914d1cb 86#define KEY_BLOCK_SHUN_DURATION "block_shun_duration"
d76ed9a9 87#define KEY_ISSUER "issuer"
88#define KEY_ISSUED "issued"
5a1daaab 89#define KEY_ADMIN_LEVEL "admin_level"
1c5f6697 90#define KEY_SILENT_LEVEL "silent_level"
47956fc5 91#define KEY_UPLINK "uplink"
92#define KEY_SECOND "secondaryuplink"
93#define KEY_PORT "port"
94#define KEY_KARMA "karma"
95#define KEY_OFFLINE "offline"
96#define KEY_ROUTINGPLAN "routingplan"
97#define KEY_ROUTINGPLAN_OPTIONS "routingplan_options"
08895577 98#define KEY_DEFCON1 "DefCon1"
99#define KEY_DEFCON2 "DefCon2"
100#define KEY_DEFCON3 "DefCon3"
101#define KEY_DEFCON4 "DefCon4"
102#define KEY_DEFCON_LEVEL "DefConLevel"
103#define KEY_DEFCON_CHANMODES "DefConChanModes"
104#define KEY_DEFCON_SESSION_LIMIT "DefConSessionLimit"
105#define KEY_DEFCON_TIMEOUT "DefConTimeOut"
08895577 106#define KEY_DEFCON_GLOBAL "GlobalOnDefcon"
107#define KEY_DEFCON_GLOBAL_MORE "GlobalOnDefconMore"
108#define KEY_DEFCON_MESSAGE "DefconMessage"
109#define KEY_DEFCON_OFF_MESSAGE "DefConOffMessage"
110#define KEY_DEFCON_GLINE_DURATION "DefConGlineExpire"
111#define KEY_DEFCON_GLINE_REASON "DefConGlineReason"
47956fc5 112
113/* Routing karma values: */
114/* What value we start out with when new servers are added: */
115#define KARMA_DEFAULT 10
116 /* max, min */
117#define KARMA_MAX 10
118#define KARMA_MIN -10
119/* ping out, reduce karma by this much: */
120#define KARMA_PINGOUT -8
121/* read err, reduce karma by this much: */
122#define KARMA_READERROR -5
123/* every 24 hours everyone gets this much added (so we eventually re-try bad servers) */
124#define KARMA_ENTROPE 1
125/* every 24 hours servers linked for 24 hours get an additional ammount: */
126#define KARMA_RELIABLE 1
127/* How often to run entrope and reliable checks */
128#define KARMA_TIMER 86400 /* 1 day */
129
130#define ROUTING_CONNECT_TIMEOUT 30 /* 30 seconds */
d76ed9a9 131
258d1427 132#define IDENT_FORMAT "%s [%s@%s/%s]"
2f61d1d7 133#define IDENT_DATA(user) user->nick, user->ident, user->hostname, irc_ntoa(&user->ip)
258d1427 134#define MAX_CHANNELS_WHOIS 50
d76ed9a9 135#define OSMSG_PART_REASON "%s has no reason."
136#define OSMSG_KICK_REQUESTED "Kick requested by %s."
137#define OSMSG_KILL_REQUESTED "Kill requested by %s."
138#define OSMSG_GAG_REQUESTED "Gag requested by %s."
139
140static const struct message_entry msgtab[] = {
de9510bc 141 { "OSMSG_BAR", "----------------------------------------" },
d76ed9a9 142 { "OSMSG_USER_ACCESS_IS", "$b%s$b (account $b%s$b) has %d access." },
143 { "OSMSG_LEVEL_TOO_LOW", "You lack sufficient access to use this command." },
144 { "OSMSG_NEED_CHANNEL", "You must specify a channel for $b%s$b." },
145 { "OSMSG_INVALID_IRCMASK", "$b%s$b is an invalid IRC hostmask." },
146 { "OSMSG_ADDED_BAN", "I have banned $b%s$b from $b%s$b." },
d914d1cb 147 { "OSMSG_SHUN_ISSUED", "Shun issued for $b%s$b." },
148 { "OSMSG_SHUN_REMOVED", "Shun removed for $b%s$b." },
149 { "OSMSG_SHUN_FORCE_REMOVED", "Unknown/expired Shun removed for $b%s$b." },
150 { "OSMSG_SHUN_ONE_REFRESHED", "All Shuns resent to $b%s$b." },
151 { "OSMSG_SHUN_REFRESHED", "All Shuns refreshed." },
d76ed9a9 152 { "OSMSG_GLINE_ISSUED", "G-line issued for $b%s$b." },
153 { "OSMSG_GLINE_REMOVED", "G-line removed for $b%s$b." },
154 { "OSMSG_GLINE_FORCE_REMOVED", "Unknown/expired G-line removed for $b%s$b." },
155 { "OSMSG_GLINES_ONE_REFRESHED", "All G-lines resent to $b%s$b." },
156 { "OSMSG_GLINES_REFRESHED", "All G-lines refreshed." },
157 { "OSMSG_CLEARBANS_DONE", "Cleared all bans from channel $b%s$b." },
158 { "OSMSG_CLEARMODES_DONE", "Cleared all modes from channel $b%s$b." },
159 { "OSMSG_NO_CHANNEL_MODES", "Channel $b%s$b had no modes to clear." },
160 { "OSMSG_DEOP_DONE", "Deopped the requested lusers." },
161 { "OSMSG_DEOPALL_DONE", "Deopped everyone on $b%s$b." },
55342ce8 162 { "OSMSG_DEHOP_DONE", "Dehalfopped the requested lusers." },
163 { "OSMSG_DEHOPALL_DONE", "Dehalfopped everyone on $b%s$b." },
d76ed9a9 164 { "OSMSG_NO_DEBUG_CHANNEL", "No debug channel has been configured." },
165 { "OSMSG_INVITE_DONE", "Invited $b%s$b to $b%s$b." },
166 { "OSMSG_ALREADY_THERE", "You are already in $b%s$b." },
39c1a4ef 167 { "OSMSG_NOT_THERE", "You not in $b%s$b." },
d76ed9a9 168 { "OSMSG_JOIN_DONE", "I have joined $b%s$b." },
d82cf2f0 169 { "OSMSG_MARK_SET", "Set the MARK." },
c408f18a 170 { "OSMSG_SVSJOIN_SENT", "Sent the SVSJOIN." },
39c1a4ef 171 { "OSMSG_SVSPART_SENT", "Sent the SVSPART." },
d76ed9a9 172 { "OSMSG_ALREADY_JOINED", "I am already in $b%s$b." },
173 { "OSMSG_NOT_ON_CHANNEL", "$b%s$b does not seem to be on $b%s$b." },
174 { "OSMSG_KICKALL_DONE", "I have cleared out %s." },
175 { "OSMSG_LEAVING", "Leaving $b%s$b." },
d82cf2f0 176 { "OSMSG_MARK_INVALID", "Sorry, marks must contain only letters, numbers, and dashes ('-')." },
d76ed9a9 177 { "OSMSG_MODE_SET", "I have set the modes for $b%s$b." },
178 { "OSMSG_OP_DONE", "Opped the requested lusers." },
179 { "OSMSG_OPALL_DONE", "Opped everyone on $b%s$b." },
55342ce8 180 { "OSMSG_HOP_DONE", "Halfopped the requested lusers." },
181 { "OSMSG_HOPALL_DONE", "Halfopped everyone on $b%s$b." },
37ef8ee3 182 { "OSMSG_WHOIS_IDENT", "%s (%s@%s) from %d.%d.%d.%d" },
21f6caee 183 { "OSMSG_WHOIS_NICK", "Nick : %s" },
184 { "OSMSG_WHOIS_HOST", "Host : %s@%s" },
185 { "OSMSG_WHOIS_FAKEHOST", "Fakehost : %s" },
186 { "OSMSG_WHOIS_CRYPT_HOST", "Crypt Host : %s" },
187 { "OSMSG_WHOIS_CRYPT_IP", "Crypt IP : %s" },
188 { "OSMSG_WHOIS_IP", "Real IP : %s" },
189 { "OSMSG_WHOIS_COUNTRY", "Country : %s" },
190 { "OSMSG_WHOIS_COUNTRY_CODE","Country Code : %s" },
191 { "OSMSG_WHOIS_CITY", "City : %s" },
192 { "OSMSG_WHOIS_REGION", "Region/State : %s" },
193 { "OSMSG_WHOIS_POSTAL_CODE","Postal Code : %s" },
194 { "OSMSG_WHOIS_LATITUDE", "Latitude : %f" },
195 { "OSMSG_WHOIS_LONGITUDE", "Longitude : %f" },
1ad7ac15 196 { "OSMSG_WHOIS_MAP", "Map : %s" },
21f6caee 197 { "OSMSG_WHOIS_DMA_CODE", "DMA Code : %d" },
198 { "OSMSG_WHOIS_AREA_CODE", "Area Code : %d" },
199 { "OSMSG_WHOIS_MODES", "Modes : +%s " },
200 { "OSMSG_WHOIS_INFO", "Info : %s" },
201 { "OSMSG_WHOIS_NUMERIC", "Numnick : %s" },
202 { "OSMSG_WHOIS_SERVER", "Server : %s" },
203 { "OSMSG_WHOIS_NICK_AGE", "Nick Age : %s" },
204 { "OSMSG_WHOIS_ACCOUNT", "Account : %s" },
89d871d8 205 { "OSMSG_WHOIS_PRIVS", "IRCd Privs : %s" },
21f6caee 206 { "OSMSG_WHOIS_CHANNELS", "Channels : %s" },
37ef8ee3 207 { "OSMSG_WHOIS_HIDECHANS", "Channel list omitted for your sanity." },
0e08a8e0 208 { "OSMSG_WHOIS_VERSION", "Version : %s" },
da5b7dfc 209 { "OSMSG_WHOIS_NO_NOTICE", "No_notices : %s" },
d76ed9a9 210 { "OSMSG_UNBAN_DONE", "Ban(s) removed from channel %s." },
211 { "OSMSG_CHANNEL_VOICED", "All users on %s voiced." },
212 { "OSMSG_CHANNEL_DEVOICED", "All voiced users on %s de-voiced." },
213 { "OSMSG_BAD_MODIFIER", "Unknown bad-word modifier $b%s$b." },
214 { "OSMSG_BAD_REDUNDANT", "$b%s$b is already covered by a bad word ($b%s$b)." },
215 { "OSMSG_BAD_GROWING", "Replacing bad word $b%s$b with shorter bad word $b%s$b." },
216 { "OSMSG_BAD_NUKING", " .. and removing redundant bad word $b%s$b." },
217 { "OSMSG_ADDED_BAD", "Added $b%s$b to the bad-word list." },
218 { "OSMSG_REMOVED_BAD", "Removed $b%s$b from the bad-word list." },
219 { "OSMSG_NOT_BAD_WORD", "$b%s$b is not a bad word." },
220 { "OSMSG_ADDED_EXEMPTION", "Added $b%s$b to the bad-word exemption list." },
221 { "OSMSG_ADDED_EXEMPTIONS", "Added %d exception(s) to the bad word list." },
222 { "OSMSG_REMOVED_EXEMPTION", "Removed $b%s$b from the exemption list." },
223 { "OSMSG_NOT_EXEMPT", "$b%s$b is not on the exempt list." },
224 { "OSMSG_ALREADY_TRUSTED", "Host $b%s$b is already trusted (use $bdeltrust$b and then $baddtrust$b to adjust)." },
225 { "OSMSG_NOT_TRUSTED", "Host $b%s$b is not trusted." },
226 { "OSMSG_BAD_IP", "$b%s$b is not a valid IP address" },
227 { "OSMSG_BAD_NUMBER", "$b%s$b is not a number" },
228 { "OSMSG_ADDED_TRUSTED", "Added trusted hosts to the trusted-hosts list." },
229 { "OSMSG_UPDATED_TRUSTED", "Updated trusted host $b%s$b." },
230 { "OSMSG_REMOVED_TRUSTED", "Removed trusted hosts from the trusted-hosts list." },
231 { "OSMSG_CLONE_EXISTS", "Nick $b%s$b is already in use." },
232 { "OSMSG_NOT_A_HOSTMASK", "The hostmask must be in user@host form." },
233 { "OSMSG_BADWORD_LIST", "Bad words: %s" },
234 { "OSMSG_EXEMPTED_LIST", "Exempted channels: %s" },
235 { "OSMSG_GLINE_COUNT", "There are %d glines active on the network." },
d914d1cb 236 { "OSMSG_SHUN_COUNT", "There are %d shuns active on the network." },
d76ed9a9 237 { "OSMSG_LINKS_SERVER", "%s%s (%u clients; %s)" },
238 { "OSMSG_MAX_CLIENTS", "Max clients: %d at %s" },
239 { "OSMSG_NETWORK_INFO", "Total users: %d (%d invisible, %d opers)" },
240 { "OSMSG_RESERVED_LIST", "List of reserved nicks:" },
de9510bc 241 { "OSMSG_TRUSTED_LIST", "$bTrusted Hosts$b" },
242 { "OSMSG_TRUSTED_LIST_HEADER", "IP Address Limit By Time" },
243 { "OSMSG_HOST_IS_TRUSTED", "%-15s %-5s %-10s set %s ago, expires %s" },
244 { "OSMSG_HOST_IS_TRUSTED_DESC", " Reason: %s" },
245 { "OSMSG_TRUSTED_LIST_BAR", "----------------------------------------" },
246 { "OSMSG_TRUSTED_LIST_END", "----------End of Trusted Hosts----------" },
d76ed9a9 247 { "OSMSG_HOST_NOT_TRUSTED", "%s does not have a special trust." },
248 { "OSMSG_UPTIME_STATS", "Uptime: %s (%u lines processed, CPU time %.2fu/%.2fs)" },
249 { "OSMSG_LINE_DUMPED", "Raw line sent." },
250 { "OSMSG_RAW_PARSE_ERROR", "Error parsing raw line (not dumping to uplink)." },
251 { "OSMSG_COLLIDED_NICK", "Now temporarily holding nick $b%s$b." },
252 { "OSMSG_RESERVED_NICK", "Now reserving nick $b%s$b." },
253 { "OSMSG_NICK_UNRESERVED", "Nick $b%s$b is no longer reserved." },
254 { "OSMSG_NOT_RESERVED", "Nick $b%s$b is not reserved." },
255 { "OSMSG_ILLEGAL_REASON", "This channel is illegal." },
256 { "OSMSG_ILLEGAL_KILL_REASON", "Joined an illegal modeless channel - do not repeat." },
257 { "OSMSG_ILLEGAL_CHANNEL", "$b%s$b is an ILLEGAL channel. Do not re-join it." },
258 { "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." },
259 { "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." },
260 { "OSMSG_CLONE_ADDED", "Added clone $b%s$b." },
261 { "OSMSG_CLONE_FAILED", "Unable to add user $b%s$b." },
262 { "OSMSG_NOT_A_CLONE", "Har har. $b%s$b isn't a clone." },
263 { "OSMSG_CLONE_REMOVED", "Removed clone $b%s$b." },
264 { "OSMSG_CLONE_JOINED", "$b%s$b has joined $b%s$b." },
265 { "OSMSG_CLONE_PARTED", "$b%s$b has left $b%s$b." },
266 { "OSMSG_OPS_GIVEN", "I have given ops in $b%s$b to $b%s$b." },
55342ce8 267 { "OSMSG_HOPS_GIVEN", "I have given halfops in $b%s$b to $b%s$b." },
d76ed9a9 268 { "OSMSG_CLONE_SAID", "$b%s$b has spoken to $b%s$b." },
269 { "OSMSG_UNKNOWN_SUBCOMMAND", "$b%s$b is not a valid subcommand of $b%s$b." },
270 { "OSMSG_UNKNOWN_OPTION", "$b%s$b has not been set." },
271 { "OSMSG_OPTION_IS", "$b%s$b is set to $b%s$b." },
272 { "OSMSG_OPTION_ROOT", "The following keys can be queried:" },
273 { "OSMSG_OPTION_LIST", "$b%s$b contains the following values:" },
274 { "OSMSG_OPTION_KEYS", "$b%s$b contains the following keys:" },
275 { "OSMSG_OPTION_LIST_EMPTY", "Empty list." },
276 { "OSMSG_SET_NOT_SET", "$b%s$b does not exist, and cannot be set." },
277 { "OSMSG_SET_BAD_TYPE", "$b%s$b is not a string, and cannot be set." },
278 { "OSMSG_SET_SUCCESS", "$b%s$b has been set to $b%s$b." },
279 { "OSMSG_SETTIME_SUCCESS", "Set time for servers named like $b%s$b." },
280 { "OSMSG_BAD_ACTION", "Unrecognized trace action $b%s$b." },
281 { "OSMSG_USER_SEARCH_RESULTS", "The following users were found:" },
de9510bc 282 { "OSMSG_USER_SEARCH_HEADER", "Nick User@Host (Account)" },
283 { "OSMSG_USER_SEARCH_BAR", "-------------------------------------------" },
8e11460f 284 { "OSMSG_USER_SEARCH_COUNT", "There were %4u matches" },
285 { "OSMSG_USER_SEARCH_COUNT_BAR", "------------ Found %4u matches -----------" },
d82cf2f0 286 { "OSMSG_MARK_NO_MARK", "MARK action requires mark criteria (what do you want to mark them as?)" },
a62ba70c 287 { "OSMSG_SVSJOIN_NO_TARGET", "SVSJOIN action requires chantarget criteria (where should they join?)" },
39c1a4ef 288 { "OSMSG_SVSPART_NO_TARGET", "SVSPART action requires chantarget criteria (where should they join?)" },
d76ed9a9 289 { "OSMSG_CHANNEL_SEARCH_RESULTS", "The following channels were found:" },
290 { "OSMSG_GLINE_SEARCH_RESULTS", "The following glines were found:" },
d914d1cb 291 { "OSMSG_SHUN_SEARCH_RESULTS", "The following shun were found:" },
d76ed9a9 292 { "OSMSG_LOG_SEARCH_RESULTS", "The following log entries were found:" },
293 { "OSMSG_GSYNC_RUNNING", "Synchronizing glines from %s." },
d914d1cb 294 { "OSMSG_SSYNC_RUNNING", "Synchronizing shuns from %s." },
d76ed9a9 295 { "OSMSG_GTRACE_FORMAT", "%s (issued %s by %s, expires %s): %s" },
d914d1cb 296 { "OSMSG_STRACE_FORMAT", "%s (issued %s by %s, expires %s): %s" },
d76ed9a9 297 { "OSMSG_GAG_APPLIED", "Gagged $b%s$b, affecting %d users." },
298 { "OSMSG_GAG_ADDED", "Gagged $b%s$b." },
299 { "OSMSG_REDUNDANT_GAG", "Gag $b%s$b is redundant." },
300 { "OSMSG_GAG_NOT_FOUND", "Could not find gag $b%s$b." },
301 { "OSMSG_NO_GAGS", "No gags have been set." },
302 { "OSMSG_UNGAG_APPLIED", "Ungagged $b%s$b, affecting %d users." },
303 { "OSMSG_UNGAG_ADDED", "Ungagged $b%s$b." },
304 { "OSMSG_TIMEQ_INFO", "%u events in timeq; next in %lu seconds." },
305 { "OSMSG_ALERT_EXISTS", "An alert named $b%s$b already exists." },
306 { "OSMSG_UNKNOWN_REACTION", "Unknown alert reaction $b%s$b." },
307 { "OSMSG_ADDED_ALERT", "Added alert named $b%s$b." },
a62ba70c 308 { "OSMSG_ALERT_ADD_FAILED", "Unable to add alert. Check syntax, required parts, and access" },
d76ed9a9 309 { "OSMSG_REMOVED_ALERT", "Removed alert named $b%s$b." },
310 { "OSMSG_NO_SUCH_ALERT", "No alert named $b%s$b could be found." },
eaf715f1 311 { "OSMSG_ALERTS_LIST", "$bCurrent $O alerts matching '$b%s$b'$b" },
de9510bc 312 { "OSMSG_ALERTS_BAR", "----------------------------------------------" },
313 { "OSMSG_ALERTS_HEADER", "Name Action (by Oper)" },
314 { "OSMSG_ALERTS_DESC", " Criteria: %s" },
315 { "OSMSG_ALERT_IS", "$b%-20s$b %-6s (by %s)" },
316 { "OSMSG_ALERT_END", "----------------End of Alerts-----------------" },
47956fc5 317 /* routing messages */
5c6bff84 318 { "OSMSG_ROUTINGPLAN", "$bRouting Plan(s)$b" },
319 { "OSMSG_ROUTINGPLAN_LIST_HEAD", "$bRouting Plans$b" },
47956fc5 320 { "OSMSG_ROUTINGPLAN_BAR", "----------------------------------------------" },
5c6bff84 321 { "OSMSG_ROUTINGPLAN_END", "------------End of Routing Plan(s)------------" },
47956fc5 322 { "OSMSG_ROUTINGPLAN_OPTION", "%s is set to %s" },
323 { "OSMSG_ROUTINGPLAN_ACTIVE", "Auto routing is active, using plan '%s'." },
324 { "OSMSG_ROUTING_ACTIVATION_ERROR", "There was an error activating the routing plan. Check for loops, and make sure the map includes my own uplink." },
325 { "OSMSG_ROUTINGPLAN_OPTION_NOT_FOUND", "There is no routing plan option '%s'." },
326 { "OSMSG_ROUTINGPLAN_OPTION_NOT_SET", "Option '%s' is not currently set." },
327 { "OSMSG_ROUTINGPLAN_NAME", "$b%s:$b" },
5c6bff84 328 { "OSMSG_ROUTINGPLAN_LIST", "$b%s$b" },
47956fc5 329 { "OSMSG_ROUTINGPLAN_SERVER"," %s:%d <-- %s[%d/%s] (%s)" },
330 { "OSMSG_ADDPLAN_SUCCESS", "Added new routing plan '%s'." },
ce9266cf 331 { "OSMSG_ADDPLAN_FAILED", "Could not add new plan '%s' (does it already exist?)." },
47956fc5 332 { "OSMSG_INVALID_PLAN", "That routing plan name is not valid." },
333 { "OSMSG_PLAN_DELETED", "The routing plan was sucessfully deleted." },
334 { "OSMSG_PLAN_NOT_FOUND", "There is no routing plan called '%s'." },
335 { "OSMSG_PLAN_SERVER_ADDED", "Added %s to the routing plan." },
336 { "OSMSG_PLAN_SERVER_DELETED", "The server has been deleted." },
337 { "OSMSG_PLAN_SERVER_NOT_FOUND", "The server '%s' was not found in that routing plan." },
338 { "OSMSG_ROUTING_DISABLED", "Routing is now disabled." },
339 { "OSMSG_DOWNLINKS_FORMAT_A", "%s%s-$b%s$b [%s]" },
340 { "OSMSG_DOWNLINKS_FORMAT_B", "$b%s$b (me)" },
341 { "OSMSG_ROUTELIST_EMPTY", "No servers in route list" },
342 { "OSMSG_ROUTELIST_AS_PLANNED", "Routing plan: Servers as they SHOULD be linked" },
343 { "OSMSG_MAP_CENTERED", "map %s centered, Maxdepth:%d" },
344 { "OSMSG_NO_SERVERS_MISSING", "No servers are missing." },
345 { "OSMSG_CONNECTING_MISSING", "Attempted to connect %d missing servers." },
346 { "OSMSG_CONNECT", "->connect %s %d %s" },
347 { "OSMSG_SQUIT", "->squit %s" },
348 { "OSMSG_COULDNT_FIND_SERVER", "Couldnt find %s, so using %s to link %s" },
349 { "OSMSG_INSPECTING_SERVER", "Inspecting server [%s]" },
350 { "OSMSG_REROUTING_ACC_MAP", "Rerouting network according to loaded map.." },
964abe6b 351 { "OSMSG_REROUTING_NOTCONFIGURED", "You have not configured routing. See $/msg $O help routing$b." },
47956fc5 352 { "OSMSG_CONNECTING_MISSING_ONLY", "Connecting missing servers only.." },
353 { "OSMSG_NO_ROUTING_NECESSARY", "No rerouting appears necessary." },
354 { "OSMSG_TESTING_REROUTE", "Testing Reroute(): Commands not sent to socket.." },
355 { "OSMSG_INVALID_DIRECTIVE", "Reroute(): Invalid directive %s", },
356 { "OSMSG_UPLINKS_MISSING", "%d servers' uplinks were missing, and were not connected." },
357 { "OSMSG_REROUTE_COMPLETE", "Reroute complete: Moved %d, connected %d, total %d changes." },
358 /* end of routing */
d76ed9a9 359 { "OSMSG_REHASH_COMPLETE", "Completed rehash of configuration database." },
360 { "OSMSG_REHASH_FAILED", "Rehash of configuration database failed, previous configuration is intact." },
361 { "OSMSG_REOPEN_COMPLETE", "Closed and reopened all log files." },
362 { "OSMSG_RECONNECTING", "Reconnecting to my uplink." },
363 { "OSMSG_NUMERIC_COLLIDE", "Numeric %d (%s) is already in use." },
364 { "OSMSG_NAME_COLLIDE", "That name is already in use." },
365 { "OSMSG_SRV_CREATE_FAILED", "Server creation failed -- check log files." },
366 { "OSMSG_SERVER_JUPED", "Added new jupe server %s." },
df5f6070 367 { "OSMSG_INVALID_NUMERIC", "Invalid numeric" },
368 { "OSMSG_INVALID_SERVERNAME", "Server name must contain a '.'." },
d76ed9a9 369 { "OSMSG_SERVER_NOT_JUPE", "That server is not a juped server." },
370 { "OSMSG_SERVER_UNJUPED", "Server jupe removed." },
c52666c6 371 /*
d76ed9a9 372 { "OSMSG_WARN_ADDED", "Added channel activity warning for $b%s$b (%s)" },
373 { "OSMSG_WARN_EXISTS", "Channel activity warning for $b%s$b already exists." },
374 { "OSMSG_WARN_DELETED", "Removed channel activity warning for $b%s$b" },
375 { "OSMSG_WARN_NOEXIST", "Channel activity warning for $b%s$b does not exist." },
376 { "OSMSG_WARN_LISTSTART", "Channel activity warnings:" },
377 { "OSMSG_WARN_LISTENTRY", "%s (%s)" },
378 { "OSMSG_WARN_LISTEND", "End of activity warning list." },
c52666c6 379 */
d76ed9a9 380 { "OSMSG_UPLINK_CONNECTING", "Establishing connection with %s (%s:%d)." },
381 { "OSMSG_CURRENT_UPLINK", "$b%s$b is already the current uplink." },
382 { "OSMSG_INVALID_UPLINK", "$b%s$b is not a valid uplink name." },
383 { "OSMSG_UPLINK_DISABLED", "$b%s$b is a disabled or unavailable uplink." },
384 { "OSMSG_UPLINK_START", "Uplink $b%s$b:" },
385 { "OSMSG_UPLINK_ADDRESS", "Address: %s:%d" },
386 { "OSMSG_STUPID_GLINE", "Gline %s? Now $bthat$b would be smooth." },
d914d1cb 387 { "OSMSG_STUPID_SHUN", "Shun %s? Now $bthat$b would be smooth." },
d76ed9a9 388 { "OSMSG_ACCOUNTMASK_AUTHED", "Invalid criteria: it is impossible to match an account mask but not be authed" },
389 { "OSMSG_CHANINFO_HEADER", "%s Information" },
390 { "OSMSG_CHANINFO_TIMESTAMP", "Created on: %a %b %d %H:%M:%S %Y (%s)" },
391 { "OSMSG_CHANINFO_MODES", "Modes: %s" },
392 { "OSMSG_CHANINFO_MODES_BADWORD", "Modes: %s; bad-word channel" },
393 { "OSMSG_CHANINFO_TOPIC", "Topic (set by %%s, %a %b %d %H:%M:%S %Y): %%s" },
394 { "OSMSG_CHANINFO_TOPIC_UNKNOWN", "Topic: (none / not gathered)" },
395 { "OSMSG_CHANINFO_BAN_COUNT", "Bans (%d):" },
396 { "OSMSG_CHANINFO_BAN", "%%s by %%s (%a %b %d %H:%M:%S %Y)" },
2aef5f4b 397 { "OSMSG_CHANINFO_EXEMPT_COUNT", "Exempts (%d):" },
398 { "OSMSG_CHANINFO_EXEMPT", "%%s by %%s (%a %b %d %H:%M:%S %Y)" },
d76ed9a9 399 { "OSMSG_CHANINFO_MANY_USERS", "%d users (\"/msg $S %s %s users\" for the list)" },
400 { "OSMSG_CHANINFO_USER_COUNT", "Users (%d):" },
401 { "OSMSG_CSEARCH_CHANNEL_INFO", "%s [%d users] %s %s" },
27eaa617 402 { "OSMSG_INVALID_REGEX", "Invalid regex: %s: %s (%d)" },
ec311f39 403 { "OSMSG_TRACK_DISABLED", "Tracking is not currently compiled into X3" },
7637f48f 404 { "OSMSG_MAXUSERS_RESET", "Max clients has been reset to $b%d$b" },
08895577 405
406 { "OSMSG_DEFCON_INVALID", "DefCon level %d is invalid, please choose a value between 1 and 5" },
407 { "OSMSG_DEFCON_ALLOWING_ALL", "DefCon is at level 5 and allowing everything" },
408 { "OSMSG_DEFCON_DISALLOWING", "DefCon is at level %d and enforcing:" },
409 { "OSMSG_DEFCON_NO_NEW_CHANNELS", "No Channel Registrations" },
410 { "OSMSG_DEFCON_NO_NEW_NICKS", "No Nickname/Account Registrations" },
411 { "OSMSG_DEFCON_NO_MODE_CHANGE", "No Channel Mode Changes" },
412 { "OSMSG_DEFCON_NO_NEW_CLIENTS", "No New Clients" },
413 { "OSMSG_DEFCON_FORCE_CHANMODES", "Forcing Channel Mode(s): %s" },
414 { "OSMSG_DEFCON_REDUCE_SESSION", "Forcing Reduced Session: %d" },
415 { "OSMSG_DEFCON_OPER_ONLY", "Allowing Services Communication With Opers Only" },
416 { "OSMSG_DEFCON_SILENT_OPER_ONLY", "Allowing Services Communication With Opers Only AND Silently Ignoring Regular Users" },
417 { "OSMSG_DEFCON_GLINE_NEW_CLIENTS", "Glining New Clients" },
0272358e 418 { "OSMSG_DEFCON_SHUN_NEW_CLIENTS", "Shunning New Clients" },
08895577 419 { "OSMSG_DEFCON_NO_NEW_MEMOS", "Disallowing New Memos" },
420
6c34bb5a 421 { "OSMSG_PRIV_UNKNOWN", "Unknown privilege flag %s, see /msg $O HELP PRIVFLAGS for a flag list" },
422 { "OSMSG_PRIV_SET", "Privilege flag %s has been %sset" },
423
d76ed9a9 424 { NULL, NULL }
425};
426
b1bf690d 427#define OPSERV_SYNTAX() svccmd_send_help_brief(user, opserv, cmd)
d76ed9a9 428
08895577 429int DefConLevel = 5;
430int DefCon[6];
431int DefConTimeOut;
432int GlobalOnDefcon = 0;
433int GlobalOnDefconMore = 0;
434int DefConGlineExpire;
435int DefConModesSet = 0;
08895577 436unsigned int DefConSessionLimit;
437char *DefConChanModes;
438char *DefConGlineReason;
439char *DefConMessage;
440char *DefConOffMessage;
441
ec311f39 442extern void add_track_user(struct userNode *user);
d76ed9a9 443typedef int (*discrim_search_func)(struct userNode *match, void *extra);
444
445struct userNode *opserv;
258d1427 446static struct service *opserv_service;
d76ed9a9 447
de9510bc 448/*static dict_t opserv_chan_warn; */ /* data is char* */
d76ed9a9 449static dict_t opserv_reserved_nick_dict; /* data is struct userNode* */
450static struct string_list *opserv_bad_words;
451static dict_t opserv_exempt_channels; /* data is not used */
452static dict_t opserv_trusted_hosts; /* data is struct trusted_host* */
47956fc5 453static dict_t opserv_routing_plans; /* data is struct routingPlan */
454static dict_t opserv_routing_plan_options; /* data is a dict_t key->val list*/
455static dict_t opserv_waiting_connections; /* data is struct waitingConnection */
d76ed9a9 456static dict_t opserv_hostinfo_dict; /* data is struct opserv_hostinfo* */
457static dict_t opserv_user_alerts; /* data is struct opserv_user_alert* */
458static dict_t opserv_nick_based_alerts; /* data is struct opserv_user_alert* */
459static dict_t opserv_channel_alerts; /* data is struct opserv_user_alert* */
460static struct module *opserv_module;
461static struct log_type *OS_LOG;
462static unsigned int new_user_flood;
463static char *level_strings[1001];
7637f48f 464struct string_list *autojoin_channels;
47956fc5 465struct route *opserv_route = NULL; /* Main active routing table from activate_routing()*/
d76ed9a9 466
467static struct {
468 struct chanNode *debug_channel;
469 struct chanNode *alert_channel;
470 struct chanNode *staff_auth_channel;
471 struct policer_params *join_policer_params;
472 struct policer new_user_policer;
473 unsigned long untrusted_max;
474 unsigned long clone_gline_duration;
475 unsigned long block_gline_duration;
d914d1cb 476 unsigned long block_shun_duration;
d76ed9a9 477 unsigned long purge_lock_delay;
478 unsigned long join_flood_moderate;
479 unsigned long join_flood_moderate_threshold;
5a1daaab 480 unsigned long admin_level;
1c5f6697 481 unsigned long silent_level;
d76ed9a9 482} opserv_conf;
483
484struct trusted_host {
485 char *ipaddr;
486 char *issuer;
487 char *reason;
488 unsigned long limit;
489 time_t issued;
490 time_t expires;
491};
492
493struct gag_entry {
494 char *mask;
495 char *owner;
496 char *reason;
497 time_t expires;
498 struct gag_entry *next;
499};
500
501static struct gag_entry *gagList;
502
503struct opserv_hostinfo {
504 struct userList clients;
505 struct trusted_host *trusted;
506};
507
508static void
509opserv_free_hostinfo(void *data)
510{
511 struct opserv_hostinfo *ohi = data;
512 userList_clean(&ohi->clients);
513 free(ohi);
514}
515
47956fc5 516static void
517opserv_free_waiting_connection(void *data)
518{
519 struct waitingConnection *wc = data;
520 free(wc->server);
521 free(wc->target);
522 free(wc);
523}
524
d76ed9a9 525typedef struct opservDiscrim {
526 struct chanNode *channel;
d82cf2f0 527 char *mask_nick, *mask_ident, *mask_host, *mask_info, *mask_version, *server, *reason, *accountmask, *chantarget, *mark;
2f61d1d7 528 irc_in_addr_t ip_mask;
529 unsigned long limit;
530 time_t min_ts, max_ts;
0e08a8e0 531 regex_t regex_nick, regex_ident, regex_host, regex_info, regex_version;
532 unsigned int has_regex_nick : 1, has_regex_ident : 1, has_regex_host : 1, has_regex_info : 1, has_regex_version : 1;
d76ed9a9 533 unsigned int min_level, max_level, domain_depth, duration, min_clones, min_channels, max_channels;
2f61d1d7 534 unsigned char ip_mask_bits;
d76ed9a9 535 unsigned int match_opers : 1, option_log : 1;
536 unsigned int chan_req_modes : 2, chan_no_modes : 2;
537 int authed : 2, info_space : 2;
63665495 538 unsigned int intra_scmp : 2, intra_dcmp : 2;
27eaa617 539 unsigned int use_regex : 1;
1c5f6697 540 unsigned int silent : 1;
5aa400d2 541 unsigned int checkrestrictions : 2;
d76ed9a9 542} *discrim_t;
543
544struct discrim_and_source {
545 discrim_t discrim;
546 struct userNode *source;
258d1427 547 struct userNode *destination;
d76ed9a9 548 dict_t dict;
549 unsigned int disp_limit;
550};
551
258d1427 552static discrim_t opserv_discrim_create(struct userNode *user, struct userNode *bot, unsigned int argc, char *argv[], int allow_channel);
d76ed9a9 553static unsigned int opserv_discrim_search(discrim_t discrim, discrim_search_func dsf, void *data);
554static int gag_helper_func(struct userNode *match, void *extra);
555static int ungag_helper_func(struct userNode *match, void *extra);
556
557typedef enum {
558 REACT_NOTICE,
559 REACT_KILL,
d914d1cb 560 REACT_GLINE,
ec311f39 561 REACT_TRACK,
c408f18a 562 REACT_SHUN,
0e08a8e0 563 REACT_SVSJOIN,
39c1a4ef 564 REACT_SVSPART,
d82cf2f0 565 REACT_VERSION,
566 REACT_MARK
d76ed9a9 567} opserv_alert_reaction;
568
569struct opserv_user_alert {
570 char *owner;
571 char *text_discrim, *split_discrim;
572 discrim_t discrim;
573 opserv_alert_reaction reaction;
574};
575
576/* funny type to make it acceptible to dict_set_free_data, far below */
577static void
578opserv_free_user_alert(void *data)
579{
580 struct opserv_user_alert *alert = data;
581 if (alert->discrim->channel)
582 UnlockChannel(alert->discrim->channel);
583 free(alert->owner);
584 free(alert->text_discrim);
585 free(alert->split_discrim);
27eaa617 586 if(alert->discrim->has_regex_nick)
587 regfree(&alert->discrim->regex_nick);
588 if(alert->discrim->has_regex_ident)
589 regfree(&alert->discrim->regex_ident);
590 if(alert->discrim->has_regex_host)
591 regfree(&alert->discrim->regex_host);
592 if(alert->discrim->has_regex_info)
593 regfree(&alert->discrim->regex_info);
0e08a8e0 594 if(alert->discrim->has_regex_version)
595 regfree(&alert->discrim->regex_version);
d76ed9a9 596 free(alert->discrim->reason);
597 free(alert->discrim);
598 free(alert);
599}
600
601#define opserv_debug(format...) do { if (opserv_conf.debug_channel) send_channel_notice(opserv_conf.debug_channel , opserv , ## format); } while (0)
602#define opserv_alert(format...) do { if (opserv_conf.alert_channel) send_channel_notice(opserv_conf.alert_channel , opserv , ## format); } while (0)
603
7637f48f 604
08895577 605char *defconReverseModes(const char *modes)
606{
607 char *newmodes = NULL;
608 unsigned int i = 0;
609 if (!modes) {
610 return NULL;
611 }
612 if (!(newmodes = malloc(sizeof(char) * strlen(modes) + 1))) {
613 return NULL;
614 }
615 for (i = 0; i < strlen(modes); i++) {
616 if (modes[i] == '+')
617 newmodes[i] = '-';
618 else if (modes[i] == '-')
619 newmodes[i] = '+';
620 else
621 newmodes[i] = modes[i];
622 }
623 newmodes[i] = '\0';
624 return newmodes;
625}
626
627int checkDefCon(int level)
628{
629 return DefCon[DefConLevel] & level;
630}
631
632void showDefConSettings(struct userNode *user, struct svccmd *cmd)
633{
634 if (DefConLevel == 5) {
635 reply("OSMSG_DEFCON_ALLOWING_ALL");
636 return;
637 } else
638 reply("OSMSG_DEFCON_DISALLOWING", DefConLevel);
639
640 if (checkDefCon(DEFCON_NO_NEW_CHANNELS))
641 reply("OSMSG_DEFCON_NO_NEW_CHANNELS");
642
643 if (checkDefCon(DEFCON_NO_NEW_NICKS))
644 reply("OSMSG_DEFCON_NO_NEW_NICKS");
645
646 if (checkDefCon(DEFCON_NO_MODE_CHANGE))
647 reply("OSMSG_DEFCON_NO_MODE_CHANGE");
648
649 if (checkDefCon(DEFCON_FORCE_CHAN_MODES) && (DefConChanModes))
650 reply("OSMSG_DEFCON_FORCE_CHANMODES", DefConChanModes);
651
652 if (checkDefCon(DEFCON_REDUCE_SESSION))
653 reply("OSMSG_DEFCON_REDUCE_SESSION", DefConSessionLimit);
654
655 if (checkDefCon(DEFCON_NO_NEW_CLIENTS))
656 reply("OSMSG_DEFCON_NO_NEW_CLIENTS");
657
658 if (checkDefCon(DEFCON_OPER_ONLY))
659 reply("OSMSG_DEFCON_OPER_ONLY");
660
661 if (checkDefCon(DEFCON_SILENT_OPER_ONLY))
662 reply("OSMSG_DEFCON_SILENT_OPER_ONLY");
663
664 if (checkDefCon(DEFCON_GLINE_NEW_CLIENTS))
665 reply("OSMSG_DEFCON_GLINE_NEW_CLIENTS");
666
0272358e 667 if (checkDefCon(DEFCON_SHUN_NEW_CLIENTS))
668 reply("OSMSG_DEFCON_SHUN_NEW_CLIENTS");
669
08895577 670 if (checkDefCon(DEFCON_NO_NEW_MEMOS))
671 reply("OSMSG_DEFCON_NO_NEW_MEMOS");
672
673 return;
674}
675
676void do_mass_mode(char *modes)
677{
678 dict_iterator_t it;
679
680 if (!modes)
681 return;
682
683 for (it = dict_first(channels); it; it = iter_next(it)) {
684 struct chanNode *chan = iter_data(it);
685
686 irc_mode(opserv, chan, modes);
687 }
688
689}
690
691void DefConProcess(struct userNode *user)
692{
693 char *newmodes;
08895577 694
985d4109 695 if (GlobalOnDefcon)
696 global_message_args(MESSAGE_RECIPIENT_LUSERS, "DEFCON_NETWORK_CHANGED", DefConLevel);
08895577 697
985d4109 698 if (GlobalOnDefconMore && GlobalOnDefcon)
699 global_message(MESSAGE_RECIPIENT_LUSERS, DefConMessage);
08895577 700
701 if ((DefConLevel == 5) && !GlobalOnDefconMore && !GlobalOnDefcon)
985d4109 702 global_message(MESSAGE_RECIPIENT_LUSERS, DefConOffMessage);
08895577 703
985d4109 704 if (user)
705 global_message_args(MESSAGE_RECIPIENT_OPERS, "DEFCON_OPER_LEVEL_CHANGE", user->nick, DefConLevel);
706 else
707 global_message_args(MESSAGE_RECIPIENT_OPERS, "DEFCON_TIMEOUT_LEVEL_CHANGE", DefConLevel);
08895577 708
709 if (checkDefCon(DEFCON_FORCE_CHAN_MODES)) {
710 if (DefConChanModes && !DefConModesSet) {
711 if (DefConChanModes[0] == '+' || DefConChanModes[0] == '-') {
712 do_mass_mode(DefConChanModes);
713 DefConModesSet = 1;
714 }
715 }
716 } else {
717 if (DefConChanModes && (DefConModesSet != 0)) {
718 if (DefConChanModes[0] == '+' || DefConChanModes[0] == '-') {
719 if ((newmodes = defconReverseModes(DefConChanModes))) {
720 do_mass_mode(newmodes);
721 free(newmodes);
722 }
723 DefConModesSet = 0;
724 }
725 }
726 }
727
728 return;
729}
730
731void
732defcon_timeout(UNUSED_ARG(void *data))
733{
734 DefConLevel = 5;
735 DefConProcess(NULL);
736}
737
738static MODCMD_FUNC(cmd_defcon)
739{
740 if ((argc < 2) || (atoi(argv[1]) == DefConLevel)) {
741 showDefConSettings(user, cmd);
742 return 1;
743 }
744
745 if ((atoi(argv[1]) < 1) || (atoi(argv[1]) > 5)) {
746 reply("OSMSG_DEFCON_INVALID", atoi(argv[1]));
747 return 0;
748 }
749
750 DefConLevel = atoi(argv[1]);
751 showDefConSettings(user, cmd);
752
753 if (DefConTimeOut > 0) {
9079d26c 754 timeq_del(0, defcon_timeout, NULL, TIMEQ_IGNORE_DATA | TIMEQ_IGNORE_WHEN);
08895577 755 timeq_add(now + DefConTimeOut, defcon_timeout, NULL);
756 }
757
758 DefConProcess(user);
759 return 1;
760}
761
6c34bb5a 762/* TODO
763static MODCMD_FUNC(cmd_privallow)
764{
765//privallow servername/username +/-flag (global is set in conf)
766}
767
768static MODCMD_FUNC(cmd_privdissallow)
769{
770//privdisallow servername/username +/-flag (global is set in conf)
771}
772
773static MODCMD_FUNC(cmd_privlist)
774{
775//privlist servername/user (global with none)
776}
777*/
778
779static MODCMD_FUNC(cmd_privset)
780{
781 struct userNode *target;
782 char *flag;
783 int add = PRIV_ADD;
784
785 flag = argv[2];
786 if (*flag == '-') {
787 add = PRIV_DEL;
788 flag++;
789 } else if (*flag == '+') {
790 add = PRIV_ADD;
791 flag++;
792 }
793
794 target = GetUserH(argv[1]);
795 if (!target) {
796 reply("MSG_NICK_UNKNOWN", argv[1]);
797 return 0;
798 }
799
800 if (check_priv(flag)) {
801 irc_privs(target, flag, add);
802 reply("OSMSG_PRIV_SET", argv[2], (add == 1) ? "" : "un");
803 } else {
804 reply("OSMSG_PRIV_UNKNOWN", argv[2]);
805 return 0;
806 }
807
808 return 1;
809}
810
d76ed9a9 811/* A lot of these commands are very similar to what ChanServ can do,
812 * but OpServ can do them even on channels that aren't registered.
813 */
814
815static MODCMD_FUNC(cmd_access)
816{
817 struct handle_info *hi;
818 const char *target;
819 unsigned int res;
820
821 target = (argc > 1) ? (const char*)argv[1] : user->nick;
822 if (!irccasecmp(target, "*")) {
823 nickserv_show_oper_accounts(user, cmd);
824 return 1;
825 }
826 if (!(hi = modcmd_get_handle_info(user, target)))
827 return 0;
258d1427 828 res = (argc > 2) ? oper_try_set_access(user, opserv_service->bot, hi, strtoul(argv[2], NULL, 0)) : 0;
d76ed9a9 829 reply("OSMSG_USER_ACCESS_IS", target, hi->handle, hi->opserv_level);
830 return res;
831}
832
833static MODCMD_FUNC(cmd_ban)
834{
835 struct mod_chanmode change;
836 struct userNode *victim;
837
838 mod_chanmode_init(&change);
839 change.argc = 1;
840 change.args[0].mode = MODE_BAN;
841 if (is_ircmask(argv[1]))
a32da4c7 842 change.args[0].u.hostmask = strdup(argv[1]);
d76ed9a9 843 else if ((victim = GetUserH(argv[1])))
a32da4c7 844 change.args[0].u.hostmask = generate_hostmask(victim, 0);
d76ed9a9 845 else {
258d1427 846 reply("OSMSG_INVALID_IRCMASK", argv[1]);
847 return 0;
d76ed9a9 848 }
849 modcmd_chanmode_announce(&change);
a32da4c7 850 reply("OSMSG_ADDED_BAN", change.args[0].u.hostmask, channel->name);
851 free((char*)change.args[0].u.hostmask);
d76ed9a9 852 return 1;
853}
854
855static MODCMD_FUNC(cmd_chaninfo)
856{
857 char buffer[MAXLEN];
858 const char *fmt;
859 struct banNode *ban;
2aef5f4b 860 struct exemptNode *exempt;
d76ed9a9 861 struct modeNode *moden;
862 unsigned int n;
863
864 reply("OSMSG_CHANINFO_HEADER", channel->name);
865 fmt = user_find_message(user, "OSMSG_CHANINFO_TIMESTAMP");
866 strftime(buffer, sizeof(buffer), fmt, gmtime(&channel->timestamp));
867 send_message_type(4, user, cmd->parent->bot, "%s", buffer);
868 irc_make_chanmode(channel, buffer);
869 if (channel->bad_channel)
870 reply("OSMSG_CHANINFO_MODES_BADWORD", buffer);
871 else
872 reply("OSMSG_CHANINFO_MODES", buffer);
873 if (channel->topic_time) {
874 fmt = user_find_message(user, "OSMSG_CHANINFO_TOPIC");
875 strftime(buffer, sizeof(buffer), fmt, gmtime(&channel->topic_time));
876 send_message_type(4, user, cmd->parent->bot, buffer, channel->topic_nick, channel->topic);
877 } else {
258d1427 878 irc_fetchtopic(cmd->parent->bot, channel->name);
879 reply("OSMSG_CHANINFO_TOPIC_UNKNOWN");
d76ed9a9 880 }
881 if (channel->banlist.used) {
258d1427 882 reply("OSMSG_CHANINFO_BAN_COUNT", channel->banlist.used);
d76ed9a9 883 fmt = user_find_message(user, "OSMSG_CHANINFO_BAN");
258d1427 884 for (n = 0; n < channel->banlist.used; n++) {
885 ban = channel->banlist.list[n];
886 strftime(buffer, sizeof(buffer), fmt, localtime(&ban->set));
887 send_message_type(4, user, cmd->parent->bot, buffer, ban->ban, ban->who);
888 }
d76ed9a9 889 }
2aef5f4b 890 if (channel->exemptlist.used) {
891 reply("OSMSG_CHANINFO_EXEMPT_COUNT", channel->exemptlist.used);
892 fmt = user_find_message(user, "OSMSG_CHANINFO_EXEMPT");
893 for (n = 0; n < channel->exemptlist.used; n++) {
894 exempt = channel->exemptlist.list[n];
895 strftime(buffer, sizeof(buffer), fmt, localtime(&exempt->set));
896 send_message_type(4, user, cmd->parent->bot, buffer, exempt->exempt, exempt->who);
897 }
898 }
d76ed9a9 899 if ((argc < 2) && (channel->members.used >= 50)) {
900 /* early out unless they ask for users */
901 reply("OSMSG_CHANINFO_MANY_USERS", channel->members.used, argv[0], channel->name);
902 return 1;
903 }
904 reply("OSMSG_CHANINFO_USER_COUNT", channel->members.used);
905 for (n=0; n<channel->members.used; n++) {
258d1427 906 moden = channel->members.list[n];
2f61d1d7 907 if (moden->modes & MODE_CHANOP) {
908 if (moden->oplevel >= 0)
909 send_message_type(4, user, cmd->parent->bot, " @%s:%d (%s@%s)", moden->user->nick, moden->oplevel, moden->user->ident, moden->user->hostname);
910 else
911 send_message_type(4, user, cmd->parent->bot, " @%s (%s@%s)", moden->user->nick, moden->user->ident, moden->user->hostname);
912 }
d76ed9a9 913 }
55342ce8 914 for (n=0; n<channel->members.used; n++) {
915 moden = channel->members.list[n];
916 if ((moden->modes & (MODE_CHANOP|MODE_HALFOP|MODE_VOICE)) == MODE_HALFOP)
917 send_message_type(4, user, cmd->parent->bot, " %s%s (%s@%s)", "%", moden->user->nick, moden->user->ident, moden->user->hostname);
918 }
d76ed9a9 919 for (n=0; n<channel->members.used; n++) {
258d1427 920 moden = channel->members.list[n];
921 if ((moden->modes & (MODE_CHANOP|MODE_HALFOP|MODE_VOICE)) == MODE_VOICE)
d76ed9a9 922 send_message_type(4, user, cmd->parent->bot, " +%s (%s@%s)", moden->user->nick, moden->user->ident, moden->user->hostname);
923 }
924 for (n=0; n<channel->members.used; n++) {
258d1427 925 moden = channel->members.list[n];
926 if ((moden->modes & (MODE_CHANOP|MODE_HALFOP|MODE_VOICE)) == 0)
d76ed9a9 927 send_message_type(4, user, cmd->parent->bot, " %s (%s@%s)", moden->user->nick, moden->user->ident, moden->user->hostname);
928 }
929 return 1;
930}
931
de9510bc 932/* This command has been replaced by 'alert notice channel #foo' */
933/*
2f61d1d7 934static MODCMD_FUNC(cmd_warn)
d76ed9a9 935{
936 char *reason, *message;
937
938 if (!IsChannelName(argv[1])) {
258d1427 939 reply("OSMSG_NEED_CHANNEL", argv[0]);
940 return 0;
d76ed9a9 941 }
942 reason = dict_find(opserv_chan_warn, argv[1], NULL);
943 if (reason) {
944 reply("OSMSG_WARN_EXISTS", argv[1]);
945 return 0;
946 }
947 if (argv[2])
948 reason = strdup(unsplit_string(argv+2, argc-2, NULL));
949 else
950 reason = strdup("No reason");
951 dict_insert(opserv_chan_warn, strdup(argv[1]), reason);
952 reply("OSMSG_WARN_ADDED", argv[1], reason);
953 if (dict_find(channels, argv[1], NULL)) {
57692f5e 954 global_message_args(MESSAGE_RECIPIENT_OPERS, "OSMSG_CHANNEL_ACTIVITY_WARN" argv[1], reason);
d76ed9a9 955 }
956 return 1;
957}
958
959static MODCMD_FUNC(cmd_unwarn)
960{
961 if ((argc < 2) || !IsChannelName(argv[1])) {
962 reply("OSMSG_NEED_CHANNEL", argv[0]);
258d1427 963 return 0;
d76ed9a9 964 }
965 if (!dict_remove(opserv_chan_warn, argv[1])) {
966 reply("OSMSG_WARN_NOEXIST", argv[1]);
967 return 0;
968 }
969 reply("OSMSG_WARN_DELETED", argv[1]);
970 return 1;
971}
de9510bc 972*/
d76ed9a9 973
974static MODCMD_FUNC(cmd_clearbans)
975{
976 struct mod_chanmode *change;
977 unsigned int ii;
978
979 change = mod_chanmode_alloc(channel->banlist.used);
980 for (ii=0; ii<channel->banlist.used; ii++) {
981 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
ec1a68c8 982 change->args[ii].u.hostmask = strdup(channel->banlist.list[ii]->ban);
d76ed9a9 983 }
984 modcmd_chanmode_announce(change);
ec1a68c8 985 for (ii=0; ii<change->argc; ++ii)
986 free((char*)change->args[ii].u.hostmask);
d76ed9a9 987 mod_chanmode_free(change);
988 reply("OSMSG_CLEARBANS_DONE", channel->name);
989 return 1;
990}
991
992static MODCMD_FUNC(cmd_clearmodes)
993{
994 struct mod_chanmode change;
995
996 if (!channel->modes) {
258d1427 997 reply("OSMSG_NO_CHANNEL_MODES", channel->name);
d76ed9a9 998 return 0;
999 }
1000 mod_chanmode_init(&change);
1001 change.modes_clear = channel->modes;
1002 modcmd_chanmode_announce(&change);
1003 reply("OSMSG_CLEARMODES_DONE", channel->name);
1004 return 1;
1005}
1006
1007static MODCMD_FUNC(cmd_deop)
1008{
1009 struct mod_chanmode *change;
1010 unsigned int arg, count;
1011
1012 change = mod_chanmode_alloc(argc-1);
1013 for (arg = 1, count = 0; arg < argc; ++arg) {
1014 struct userNode *victim = GetUserH(argv[arg]);
1015 struct modeNode *mn;
258d1427 1016 if (!victim || IsService(victim)
d76ed9a9 1017 || !(mn = GetUserMode(channel, victim))
1018 || !(mn->modes & MODE_CHANOP))
1019 continue;
1020 change->args[count].mode = MODE_REMOVE | MODE_CHANOP;
a32da4c7 1021 change->args[count++].u.member = mn;
d76ed9a9 1022 }
1023 if (count) {
1024 change->argc = count;
1025 modcmd_chanmode_announce(change);
1026 }
1027 mod_chanmode_free(change);
1028 reply("OSMSG_DEOP_DONE");
1029 return 1;
1030}
1031
55342ce8 1032static MODCMD_FUNC(cmd_dehop)
1033{
1034 struct mod_chanmode *change;
1035 unsigned int arg, count;
1036
1037 change = mod_chanmode_alloc(argc-1);
1038 for (arg = 1, count = 0; arg < argc; ++arg) {
1039 struct userNode *victim = GetUserH(argv[arg]);
1040 struct modeNode *mn;
1041 if (!victim || IsService(victim)
1042 || !(mn = GetUserMode(channel, victim))
1043 || !(mn->modes & MODE_HALFOP))
1044 continue;
1045 change->args[count].mode = MODE_REMOVE | MODE_HALFOP;
11408ce4 1046 change->args[count++].u.member = mn;
55342ce8 1047 }
1048 if (count) {
1049 change->argc = count;
1050 modcmd_chanmode_announce(change);
1051 }
1052 mod_chanmode_free(change);
1053 reply("OSMSG_DEHOP_DONE");
1054 return 1;
1055}
1056
d76ed9a9 1057static MODCMD_FUNC(cmd_deopall)
1058{
1059 struct mod_chanmode *change;
1060 unsigned int ii, count;
1061
1062 change = mod_chanmode_alloc(channel->members.used);
1063 for (ii = count = 0; ii < channel->members.used; ++ii) {
258d1427 1064 struct modeNode *mn = channel->members.list[ii];
1065 if (IsService(mn->user) || !(mn->modes & MODE_CHANOP))
d76ed9a9 1066 continue;
1067 change->args[count].mode = MODE_REMOVE | MODE_CHANOP;
a32da4c7 1068 change->args[count++].u.member = mn;
d76ed9a9 1069 }
1070 if (count) {
1071 change->argc = count;
1072 modcmd_chanmode_announce(change);
1073 }
1074 mod_chanmode_free(change);
1075 reply("OSMSG_DEOPALL_DONE", channel->name);
1076 return 1;
1077}
1078
55342ce8 1079static MODCMD_FUNC(cmd_dehopall)
1080{
1081 struct mod_chanmode *change;
1082 unsigned int ii, count;
1083
1084 change = mod_chanmode_alloc(channel->members.used);
1085 for (ii = count = 0; ii < channel->members.used; ++ii) {
1086 struct modeNode *mn = channel->members.list[ii];
1087 if (IsService(mn->user) || !(mn->modes & MODE_HALFOP))
1088 continue;
1089 change->args[count].mode = MODE_REMOVE | MODE_HALFOP;
11408ce4 1090 change->args[count++].u.member = mn;
55342ce8 1091 }
1092 if (count) {
1093 change->argc = count;
1094 modcmd_chanmode_announce(change);
1095 }
1096 mod_chanmode_free(change);
1097 reply("OSMSG_DEHOPALL_DONE", channel->name);
1098 return 1;
1099}
1100
7637f48f 1101static MODCMD_FUNC(cmd_resetmax)
1102{
1103 max_clients = dict_size(clients);
1104 max_clients_time = now;
1105 reply("OSMSG_MAXUSERS_RESET", max_clients);
1106 return 1;
1107}
1108
d76ed9a9 1109static MODCMD_FUNC(cmd_rehash)
1110{
1111 extern char *services_config;
1112
1113 if (conf_read(services_config))
258d1427 1114 reply("OSMSG_REHASH_COMPLETE");
d76ed9a9 1115 else
258d1427 1116 reply("OSMSG_REHASH_FAILED");
d76ed9a9 1117 return 1;
1118}
1119
1120static MODCMD_FUNC(cmd_reopen)
1121{
1122 log_reopen();
1123 reply("OSMSG_REOPEN_COMPLETE");
1124 return 1;
1125}
1126
1127static MODCMD_FUNC(cmd_reconnect)
1128{
1129 reply("OSMSG_RECONNECTING");
1130 irc_squit(self, "Reconnecting.", NULL);
1131 return 1;
1132}
1133
1134static MODCMD_FUNC(cmd_jupe)
1135{
1136 extern int force_n2k;
1137 struct server *newsrv;
1138 unsigned int num;
1139 char numeric[COMBO_NUMERIC_LEN+1], srvdesc[SERVERDESCRIPTMAX+1];
1140
1141 num = atoi(argv[2]);
df5f6070 1142 if(num == 0) {
1143 reply("OSMSG_INVALID_NUMERIC");
1144 return 0;
1145 }
d76ed9a9 1146 if ((num < 64) && !force_n2k) {
1147 inttobase64(numeric, num, 1);
1148 inttobase64(numeric+1, 64*64-1, 2);
1149 } else {
1150 inttobase64(numeric, num, 2);
1151 inttobase64(numeric+2, 64*64*64-1, 3);
1152 }
1153#ifdef WITH_PROTOCOL_P10
1154 if (GetServerN(numeric)) {
1155 reply("OSMSG_NUMERIC_COLLIDE", num, numeric);
1156 return 0;
1157 }
1158#endif
1159 if (GetServerH(argv[1])) {
1160 reply("OSMSG_NAME_COLLIDE");
1161 return 0;
1162 }
1163 snprintf(srvdesc, sizeof(srvdesc), "JUPE %s", unsplit_string(argv+3, argc-3, NULL));
df5f6070 1164 if(!strchr(argv[1], '.')) {
1165 reply("OSMSG_INVALID_SERVERNAME");
1166 return 0;
1167 }
d76ed9a9 1168 newsrv = AddServer(self, argv[1], 1, now, now, numeric, srvdesc);
1169 if (!newsrv) {
1170 reply("OSMSG_SRV_CREATE_FAILED");
1171 return 0;
1172 }
1173 irc_server(newsrv);
1174 reply("OSMSG_SERVER_JUPED", argv[1]);
1175 return 1;
1176}
1177
1178static MODCMD_FUNC(cmd_unjupe)
1179{
1180 struct server *srv;
1181 char *reason;
1182
1183 srv = GetServerH(argv[1]);
1184 if (!srv) {
1185 reply("MSG_SERVER_UNKNOWN", argv[1]);
1186 return 0;
1187 }
1188 if (strncmp(srv->description, "JUPE", 4)) {
1189 reply("OSMSG_SERVER_NOT_JUPE");
1190 return 0;
1191 }
1192 reason = (argc > 2) ? unsplit_string(argv+2, argc-2, NULL) : "Unjuping server";
1193 DelServer(srv, 1, reason);
1194 reply("OSMSG_SERVER_UNJUPED");
1195 return 1;
1196}
1197
1198static MODCMD_FUNC(cmd_jump)
1199{
1200 extern struct cManagerNode cManager;
1201 void uplink_select(char *name);
1202 struct uplinkNode *uplink_find(char *name);
1203 struct uplinkNode *uplink;
1204 char *target;
1205
1206 target = unsplit_string(argv+1, argc-1, NULL);
1207
1208 if (!strcmp(cManager.uplink->name, target)) {
258d1427 1209 reply("OSMSG_CURRENT_UPLINK", cManager.uplink->name);
1210 return 0;
d76ed9a9 1211 }
1212
1213 uplink = uplink_find(target);
1214 if (!uplink) {
258d1427 1215 reply("OSMSG_INVALID_UPLINK", target);
1216 return 0;
d76ed9a9 1217 }
1218 if (uplink->flags & UPLINK_UNAVAILABLE) {
1219 reply("OSMSG_UPLINK_DISABLED", uplink->name);
1220 return 0;
1221 }
1222
1223 reply("OSMSG_UPLINK_CONNECTING", uplink->name, uplink->host, uplink->port);
1224 uplink_select(target);
1225 irc_squit(self, "Reconnecting.", NULL);
1226 return 1;
1227}
1228
1229static MODCMD_FUNC(cmd_die)
1230{
1231 char *reason, *text;
1232
1233 text = unsplit_string(argv+1, argc-1, NULL);
1234 reason = alloca(strlen(text) + strlen(user->nick) + 20);
1235 sprintf(reason, "Disconnected by %s [%s]", user->nick, text);
1236 irc_squit(self, reason, text);
1237 quit_services = 1;
1238 return 1;
1239}
1240
1241static MODCMD_FUNC(cmd_restart)
1242{
1243 extern int services_argc;
1244 extern char **services_argv;
1245 char **restart_argv, *reason, *text;
1246
1247 text = unsplit_string(argv+1, argc-1, NULL);
1248 reason = alloca(strlen(text) + strlen(user->nick) + 17);
1249 sprintf(reason, "Restarted by %s [%s]", user->nick, text);
1250 irc_squit(self, reason, text);
1251
1252 /* Append a NULL to the end of argv[]. */
1253 restart_argv = (char **)alloca((services_argc + 1) * sizeof(char *));
1254 memcpy(restart_argv, services_argv, services_argc * sizeof(char *));
1255 restart_argv[services_argc] = NULL;
1256
1257 call_exit_funcs();
1258
1259 /* Don't blink. */
1260 execv(services_argv[0], restart_argv);
1261
1262 /* If we're still here, that means something went wrong. Reconnect. */
1263 return 1;
1264}
1265
1266static struct gline *
9a75756e 1267opserv_block(struct userNode *target, char *src_handle, char *reason, unsigned long duration, int silent)
d76ed9a9 1268{
2f61d1d7 1269 char mask[IRC_NTOP_MAX_SIZE+3] = { '*', '@', '\0' };
1270 irc_ntop(mask + 2, sizeof(mask) - 2, &target->ip);
1271 if (!reason)
1272 snprintf(reason = alloca(MAXLEN), MAXLEN,
1273 "G-line requested by %s.", src_handle);
1274 if (!duration)
1275 duration = opserv_conf.block_gline_duration;
9a75756e 1276 return gline_add(src_handle, mask, duration, reason, now, 1, silent ? 1 : 0);
d76ed9a9 1277}
1278
1279static MODCMD_FUNC(cmd_block)
1280{
1281 struct userNode *target;
1282 struct gline *gline;
1283 char *reason;
1284
1285 target = GetUserH(argv[1]);
1286 if (!target) {
258d1427 1287 reply("MSG_NICK_UNKNOWN", argv[1]);
1288 return 0;
d76ed9a9 1289 }
1290 if (IsService(target)) {
258d1427 1291 reply("MSG_SERVICE_IMMUNE", target->nick);
1292 return 0;
d76ed9a9 1293 }
1294 reason = (argc > 2) ? unsplit_string(argv+2, argc-2, NULL) : NULL;
9a75756e 1295 gline = opserv_block(target, user->handle_info->handle, reason, 0, 0);
d76ed9a9 1296 reply("OSMSG_GLINE_ISSUED", gline->target);
1297 return 1;
1298}
1299
1300static MODCMD_FUNC(cmd_gline)
1301{
1302 unsigned long duration;
1303 char *reason;
1304 struct gline *gline;
1305
1306 reason = unsplit_string(argv+3, argc-3, NULL);
1307 if (!is_gline(argv[1]) && !IsChannelName(argv[1]) && (argv[1][0] != '&')) {
258d1427 1308 reply("MSG_INVALID_GLINE", argv[1]);
1309 return 0;
d76ed9a9 1310 }
1311 if (!argv[1][strspn(argv[1], "#&*?@.")] && (strlen(argv[1]) < 10)) {
1312 reply("OSMSG_STUPID_GLINE", argv[1]);
1313 return 0;
1314 }
1315 duration = ParseInterval(argv[2]);
1316 if (!duration) {
1317 reply("MSG_INVALID_DURATION", argv[2]);
1318 return 0;
1319 }
9a75756e 1320 gline = gline_add(user->handle_info->handle, argv[1], duration, reason, now, 1, 0);
d76ed9a9 1321 reply("OSMSG_GLINE_ISSUED", gline->target);
1322 return 1;
1323}
1324
1325static MODCMD_FUNC(cmd_ungline)
1326{
1327 if (gline_remove(argv[1], 1))
1328 reply("OSMSG_GLINE_REMOVED", argv[1]);
1329 else
1330 reply("OSMSG_GLINE_FORCE_REMOVED", argv[1]);
1331 return 1;
1332}
1333
1334static MODCMD_FUNC(cmd_refreshg)
1335{
1336 if (argc > 1) {
1337 unsigned int count;
1338 dict_iterator_t it;
1339 struct server *srv;
1340
1341 for (it=dict_first(servers), count=0; it; it=iter_next(it)) {
1342 srv = iter_data(it);
1343 if ((srv == self) || !match_ircglob(srv->name, argv[1]))
1344 continue;
1345 gline_refresh_server(srv);
1346 reply("OSMSG_GLINES_ONE_REFRESHED", srv->name);
1347 count++;
1348 }
1349 if (!count) {
1350 reply("MSG_SERVER_UNKNOWN", argv[1]);
1351 return 0;
1352 }
1353 } else {
1354 gline_refresh_all();
1355 reply("OSMSG_GLINES_REFRESHED");
1356 }
1357 return 1;
1358}
1359
c408f18a 1360static void
0e08a8e0 1361opserv_version(struct userNode *target)
1362{
1363 irc_version_user(opserv, target);
1364}
1365
d82cf2f0 1366static void
1367opserv_mark(struct userNode *target, UNUSED_ARG(char *src_handle), UNUSED_ARG(char *reason), char *mark)
1368{
1369 if(!mark)
1370 return;
1371 irc_mark(target, mark);
1372}
1373
0e08a8e0 1374static void
5aa400d2 1375opserv_svsjoin(struct userNode *target, UNUSED_ARG(char *src_handle), UNUSED_ARG(char *reason), char *channame, unsigned int checkrestrictions)
c408f18a 1376{
1377 struct chanNode *channel;
1378
1379 if(!channame || !IsChannelName(channame)) {
1380 /* Not a valid channel name. We shouldnt ever get this if we check properly in addalert */
1381 return;
1382 }
1383
1384 if (!(channel = GetChannel(channame))) {
1385 channel = AddChannel(channame, now, NULL, NULL, NULL);
1386 }
1387 if (GetUserMode(channel, target)) {
1388 /* already in it */
1389 return;
1390 }
1391
5aa400d2 1392 if (checkrestrictions) {
1393 if (trace_check_bans(target, channel) == 1) {
1394 return; /* found on lamer list */
1395 }
1396
1397 if (channel->modes & MODE_INVITEONLY) {
1398 return; /* channel is invite only */
1399 }
1400
1401 if (channel->members.used >= channel->limit) {
1402 return; /* channel is invite on */
1403 }
1404
1405 if (*channel->key) {
1406 return; /* channel is password protected */
1407 }
1408 }
1409
c408f18a 1410 irc_svsjoin(opserv, target, channel);
1411 /* Should we tell the user they got joined? -Rubin*/
1412}
1413
39c1a4ef 1414static void
1415opserv_svspart(struct userNode *target, UNUSED_ARG(char *src_handle), UNUSED_ARG(char *reason), char *channame)
1416{
1417 struct chanNode *channel;
1418
1419 if(!channame || !IsChannelName(channame)) {
1420 /* Not a valid channel name. We shouldnt ever get this if we check properly in addalert */
1421 return;
1422 }
1423
1424 if (!(channel = GetChannel(channame))) {
1425 /* channel doesnt exist */
1426 return;
1427 }
1428
1429 if (!GetUserMode(channel, target)) {
1430 /* not in it */
1431 return;
1432 }
1433
1434 irc_svspart(opserv, target, channel);
1435}
1436
d914d1cb 1437static struct shun *
1438opserv_shun(struct userNode *target, char *src_handle, char *reason, unsigned long duration)
1439{
1440 char *mask;
1441 mask = alloca(MAXLEN);
1442 snprintf(mask, MAXLEN, "*@%s", target->hostname);
1443 if (!reason) {
1444 reason = alloca(MAXLEN);
1445 snprintf(reason, MAXLEN, "Shun requested by %s.", src_handle);
1446 }
1447 if (!duration) duration = opserv_conf.block_shun_duration;
1448 return shun_add(src_handle, mask, duration, reason, now, 1);
1449}
1450
1451static MODCMD_FUNC(cmd_sblock)
1452{
1453 struct userNode *target;
1454 struct shun *shun;
1455 char *reason;
1456
1457 target = GetUserH(argv[1]);
1458 if (!target) {
1459 reply("MSG_NICK_UNKNOWN", argv[1]);
1460 return 0;
1461 }
1462 if (IsService(target)) {
1463 reply("MSG_SERVICE_IMMUNE", target->nick);
1464 return 0;
1465 }
1466 reason = (argc > 2) ? unsplit_string(argv+2, argc-2, NULL) : NULL;
1467 shun = opserv_shun(target, user->handle_info->handle, reason, 0);
1468 reply("OSMSG_SHUN_ISSUED", shun->target);
1469 return 1;
1470}
1471
1472static MODCMD_FUNC(cmd_shun)
1473{
1474 unsigned long duration;
1475 char *reason;
1476 struct shun *shun;
1477
1478 reason = unsplit_string(argv+3, argc-3, NULL);
1479 if (!is_shun(argv[1]) && !IsChannelName(argv[1]) && (argv[1][0] != '&')) {
1480 reply("MSG_INVALID_SHUN", argv[1]);
1481 return 0;
1482 }
1483 if (!argv[1][strspn(argv[1], "#&*?@.")] && (strlen(argv[1]) < 10)) {
1484 reply("OSMSG_STUPID_SHUN", argv[1]);
1485 return 0;
1486 }
1487 duration = ParseInterval(argv[2]);
1488 if (!duration) {
1489 reply("MSG_INVALID_DURATION", argv[2]);
1490 return 0;
1491 }
1492 shun = shun_add(user->handle_info->handle, argv[1], duration, reason, now, 1);
1493 reply("OSMSG_SHUN_ISSUED", shun->target);
1494 return 1;
1495}
1496
1497static MODCMD_FUNC(cmd_unshun)
1498{
1499 if (shun_remove(argv[1], 1))
1500 reply("OSMSG_SHUN_REMOVED", argv[1]);
1501 else
1502 reply("OSMSG_SHUN_FORCE_REMOVED", argv[1]);
1503 return 1;
1504}
1505
1506static MODCMD_FUNC(cmd_refreshs)
1507{
1508 if (argc > 1) {
1509 unsigned int count;
1510 dict_iterator_t it;
1511 struct server *srv;
1512
1513 for (it=dict_first(servers), count=0; it; it=iter_next(it)) {
1514 srv = iter_data(it);
1515 if ((srv == self) || !match_ircglob(srv->name, argv[1]))
1516 continue;
1517 shun_refresh_server(srv);
1518 reply("OSMSG_SHUNS_ONE_REFRESHED", srv->name);
1519 count++;
1520 }
1521 if (!count) {
1522 reply("MSG_SERVER_UNKNOWN", argv[1]);
1523 return 0;
1524 }
1525 } else {
1526 shun_refresh_all();
1527 reply("OSMSG_SHUNS_REFRESHED");
1528 }
1529 return 1;
1530}
1531
d76ed9a9 1532static void
4b44eb0f 1533opserv_ison(struct userNode *bot, struct userNode *tell, struct userNode *target, const char *message)
d76ed9a9 1534{
1535 struct modeNode *mn;
1536 unsigned int count, here_len, n, maxlen;
1537 char buff[MAXLEN];
1538
1539 maxlen = tell->handle_info ? tell->handle_info->screen_width : 0;
1540 if (!maxlen)
1541 maxlen = MAX_LINE_SIZE;
1542 for (n=count=0; n<target->channels.used; n++) {
258d1427 1543 mn = target->channels.list[n];
1544 here_len = strlen(mn->channel->name);
1545 if ((count + here_len + 4) > maxlen) {
1546 buff[count] = 0;
4b44eb0f 1547 send_message(tell, bot, message, buff);
258d1427 1548 count = 0;
1549 }
1550 if (mn->modes & MODE_CHANOP)
d76ed9a9 1551 buff[count++] = '@';
55342ce8 1552 if (mn->modes & MODE_HALFOP)
1553 buff[count++] = '%';
258d1427 1554 if (mn->modes & MODE_VOICE)
d76ed9a9 1555 buff[count++] = '+';
258d1427 1556 memcpy(buff+count, mn->channel->name, here_len);
1557 count += here_len;
1558 buff[count++] = ' ';
d76ed9a9 1559 }
1560 if (count) {
258d1427 1561 buff[count] = 0;
4b44eb0f 1562 send_message(tell, bot, message, buff);
d76ed9a9 1563 }
1564}
1565
1566static MODCMD_FUNC(cmd_inviteme)
1567{
1568 struct userNode *target;
1569
1570 if (argc < 2) {
258d1427 1571 target = user;
d76ed9a9 1572 } else {
258d1427 1573 target = GetUserH(argv[1]);
1574 if (!target) {
1575 reply("MSG_NICK_UNKNOWN", argv[1]);
1576 return 0;
1577 }
d76ed9a9 1578 }
1579 if (opserv_conf.debug_channel == NULL) {
258d1427 1580 reply("OSMSG_NO_DEBUG_CHANNEL");
1581 return 0;
d76ed9a9 1582 }
1583 if (GetUserMode(opserv_conf.debug_channel, user)) {
a32da4c7 1584 reply("OSMSG_ALREADY_THERE", opserv_conf.debug_channel->name);
d76ed9a9 1585 return 0;
1586 }
1587 irc_invite(cmd->parent->bot, target, opserv_conf.debug_channel);
1588 if (target != user)
258d1427 1589 reply("OSMSG_INVITE_DONE", target->nick, opserv_conf.debug_channel->name);
d76ed9a9 1590 return 1;
1591}
1592
1593static MODCMD_FUNC(cmd_invite)
1594{
1595 if (GetUserMode(channel, user)) {
1596 reply("OSMSG_ALREADY_THERE", channel->name);
1597 return 0;
1598 }
1599 irc_invite(cmd->parent->bot, user, channel);
1600 return 1;
1601}
1602
c408f18a 1603static MODCMD_FUNC(cmd_svsjoin)
1604{
1605 struct userNode *target;
1606
1607
1608 if(!IsChannelName(argv[2])) {
1609 reply("MSG_NOT_CHANNEL_NAME");
1610 return 0;
1611 }
1612 target = GetUserH(argv[1]);
1613 if (!target) {
1614 reply("MSG_NICK_UNKNOWN", argv[1]);
1615 return 0;
1616 }
1617
1618 if (!(channel = GetChannel(argv[2]))) {
1619 channel = AddChannel(argv[2], now, NULL, NULL, NULL);
1620 }
1621 if (GetUserMode(channel, target)) {
1622 reply("OSMSG_ALREADY_THERE", channel->name);
1623 return 0;
1624 }
1625 irc_svsjoin(opserv, target, channel);
1626 reply("OSMSG_SVSJOIN_SENT");
1627 return 1;
1628}
1629
d76ed9a9 1630static MODCMD_FUNC(cmd_join)
1631{
1632 struct userNode *bot = cmd->parent->bot;
1633
1634 if (!IsChannelName(argv[1])) {
1635 reply("MSG_NOT_CHANNEL_NAME");
1636 return 0;
1637 } else if (!(channel = GetChannel(argv[1]))) {
2aef5f4b 1638 channel = AddChannel(argv[1], now, NULL, NULL, NULL);
d76ed9a9 1639 AddChannelUser(bot, channel)->modes |= MODE_CHANOP;
1640 } else if (GetUserMode(channel, bot)) {
1641 reply("OSMSG_ALREADY_JOINED", channel->name);
1642 return 0;
1643 } else {
1644 struct mod_chanmode change;
1645 mod_chanmode_init(&change);
1646 change.argc = 1;
1647 change.args[0].mode = MODE_CHANOP;
a32da4c7 1648 change.args[0].u.member = AddChannelUser(bot, channel);
d76ed9a9 1649 modcmd_chanmode_announce(&change);
1650 }
1651 irc_fetchtopic(bot, channel->name);
1652 reply("OSMSG_JOIN_DONE", channel->name);
1653 return 1;
1654}
1655
1656static MODCMD_FUNC(cmd_kick)
1657{
1658 struct userNode *target;
1659 char *reason;
1660
1661 if (argc < 3) {
258d1427 1662 reason = alloca(strlen(OSMSG_KICK_REQUESTED)+strlen(user->nick)+1);
1663 sprintf(reason, OSMSG_KICK_REQUESTED, user->nick);
d76ed9a9 1664 } else {
258d1427 1665 reason = unsplit_string(argv+2, argc-2, NULL);
d76ed9a9 1666 }
1667 target = GetUserH(argv[1]);
1668 if (!target) {
258d1427 1669 reply("MSG_NICK_UNKNOWN", argv[1]);
1670 return 0;
d76ed9a9 1671 }
1672 if (!GetUserMode(channel, target)) {
258d1427 1673 reply("OSMSG_NOT_ON_CHANNEL", target->nick, channel->name);
1674 return 0;
d76ed9a9 1675 }
1676 KickChannelUser(target, channel, cmd->parent->bot, reason);
1677 return 1;
1678}
1679
1680static MODCMD_FUNC(cmd_kickall)
1681{
1682 unsigned int limit, n, inchan;
1683 struct modeNode *mn;
1684 char *reason;
1685 struct userNode *bot = cmd->parent->bot;
1686
1687 /* ircu doesn't let servers KICK users, so if OpServ's not in the
1688 * channel, we have to join it in temporarily. */
1689 if (!(inchan = GetUserMode(channel, bot) ? 1 : 0)) {
1690 struct mod_chanmode change;
1691 mod_chanmode_init(&change);
1692 change.args[0].mode = MODE_CHANOP;
a32da4c7 1693 change.args[0].u.member = AddChannelUser(bot, channel);
d76ed9a9 1694 modcmd_chanmode_announce(&change);
1695 }
1696 if (argc < 2) {
258d1427 1697 reason = alloca(strlen(OSMSG_KICK_REQUESTED)+strlen(user->nick)+1);
1698 sprintf(reason, OSMSG_KICK_REQUESTED, user->nick);
d76ed9a9 1699 } else {
258d1427 1700 reason = unsplit_string(argv+1, argc-1, NULL);
d76ed9a9 1701 }
1702 limit = user->handle_info->opserv_level;
1703 for (n=channel->members.used; n>0;) {
258d1427 1704 mn = channel->members.list[--n];
1705 if (IsService(mn->user)
1706 || (mn->user->handle_info
1707 && (mn->user->handle_info->opserv_level >= limit))) {
1708 continue;
1709 }
1710 KickChannelUser(mn->user, channel, bot, reason);
d76ed9a9 1711 }
1712 if (!inchan)
1713 DelChannelUser(bot, channel, "My work here is done", 0);
1714 reply("OSMSG_KICKALL_DONE", channel->name);
1715 return 1;
1716}
1717
1718static MODCMD_FUNC(cmd_kickban)
1719{
1720 struct mod_chanmode change;
1721 struct userNode *target;
1722 char *reason;
1723 char *mask;
1724
1725 if (argc == 2) {
258d1427 1726 reason = alloca(strlen(OSMSG_KICK_REQUESTED)+strlen(user->nick)+1);
1727 sprintf(reason, OSMSG_KICK_REQUESTED, user->nick);
d76ed9a9 1728 } else {
258d1427 1729 reason = unsplit_string(argv+2, argc-2, NULL);
d76ed9a9 1730 }
1731 target = GetUserH(argv[1]);
1732 if (!target) {
258d1427 1733 reply("MSG_NICK_UNKNOWN", argv[1]);
1734 return 0;
d76ed9a9 1735 }
1736 if (!GetUserMode(channel, target)) {
258d1427 1737 reply("OSMSG_NOT_ON_CHANNEL", target->nick, channel->name);
1738 return 0;
d76ed9a9 1739 }
1740 mod_chanmode_init(&change);
1741 change.argc = 1;
1742 change.args[0].mode = MODE_BAN;
a32da4c7 1743 change.args[0].u.hostmask = mask = generate_hostmask(target, 0);
d76ed9a9 1744 modcmd_chanmode_announce(&change);
1745 KickChannelUser(target, channel, cmd->parent->bot, reason);
1746 free(mask);
1747 return 1;
1748}
1749
1750static MODCMD_FUNC(cmd_kickbanall)
1751{
1752 struct modeNode *mn;
1753 struct userNode *bot = cmd->parent->bot;
1754 struct mod_chanmode *change;
1755 char *reason;
1756 unsigned int limit, n, inchan;
1757
1758 /* ircu doesn't let servers KICK users, so if OpServ's not in the
1759 * channel, we have to join it in temporarily. */
1760 if (!(inchan = GetUserMode(channel, bot) ? 1 : 0)) {
1761 change = mod_chanmode_alloc(2);
1762 change->args[0].mode = MODE_CHANOP;
a32da4c7 1763 change->args[0].u.member = AddChannelUser(bot, channel);
d76ed9a9 1764 change->args[1].mode = MODE_BAN;
a32da4c7 1765 change->args[1].u.hostmask = "*!*@*";
d76ed9a9 1766 } else {
1767 change = mod_chanmode_alloc(1);
1768 change->args[0].mode = MODE_BAN;
a32da4c7 1769 change->args[0].u.hostmask = "*!*@*";
d76ed9a9 1770 }
1771 modcmd_chanmode_announce(change);
a32da4c7 1772 mod_chanmode_free(change);
d76ed9a9 1773 if (argc < 2) {
258d1427 1774 reason = alloca(strlen(OSMSG_KICK_REQUESTED)+strlen(user->nick)+1);
1775 sprintf(reason, OSMSG_KICK_REQUESTED, user->nick);
d76ed9a9 1776 } else {
258d1427 1777 reason = unsplit_string(argv+1, argc-1, NULL);
d76ed9a9 1778 }
1779 /* now kick them */
1780 limit = user->handle_info->opserv_level;
1781 for (n=channel->members.used; n>0; ) {
258d1427 1782 mn = channel->members.list[--n];
1783 if (IsService(mn->user)
1784 || (mn->user->handle_info
1785 && (mn->user->handle_info->opserv_level >= limit))) {
1786 continue;
1787 }
1788 KickChannelUser(mn->user, channel, bot, reason);
d76ed9a9 1789 }
1790 if (!inchan)
1791 DelChannelUser(bot, channel, "My work here is done", 0);
1792 reply("OSMSG_KICKALL_DONE", channel->name);
1793 return 1;
1794}
1795
39c1a4ef 1796static MODCMD_FUNC(cmd_svspart)
1797{
1798 struct userNode *target;
1799
1800 if(!IsChannelName(argv[2])) {
1801 reply("MSG_NOT_CHANNEL_NAME");
1802 return 0;
1803 }
1804 target = GetUserH(argv[1]);
1805 if (!target) {
1806 reply("MSG_NICK_UNKNOWN", argv[1]);
1807 return 0;
1808 }
1809
1810 if (!(channel = GetChannel(argv[2]))) {
1811 reply("OSMSG_NOT_ON_CHANNEL", cmd->parent->bot->nick, channel->name);
1812 return 0;
1813 }
1814
1815 if (!GetUserMode(channel, target)) {
1816 reply("OSMSG_NOT_ON_CHANNEL", cmd->parent->bot->nick, channel->name);
1817 return 0;
1818 }
1819
1820 irc_svspart(opserv, target, channel);
1821 reply("OSMSG_SVSPART_SENT");
1822 return 1;
1823}
1824
d76ed9a9 1825static MODCMD_FUNC(cmd_part)
1826{
1827 char *reason;
1828
1829 if (!IsChannelName(argv[1])) {
1830 reply("MSG_NOT_CHANNEL_NAME");
1831 return 0;
1832 }
1833 if ((channel = GetChannel(argv[1]))) {
1834 if (!GetUserMode(channel, cmd->parent->bot)) {
1835 reply("OSMSG_NOT_ON_CHANNEL", cmd->parent->bot->nick, channel->name);
1836 return 0;
1837 }
1838 reason = (argc < 3) ? "Leaving." : unsplit_string(argv+2, argc-2, NULL);
1839 reply("OSMSG_LEAVING", channel->name);
1840 DelChannelUser(cmd->parent->bot, channel, reason, 0);
1841 }
1842 return 1;
1843}
1844
1845static MODCMD_FUNC(cmd_mode)
1846{
1847 if (!modcmd_chanmode(argv+1, argc-1, MCP_ALLOW_OVB|MCP_KEY_FREE|MC_ANNOUNCE)) {
1848 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
1849 return 0;
1850 }
1851 reply("OSMSG_MODE_SET", channel->name);
1852 return 1;
1853}
1854
d82cf2f0 1855int is_valid_mark(char *mark)
1856{
1857 char *ptr;
1858
1859 if(!mark || !*mark)
1860 return 0;
1861 if(strlen(mark) > MARKLEN)
1862 return 0;
1863
1864 for(ptr = mark; *ptr; ptr++) {
1865 if(! (isalnum(*ptr) || *ptr == '-'))
1866 return 0;
1867 }
1868
1869 return 1;
1870}
1871
1872static MODCMD_FUNC(cmd_mark)
1873{
1874 char *mark = argv[2];
1875 struct userNode *victim = GetUserH(argv[1]);
1876
1877 if(!victim)
1878 reply("MSG_NICK_UNKNOWN", argv[1]);
1879 else if(!is_valid_mark(mark))
1880 reply("OSMSG_MARK_INVALID");
1881 else {
1882 irc_mark(victim, mark);
1883 reply("OSMSG_MARK_SET");
1884 return 1;
1885 }
1886 return 0;
1887}
1888
d76ed9a9 1889static MODCMD_FUNC(cmd_op)
1890{
1891 struct mod_chanmode *change;
1892 unsigned int arg, count;
1893
1894 change = mod_chanmode_alloc(argc-1);
1895 for (arg = 1, count = 0; arg < argc; ++arg) {
1896 struct userNode *victim;
1897 struct modeNode *mn;
1898 if (!(victim = GetUserH(argv[arg])))
1899 continue;
1900 if (!(mn = GetUserMode(channel, victim)))
1901 continue;
1902 if (mn->modes & MODE_CHANOP)
1903 continue;
1904 change->args[count].mode = MODE_CHANOP;
a32da4c7 1905 change->args[count++].u.member = mn;
d76ed9a9 1906 }
1907 if (count) {
1908 change->argc = count;
1909 modcmd_chanmode_announce(change);
1910 }
1911 mod_chanmode_free(change);
1912 reply("OSMSG_OP_DONE");
1913 return 1;
1914}
1915
55342ce8 1916static MODCMD_FUNC(cmd_hop)
1917{
1918 struct mod_chanmode *change;
1919 unsigned int arg, count;
1920
1921 change = mod_chanmode_alloc(argc-1);
1922 for (arg = 1, count = 0; arg < argc; ++arg) {
1923 struct userNode *victim;
1924 struct modeNode *mn;
1925 if (!(victim = GetUserH(argv[arg])))
1926 continue;
1927 if (!(mn = GetUserMode(channel, victim)))
1928 continue;
1929 if (mn->modes & MODE_HALFOP)
1930 continue;
1931 change->args[count].mode = MODE_HALFOP;
11408ce4 1932 change->args[count++].u.member = mn;
55342ce8 1933 }
1934 if (count) {
1935 change->argc = count;
1936 modcmd_chanmode_announce(change);
1937 }
1938 mod_chanmode_free(change);
1939 reply("OSMSG_HOP_DONE");
1940 return 1;
1941}
1942
d76ed9a9 1943static MODCMD_FUNC(cmd_opall)
1944{
1945 struct mod_chanmode *change;
1946 unsigned int ii, count;
1947
1948 change = mod_chanmode_alloc(channel->members.used);
1949 for (ii = count = 0; ii < channel->members.used; ++ii) {
258d1427 1950 struct modeNode *mn = channel->members.list[ii];
1951 if (mn->modes & MODE_CHANOP)
d76ed9a9 1952 continue;
1953 change->args[count].mode = MODE_CHANOP;
a32da4c7 1954 change->args[count++].u.member = mn;
d76ed9a9 1955 }
1956 if (count) {
1957 change->argc = count;
258d1427 1958 modcmd_chanmode_announce(change);
d76ed9a9 1959 }
1960 mod_chanmode_free(change);
1961 reply("OSMSG_OPALL_DONE", channel->name);
1962 return 1;
1963}
1964
55342ce8 1965static MODCMD_FUNC(cmd_hopall)
1966{
1967 struct mod_chanmode *change;
1968 unsigned int ii, count;
1969
1970 change = mod_chanmode_alloc(channel->members.used);
1971 for (ii = count = 0; ii < channel->members.used; ++ii) {
1972 struct modeNode *mn = channel->members.list[ii];
1973 if (mn->modes & MODE_HALFOP)
1974 continue;
1975 change->args[count].mode = MODE_HALFOP;
11408ce4 1976 change->args[count++].u.member = mn;
55342ce8 1977 }
1978 if (count) {
1979 change->argc = count;
1980 modcmd_chanmode_announce(change);
1981 }
1982 mod_chanmode_free(change);
1983 reply("OSMSG_HOPALL_DONE", channel->name);
1984 return 1;
1985}
1986
d76ed9a9 1987static MODCMD_FUNC(cmd_whois)
1988{
1989 struct userNode *target;
1990 char buffer[128];
1991 int bpos, herelen;
1992
1993#ifdef WITH_PROTOCOL_P10
1994 if (argv[1][0] == '*')
1995 target = GetUserN(argv[1]+1);
1996 else
d76ed9a9 1997#endif
1117fc5a 1998 target = GetUserH(argv[1]);
d76ed9a9 1999 if (!target) {
2000 reply("MSG_NICK_UNKNOWN", argv[1]);
2001 return 0;
2002 }
2003 reply("OSMSG_WHOIS_NICK", target->nick);
2004 reply("OSMSG_WHOIS_HOST", target->ident, target->hostname);
2005 if (IsFakeHost(target))
2006 reply("OSMSG_WHOIS_FAKEHOST", target->fakehost);
37ef8ee3 2007 reply("OSMSG_WHOIS_CRYPT_HOST", target->crypthost);
2008 reply("OSMSG_WHOIS_CRYPT_IP", target->cryptip);
2f61d1d7 2009 reply("OSMSG_WHOIS_IP", irc_ntoa(&target->ip));
21f6caee 2010
2011 if (target->city) {
2012 reply("OSMSG_WHOIS_COUNTRY", target->country_name);
2013 reply("OSMSG_WHOIS_COUNTRY_CODE", target->country_code);
2014 reply("OSMSG_WHOIS_CITY", target->city);
2015 reply("OSMSG_WHOIS_REGION", target->region);
2016
2017 reply("OSMSG_WHOIS_POSTAL_CODE", target->postal_code);
2018 reply("OSMSG_WHOIS_LATITUDE", target->latitude);
2019 reply("OSMSG_WHOIS_LONGITUDE", target->longitude);
1ad7ac15 2020 /* Only show a map url if we have a city, latitude and longitude.
2021 * Theres not much point of latitude and longitude coordinates are
2022 * returned but no city, the coordinates are useless.
2023 */
2024 if (target->latitude && target->longitude && target->city) {
2025 char map_url[MAXLEN];
2026 snprintf(map_url, sizeof(map_url), "http://www.mapquest.com/maps/map.adp?searchtype=address&formtype=address&latlongtype=decimal&latitude=%f&longitude=%f",
2027 target->latitude, target->longitude);
2028 reply("OSMSG_WHOIS_MAP", map_url);
2029 }
21f6caee 2030 reply("OSMSG_WHOIS_DMA_CODE", target->dma_code);
2031 reply("OSMSG_WHOIS_AREA_CODE", target->area_code);
2032 } else if (target->country_name) {
2033 reply("OSMSG_WHOIS_COUNTRY", target->country_name);
2034 }
0e08a8e0 2035 if(target->version_reply) {
2036 reply("OSMSG_WHOIS_VERSION", target->version_reply);
2037 }
da5b7dfc 2038 reply("OSMSG_WHOIS_NO_NOTICE", target->no_notice ? "YES":"NO");
21f6caee 2039
d76ed9a9 2040 if (target->modes) {
258d1427 2041 bpos = 0;
d76ed9a9 2042#define buffer_cat(str) (herelen = strlen(str), memcpy(buffer+bpos, str, herelen), bpos += herelen)
258d1427 2043 if (IsInvisible(target)) buffer[bpos++] = 'i';
2044 if (IsWallOp(target)) buffer[bpos++] = 'w';
2045 if (IsOper(target)) buffer[bpos++] = 'o';
2046 if (IsGlobal(target)) buffer[bpos++] = 'g';
2047 if (IsServNotice(target)) buffer[bpos++] = 's';
2048
2049 // sethost - reed/apples
2050 // if (IsHelperIrcu(target)) buffer[bpos++] = 'h';
2051 if (IsSetHost(target)) buffer[bpos++] = 'h';
2052
2053 if (IsService(target)) buffer[bpos++] = 'k';
2054 if (IsDeaf(target)) buffer[bpos++] = 'd';
b2cf3d66 2055 if (target->handle_info) buffer[bpos++] = 'r';
d76ed9a9 2056 if (IsHiddenHost(target)) buffer[bpos++] = 'x';
2057 if (IsGagged(target)) buffer_cat(" (gagged)");
258d1427 2058 if (IsRegistering(target)) buffer_cat(" (registered account)");
2059 buffer[bpos] = 0;
2060 if (bpos > 0)
d76ed9a9 2061 reply("OSMSG_WHOIS_MODES", buffer);
2062 }
2063 reply("OSMSG_WHOIS_INFO", target->info);
2064#ifdef WITH_PROTOCOL_P10
2065 reply("OSMSG_WHOIS_NUMERIC", target->numeric);
2066#endif
2067 reply("OSMSG_WHOIS_SERVER", target->uplink->name);
2068 reply("OSMSG_WHOIS_ACCOUNT", (target->handle_info ? target->handle_info->handle : "Not authenticated"));
89d871d8 2069
2070 reply("OSMSG_WHOIS_PRIVS", client_report_privs(target));
2071
d76ed9a9 2072 intervalString(buffer, now - target->timestamp, user->handle_info);
2073 reply("OSMSG_WHOIS_NICK_AGE", buffer);
2074 if (target->channels.used <= MAX_CHANNELS_WHOIS)
4b44eb0f 2075 opserv_ison(cmd->parent->bot, user, target, "OSMSG_WHOIS_CHANNELS");
d76ed9a9 2076 else
258d1427 2077 reply("OSMSG_WHOIS_HIDECHANS");
d76ed9a9 2078 return 1;
2079}
2080
2081static MODCMD_FUNC(cmd_unban)
2082{
2083 struct mod_chanmode change;
2084 mod_chanmode_init(&change);
2085 change.argc = 1;
2086 change.args[0].mode = MODE_REMOVE | MODE_BAN;
a32da4c7 2087 change.args[0].u.hostmask = argv[1];
d76ed9a9 2088 modcmd_chanmode_announce(&change);
2089 reply("OSMSG_UNBAN_DONE", channel->name);
2090 return 1;
2091}
2092
2093static MODCMD_FUNC(cmd_voiceall)
2094{
2095 struct mod_chanmode *change;
2096 unsigned int ii, count;
2097
2098 change = mod_chanmode_alloc(channel->members.used);
2099 for (ii = count = 0; ii < channel->members.used; ++ii) {
258d1427 2100 struct modeNode *mn = channel->members.list[ii];
2101 if (mn->modes & (MODE_CHANOP|MODE_HALFOP|MODE_VOICE))
d76ed9a9 2102 continue;
2103 change->args[count].mode = MODE_VOICE;
a32da4c7 2104 change->args[count++].u.member = mn;
d76ed9a9 2105 }
2106 if (count) {
2107 change->argc = count;
258d1427 2108 modcmd_chanmode_announce(change);
d76ed9a9 2109 }
2110 mod_chanmode_free(change);
2111 reply("OSMSG_CHANNEL_VOICED", channel->name);
2112 return 1;
2113}
2114
2115static MODCMD_FUNC(cmd_devoiceall)
2116{
2117 struct mod_chanmode *change;
2118 unsigned int ii, count;
2119
2120 change = mod_chanmode_alloc(channel->members.used);
2121 for (ii = count = 0; ii < channel->members.used; ++ii) {
258d1427 2122 struct modeNode *mn = channel->members.list[ii];
2123 if (!(mn->modes & MODE_VOICE))
d76ed9a9 2124 continue;
2125 change->args[count].mode = MODE_REMOVE | MODE_VOICE;
a32da4c7 2126 change->args[count++].u.member = mn;
d76ed9a9 2127 }
2128 if (count) {
2129 change->argc = count;
258d1427 2130 modcmd_chanmode_announce(change);
d76ed9a9 2131 }
2132 mod_chanmode_free(change);
2133 reply("OSMSG_CHANNEL_DEVOICED", channel->name);
2134 return 1;
2135}
2136
2137static MODCMD_FUNC(cmd_stats_bad) {
2138 dict_iterator_t it;
2139 unsigned int ii, end, here_len;
2140 char buffer[400];
2141
2142 /* Show the bad word list.. */
de9510bc 2143 /* TODO: convert nonprinting chars like bold to $b etc in a usable way */
d76ed9a9 2144 for (ii=end=0; ii<opserv_bad_words->used; ii++) {
2145 here_len = strlen(opserv_bad_words->list[ii]);
de9510bc 2146 /* If the line is full output it & start again */
d76ed9a9 2147 if ((end + here_len + 2) > sizeof(buffer)) {
2148 buffer[end] = 0;
2149 reply("OSMSG_BADWORD_LIST", buffer);
2150 end = 0;
2151 }
2152 memcpy(buffer+end, opserv_bad_words->list[ii], here_len);
2153 end += here_len;
2154 buffer[end++] = ' ';
2155 }
2156 buffer[end] = 0;
2157 reply("OSMSG_BADWORD_LIST", buffer);
2158
2159 /* Show the exemption list.. */
2160 for (it=dict_first(opserv_exempt_channels), end=0; it; it=iter_next(it)) {
2161 here_len = strlen(iter_key(it));
2162 if ((end + here_len + 2) > sizeof(buffer)) {
2163 buffer[end] = 0;
2164 reply("OSMSG_EXEMPTED_LIST", buffer);
2165 end = 0;
2166 }
2167 memcpy(buffer+end, iter_key(it), here_len);
2168 end += here_len;
2169 buffer[end++] = ' ';
2170 }
2171 buffer[end] = 0;
2172 reply("OSMSG_EXEMPTED_LIST", buffer);
2173 return 1;
2174}
2175
2176static MODCMD_FUNC(cmd_stats_glines) {
2177 reply("OSMSG_GLINE_COUNT", gline_count());
2178 return 1;
2179}
2180
d914d1cb 2181static MODCMD_FUNC(cmd_stats_shuns) {
2182 reply("OSMSG_SHUN_COUNT", shun_count());
2183 return 1;
2184}
2185
d76ed9a9 2186static void
2187trace_links(struct userNode *bot, struct userNode *user, struct server *server, unsigned int depth) {
2188 unsigned int nn, pos;
2189 char buffer[400];
2190
2191 for (nn=1; nn<=depth; nn<<=1) ;
2192 for (pos=0, nn>>=1; nn>1; ) {
2193 nn >>= 1;
2194 buffer[pos++] = (depth & nn) ? ((nn == 1) ? '`' : ' ') : '|';
2195 buffer[pos++] = (nn == 1) ? '-': ' ';
2196 }
2197 buffer[pos] = 0;
2198 send_message(user, bot, "OSMSG_LINKS_SERVER", buffer, server->name, server->clients, server->description);
2199 if (!server->children.used)
2200 return;
2201 for (nn=0; nn<server->children.used-1; nn++) {
2202 trace_links(bot, user, server->children.list[nn], depth<<1);
2203 }
2204 trace_links(bot, user, server->children.list[nn], (depth<<1)|1);
2205}
2206
2207static MODCMD_FUNC(cmd_stats_links) {
2208 trace_links(cmd->parent->bot, user, self, 1);
2209 return 1;
2210}
2211
2212
2213static MODCMD_FUNC(cmd_stats_max) {
2214 reply("OSMSG_MAX_CLIENTS", max_clients, asctime(localtime(&max_clients_time)));
2215 return 1;
2216}
2217
2218static MODCMD_FUNC(cmd_stats_network) {
2219 struct helpfile_table tbl;
2220 unsigned int nn, tot_clients;
2221 dict_iterator_t it;
2222
2223 tot_clients = dict_size(clients);
2224 reply("OSMSG_NETWORK_INFO", tot_clients, invis_clients, curr_opers.used);
2225 tbl.length = dict_size(servers)+1;
2226 tbl.width = 3;
2227 tbl.flags = TABLE_NO_FREE;
2228 tbl.contents = calloc(tbl.length, sizeof(*tbl.contents));
2229 tbl.contents[0] = calloc(tbl.width, sizeof(**tbl.contents));
2230 tbl.contents[0][0] = "Server Name";
2231 tbl.contents[0][1] = "Clients";
2232 tbl.contents[0][2] = "Load";
2233 for (it=dict_first(servers), nn=1; it; it=iter_next(it)) {
2234 struct server *server = iter_data(it);
2235 char *buffer = malloc(32);
2236 tbl.contents[nn] = calloc(tbl.width, sizeof(**tbl.contents));
2237 tbl.contents[nn][0] = server->name;
2238 tbl.contents[nn][1] = buffer;
2239 sprintf(buffer, "%u", server->clients);
2240 tbl.contents[nn][2] = buffer + 16;
2241 sprintf(buffer+16, "%3.3g%%", ((double)server->clients/tot_clients)*100);
2242 nn++;
2243 }
2244 table_send(cmd->parent->bot, user->nick, 0, 0, tbl);
2245 for (nn=1; nn<tbl.length; nn++) {
2246 free((char*)tbl.contents[nn][1]);
2247 free(tbl.contents[nn]);
2248 }
2249 free(tbl.contents[0]);
2250 free(tbl.contents);
2251 return 1;
2252}
2253
2254static MODCMD_FUNC(cmd_stats_network2) {
2255 struct helpfile_table tbl;
2256 unsigned int nn;
2257 dict_iterator_t it;
2258
2259 tbl.length = dict_size(servers)+1;
2260 tbl.width = 3;
2261 tbl.flags = TABLE_NO_FREE;
2262 tbl.contents = calloc(tbl.length, sizeof(*tbl.contents));
2263 tbl.contents[0] = calloc(tbl.width, sizeof(**tbl.contents));
2264 tbl.contents[0][0] = "Server Name";
2265 tbl.contents[0][1] = "Numeric";
2266 tbl.contents[0][2] = "Link Time";
2267 for (it=dict_first(servers), nn=1; it; it=iter_next(it)) {
2268 struct server *server = iter_data(it);
2269 char *buffer = malloc(64);
2270 int ofs;
2271
2272 tbl.contents[nn] = calloc(tbl.width, sizeof(**tbl.contents));
2273 tbl.contents[nn][0] = server->name;
2274#ifdef WITH_PROTOCOL_P10
2275 sprintf(buffer, "%s (%ld)", server->numeric, base64toint(server->numeric, strlen(server->numeric)));
2276#else
2277 buffer[0] = 0;
2278#endif
2279 tbl.contents[nn][1] = buffer;
2280 ofs = strlen(buffer) + 1;
2281 intervalString(buffer + ofs, now - server->link, user->handle_info);
2282 if (server->self_burst)
2283 strcat(buffer + ofs, " Bursting");
2284 tbl.contents[nn][2] = buffer + ofs;
2285 nn++;
2286 }
2287 table_send(cmd->parent->bot, user->nick, 0, 0, tbl);
2288 for (nn=1; nn<tbl.length; nn++) {
2289 free((char*)tbl.contents[nn][1]);
2290 free(tbl.contents[nn]);
2291 }
2292 free(tbl.contents[0]);
2293 free(tbl.contents);
2294 return 1;
2295}
2296
2297static MODCMD_FUNC(cmd_stats_reserved) {
2298 dict_iterator_t it;
2299
2300 reply("OSMSG_RESERVED_LIST");
2301 for (it = dict_first(opserv_reserved_nick_dict); it; it = iter_next(it))
2302 send_message_type(4, user, cmd->parent->bot, "%s", iter_key(it));
2303 return 1;
2304}
2305
2306static MODCMD_FUNC(cmd_stats_trusted) {
2307 dict_iterator_t it;
2308 struct trusted_host *th;
2309 char length[INTERVALLEN], issued[INTERVALLEN], limit[32];
2310
de9510bc 2311 reply("OSMSG_TRUSTED_LIST");
2312 reply("OSMSG_TRUSTED_LIST_BAR");
2313 reply("OSMSG_TRUSTED_LIST_HEADER");
2314 reply("OSMSG_TRUSTED_LIST_BAR");
d76ed9a9 2315 if (argc > 1) {
2316 th = dict_find(opserv_trusted_hosts, argv[1], NULL);
2317 if (th) {
2318 if (th->issued)
2319 intervalString(issued, now - th->issued, user->handle_info);
2320 if (th->expires)
2321 intervalString(length, th->expires - now, user->handle_info);
2322 if (th->limit)
de9510bc 2323 sprintf(limit, "%lu", th->limit);
d76ed9a9 2324 reply("OSMSG_HOST_IS_TRUSTED",
2325 th->ipaddr,
de9510bc 2326 (th->limit ? limit : "none"),
d76ed9a9 2327 (th->issuer ? th->issuer : "<unknown>"),
de9510bc 2328 (th->issued ? issued : "some time"),
2329 (th->expires ? length : "never"));
2330 reply("OSMSG_HOST_IS_TRUSTED_DESC", (th->reason ? th->reason : "<unknown>"));
d76ed9a9 2331 } else {
2332 reply("OSMSG_HOST_NOT_TRUSTED", argv[1]);
2333 }
2334 } else {
d76ed9a9 2335 for (it = dict_first(opserv_trusted_hosts); it; it = iter_next(it)) {
2336 th = iter_data(it);
2337 if (th->issued)
2338 intervalString(issued, now - th->issued, user->handle_info);
2339 if (th->expires)
2340 intervalString(length, th->expires - now, user->handle_info);
2341 if (th->limit)
de9510bc 2342 sprintf(limit, "%lu", th->limit);
d76ed9a9 2343 reply("OSMSG_HOST_IS_TRUSTED", iter_key(it),
de9510bc 2344 (th->limit ? limit : "none"),
d76ed9a9 2345 (th->issuer ? th->issuer : "<unknown>"),
de9510bc 2346 (th->issued ? issued : "some time"),
2347 (th->expires ? length : "never"));
2348 reply("OSMSG_HOST_IS_TRUSTED_DESC", (th->reason ? th->reason : "<unknown>"));
d76ed9a9 2349 }
2350 }
de9510bc 2351 reply("OSMSG_TRUSTED_LIST_END");
d76ed9a9 2352 return 1;
2353}
2354
2355static MODCMD_FUNC(cmd_stats_uplink) {
2356 extern struct cManagerNode cManager;
2357 struct uplinkNode *uplink;
2358
2359 uplink = cManager.uplink;
2360 reply("OSMSG_UPLINK_START", uplink->name);
2361 reply("OSMSG_UPLINK_ADDRESS", uplink->host, uplink->port);
2362 return 1;
2363}
2364
2365static MODCMD_FUNC(cmd_stats_uptime) {
2366 char uptime[INTERVALLEN];
2367 struct tms buf;
2368 extern time_t boot_time;
2369 extern int lines_processed;
2370 static long clocks_per_sec;
2371
2372 if (!clocks_per_sec) {
2373#if defined(HAVE_SYSCONF) && defined(_SC_CLK_TCK)
2374 clocks_per_sec = sysconf(_SC_CLK_TCK);
2375 if (clocks_per_sec <= 0)
2376#endif
2377 {
2378 log_module(OS_LOG, LOG_ERROR, "Unable to query sysconf(_SC_CLK_TCK), output of 'stats uptime' will be wrong");
2379 clocks_per_sec = CLOCKS_PER_SEC;
2380 }
2381 }
2382 intervalString(uptime, time(NULL)-boot_time, user->handle_info);
2383 times(&buf);
2384 reply("OSMSG_UPTIME_STATS",
2385 uptime, lines_processed,
2386 buf.tms_utime/(double)clocks_per_sec,
2387 buf.tms_stime/(double)clocks_per_sec);
2388 return 1;
2389}
2390
2391static MODCMD_FUNC(cmd_stats_alerts) {
2392 dict_iterator_t it;
2393 struct opserv_user_alert *alert;
2394 const char *reaction;
eaf715f1 2395 char *m = NULL;
d76ed9a9 2396
eaf715f1 2397 if(argc > 1)
2398 m = unsplit_string(argv + 1, argc - 1, NULL);
2399 reply("OSMSG_ALERTS_LIST", m ? m : "*");
de9510bc 2400 reply("OSMSG_ALERTS_BAR");
2401 reply("OSMSG_ALERTS_HEADER");
2402 reply("OSMSG_ALERTS_BAR");
d76ed9a9 2403 for (it = dict_first(opserv_user_alerts); it; it = iter_next(it)) {
2404 alert = iter_data(it);
eaf715f1 2405 if(m && (!match_ircglob(alert->text_discrim, m) && strcasecmp(alert->owner, m)) )
61eb54f9 2406 continue; /* not a match to requested filter */
d76ed9a9 2407 switch (alert->reaction) {
2408 case REACT_NOTICE: reaction = "notice"; break;
2409 case REACT_KILL: reaction = "kill"; break;
1c5f6697 2410// case REACT_SILENT: reaction = "silent"; break;
d76ed9a9 2411 case REACT_GLINE: reaction = "gline"; break;
ec311f39 2412 case REACT_TRACK: reaction = "track"; break;
d914d1cb 2413 case REACT_SHUN: reaction = "shun"; break;
c408f18a 2414 case REACT_SVSJOIN: reaction = "svsjoin"; break;
39c1a4ef 2415 case REACT_SVSPART: reaction = "svspart"; break;
0e08a8e0 2416 case REACT_VERSION: reaction = "version"; break;
d82cf2f0 2417 case REACT_MARK: reaction = "mark"; break;
d76ed9a9 2418 default: reaction = "<unknown>"; break;
2419 }
de9510bc 2420 reply("OSMSG_ALERT_IS", iter_key(it), reaction, alert->owner);
2421 reply("OSMSG_ALERTS_DESC", alert->text_discrim);
d76ed9a9 2422 }
de9510bc 2423 reply("OSMSG_ALERT_END");
d76ed9a9 2424 return 1;
2425}
2426
2427static MODCMD_FUNC(cmd_stats_gags) {
2428 struct gag_entry *gag;
2429 struct helpfile_table table;
2430 unsigned int nn;
2431
2432 if (!gagList) {
258d1427 2433 reply("OSMSG_NO_GAGS");
d76ed9a9 2434 return 1;
2435 }
2436 for (nn=0, gag=gagList; gag; nn++, gag=gag->next) ;
2437 table.length = nn+1;
2438 table.width = 4;
2439 table.flags = TABLE_NO_FREE;
2440 table.contents = calloc(table.length, sizeof(char**));
2441 table.contents[0] = calloc(table.width, sizeof(char*));
2442 table.contents[0][0] = "Mask";
2443 table.contents[0][1] = "Owner";
2444 table.contents[0][2] = "Expires";
2445 table.contents[0][3] = "Reason";
2446 for (nn=1, gag=gagList; gag; nn++, gag=gag->next) {
2447 char expstr[INTERVALLEN];
2448 if (gag->expires)
2449 intervalString(expstr, gag->expires - now, user->handle_info);
2450 else
2451 strcpy(expstr, "Never");
2452 table.contents[nn] = calloc(table.width, sizeof(char*));
2453 table.contents[nn][0] = gag->mask;
2454 table.contents[nn][1] = gag->owner;
2455 table.contents[nn][2] = strdup(expstr);
2456 table.contents[nn][3] = gag->reason;
2457 }
2458 table_send(cmd->parent->bot, user->nick, 0, NULL, table);
2459 for (nn=1; nn<table.length; nn++) {
2460 free((char*)table.contents[nn][2]);
2461 free(table.contents[nn]);
2462 }
2463 free(table.contents[0]);
2464 free(table.contents);
2465 return 1;
2466}
2467
2468static MODCMD_FUNC(cmd_stats_timeq) {
2469 reply("OSMSG_TIMEQ_INFO", timeq_size(), timeq_next()-now);
2470 return 1;
2471}
2472
de9510bc 2473/*
d76ed9a9 2474static MODCMD_FUNC(cmd_stats_warn) {
2475 dict_iterator_t it;
2476
2477 reply("OSMSG_WARN_LISTSTART");
2478 for (it=dict_first(opserv_chan_warn); it; it=iter_next(it))
2479 reply("OSMSG_WARN_LISTENTRY", iter_key(it), (char*)iter_data(it));
2480 reply("OSMSG_WARN_LISTEND");
2481 return 1;
2482}
de9510bc 2483*/
d76ed9a9 2484
f14e4f83 2485#if defined(WITH_MALLOC_X3)
ec1a68c8 2486static MODCMD_FUNC(cmd_stats_memory) {
2487 extern unsigned long alloc_count, alloc_size;
0d16e639 2488 send_message_type(MSG_TYPE_NOXLATE, user, cmd->parent->bot,
2489 "%u allocations totalling %u bytes.",
2490 alloc_count, alloc_size);
2491 return 1;
2492}
2493#elif defined(WITH_MALLOC_SLAB)
2494static MODCMD_FUNC(cmd_stats_memory) {
2495 extern unsigned long slab_alloc_count, slab_count, slab_alloc_size;
2496 extern unsigned long big_alloc_count, big_alloc_size;
2497 send_message_type(MSG_TYPE_NOXLATE, user, cmd->parent->bot,
2498 "%u allocations in %u slabs totalling %u bytes.",
2499 slab_alloc_count, slab_count, slab_alloc_size);
63c95a47 2500 send_message_type(MSG_TYPE_NOXLATE, user, cmd->parent->bot,
0d16e639 2501 "%u big allocations totalling %u bytes.",
63c95a47 2502 big_alloc_count, big_alloc_size);
ec1a68c8 2503 return 1;
2504}
2505#endif
2506
d76ed9a9 2507static MODCMD_FUNC(cmd_dump)
2508{
b336c8db 2509 char linedup[MAXLEN], original[MAXLEN];
d76ed9a9 2510
b336c8db 2511 unsplit_string(argv+1, argc-1, original);
d76ed9a9 2512 safestrncpy(linedup, original, sizeof(linedup));
2513 /* assume it's only valid IRC if we can parse it */
2514 if (parse_line(linedup, 1)) {
258d1427 2515 irc_raw(original);
2516 reply("OSMSG_LINE_DUMPED");
d76ed9a9 2517 } else
258d1427 2518 reply("OSMSG_RAW_PARSE_ERROR");
d76ed9a9 2519 return 1;
2520}
2521
2522static MODCMD_FUNC(cmd_raw)
2523{
b336c8db 2524 char linedup[MAXLEN], original[MAXLEN];
d76ed9a9 2525
b336c8db 2526 unsplit_string(argv+1, argc-1, original);
d76ed9a9 2527 safestrncpy(linedup, original, sizeof(linedup));
2528 /* Try to parse the line before sending it; if it's too wrong,
2529 * maybe it will core us instead of our uplink. */
2530 parse_line(linedup, 1);
2531 irc_raw(original);
2532 reply("OSMSG_LINE_DUMPED");
2533 return 1;
2534}
2535
2536static struct userNode *
2537opserv_add_reserve(struct svccmd *cmd, struct userNode *user, const char *nick, const char *ident, const char *host, const char *desc)
2538{
2539 struct userNode *resv = GetUserH(nick);
2540 if (resv) {
258d1427 2541 if (IsService(resv)) {
2542 reply("MSG_SERVICE_IMMUNE", resv->nick);
2543 return NULL;
2544 }
2545 if (resv->handle_info
2546 && resv->handle_info->opserv_level > user->handle_info->opserv_level) {
2547 reply("OSMSG_LEVEL_TOO_LOW");
2548 return NULL;
2549 }
d76ed9a9 2550 }
2551 if ((resv = AddClone(nick, ident, host, desc))) {
2552 dict_insert(opserv_reserved_nick_dict, resv->nick, resv);
2553 }
2554 return resv;
2555}
2556
2557static MODCMD_FUNC(cmd_collide)
2558{
2559 struct userNode *resv;
2560
2561 resv = opserv_add_reserve(cmd, user, argv[1], argv[2], argv[3], unsplit_string(argv+4, argc-4, NULL));
2562 if (resv) {
258d1427 2563 reply("OSMSG_COLLIDED_NICK", resv->nick);
2564 return 1;
d76ed9a9 2565 } else {
2566 reply("OSMSG_CLONE_FAILED", argv[1]);
258d1427 2567 return 0;
d76ed9a9 2568 }
2569}
2570
2571static MODCMD_FUNC(cmd_reserve)
2572{
2573 struct userNode *resv;
2574
2575 resv = opserv_add_reserve(cmd, user, argv[1], argv[2], argv[3], unsplit_string(argv+4, argc-4, NULL));
2576 if (resv) {
258d1427 2577 resv->modes |= FLAGS_PERSISTENT;
2578 reply("OSMSG_RESERVED_NICK", resv->nick);
2579 return 1;
d76ed9a9 2580 } else {
2581 reply("OSMSG_CLONE_FAILED", argv[1]);
258d1427 2582 return 0;
d76ed9a9 2583 }
2584}
2585
2586static int
2587free_reserve(char *nick)
2588{
2589 struct userNode *resv;
2590 unsigned int rlen;
2591 char *reason;
2592
2593 resv = dict_find(opserv_reserved_nick_dict, nick, NULL);
2594 if (!resv)
2595 return 0;
2596
2597 rlen = strlen(resv->nick)+strlen(OSMSG_PART_REASON);
2598 reason = alloca(rlen);
2599 snprintf(reason, rlen, OSMSG_PART_REASON, resv->nick);
2600 DelUser(resv, NULL, 1, reason);
2601 dict_remove(opserv_reserved_nick_dict, nick);
2602 return 1;
2603}
2604
2605static MODCMD_FUNC(cmd_unreserve)
2606{
2607 if (free_reserve(argv[1]))
258d1427 2608 reply("OSMSG_NICK_UNRESERVED", argv[1]);
d76ed9a9 2609 else
258d1427 2610 reply("OSMSG_NOT_RESERVED", argv[1]);
d76ed9a9 2611 return 1;
2612}
2613
2614static void
2615opserv_part_channel(void *data)
2616{
2617 DelChannelUser(opserv, data, "Leaving.", 0);
2618}
2619
2620static int alert_check_user(const char *key, void *data, void *extra);
2621
2622static int
2623opserv_new_user_check(struct userNode *user)
2624{
2625 struct opserv_hostinfo *ohi;
2626 struct gag_entry *gag;
2f61d1d7 2627 char addr[IRC_NTOP_MAX_SIZE];
d76ed9a9 2628
2629 /* Check to see if we should ignore them entirely. */
2630 if (IsLocal(user) || IsService(user))
2631 return 0;
2632
2633 /* Check for alerts, and stop if we find one that kills them. */
2634 if (dict_foreach(opserv_user_alerts, alert_check_user, user))
2635 return 1;
2636
2637 /* Gag them if appropriate. */
2638 for (gag = gagList; gag; gag = gag->next) {
2f61d1d7 2639 if (user_matches_glob(user, gag->mask, MATCH_USENICK)) {
d76ed9a9 2640 gag_helper_func(user, NULL);
2641 break;
2642 }
2643 }
2644
2645 /* Add to host info struct */
2f61d1d7 2646 irc_ntop(addr, sizeof(addr), &user->ip);
2647 if (!(ohi = dict_find(opserv_hostinfo_dict, addr, NULL))) {
d76ed9a9 2648 ohi = calloc(1, sizeof(*ohi));
2f61d1d7 2649 dict_insert(opserv_hostinfo_dict, strdup(addr), ohi);
d76ed9a9 2650 userList_init(&ohi->clients);
2651 }
2652 userList_append(&ohi->clients, user);
2653
2654 /* Only warn of new user floods outside of bursts. */
2655 if (!user->uplink->burst) {
2656 if (!policer_conforms(&opserv_conf.new_user_policer, now, 10)) {
2657 if (!new_user_flood) {
2658 new_user_flood = 1;
2659 opserv_alert("Warning: Possible new-user flood.");
2660 }
2661 } else {
2662 new_user_flood = 0;
2663 }
2664 }
2665
08895577 2666 if (checkDefCon(DEFCON_NO_NEW_CLIENTS)) {
2667 irc_kill(opserv, user, DefConGlineReason);
2668 return 0;
2669 }
2670
0272358e 2671 if ( (checkDefCon(DEFCON_GLINE_NEW_CLIENTS) || checkDefCon(DEFCON_SHUN_NEW_CLIENTS)) && !IsOper(user)) {
2672 char target[IRC_NTOP_MAX_SIZE + 3] = { '*', '@', '\0' };
2673
2674 strcpy(target + 2, user->hostname);
2675 if (checkDefCon(DEFCON_GLINE_NEW_CLIENTS))
2676 gline_add(opserv->nick, target, DefConGlineExpire, DefConGlineReason, now, 1, 0);
2677 else if (checkDefCon(DEFCON_SHUN_NEW_CLIENTS))
2678 shun_add(opserv->nick, target, DefConGlineExpire, DefConGlineReason, now, 1);
2679
2680 return 0;
2681 }
2682
d76ed9a9 2683 /* Only warn or G-line if there's an untrusted max and their IP is sane. */
2f61d1d7 2684 if (opserv_conf.untrusted_max
2685 && irc_in_addr_is_valid(user->ip)
2686 && !irc_in_addr_is_loopback(user->ip)) {
2687 struct trusted_host *th = dict_find(opserv_trusted_hosts, addr, NULL);
d76ed9a9 2688 unsigned int limit = th ? th->limit : opserv_conf.untrusted_max;
08895577 2689
2690 if (checkDefCon(DEFCON_REDUCE_SESSION) && !th)
2691 limit = DefConSessionLimit;
2692
d76ed9a9 2693 if (!limit) {
2694 /* 0 means unlimited hosts */
2695 } else if (ohi->clients.used == limit) {
2696 unsigned int nn;
2697 for (nn=0; nn<ohi->clients.used; nn++)
2698 send_message(ohi->clients.list[nn], opserv, "OSMSG_CLONE_WARNING");
2699 } else if (ohi->clients.used > limit) {
2f61d1d7 2700 char target[IRC_NTOP_MAX_SIZE + 3] = { '*', '@', '\0' };
2701 strcpy(target + 2, addr);
9a75756e 2702 gline_add(opserv->nick, target, opserv_conf.clone_gline_duration, "Excessive connections from a single host.", now, 1, 1);
d76ed9a9 2703 }
2704 }
2705
2706 return 0;
2707}
2708
2709static void
2710opserv_user_cleanup(struct userNode *user, UNUSED_ARG(struct userNode *killer), UNUSED_ARG(const char *why))
2711{
2712 struct opserv_hostinfo *ohi;
2f61d1d7 2713 char addr[IRC_NTOP_MAX_SIZE];
d76ed9a9 2714
2715 if (IsLocal(user)) {
2716 /* Try to remove it from the reserved nick dict without
2717 * calling free_reserve, because that would call DelUser(),
2718 * and we'd loop back to here. */
2719 dict_remove(opserv_reserved_nick_dict, user->nick);
2720 return;
2721 }
2f61d1d7 2722 irc_ntop(addr, sizeof(addr), &user->ip);
2723 if ((ohi = dict_find(opserv_hostinfo_dict, addr, NULL))) {
d76ed9a9 2724 userList_remove(&ohi->clients, user);
2f61d1d7 2725 if (ohi->clients.used == 0)
2726 dict_remove(opserv_hostinfo_dict, addr);
d76ed9a9 2727 }
2728}
2729
2730int
2731opserv_bad_channel(const char *name)
2732{
2733 unsigned int found;
697f4c9a 2734 int present;
d76ed9a9 2735
697f4c9a 2736 dict_find(opserv_exempt_channels, name, &present);
2737 if (present)
d76ed9a9 2738 return 0;
2739
2740 if (gline_find(name))
2741 return 1;
2742
2743 for (found=0; found<opserv_bad_words->used; ++found)
2744 if (irccasestr(name, opserv_bad_words->list[found]))
2745 return 1;
2746
2747 return 0;
2748}
2749
2750static void
2751opserv_shutdown_channel(struct chanNode *channel, const char *reason)
2752{
2753 struct mod_chanmode *change;
2754 unsigned int nn;
2755
2756 change = mod_chanmode_alloc(2);
2757 change->modes_set = MODE_SECRET | MODE_INVITEONLY;
2758 change->args[0].mode = MODE_CHANOP;
a32da4c7 2759 change->args[0].u.member = AddChannelUser(opserv, channel);
d76ed9a9 2760 change->args[1].mode = MODE_BAN;
a32da4c7 2761 change->args[1].u.hostmask = "*!*@*";
d76ed9a9 2762 mod_chanmode_announce(opserv, channel, change);
2763 mod_chanmode_free(change);
2764 for (nn=channel->members.used; nn>0; ) {
2765 struct modeNode *mNode = channel->members.list[--nn];
2766 if (IsService(mNode->user))
2767 continue;
2768 KickChannelUser(mNode->user, channel, opserv, user_find_message(mNode->user, reason));
2769 }
2770 timeq_add(now + opserv_conf.purge_lock_delay, opserv_part_channel, channel);
2771}
2772
2773static void
2774opserv_channel_check(struct chanNode *newchan)
2775{
c52666c6 2776 /*char *warning; */
d76ed9a9 2777
2778 if (!newchan->join_policer.params) {
2779 newchan->join_policer.last_req = now;
2780 newchan->join_policer.params = opserv_conf.join_policer_params;
2781 }
c52666c6 2782 /*
d76ed9a9 2783 if ((warning = dict_find(opserv_chan_warn, newchan->name, NULL))) {
57692f5e 2784 global_message_args(MESSAGE_RECIPIENT_OPERS, "OSMSG_CHANNEL_ACTIVITY_WARN", newchan->name, warning);
d76ed9a9 2785 }
c52666c6 2786 */
d76ed9a9 2787
c52666c6 2788 /* Wait until the join check to shut channels down. */
d76ed9a9 2789 newchan->bad_channel = opserv_bad_channel(newchan->name);
2790}
2791
2792static void
2793opserv_channel_delete(struct chanNode *chan)
2794{
2795 timeq_del(0, opserv_part_channel, chan, TIMEQ_IGNORE_WHEN);
2796}
2797
0e08a8e0 2798static void
2799opserv_notice_handler(struct userNode *user, struct userNode *bot, char *text, UNUSED_ARG(int server_qualified))
2800{
2801 char *cmd;
2802 /* if its a version reply, do an alert check (only alerts with version=something) */
2803 if(bot == opserv) {
2804 if(text[0] == '\001') {
2805 text++;
2806 cmd = mysep(&text, " ");
2807 if(!irccasecmp(cmd, "VERSION")) {
2808 char *version = mysep(&text, "\n");
2809 if(!version)
2810 version = "";
2811 /* opserv_debug("Opserv got CTCP VERSION Notice from %s: %s", user->nick, version); */
da5b7dfc 2812 /* user->version_reply = strdup(version); done in parse-p10.c now */
0e08a8e0 2813 dict_foreach(opserv_user_alerts, alert_check_user, user);
2814 }
2815 }
2816 }
2817}
2818
d76ed9a9 2819static int
2820opserv_join_check(struct modeNode *mNode)
2821{
2822 struct userNode *user = mNode->user;
2823 struct chanNode *channel = mNode->channel;
2824 const char *msg;
2825
2826 if (IsService(user))
2827 return 0;
2828
2829 dict_foreach(opserv_channel_alerts, alert_check_user, user);
2830
2831 if (channel->bad_channel) {
2832 opserv_debug("Found $b%s$b in bad-word channel $b%s$b; removing the user.", user->nick, channel->name);
2833 if (channel->name[0] != '#')
2834 DelUser(user, opserv, 1, "OSMSG_ILLEGAL_KILL_REASON");
2835 else if (!GetUserMode(channel, opserv))
2836 opserv_shutdown_channel(channel, "OSMSG_ILLEGAL_REASON");
2837 else {
2838 send_message(user, opserv, "OSMSG_ILLEGAL_CHANNEL", channel->name);
2839 msg = user_find_message(user, "OSMSG_ILLEGAL_REASON");
2840 KickChannelUser(user, channel, opserv, msg);
2841 }
2842 return 1;
2843 }
2844
2845 if (user->uplink->burst)
2846 return 0;
2847 if (policer_conforms(&channel->join_policer, now, 1.0)) {
2848 channel->join_flooded = 0;
2849 return 0;
2850 }
2851 if (!channel->join_flooded) {
2852 /* Don't moderate the channel unless it is activated and
2853 the number of users in the channel is over the threshold. */
2854 struct mod_chanmode change;
2855 mod_chanmode_init(&change);
2856 channel->join_flooded = 1;
2857 if (opserv_conf.join_flood_moderate && (channel->members.used > opserv_conf.join_flood_moderate_threshold)) {
2858 if (!GetUserMode(channel, opserv)) {
2859 /* If we aren't in the channel, join it. */
2860 change.args[0].mode = MODE_CHANOP;
a32da4c7 2861 change.args[0].u.member = AddChannelUser(opserv, channel);
d76ed9a9 2862 change.argc++;
2863 }
2864 if (!(channel->modes & MODE_MODERATED))
2865 change.modes_set |= MODE_MODERATED;
2866 if (change.modes_set || change.argc)
2867 mod_chanmode_announce(opserv, channel, &change);
2868 send_target_message(0, channel->name, opserv, "OSMSG_FLOOD_MODERATE");
2869 opserv_alert("Warning: Possible join flood in %s (currently %d users; channel moderated).", channel->name, channel->members.used);
2870 } else {
2871 opserv_alert("Warning: Possible join flood in %s (currently %d users).", channel->name, channel->members.used);
2872 }
2873 }
2874 log_module(OS_LOG, LOG_INFO, "Join to %s during flood: "IDENT_FORMAT, channel->name, IDENT_DATA(user));
2875 return 0;
2876}
2877
2878static int
2879opserv_add_bad_word(struct svccmd *cmd, struct userNode *user, const char *new_bad) {
2880 unsigned int bad_idx;
2881
2882 for (bad_idx = 0; bad_idx < opserv_bad_words->used; ++bad_idx) {
2883 char *orig_bad = opserv_bad_words->list[bad_idx];
2884 if (irccasestr(new_bad, orig_bad)) {
2885 if (user)
2886 reply("OSMSG_BAD_REDUNDANT", new_bad, orig_bad);
2887 return 0;
2888 } else if (irccasestr(orig_bad, new_bad)) {
2889 if (user)
2890 reply("OSMSG_BAD_GROWING", orig_bad, new_bad);
2891 free(orig_bad);
2892 opserv_bad_words->list[bad_idx] = strdup(new_bad);
2893 for (bad_idx++; bad_idx < opserv_bad_words->used; bad_idx++) {
2894 orig_bad = opserv_bad_words->list[bad_idx];
2895 if (!irccasestr(orig_bad, new_bad))
2896 continue;
2897 if (user)
2898 reply("OSMSG_BAD_NUKING", orig_bad);
2899 string_list_delete(opserv_bad_words, bad_idx);
2900 bad_idx--;
2901 free(orig_bad);
2902 }
2903 return 1;
2904 }
2905 }
2906 string_list_append(opserv_bad_words, strdup(new_bad));
2907 if (user)
2908 reply("OSMSG_ADDED_BAD", new_bad);
2909 return 1;
2910}
2911
47956fc5 2912static int
2913opserv_routing_plan_add_server(struct routingPlan *rp, const char *name, const char *uplink, const unsigned int port, int karma, const char *second, const unsigned int offline)
2914{
2915 struct routingPlanServer *rps;
2916 rps = calloc(1, sizeof(*rps));
2917 if(!rps)
2918 return 0;
2919 /* duplicate servers replace */
2920 rps->uplink = strdup(uplink);
2921 if(second)
2922 rps->secondaryuplink = strdup(second);
2923 else
2924 rps->secondaryuplink = NULL;
2925 rps->port = port ? port : 4400; /* lame hardcodede default port. maybe get from config file somewhere? */
2926 rps->karma = karma;
2927 rps->offline = offline; /* 1 = yes, 0 = no */
2928 dict_insert(rp->servers, strdup(name), rps);
2929 log_module(OS_LOG, LOG_DEBUG, "Adding rp server %s with uplink %s", name, uplink);
2930 return 1;
2931}
2932
2933static void
2934free_routing_plan_server(void *data)
2935{
2936 struct routingPlanServer *rps = data;
2937 free(rps->uplink);
2938 if(rps->secondaryuplink)
2939 free(rps->secondaryuplink);
2940 free(rps);
2941}
2942
2943struct routingPlan*
2944opserv_add_routing_plan(const char *name)
2945{
2946 struct routingPlan *rp;
2947 rp = calloc(1, sizeof(*rp));
2948 if (!rp)
2949 return NULL;
2950 if(dict_find(opserv_routing_plans, name, NULL))
2951 return NULL; /* plan already exists */
2952 rp->servers = dict_new();
2953 dict_set_free_data(rp->servers, free_routing_plan_server);
2954
2955 dict_insert(opserv_routing_plans, strdup(name), rp);
2956 /* TODO: check for duplicate */
2957 return rp;
2958}
2959
2960static void
2961free_routing_plan(void *data)
2962{
2963 struct routingPlan *rp = data;
2964 /* delete all the servers attached to this plan */
2965 dict_delete(rp->servers);
2966 /* free the plan struct */
2967 free(rp);
2968}
2969
47956fc5 2970/*************************************************
2971* Functions to handle the active routing struct */
2972
2973struct routeList
2974*find_routeList_server(struct route *route, const char *server)
2975{
2976 struct routeList *rptr;
2977 if(!server)
2978 return(NULL);
2979 for(rptr = route->servers;rptr;rptr=rptr->next) {
2980 if(!strcasecmp(rptr->server, server))
2981 return(rptr);
2982 }
2983 return(NULL);
2984}
2985
2986/* Wipes out the routing structure, freeing properly.
2987 * note: does NOT free itself, we just re-use it usually.*/
2988void
2989wipe_route_list(struct route *route) {
2990 struct routeList *nextptr, *rptr;
2991 if(!route)
2992 return;
2993 for(rptr = opserv_route->servers; rptr; rptr=nextptr)
2994 {
2995 nextptr = rptr->next;
2996 free(rptr->server);
2997 if(rptr->uplink)
2998 free(rptr->uplink);
2999 if(rptr->secondaryuplink)
3000 free(rptr->secondaryuplink);
3001 free(rptr);
3002 }
3003 route->centered = true;
3004 route->count = 0;
3005 route->maxdepth = 0;
3006 route->servers = NULL;
3007}
3008
3009
3010int
3011rank_outside_rec(struct route *route, char *server, int count)
3012{
3013 struct routeList *rptr;
3014 int n, max = 0;
3015 int i = 0;
3016 if(count > 256) { /* XXX: 256 becomes max # of servers this works with, whats the real #? */
3017 return -1;
3018 }
3019 for(rptr = route->servers; rptr; rptr = rptr->next) {
3020 i++;
3021 if(!strcasecmp(server, rptr->uplink)) {
3022 log_module(MAIN_LOG, LOG_DEBUG, "%d:%d: rank_outside_rec(%s) calling rank_outside_rec(%s)", count, i, rptr->server, rptr->uplink);
3023 n = rank_outside_rec(route, rptr->server, count +1);
3024 if(n < 0) /* handle error condition */
3025 return n;
3026 if(n > max)
3027 max = n;
3028 }
3029 }
3030 if((rptr = find_routeList_server(route, server))) {
3031 rptr->outsideness = max;
3032 return(max + 1);
3033 }
3034 else {
3035 log_module(MAIN_LOG, LOG_ERROR, "routing struct rank_outsideness() couldnt find %s", server);
3036 return 0;
3037 }
3038}
3039
3040int
3041rank_outsideness(struct route *route)
3042{
3043 log_module(MAIN_LOG, LOG_DEBUG, "rank_outsideness(): Running...");
3044 route->maxdepth = rank_outside_rec(route, self->uplink->name, 0) - 1;
3045 if(route->maxdepth < 0) { /* if the rank failed, remove route */
3046 log_module(MAIN_LOG, LOG_WARNING, "The active routing plan has a loop! auto routing disabled.");
3047 wipe_route_list(route);
3048 return false;
3049 }
3050 return true;
3051}
3052
3053
3054/* Add servers to the routing structure */
3055void
3056add_routestruct_server(struct route *route, const char *server, unsigned int port, char *uplink, char *secondary)
3057{
3058 struct routeList *rptr;
3059 char *hname;
3060 if(find_routeList_server(route, server))
3061 {
3062 log_module(MAIN_LOG, LOG_WARNING, "Routing structure add server Skipping duplicate [%s]. This should never really be possible.", server);
3063 return;
3064 }
3065 rptr = calloc(1, sizeof(*rptr));
3066 rptr->server = strdup(server);
3067 rptr->port = port;
3068 if(!uplink) {
3069 hname = conf_get_data("server/hostname", RECDB_QSTRING);
3070 uplink = hname;
3071 }
3072 rptr->uplink = strdup(uplink);
3073 if(secondary)
3074 rptr->secondaryuplink = strdup(secondary);
3075 /* tack this server on the front of the list */
3076 rptr->next = route->servers;
3077 route->servers = rptr;
3078 route->count++;
3079
3080#ifdef notdef /* I dont quite get this. there could be uncentered things
3081 * added after our own uplink, and this function doesnt center
3082 * as it adds. -Rubin */
3083 /* If the map hasnt been centered yet... */
3084 if(route->centered == false) {
3085 /* AND we just added our own uplink to it... */
3086 if(!strcasecmp(server, self->uplink->name)) {
3087 change_route_uplinks(route); /* recenter it, n mark it centered. */
3088 }
3089 }
3090#endif
3091}
3092
3093/* Recenter the routing struct around our current uplink */
3094int
3095change_route_uplinks(struct route *route)
3096{
3097 struct routeList *rptr;
3098 char lastserver[MAXLEN];
3099 char nextserver[MAXLEN];
3100
3101 if(!route->servers)
3102 return false; /* no map to recenter */
3103 log_module(MAIN_LOG, LOG_DEBUG, "change_route_uplinks(): running...");
3104 char *servicename = conf_get_data("server/hostname", RECDB_QSTRING);
3105 strcpy(lastserver, servicename);
3106 rptr = find_routeList_server(route, self->uplink->name);
3107 if(!rptr) {
3108 log_module(MAIN_LOG, LOG_ERROR, "Cannot convert routing map to center: My uplink is not on the map! Marking map as uncentered.");
3109 route->centered = false;
3110 return false;
3111 }
3112 if(!strcasecmp(rptr->uplink, servicename)) {
3113 log_module(MAIN_LOG, LOG_DEBUG, "Already centered");
3114 }
3115 else { /* else, center it */
3116 while(rptr) {
3117 strcpy(nextserver, rptr->uplink);
3118 log_module(MAIN_LOG, LOG_DEBUG, "change_route_uplinks() changing %s uplink to %s.", rptr->server, lastserver);
3119 free(rptr->uplink);
3120 rptr->uplink = strdup(lastserver);
3121 strcpy(lastserver, rptr->server);
3122 rptr = find_routeList_server(route, nextserver);
3123 }
3124 }
3125 if(rank_outsideness(route) > 0) {
3126 route->centered = true;
3127 return true;
3128 }
3129 else
3130 return false;
3131}
3132
3133int
3134activate_routing(struct svccmd *cmd, struct userNode *user, char *plan_name)
3135{
3136 static struct routingPlan *rp;
3137 dict_iterator_t it;
3138 char *karma;
3139
3140 if(plan_name) { /* make this the new active plan */
3141 if(!strcmp(plan_name, "*")) {
3142 /* disable routing */
3143 dict_remove(opserv_routing_plan_options, "ACTIVE");
3144 plan_name = NULL;
3145 }
3146 else {
3147 rp = dict_find(opserv_routing_plans, plan_name, NULL);
3148 if(!rp) {
3149 if(cmd && user)
3150 reply("OSMSG_PLAN_NOT_FOUND", plan_name);
3151 else {
3152 /* since it doesnt exist, remove the active setting */
3153 dict_remove(opserv_routing_plan_options, plan_name);
3154 }
3155 log_module(MAIN_LOG, LOG_ERROR, "activate_routing() couldnt find active routing plan!");
3156 return 0;
3157 }
3158 }
3159 }
3160 else { /* find the active plan in settings */
3161 plan_name = dict_find(opserv_routing_plan_options, "ACTIVE", NULL);
3162 }
3163 if(!plan_name) { /* deactivated, or no plan was set active */
3164 /* TODO: delete routing map if it exists */
3165 wipe_route_list(opserv_route);
3166 return 1;
3167 }
3168
3169 karma = dict_find(opserv_routing_plan_options, "KARMA", NULL);
3170
3171 rp = dict_find(opserv_routing_plans, plan_name, NULL);
3172
3173 /* this should really be done during opserv init */
3174 if(!opserv_route)
3175 opserv_route = calloc(1, sizeof(*opserv_route));
3176
3177 /* Delete the existing active route */
3178 wipe_route_list(opserv_route);
3179
3180 for(it = dict_first(rp->servers); it; it = iter_next(it)) {
3181 const char* servername = iter_key(it);
3182 struct routingPlanServer *rps = iter_data(it),
3183 *rp_uplink, *rp_second = NULL;
3184 char *uplink = rps->uplink;
3185 rp_uplink = dict_find(rp->servers, rps->uplink, NULL);
3186 if(rps->secondaryuplink)
3187 rp_second = dict_find(rp->servers, rps->secondaryuplink, NULL);
3188
3189 /* If the normal uplink has bad karma, don't use it as a hub,
3190 * switch to the secondary uplink.
3191 */
3192 if(karma && enabled_string(karma) && rp_uplink && rp_uplink->karma < 0) {
3193 if(rps->secondaryuplink) {
3194 uplink = rps->secondaryuplink;
3195 /* unless the secondary uplinks karma is worse than the uplink. */
3196 if((rp_second = dict_find(rp->servers, uplink, NULL)) && rp_second->karma < rp_uplink->karma)
3197 uplink = rps->uplink;
3198 }
3199 }
3200 /*
3201 * If _WE_ have bad karma, don't link us to our normal uplink, maybe
3202 * its a bad route. switch to secondary. Important: dont neg karma when we arnt on
3203 * our primary uplink, or we'll get stuck on secondary when THAT link is worse.
3204 */
3205 if(karma && enabled_string(karma) && (rps->karma < 0 || rps->offline) ) {
3206 if(rps->secondaryuplink) {
3207 uplink = rps->secondaryuplink;
3208 }
3209 }
3210 log_module(MAIN_LOG, LOG_DEBUG, "activate_routing() adding %s:%d %s", servername, rps->port, uplink);
3211 add_routestruct_server(opserv_route, servername, rps->port, uplink, NULL);
3212 }
3213 if(change_route_uplinks(opserv_route))
9079d26c 3214 {
47956fc5 3215 return 1;
9079d26c 3216 }
47956fc5 3217 else if(user) {
3218 reply("OSMSG_ROUTING_ACTIVATION_ERROR");
3219 activate_routing(cmd, user, "*");
3220 return 0;
3221 }
9079d26c 3222 /* routing activation failed but we dont do anything? */
47956fc5 3223 return 1;
3224}
3225
9079d26c 3226
3227void routing_init()
3228{
3229 activate_routing(NULL, NULL, NULL);
3230
3231 /* start auto-routing system */
3232 reroute_timer_reset(0);
3233}
3234
47956fc5 3235/*******************************************************
3236 * Functions to handle online route configuration via opserv
3237 */
3238static void route_show_option(struct svccmd *cmd, struct userNode *user, char *name)
3239{
3240 char *value = dict_find(opserv_routing_plan_options, name, NULL);
3241 if(value) {
3242 if(!strcmp("RETRY_PERIOD", name)) { /* Show as an interval */
3243 char buff[INTERVALLEN+1];
3244 reply("OSMSG_ROUTINGPLAN_OPTION", name, intervalString(buff, atoi(value), user->handle_info));
3245 }
3246 else if(!strcmp("ACTIVE", name)) {
3247 if(opserv_route && opserv_route->servers)
3248 reply("OSMSG_ROUTINGPLAN_ACTIVE", value);
3249 else
3250 reply("OSMSG_ROUTINGPLAN_OPTION_NOT_SET", name);
3251 }
3252 else {
3253 reply("OSMSG_ROUTINGPLAN_OPTION", name, value);
3254 }
3255 }
3256 else {
3257 reply("OSMSG_ROUTINGPLAN_OPTION_NOT_SET", name);
3258 }
3259}
3260
3261static void route_show_options(struct svccmd *cmd, struct userNode *user)
3262{
3263 char *options[] = {"ACTIVE", "RETRY_PERIOD", "CONN_PINGOUT", "CONN_READERROR", "KARMA", "DEFAULT_PORT", NULL};
3264 int i;
3265 for(i = 0; options[i]; i++) {
3266 route_show_option(cmd, user, options[i]);
3267 }
3268}
3269
3270/* called from timeq */
3271void routing_connect_timeout(void *data)
3272{
3273 struct waitingConnection *wc = data;
3274 struct server *target = GetServerH(wc->target);
3275 if(!target) {
3276 dict_remove(opserv_waiting_connections, wc->server);
3277 return; /* server we wanted to connect new server to is gone, just give up */
3278 }
3279 routing_handle_connect_failure(target, wc->server, "Connection timed out");
3280 /* the following invalidates server variable! */
3281 dict_remove(opserv_waiting_connections, wc->server);
3282}
3283
3284void routing_delete_connect_timer(char *server)
3285{
3286 struct waitingConnection *wc = dict_find(opserv_waiting_connections, server, 0);
3287 if(wc) {
3288 timeq_del(0, routing_connect_timeout, wc, TIMEQ_IGNORE_WHEN);
3289 dict_remove(opserv_waiting_connections, server);
3290 }
3291}
3292
3293
3294void
3295routing_connect_server(char *server, int port, struct server *to)
3296{
3297 struct waitingConnection *wc = calloc(sizeof(*wc), 1);
3298
3299 wc->server = strdup(server);
3300 wc->target = strdup(to->name);
e6dce34c 3301 /* Just to make sure there isn't one left hanging
3302 * if 2 connections are attempted at once..
3303 * */
3304 routing_delete_connect_timer(server);
47956fc5 3305 dict_insert(opserv_waiting_connections, strdup(server), wc);
3306 timeq_add(now + ROUTING_CONNECT_TIMEOUT, routing_connect_timeout, wc);
3307
3308 irc_connect(opserv, server, port, to);
3309}
3310
3311int
3312routing_connect_one(struct route *route, char *server)
3313{
3314 struct routeList *rptr;
3315 struct server *sptr, *suptr;
3316 for(rptr = route->servers; rptr; rptr = rptr->next) {
3317 if(!strcasecmp(rptr->server, server)) {
3318 /* this is the one, connect it */
3319 suptr = GetServerH(rptr->uplink);
3320 sptr = GetServerH(rptr->server);
3321 if(sptr)
3322 return 1; /* already linked */
3323 if(suptr) {
3324 routing_connect_server(rptr->server, rptr->port, suptr);
3325 return 1; /* attempted link */
3326 }
3327 return 0; /* its uplink isnt here to link to */
3328 }
3329 }
da0c436d 3330 log_module(MAIN_LOG, LOG_DEBUG, "Tried to link %s but its not in the active routing struct!", server);
3331 return 0; /* server wasnt found in active route struct. */
47956fc5 3332}
3333
3334int routing_connect_children(struct route *route, char *server)
3335{
3336 struct routeList *rptr;
3337 struct server *sptr, *suptr;
3338 for(rptr = route->servers; rptr; rptr = rptr->next) {
3339 if(!strcasecmp(rptr->uplink, server)) {
3340 /* this is the one, connect it */
3341 suptr = GetServerH(rptr->uplink);
3342 sptr = GetServerH(rptr->server);
3343 if(sptr)
3344 continue; /* already linked */
3345 if(suptr) {
3346 routing_connect_server(rptr->server, rptr->port, suptr);
3347 continue; /* attempted link */
3348 }
3349 continue; /* its uplink isnt here to link to */
3350 }
3351 }
3352 return 1; /* server wasnt found in active route struct ?! */
3353}
3354
3355int reroute(struct route *route, struct userNode *user, struct svccmd *cmd, char *directive)
3356{
3357 struct routeList *rptr;
3358 struct server *sptr, *suptr;
3359 int connect = 0, move = 0, missing = 0, i;
3360 char d = toupper(*directive);
3361
964abe6b 3362 if(!route || !route->servers) {
3363 reply("OSMSG_REROUTING_NOTCONFIGURED");
3364 return 0;
3365 }
47956fc5 3366 if(user) {
3367 if(d == 'N') { /* normal */
3368 irc_wallops("%s", "Attempting a reroute of the network according to loaded map...");
3369 reply("OSMSG_REROUTING_ACC_MAP");
3370 }
3371 else if(d == 'C') { /* only connect */
3372 reply("OSMSG_CONNECTING_MISSING_ONLY");
3373 }
3374 else if(d == 'T') { /* test */
3375 reply("OSMSG_TESTING_REROUTE");
3376 }
3377 else
3378 {
3379 reply("OSMSG_INVALID_DIRECTIVE", directive);
3380 return 0;
3381 }
3382 }
3383 for(i = 0; i <= route->maxdepth-1; i++) {
3384 for(rptr = route->servers; rptr; rptr = rptr->next) {
3385 if(rptr->outsideness == i) {
3386 /* debugging */
3387 if(user && d=='T')
3388 reply("OSMSG_INSPECTING_SERVER", rptr->server);
3389 suptr = GetServerH(rptr->uplink);
3390 if(!suptr) {
3391 if(rptr->secondaryuplink && (suptr = GetServerH(rptr->secondaryuplink))) {
3392 if(user)
3393 reply("OSMSG_COULDNT_FIND_SERVER", rptr->uplink, rptr->secondaryuplink, rptr->server);
3394 }
3395 }
3396 if(suptr) { /* if the proper uplink is connected.. */
3397 sptr = GetServerH(rptr->server);
3398 if(d == 'C' && sptr) {
3399 continue; /* Already linked */
3400 }
3401 /* If server is missing or the uplinks are not the same then... */
3402 else if(!sptr || strcasecmp(sptr->uplink->name, rptr->uplink)) {
3403 if(!sptr) {
3404 connect++;
3405 }
3406 else { /* Server is already connected somewhere */
3407 if(strcasecmp(sptr->uplink->name, rptr->uplink)) {
3408 if(d != 'T') { /* do it for real */
3409 irc_squit_route(sptr, "%s issued reroute.", user ? user->nick : opserv->nick);
3410 }
3411 else { /* just pretend */
3412 reply("OSMSG_SQUIT", rptr->server);
3413 }
3414 move++;
3415 }
3416 }
3417 if(d != 'T') /* do the real thing */
3418 routing_connect_server(rptr->server, rptr->port, suptr);
3419 else /* just pretend */
3420 reply("OSMSG_CONNECT", rptr->server, rptr->port, suptr->name);
3421 }
3422 }
3423 else {
3424 log_module(MAIN_LOG, LOG_DEBUG, "server uplink %s was not found, cant connect %s", rptr->uplink, rptr->server);
3425 missing++;
3426 }
3427 } /* outsideness = 1 */
3428 } /* rptr */
3429 } /* maxdepth */
3430 if(user) { /* report on what we did */
3431 if(!strcasecmp(directive, "C")) {
3432 if(connect > 0)
3433 reply("OSMSG_CONNECTING_MISSING", connect);
3434 else
3435 reply("OSMSG_NO_SERVERS_MISSING");
3436 }
3437 else {
3438 if(move+connect > 0)
3439 reply("OSMSG_REROUTE_COMPLETE", move, connect, move+connect);
3440 else
3441 reply("OSMSG_NO_ROUTING_NECESSARY");
3442 if(missing > 0)
3443 reply("OSMSG_UPLINKS_MISSING", missing);
3444 }
3445 }
3446 return(move+connect);
3447}
3448
3449static MODCMD_FUNC(cmd_reroute) {
3450 char* upper;
3451 upper = argv[1];
3452 if(reroute(opserv_route, user, cmd, upper))
3453 return 1;
3454 else
3455 return 0;
3456}
3457
3458/* reroute_timer(run)
3459 * run - if it is null, just setup the timer
3460 * but dont run reroute now. otherwise reroute
3461 * and setup timer.
3462 */
3463void reroute_timer(void *data) {
9079d26c 3464 /* Delete any other timers such as this one.. */
3465 timeq_del(0, reroute_timer, NULL, TIMEQ_IGNORE_DATA | TIMEQ_IGNORE_WHEN);
3466
47956fc5 3467 if(!opserv_route || !opserv_route->servers)
3468 return; /* no active route */
3469 char *retry_period = dict_find(opserv_routing_plan_options, "RETRY_PERIOD", NULL);
3470 if(!retry_period)
3471 return; /* retry_period invalid */
3472 unsigned int freq = atoi(retry_period);
3473 if(freq < 1)
3474 return; /* retry_period set to 0, disable */
3475
9079d26c 3476 /* opserv_debug("Reroute timer checking reroute"); */
283cfa83 3477 log_module(MAIN_LOG, LOG_DEBUG, "Reroute timer checking reroute()");
9079d26c 3478
47956fc5 3479 /* Do the reroute C attempt */
3480 if(data)
3481 reroute(opserv_route, NULL, NULL, "C");
3482
3483 /* Re-add ourselves to the timer queue */
3484 timeq_add(now + freq, reroute_timer, "run");
3485}
3486
3487void routing_change_karma(struct routingPlanServer *rps, const char *server, int change) {
3488
3489 int oldkarma = rps->karma;
3490 rps->karma += change;
3491 if(rps->karma < KARMA_MIN)
3492 rps->karma = KARMA_MIN;
3493 if(rps->karma > KARMA_MAX)
3494 rps->karma = KARMA_MAX;
3495 log_module(MAIN_LOG, LOG_DEBUG, "Changing %s karma by %d. new karma %d.", server, change, rps->karma);
3496 if(oldkarma > 0 && rps->karma < 0) {
3497 /* we just crossed over to negitive */
3498 log_module(MAIN_LOG, LOG_INFO, "Server %s just went negitive karma!", server);
3499 activate_routing(NULL, NULL, NULL);
3500 }
3501 else if(oldkarma < 0 && rps->karma > 0) {
3502 /* we just crossed over to positive */
3503 log_module(MAIN_LOG, LOG_INFO, "Server %s just went back positive karma.", server);
3504 activate_routing(NULL, NULL, NULL);
3505 }
3506}
3507
3508void routing_karma_timer(void *data) {
3509 time_t next;
3510 time_t timer_init = data ? atoi(data) : 0;
3511 char buf[MAXLEN];
3512
3513 log_module(MAIN_LOG, LOG_DEBUG, "routing_karma_timer() is running. timer_init=%d.", (unsigned int) timer_init);
3514
3515 /* If theres a time passed in, dont run unless that time is overdue. */
3516 if(!timer_init || (timer_init < now)) {
3517 if(opserv_route && opserv_route->servers) {
3518 char *active = dict_find(opserv_routing_plan_options, "ACTIVE", NULL);
3519 struct routingPlan *rp;
3520 if(active && (rp = dict_find(opserv_routing_plans, active, NULL))) {
3521 dict_iterator_t it;
3522 /* Walk through each server in the active routing plan.. */
3523 for(it = dict_first(rp->servers); it; it = iter_next(it)) {
3524 struct routingPlanServer *rps = iter_data(it);
3525 struct server *server = GetServerH(iter_key(it));
3526 /* Give everyone +KARMA_ENTROPE just for nothing */
3527 routing_change_karma(rps, iter_key(it), KARMA_ENTROPE);
3528 /* give an additonal +KARMA_RELIABLE to servers that
3529 * have been linked at least KARMA_TIMER seconds. */
3530 if(server && (server->link < (now - KARMA_TIMER) ) ) {
3531 routing_change_karma(rps, iter_key(it), KARMA_RELIABLE);
3532 }
3533 }
3534 }
3535 }
3536 }
3537 if(timer_init > now) /* loading a saved value */
3538 next = timer_init;
3539 else /* no scheduled timer, or we missed it. start from now */
3540 next = now + KARMA_TIMER;
3541 /* Save when karma_timer should run again in case we restart before then */
3542 log_module(MAIN_LOG, LOG_DEBUG, "routing_karma_timer() scheduling self to run again at %d", (unsigned int) next);
3543 sprintf(buf, "%u", (unsigned int) next);
3544 dict_insert(opserv_routing_plan_options, "KARMA_TIMER", strdup(buf));
3545 /* add a timer to run this again .. */
3546 timeq_add(next, routing_karma_timer, NULL);
3547}
3548
3549void routing_handle_neg_karma(char *server, char *uplink, int change)
3550{
3551 /* if server's primary uplink is uplink, OR, uplink's primary uplink is server,
3552 * then whichever one, gets its karma changed. */
3553 char *active = dict_find(opserv_routing_plan_options, "ACTIVE", NULL);
3554 struct routingPlan *rp;
3555 struct routingPlanServer *rps;
3556 if(!active)
3557 return;
3558 if(!(rp = dict_find(opserv_routing_plans, active, NULL)))
3559 return;
3560 if((rps = dict_find(rp->servers, server, NULL))) {
3561 if(!strcasecmp(rps->uplink, uplink)) {
3562 /* server's uplink is uplink */
3563 routing_change_karma(rps, server, change);
3564 return;
3565 }
3566 }
3567 if((rps = dict_find(rp->servers, uplink, NULL))) {
3568 if(!strcasecmp(rps->uplink, server)) {
3569 /* uplink's uplink is server */
3570 routing_change_karma(rps, uplink, change);
3571 return;
3572 }
3573 }
3574}
3575
3576void
3577routing_handle_squit(char *server, char *uplink, char *message)
3578{
3579 log_module(MAIN_LOG, LOG_DEBUG, "Routing_handle_squit(%s, %s)", server, message);
3580
3581 char *val;
3582
3583 if(match_ircglob(message, "Ping timeout")) {
3584 routing_handle_neg_karma(server, uplink, KARMA_PINGOUT);
3585 /* if conn_pingout is true, try to reconnect it obaying karma rules. */
3586
3587 val = dict_find(opserv_routing_plan_options, "CONN_PINGOUT", 0);
3588 if(val && enabled_string(val))
3589 routing_connect_one(opserv_route, server);
3590 }
3591 else if(match_ircglob(message, "Read error:*")) {
3592 routing_handle_neg_karma(server, uplink, KARMA_READERROR);
3593 /* if conn_readerror is true, try to reconnect it obaying karma rules. */
3594 val = dict_find(opserv_routing_plan_options, "CONN_READERROR", 0);
3595 if(val && enabled_string(val))
3596 routing_connect_one(opserv_route, server);
3597 }
3598 /* Else whats the message (an oper squit it?) dont interfere */
3599}
3600
3601void
3602routing_handle_connect(char *server, char *uplink)
3603{
3604 char *active;
3605 struct routingPlan *rp;
3606 struct routingPlanServer *rps;
3607 dict_iterator_t it;
3608
3609 log_module(MAIN_LOG, LOG_DEBUG, "routing_handle_connect(%s, %s)", server, uplink);
3610 /* delete a pending connection timer, if any */
3611 routing_delete_connect_timer(server);
3612 /* check if routing is active... */
3613 active = dict_find(opserv_routing_plan_options, "ACTIVE", NULL);
3614 if(!active)
3615 return;
3616 rp = dict_find(opserv_routing_plans, active, NULL);
3617 if(!rp)
3618 return;
3619
3620 /* If its offline, mark it online again.. */
3621 if((rps = dict_find(rp->servers, server, NULL))) {
3622 if(rps->offline == true) {
3623 rps->offline = false;
3624 if(rps->secondaryuplink) {
3625 /* re-activate to move it back to its primary */
3626 activate_routing(NULL, NULL, NULL);
3627 }
3628 }
3629 /* if there are any servers missing who have this server as uplink try to connect them. */
3630 routing_connect_children(opserv_route, server);
3631 }
3632 /* foreach server x3 knows about, if the uplink is this server, call this function on the child. */
3633 for (it=dict_first(servers); it; it=iter_next(it)) {
3634 struct server *sptr = iter_data(it);
3635 if(sptr && sptr->uplink && !strcasecmp(server, sptr->uplink->name)) {
3636 log_module(MAIN_LOG, LOG_DEBUG, "routing_handle_connect calling self on %s's leaf %s", server, sptr->name);
3637 routing_handle_connect(sptr->name, sptr->uplink->name);
3638 }
3639 }
3640}
3641
3642/* Handle a failed attempt at connecting servers
3643 * - we should only get here regarding servers X3 attempted to link, other
3644 * opers link messages go to them not to us
3645 */
3646void
3647routing_handle_connect_failure(struct server *source, char *server, char *message)
3648{
3649 char *active;
3650 struct routingPlan *rp;
3651 struct routingPlanServer *rps;
3652 log_module(MAIN_LOG, LOG_ERROR, "Failed to connect %s to %s: %s", server, source->name, message);
3653 /* remove the waiting connection n timeq */
3654 routing_delete_connect_timer(server);
3655 /* check if routing is active.. */
3656 active = dict_find(opserv_routing_plan_options, "ACTIVE", NULL);
3657 if(!active)
3658 return;
3659 rp = dict_find(opserv_routing_plans, active, NULL);
3660 if(!rp)
3661 return;
3662
3663 if( ((rps = dict_find(rp->servers, server, NULL)) && !strcasecmp(rps->uplink, source->name))) {
3664 /* failed to connect to its primary uplink */
3665 if(rps->offline == false) {
3666 rps->offline = true;
3667 if(rps->secondaryuplink) {
3668 /* re-activate routing so the secondary
3669 * becomes its uplink, and try again */
3670 activate_routing(NULL, NULL, NULL);
3671 /* attempt to link it again. */
3672 routing_connect_one(opserv_route, server);
bf93ca8d 3673 /* TODO: reconnect any missing servers who
3674 * normally connect to server, using their backups.
3675 * Probably should just issue a reroute C here. */
47956fc5 3676 }
3677 }
3678 }
3679}
3680
3681/* Delete any existing timers, and start the timer again
3682 * using the passed time for the first run.
3683 * - this is called during a retry_period change
9079d26c 3684 * before it has saved the new value.
3685 *
3686 * If time is 0, lookup the interval. */
47956fc5 3687void reroute_timer_reset(unsigned int time)
3688{
9079d26c 3689 timeq_del(0, reroute_timer, NULL, TIMEQ_IGNORE_DATA | TIMEQ_IGNORE_WHEN);
3690 if(time == 0) {
3691 if(!opserv_route || !opserv_route->servers)
3692 return; /* no active route */
3693 char *retry_period = dict_find(opserv_routing_plan_options, "RETRY_PERIOD", NULL);
3694 if(!retry_period)
3695 return; /* retry_period invalid */
3696 time = atoi(retry_period);
3697 if(time < 1)
3698 return; /* retry_period set to 0, disable */
3699
3700 }
47956fc5 3701 timeq_add(now + time, reroute_timer, "run");
3702}
3703
3704static MODCMD_FUNC(cmd_routing_set)
3705{
3706 char *option = argv[1];
3707 char *options[] = {"ACTIVE", "RETRY_PERIOD", "CONN_PINGOUT", "CONN_READERROR", "KARMA", "DEFAULT_PORT", NULL};
3708 int i;
3709 if(argc < 2) {
3710 route_show_options(cmd, user);
3711 }
3712 else {
3713 char *found_option = NULL;
3714 for(i = 0; options[i]; i++) {
3715 if(!strcasecmp(options[i], option))
3716 found_option = options[i];
3717 }
3718 if(!found_option) {
3719 reply("OSMSG_ROUTINGPLAN_OPTION_NOT_FOUND", option);
3720 return 0;
3721 }
3722 if(argc > 2) {
3723 char *value = argv[2];
3724 char buff[MAXLEN]; /* whats the max length of unsigned int as printf'd? */
3725 if(!strcmp(found_option, "ACTIVE")) { /* must be an existing route. */
3726 if(disabled_string(value) || false_string(value)) {
3727 /* make none of the maps active */
3728 activate_routing(cmd, user, "*");
3729 reply("OSMSG_ROUTING_DISABLED");
3730 return 1;
3731 }
3732 else if(!activate_routing(cmd, user, value)) {
3733 /* neg reply handled in activate_routing */
3734 return 0;
3735 }
3736 }
3737 if(!strcmp(found_option, "CONN_READERROR") || !strcmp(found_option, "CONN_PINGOUT") ||
3738 !strcmp(found_option, "KARMA") ) {
3739 if( enabled_string(value)) {
3740 value = "ENABLED";
3741 }
3742 else if( disabled_string(value) ) {
3743 value = "DISABLED";
3744 }
3745 else {
3746 reply("MSG_INVALID_BINARY", value);
3747 return 0;
3748 }
3749 }
3750 if(!strcmp(found_option, "RETRY_PERIOD")) {
3751 unsigned int duration = ParseInterval(value);
3752 sprintf(buff, "%d", duration);
3753 value = buff;
3754 reroute_timer_reset(duration);
3755 }
3756 /* set the value here */
3757 dict_remove(opserv_routing_plan_options, found_option);
3758 dict_insert(opserv_routing_plan_options, strdup(found_option), strdup(value));
3759 route_show_option(cmd, user, found_option);
3760 }
3761 else {
3762 /* show the current value */
3763 route_show_option(cmd, user, found_option);
3764 }
3765 }
3766 return 1;
3767}
3768
3769static MODCMD_FUNC(cmd_stats_routing_plans) {
3770 dict_iterator_t rpit;
3771 dict_iterator_t it;
3772 struct routingPlan *rp;
5c6bff84 3773 if(argc > 1) {
3774 reply("OSMSG_ROUTINGPLAN");
3775 reply("OSMSG_ROUTINGPLAN_BAR");
3776 for(rpit = dict_first(opserv_routing_plans); rpit; rpit = iter_next(rpit)) {
3777 const char* name = iter_key(rpit);
3778 rp = iter_data(rpit);
3779 if(match_ircglob(name, argv[1])) {
3780 reply("OSMSG_ROUTINGPLAN_NAME", name);
3781 for(it = dict_first(rp->servers); it; it = iter_next(it)) {
3782 const char* servername = iter_key(it);
3783 struct routingPlanServer *rps = iter_data(it);
3784 reply("OSMSG_ROUTINGPLAN_SERVER", servername, rps->port, rps->uplink, rps->karma, rps->offline? "offline" : "online", rps->secondaryuplink ? rps->secondaryuplink : "None");
3785 }
3786 }
47956fc5 3787
5c6bff84 3788 }
3789 reply("OSMSG_ROUTINGPLAN_END");
3790 }
3791 else {
3792 reply("OSMSG_ROUTINGPLAN_LIST_HEAD");
3793 reply("OSMSG_ROUTINGPLAN_BAR");
3794 for(rpit = dict_first(opserv_routing_plans); rpit; rpit = iter_next(rpit)) {
3795 const char* name = iter_key(rpit);
3796 reply("OSMSG_ROUTINGPLAN_LIST", name);
3797 }
3798 reply("OSMSG_ROUTINGPLAN_END");
3799 route_show_options(cmd, user);
47956fc5 3800 }
47956fc5 3801 return 1;
3802}
3803
3804
3805static MODCMD_FUNC(cmd_routing_addplan)
3806{
3807 char *name;
3808 name = argv[1];
3809 /* dont allow things like 'off', 'false', '0' because thats how we disable routing. */
3810 if(*name && !disabled_string(name) && !false_string(name)) {
3811 if(opserv_add_routing_plan(name)) {
3812 reply("OSMSG_ADDPLAN_SUCCESS", name);
3813 return 1;
3814 }
3815 else {
3816 reply("OSMSG_ADDPLAN_FAILED", name);
3817 return 0;
3818 }
3819 }
3820 else
3821 {
3822 reply("OSMSG_INVALID_PLAN");
3823 return 0;
3824 }
3825}
3826
3827static MODCMD_FUNC(cmd_routing_delplan)
3828{
3829 char *name = argv[1];
3830 if( dict_remove(opserv_routing_plans, name) ) {
3831 char *active = dict_find(opserv_routing_plan_options, "ACTIVE", NULL);
3832 if(active && !strcasecmp(active, name)) {
3833 /* if this was the active plan, disable routing */
3834 activate_routing(cmd, user, "*");
3835 reply("OSMSG_ROUTING_DISABLED");
3836 }
3837 reply("OSMSG_PLAN_DELETED");
3838 return 1;
3839 }
3840 else {
3841 reply("OSMSG_PLAN_NOT_FOUND", name);
3842 return 0;
3843 }
3844}
3845
3846static MODCMD_FUNC(cmd_routing_addserver)
3847{
3848 char *plan;
3849 char *server;
3850 char *portstr;
3851 char *uplink;
3852 char *second;
3853 unsigned int port;
3854 struct routingPlan *rp;
3855
3856 plan = argv[1];
3857 server = strdup(argv[2]);
3858 server = strtok(server, ":");
3859 portstr = strtok(NULL, ":");
3860 if(portstr)
3861 port = atoi(portstr);
3862 else {
3863 char *str = dict_find(opserv_routing_plan_options, "DEFAULT_PORT", NULL);
3864 uplink = argv[3];
3865 port = str ? atoi(str) : 0;
3866 }
3867 uplink = argv[3];
3868 if(argc > 4)
3869 second = argv[4];
3870 else
3871 second = NULL;
3872
3873 if( (rp = dict_find(opserv_routing_plans, plan, 0))) {
3874 char *active;
3875 opserv_routing_plan_add_server(rp, server, uplink, port, KARMA_DEFAULT, second, 0);
3876 reply("OSMSG_PLAN_SERVER_ADDED", server);
3877 if((active = dict_find(opserv_routing_plan_options, "ACTIVE", 0)) && !strcasecmp(plan, active)) {
3878 /* re-activate routing with new info */
3879 activate_routing(cmd, user, NULL);
3880 }
3881
3882 free(server);
3883 return 1;
3884 }
3885 else {
3886 reply("OSMSG_PLAN_NOT_FOUND", plan);
3887 free(server);
3888 return 0;
3889 }
3890}
3891
3892static MODCMD_FUNC(cmd_routing_delserver)
3893{
3894 char *plan;
3895 char *server;
3896 struct routingPlan *rp;
3897 plan = argv[1];
3898 server = argv[2];
3899 if( (rp = dict_find(opserv_routing_plans, plan, 0))) {
3900 if(dict_remove(rp->servers, server)) {
3901 char *active;
3902 reply("OSMSG_PLAN_SERVER_DELETED");
3903 if((active = dict_find(opserv_routing_plan_options, "ACTIVE", 0)) && !strcasecmp(plan, active)) {
3904 /* re-activate routing with new info */
3905 activate_routing(cmd, user, NULL);
3906 }
3907
3908 return 1;
3909 }
3910 else {
3911 reply("OSMSG_PLAN_SERVER_NOT_FOUND", server);
3912 return 0;
3913 }
3914 }
3915 else {
3916 reply("OSMSG_PLAN_NOT_FOUND", plan);
3917 return 0;
3918 }
3919}
3920
3921
3922/*************************************************
3923 * Functions to deal with 'route map' command */
3924
3925/* Figures out how many downlinks there are for proper
3926 * drawing of the route map */
3927int
3928num_route_downlinks(struct route *route, char *name)
3929{
3930 struct routeList *rptr;
3931 int num = 0;
3932 rptr = route->servers;
3933 while(rptr) {
3934 if(!strcasecmp(rptr->uplink, name))
3935 num++;
3936 rptr = rptr->next;
3937 }
3938 return num;
3939}
3940
3941void
3942show_route_downlinks(struct svccmd *cmd, struct route *route, struct userNode *user, char *name, char *prevpre, char *arrowchar, int reset)
3943{
3944 struct routeList *servPtr;
3945 struct server *sptr;
3946 int j;
3947 char pre[MAXLEN];
3948 char *nextpre;
3949 char *status;
3950 int num = 0;
3951 static int depth = 0;
3952
3953 if(reset)
3954 depth = 0;
3955
3956 nextpre = malloc(MAXLEN);
3957 strcpy(pre, prevpre);
3958
3959 sptr = GetServerH(name);
3960 if((servPtr = find_routeList_server(route, name))) {
3961 if(!sptr)
3962 status = " ";
3963 else if (!strcasecmp(sptr->uplink->name, servPtr->uplink))
3964 status = "X";
3965 else if(servPtr->secondaryuplink && !strcasecmp(sptr->name, servPtr->secondaryuplink))
3966 status = "/";
3967 else
3968 status = "!";
3969 reply("OSMSG_DOWNLINKS_FORMAT_A", pre, arrowchar, name, status);
3970 }
3971 else
3972 reply("OSMSG_DOWNLINKS_FORMAT_B", self->name);
3973 j = num_route_downlinks(route, name);
3974 servPtr = route->servers;
3975 while(servPtr) {
3976 if(!strcasecmp(servPtr->uplink, name)) {
3977 strcpy(nextpre, pre);
3978 if(depth++ > 0) {
3979 if(arrowchar[0] == '`')
3980 strcat(nextpre, " ");
3981 else
3982 strcat(nextpre, "| ");
3983 }
3984 if(j > ++num) {
3985 show_route_downlinks(cmd, route, user, servPtr->server, nextpre, "|", 0);
3986 }
3987 else {
3988 show_route_downlinks(cmd, route, user, servPtr->server, nextpre, "`", 0);
3989 }
3990 }
3991 servPtr = servPtr->next;
3992 }
3993 free(nextpre);
3994}
3995
3996int
3997show_route_map(struct route *route, struct userNode *user, struct svccmd *cmd)
3998{
3999 if(!route || !route->servers) {
4000 reply("OSMSG_ROUTELIST_EMPTY");
4001 return 0;
4002 }
4003
4004 char *serviceName = conf_get_data("server/hostname", RECDB_QSTRING);
4005 reply("OSMSG_ROUTELIST_AS_PLANNED");
4006 show_route_downlinks(cmd, route, user, serviceName, "", "`", 1);
4007 reply("OSMSG_MAP_CENTERED", route->centered ? "is" : "is not", route->maxdepth);
4008 return 1;
4009}
4010
4011static MODCMD_FUNC(cmd_routing_map)
4012{
4013 show_route_map(opserv_route, user, cmd);
4014 return 1;
4015}
4016
4017
4018
4019
4020/* End of auto routing functions *
4021 *********************************/
4022
d76ed9a9 4023static MODCMD_FUNC(cmd_addbad)
4024{
4025 unsigned int arg, count;
4026 dict_iterator_t it;
4027 int bad_found, exempt_found;
4028
4029 /* Create the bad word if it doesn't exist. */
4030 bad_found = !opserv_add_bad_word(cmd, user, argv[1]);
4031
4032 /* Look for exception modifiers. */
4033 for (arg=2; arg<argc; arg++) {
4034 if (!irccasecmp(argv[arg], "except")) {
4035 reply("MSG_DEPRECATED_COMMAND", "addbad ... except", "addexempt");
4036 if (++arg > argc) {
4037 reply("MSG_MISSING_PARAMS", "except");
4038 break;
4039 }
4040 for (count = 0; (arg < argc) && IsChannelName(argv[arg]); arg++) {
4041 dict_find(opserv_exempt_channels, argv[arg], &exempt_found);
4042 if (!exempt_found) {
4043 dict_insert(opserv_exempt_channels, strdup(argv[arg]), NULL);
4044 count++;
4045 }
4046 }
4047 reply("OSMSG_ADDED_EXEMPTIONS", count);
4048 } else {
4049 reply("MSG_DEPRECATED_COMMAND", "addbad (with modifiers)", "addbad");
4050 reply("OSMSG_BAD_MODIFIER", argv[arg]);
4051 }
4052 }
4053
4054 /* Scan for existing channels that match the new bad word. */
4055 if (!bad_found) {
4056 for (it = dict_first(channels); it; it = iter_next(it)) {
4057 struct chanNode *channel = iter_data(it);
4058
4059 if (!opserv_bad_channel(channel->name))
4060 continue;
4061 channel->bad_channel = 1;
4062 if (channel->name[0] == '#')
4063 opserv_shutdown_channel(channel, "OSMSG_ILLEGAL_REASON");
4064 else {
4065 unsigned int nn;
4066 for (nn=0; nn<channel->members.used; nn++) {
4067 struct userNode *user = channel->members.list[nn]->user;
4068 DelUser(user, cmd->parent->bot, 1, "OSMSG_ILLEGAL_KILL_REASON");
4069 }
4070 }
4071 }
4072 }
4073
4074 return 1;
4075}
4076
4077static MODCMD_FUNC(cmd_delbad)
4078{
4079 dict_iterator_t it;
4080 unsigned int nn;
4081
4082 for (nn=0; nn<opserv_bad_words->used; nn++) {
4083 if (!irccasecmp(opserv_bad_words->list[nn], argv[1])) {
4084 string_list_delete(opserv_bad_words, nn);
4085 for (it = dict_first(channels); it; it = iter_next(it)) {
4086 channel = iter_data(it);
4087 if (irccasestr(channel->name, argv[1])
4088 && !opserv_bad_channel(channel->name)) {
4089 DelChannelUser(cmd->parent->bot, channel, "Channel name no longer contains a bad word.", 1);
4090 timeq_del(0, opserv_part_channel, channel, TIMEQ_IGNORE_WHEN);
4091 channel->bad_channel = 0;
4092 }
4093 }
4094 reply("OSMSG_REMOVED_BAD", argv[1]);
4095 return 1;
4096 }
4097 }
4098 reply("OSMSG_NOT_BAD_WORD", argv[1]);
4099 return 0;
4100}
4101
4102static MODCMD_FUNC(cmd_addexempt)
4103{
4104 const char *chanName;
4105
4106 if ((argc > 1) && IsChannelName(argv[1])) {
4107 chanName = argv[1];
4108 } else {
4109 reply("MSG_NOT_CHANNEL_NAME");
4110 OPSERV_SYNTAX();
4111 return 0;
4112 }
4113 dict_insert(opserv_exempt_channels, strdup(chanName), NULL);
4114 channel = GetChannel(chanName);
4115 if (channel) {
4116 if (channel->bad_channel) {
4117 DelChannelUser(cmd->parent->bot, channel, "Channel is now exempt from bad-word checking.", 1);
4118 timeq_del(0, opserv_part_channel, channel, TIMEQ_IGNORE_WHEN);
4119 }
4120 channel->bad_channel = 0;
4121 }
4122 reply("OSMSG_ADDED_EXEMPTION", chanName);
4123 return 1;
4124}
4125
4126static MODCMD_FUNC(cmd_delexempt)
4127{
4128 const char *chanName;
4129
4130 if ((argc > 1) && IsChannelName(argv[1])) {
4131 chanName = argv[1];
4132 } else {
4133 reply("MSG_NOT_CHANNEL_NAME");
4134 OPSERV_SYNTAX();
4135 return 0;
4136 }
4137 if (!dict_remove(opserv_exempt_channels, chanName)) {
4138 reply("OSMSG_NOT_EXEMPT", chanName);
4139 return 0;
4140 }
4141 reply("OSMSG_REMOVED_EXEMPTION", chanName);
4142 return 1;
4143}
4144
4145static void
4146opserv_expire_trusted_host(void *data)
4147{
4148 struct trusted_host *th = data;
4149 dict_remove(opserv_trusted_hosts, th->ipaddr);
4150}
4151
4152static void
4153opserv_add_trusted_host(const char *ipaddr, unsigned int limit, const char *issuer, time_t issued, time_t expires, const char *reason)
4154{
4155 struct trusted_host *th;
4156 th = calloc(1, sizeof(*th));
4157 if (!th)
4158 return;
4159 th->ipaddr = strdup(ipaddr);
4160 th->reason = reason ? strdup(reason) : NULL;
4161 th->issuer = issuer ? strdup(issuer) : NULL;
4162 th->issued = issued;
4163 th->limit = limit;
4164 th->expires = expires;
4165 dict_insert(opserv_trusted_hosts, th->ipaddr, th);
4166 if (th->expires)
4167 timeq_add(th->expires, opserv_expire_trusted_host, th);
4168}
4169
4170static void
4171free_trusted_host(void *data)
4172{
4173 struct trusted_host *th = data;
4174 free(th->ipaddr);
4175 free(th->reason);
4176 free(th->issuer);
4177 free(th);
4178}
4179
4180static MODCMD_FUNC(cmd_addtrust)
4181{
4182 unsigned long interval;
4183 char *reason, *tmp;
2f61d1d7 4184 irc_in_addr_t tmpaddr;
d76ed9a9 4185 unsigned int count;
4186
4187 if (dict_find(opserv_trusted_hosts, argv[1], NULL)) {
4188 reply("OSMSG_ALREADY_TRUSTED", argv[1]);
4189 return 0;
4190 }
4191
2f61d1d7 4192 if (!irc_pton(&tmpaddr, NULL, argv[1])) {
d76ed9a9 4193 reply("OSMSG_BAD_IP", argv[1]);
4194 return 0;
4195 }
4196
4197 count = strtoul(argv[2], &tmp, 10);
4198 if (*tmp != '\0') {
4199 reply("OSMSG_BAD_NUMBER", argv[2]);
4200 return 0;
4201 }
4202
4203 interval = ParseInterval(argv[3]);
4204 if (!interval && strcmp(argv[3], "0")) {
4205 reply("MSG_INVALID_DURATION", argv[3]);
4206 return 0;
4207 }
4208
4209 reason = unsplit_string(argv+4, argc-4, NULL);
4210 opserv_add_trusted_host(argv[1], count, user->handle_info->handle, now, interval ? (now + interval) : 0, reason);
4211 reply("OSMSG_ADDED_TRUSTED");
4212 return 1;
4213}
4214
4215static MODCMD_FUNC(cmd_edittrust)
4216{
4217 unsigned long interval;
4218 struct trusted_host *th;
4219 char *reason, *tmp;
4220 unsigned int count;
4221
4222 th = dict_find(opserv_trusted_hosts, argv[1], NULL);
4223 if (!th) {
4224 reply("OSMSG_NOT_TRUSTED", argv[1]);
4225 return 0;
4226 }
4227 count = strtoul(argv[2], &tmp, 10);
4228 if (!count || *tmp) {
4229 reply("OSMSG_BAD_NUMBER", argv[2]);
4230 return 0;
4231 }
4232 interval = ParseInterval(argv[3]);
4233 if (!interval && strcmp(argv[3], "0")) {
4234 reply("MSG_INVALID_DURATION", argv[3]);
4235 return 0;
4236 }
4237 reason = unsplit_string(argv+4, argc-4, NULL);
4238 if (th->expires)
4239 timeq_del(th->expires, opserv_expire_trusted_host, th, 0);
4240
4241 free(th->reason);
4242 th->reason = strdup(reason);
4243 free(th->issuer);
4244 th->issuer = strdup(user->handle_info->handle);
4245 th->issued = now;
4246 th->limit = count;
4247 if (interval) {
4248 th->expires = now + interval;
4249 timeq_add(th->expires, opserv_expire_trusted_host, th);
4250 } else
4251 th->expires = 0;
4252 reply("OSMSG_UPDATED_TRUSTED", th->ipaddr);
4253 return 1;
4254}
4255
4256static MODCMD_FUNC(cmd_deltrust)
4257{
4258 unsigned int n;
4259
4260 for (n=1; n<argc; n++) {
4261 struct trusted_host *th = dict_find(opserv_trusted_hosts, argv[n], NULL);
4262 if (!th)
4263 continue;
4264 if (th->expires)
4265 timeq_del(th->expires, opserv_expire_trusted_host, th, 0);
4266 dict_remove(opserv_trusted_hosts, argv[n]);
4267 }
4268 reply("OSMSG_REMOVED_TRUSTED");
4269 return 1;
4270}
4271
4272/* This doesn't use dict_t because it's a little simpler to open-code the
4273 * comparisons (and simpler arg-passing for the ADD subcommand).
4274 */
4275static MODCMD_FUNC(cmd_clone)
4276{
4277 int i;
4278 struct userNode *clone;
4279
4280 clone = GetUserH(argv[2]);
4281 if (!irccasecmp(argv[1], "ADD")) {
4282 char *userinfo;
4283 char ident[USERLEN+1];
4284
258d1427 4285 if (argc < 5) {
4286 reply("MSG_MISSING_PARAMS", argv[1]);
4287 OPSERV_SYNTAX();
4288 return 0;
4289 }
4290 if (clone) {
4291 reply("OSMSG_CLONE_EXISTS", argv[2]);
4292 return 0;
4293 }
4294 userinfo = unsplit_string(argv+4, argc-4, NULL);
4295 for (i=0; argv[3][i] && (i<USERLEN); i++) {
4296 if (argv[3][i] == '@') {
4297 ident[i++] = 0;
4298 break;
4299 } else {
d76ed9a9 4300 ident[i] = argv[3][i];
4301 }
258d1427 4302 }
4303 if (!argv[3][i] || (i==USERLEN)) {
4304 reply("OSMSG_NOT_A_HOSTMASK");
4305 return 0;
4306 }
4307 if (!(clone = AddClone(argv[2], ident, argv[3]+i, userinfo))) {
d76ed9a9 4308 reply("OSMSG_CLONE_FAILED", argv[2]);
4309 return 0;
4310 }
4311 reply("OSMSG_CLONE_ADDED", clone->nick);
258d1427 4312 return 1;
d76ed9a9 4313 }
4314 if (!clone) {
258d1427 4315 reply("MSG_NICK_UNKNOWN", argv[2]);
4316 return 0;
d76ed9a9 4317 }
4318 if (clone->uplink != self || IsService(clone)) {
258d1427 4319 reply("OSMSG_NOT_A_CLONE", clone->nick);
4320 return 0;
d76ed9a9 4321 }
4322 if (!irccasecmp(argv[1], "REMOVE")) {
258d1427 4323 const char *reason;
4324 if (argc > 3) {
4325 reason = unsplit_string(argv+3, argc-3, NULL);
4326 } else {
4327 char *tmp;
4328 tmp = alloca(strlen(clone->nick) + strlen(OSMSG_PART_REASON));
4329 sprintf(tmp, OSMSG_PART_REASON, clone->nick);
4330 reason = tmp;
4331 }
4332 DelUser(clone, NULL, 1, reason);
4333 reply("OSMSG_CLONE_REMOVED", argv[2]);
4334 return 1;
d76ed9a9 4335 }
4336 if (argc < 4) {
258d1427 4337 reply("MSG_MISSING_PARAMS", argv[1]);
4338 OPSERV_SYNTAX();
4339 return 0;
d76ed9a9 4340 }
4341 channel = GetChannel(argv[3]);
4342 if (!irccasecmp(argv[1], "JOIN")) {
258d1427 4343 if (!channel
4344 && !(channel = AddChannel(argv[3], now, NULL, NULL, NULL))) {
4345 reply("MSG_CHANNEL_UNKNOWN", argv[3]);
4346 return 0;
4347 }
4348 AddChannelUser(clone, channel);
4349 reply("OSMSG_CLONE_JOINED", clone->nick, channel->name);
4350 return 1;
d76ed9a9 4351 }
4352 if (!irccasecmp(argv[1], "PART")) {
258d1427 4353 if (!channel) {
4354 reply("MSG_CHANNEL_UNKNOWN", argv[3]);
4355 return 0;
4356 }
4357 if (!GetUserMode(channel, clone)) {
4358 reply("OSMSG_NOT_ON_CHANNEL", clone->nick, channel->name);
4359 return 0;
4360 }
4361 reply("OSMSG_CLONE_PARTED", clone->nick, channel->name);
4362 DelChannelUser(clone, channel, "Leaving.", 0);
4363 return 1;
d76ed9a9 4364 }
4365 if (!irccasecmp(argv[1], "OP")) {
4366 struct mod_chanmode change;
258d1427 4367 if (!channel) {
4368 reply("MSG_CHANNEL_UNKNOWN", argv[3]);
4369 return 0;
4370 }
d76ed9a9 4371 mod_chanmode_init(&change);
4372 change.argc = 1;
4373 change.args[0].mode = MODE_CHANOP;
a32da4c7 4374 change.args[0].u.member = GetUserMode(channel, clone);
4375 if (!change.args[0].u.member) {
d76ed9a9 4376 reply("OSMSG_NOT_ON_CHANNEL", clone->nick, channel->name);
4377 return 0;
258d1427 4378 }
d76ed9a9 4379 modcmd_chanmode_announce(&change);
258d1427 4380 reply("OSMSG_OPS_GIVEN", channel->name, clone->nick);
4381 return 1;
d76ed9a9 4382 }
55342ce8 4383 if (!irccasecmp(argv[1], "HOP")) {
4384 struct mod_chanmode change;
4385 if (!channel) {
4386 reply("MSG_CHANNEL_UNKNOWN", argv[3]);
4387 return 0;
4388 }
4389 mod_chanmode_init(&change);
4390 change.argc = 1;
4391 change.args[0].mode = MODE_HALFOP;
11408ce4 4392 change.args[0].u.member = GetUserMode(channel, clone);
4393 if (!change.args[0].u.member) {
55342ce8 4394 reply("OSMSG_NOT_ON_CHANNEL", clone->nick, channel->name);
4395 return 0;
4396 }
4397 modcmd_chanmode_announce(&change);
4398 reply("OSMSG_HOPS_GIVEN", channel->name, clone->nick);
4399 return 1;
4400 }
d76ed9a9 4401 if (argc < 5) {
258d1427 4402 reply("MSG_MISSING_PARAMS", argv[1]);
4403 OPSERV_SYNTAX();
4404 return 0;
d76ed9a9 4405 }
4406 if (!irccasecmp(argv[1], "SAY")) {
258d1427 4407 char *text = unsplit_string(argv+4, argc-4, NULL);
4408 irc_privmsg(clone, argv[3], text);
4409 reply("OSMSG_CLONE_SAID", clone->nick, argv[3]);
4410 return 1;
d76ed9a9 4411 }
4412 reply("OSMSG_UNKNOWN_SUBCOMMAND", argv[1], argv[0]);
4413 return 0;
4414}
4415
4416static struct helpfile_expansion
4417opserv_help_expand(const char *variable)
4418{
4419 extern struct userNode *message_source;
4420 struct helpfile_expansion exp;
4421 struct service *service;
4422 struct svccmd *cmd;
4423 dict_iterator_t it;
4424 int row;
4425 unsigned int level;
4426
4427 if (!(service = service_find(message_source->nick))) {
4428 exp.type = HF_STRING;
4429 exp.value.str = NULL;
4430 } else if (!irccasecmp(variable, "index")) {
4431 exp.type = HF_TABLE;
4432 exp.value.table.length = 1;
4433 exp.value.table.width = 2;
4434 exp.value.table.flags = TABLE_REPEAT_HEADERS | TABLE_REPEAT_ROWS;
4435 exp.value.table.contents = calloc(dict_size(service->commands)+1, sizeof(char**));
4436 exp.value.table.contents[0] = calloc(exp.value.table.width, sizeof(char*));
4437 exp.value.table.contents[0][0] = "Command";
4438 exp.value.table.contents[0][1] = "Level";
4439 for (it=dict_first(service->commands); it; it=iter_next(it)) {
4440 cmd = iter_data(it);
4441 row = exp.value.table.length++;
4442 exp.value.table.contents[row] = calloc(exp.value.table.width, sizeof(char*));
4443 exp.value.table.contents[row][0] = iter_key(it);
4444 level = cmd->min_opserv_level;
4445 if (!level_strings[level]) {
4446 level_strings[level] = malloc(16);
4447 snprintf(level_strings[level], 16, "%3d", level);
4448 }
4449 exp.value.table.contents[row][1] = level_strings[level];
4450 }
4451 } else if (!strncasecmp(variable, "level", 5)) {
4452 cmd = dict_find(service->commands, variable+6, NULL);
4453 exp.type = HF_STRING;
4454 if (cmd) {
4455 level = cmd->min_opserv_level;
4456 exp.value.str = malloc(16);
4457 snprintf(exp.value.str, 16, "%3d", level);
4458 } else {
4459 exp.value.str = NULL;
4460 }
4461 } else {
4462 exp.type = HF_STRING;
4463 exp.value.str = NULL;
4464 }
4465 return exp;
4466}
4467
4468struct modcmd *
4469opserv_define_func(const char *name, modcmd_func_t *func, int min_level, int reqchan, int min_argc)
4470{
4471 char buf[16], *flags = NULL;
4472 unsigned int iflags = 0;
4473 sprintf(buf, "%d", min_level);
4474 switch (reqchan) {
4475 case 1: flags = "+acceptchan"; break;
4476 case 3: flags = "+acceptpluschan"; /* fall through */
4477 case 2: iflags = MODCMD_REQUIRE_CHANNEL; break;
4478 }
4479 if (flags) {
4480 return modcmd_register(opserv_module, name, func, min_argc, iflags, "level", buf, "flags", flags, "flags", "+oper", NULL);
4481 } else {
4482 return modcmd_register(opserv_module, name, func, min_argc, iflags, "level", buf, "flags", "+oper", NULL);
4483 }
4484}
4485
4486int add_reserved(const char *key, void *data, void *extra)
4487{
7637f48f 4488 struct chanNode *chan;
d76ed9a9 4489 struct record_data *rd = data;
4490 const char *ident, *hostname, *desc;
7637f48f 4491 unsigned int i;
d76ed9a9 4492 struct userNode *reserve;
4493 ident = database_get_data(rd->d.object, KEY_IDENT, RECDB_QSTRING);
4494 if (!ident) {
258d1427 4495 log_module(OS_LOG, LOG_ERROR, "Missing ident for reserve of %s", key);
4496 return 0;
d76ed9a9 4497 }
4498 hostname = database_get_data(rd->d.object, KEY_HOSTNAME, RECDB_QSTRING);
4499 if (!hostname) {
258d1427 4500 log_module(OS_LOG, LOG_ERROR, "Missing hostname for reserve of %s", key);
4501 return 0;
d76ed9a9 4502 }
4503 desc = database_get_data(rd->d.object, KEY_DESC, RECDB_QSTRING);
4504 if (!desc) {
258d1427 4505 log_module(OS_LOG, LOG_ERROR, "Missing description for reserve of %s", key);
4506 return 0;
d76ed9a9 4507 }
4508 if ((reserve = AddClone(key, ident, hostname, desc))) {
4509 reserve->modes |= FLAGS_PERSISTENT;
4510 dict_insert(extra, reserve->nick, reserve);
4511 }
7637f48f 4512
4513 if (autojoin_channels && reserve) {
4514 for (i = 0; i < autojoin_channels->used; i++) {
4515 chan = AddChannel(autojoin_channels->list[i], now, "+nt", NULL, NULL);
4516 AddChannelUser(reserve, chan)->modes |= MODE_VOICE;
4517 }
4518 }
4519
d76ed9a9 4520 return 0;
4521}
4522
4523static unsigned int
4524foreach_matching_user(const char *hostmask, discrim_search_func func, void *extra)
4525{
4526 discrim_t discrim;
4527 char *dupmask;
4528 unsigned int matched;
4529
4530 if (!self->uplink) return 0;
4531 discrim = calloc(1, sizeof(*discrim));
4532 discrim->limit = dict_size(clients);
4533 discrim->max_level = ~0;
4534 discrim->max_ts = now;
4535 discrim->max_channels = INT_MAX;
4536 discrim->authed = -1;
4537 discrim->info_space = -1;
63665495 4538 discrim->intra_scmp = 0;
4539 discrim->intra_dcmp = 0;
27eaa617 4540 discrim->use_regex = 0;
1c5f6697 4541 discrim->silent = 0;
d76ed9a9 4542 dupmask = strdup(hostmask);
4543 if (split_ircmask(dupmask, &discrim->mask_nick, &discrim->mask_ident, &discrim->mask_host)) {
2f61d1d7 4544 if (!irc_pton(&discrim->ip_mask, &discrim->ip_mask_bits, discrim->mask_host))
4545 discrim->ip_mask_bits = 0;
d76ed9a9 4546 matched = opserv_discrim_search(discrim, func, extra);
4547 } else {
258d1427 4548 log_module(OS_LOG, LOG_ERROR, "Couldn't split IRC mask for gag %s!", hostmask);
d76ed9a9 4549 matched = 0;
4550 }
4551 free(discrim);
4552 free(dupmask);
4553 return matched;
4554}
4555
4556static unsigned int
4557gag_free(struct gag_entry *gag)
4558{
4559 unsigned int ungagged;
4560
4561 /* Remove from gag list */
4562 if (gagList == gag) {
4563 gagList = gag->next;
4564 } else {
4565 struct gag_entry *prev;
4566 for (prev = gagList; prev->next != gag; prev = prev->next) ;
4567 prev->next = gag->next;
4568 }
4569
4570 ungagged = foreach_matching_user(gag->mask, ungag_helper_func, NULL);
4571
4572 /* Deallocate storage */
4573 free(gag->reason);
4574 free(gag->owner);
4575 free(gag->mask);
4576 free(gag);
4577
4578 return ungagged;
4579}
4580
4581static void
4582gag_expire(void *data)
4583{
4584 gag_free(data);
4585}
4586
4587unsigned int
4588gag_create(const char *mask, const char *owner, const char *reason, time_t expires)
4589{
4590 struct gag_entry *gag;
4591
4592 /* Create gag and put it into linked list */
4593 gag = calloc(1, sizeof(*gag));
4594 gag->mask = strdup(mask);
4595 gag->owner = strdup(owner ? owner : "<unknown>");
4596 gag->reason = strdup(reason ? reason : "<unknown>");
4597 gag->expires = expires;
4598 if (gag->expires)
4599 timeq_add(gag->expires, gag_expire, gag);
4600 gag->next = gagList;
4601 gagList = gag;
4602
4603 /* If we're linked, see if who the gag applies to */
4604 return foreach_matching_user(mask, gag_helper_func, gag);
4605}
4606
4607static int
4608add_gag_helper(const char *key, void *data, UNUSED_ARG(void *extra))
4609{
4610 struct record_data *rd = data;
4611 char *owner, *reason, *expstr;
4612 time_t expires;
4613
4614 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
4615 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
4616 expstr = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
4617 expires = expstr ? strtoul(expstr, NULL, 0) : 0;
4618 gag_create(key, owner, reason, expires);
4619
4620 return 0;
4621}
4622
4623static struct opserv_user_alert *
4624opserv_add_user_alert(struct userNode *req, const char *name, opserv_alert_reaction reaction, const char *text_discrim)
4625{
4626 unsigned int wordc;
4627 char *wordv[MAXNUMPARAMS], *discrim_copy;
4628 struct opserv_user_alert *alert;
4629 char *name_dup;
4630
4631 if (dict_find(opserv_user_alerts, name, NULL)) {
258d1427 4632 send_message(req, opserv, "OSMSG_ALERT_EXISTS", name);
4633 return NULL;
d76ed9a9 4634 }
4635 alert = malloc(sizeof(*alert));
4636 alert->owner = strdup(req->handle_info ? req->handle_info->handle : req->nick);
4637 alert->text_discrim = strdup(text_discrim);
4638 discrim_copy = strdup(text_discrim); /* save a copy of the discrim */
4639 wordc = split_line(discrim_copy, false, ArrayLength(wordv), wordv);
258d1427 4640 alert->discrim = opserv_discrim_create(req, opserv, wordc, wordv, 0);
d82cf2f0 4641 /* Check for missing required criteria or broken records */
39c1a4ef 4642 if (!alert->discrim || (reaction==REACT_SVSJOIN && !alert->discrim->chantarget) ||
d82cf2f0 4643 (reaction==REACT_SVSPART && !alert->discrim->chantarget) ||
4644 (reaction==REACT_MARK && !alert->discrim->mark)) {
d76ed9a9 4645 free(alert->text_discrim);
4646 free(discrim_copy);
4647 free(alert);
4648 return NULL;
4649 }
4650 alert->split_discrim = discrim_copy;
4651 name_dup = strdup(name);
4652 if (!alert->discrim->reason)
4653 alert->discrim->reason = strdup(name);
4654 alert->reaction = reaction;
4655 dict_insert(opserv_user_alerts, name_dup, alert);
697f4c9a 4656 /* Stick the alert into the appropriate additional alert dict(s).
4657 * For channel alerts, we only use channels and min_channels;
4658 * max_channels would have to be checked on /part, which we do not
4659 * yet do, and which seems of questionable value.
4660 */
4661 if (alert->discrim->channel || alert->discrim->min_channels)
d76ed9a9 4662 dict_insert(opserv_channel_alerts, name_dup, alert);
697f4c9a 4663 if (alert->discrim->mask_nick)
d76ed9a9 4664 dict_insert(opserv_nick_based_alerts, name_dup, alert);
4665 return alert;
4666}
4667
de9510bc 4668/*
d76ed9a9 4669static int
4670add_chan_warn(const char *key, void *data, UNUSED_ARG(void *extra))
4671{
4672 struct record_data *rd = data;
4673 char *reason = GET_RECORD_QSTRING(rd);
4674
de9510bc 4675 * i hope this can't happen *
d76ed9a9 4676 if (!reason)
4677 reason = "No Reason";
4678
4679 dict_insert(opserv_chan_warn, strdup(key), strdup(reason));
4680 return 0;
4681}
de9510bc 4682*/
d76ed9a9 4683
47956fc5 4684
d76ed9a9 4685static int
4686add_user_alert(const char *key, void *data, UNUSED_ARG(void *extra))
4687{
4688 dict_t alert_dict;
4689 const char *discrim, *react, *owner;
4690 opserv_alert_reaction reaction;
4691 struct opserv_user_alert *alert;
4692
4693 if (!(alert_dict = GET_RECORD_OBJECT((struct record_data *)data))) {
4694 log_module(OS_LOG, LOG_ERROR, "Bad type (not a record) for alert %s.", key);
4695 return 1;
4696 }
4697 discrim = database_get_data(alert_dict, KEY_DISCRIM, RECDB_QSTRING);
4698 react = database_get_data(alert_dict, KEY_REACTION, RECDB_QSTRING);
4699 if (!react || !irccasecmp(react, "notice"))
4700 reaction = REACT_NOTICE;
4701 else if (!irccasecmp(react, "kill"))
4702 reaction = REACT_KILL;
1c5f6697 4703 /*
9a75756e 4704 else if (!irccasecmp(react, "silent"))
4705 reaction = REACT_SILENT;
1c5f6697 4706 */
d76ed9a9 4707 else if (!irccasecmp(react, "gline"))
4708 reaction = REACT_GLINE;
ec311f39 4709 else if (!irccasecmp(react, "track"))
4710 reaction = REACT_TRACK;
d914d1cb 4711 else if (!irccasecmp(react, "shun"))
4712 reaction = REACT_SHUN;
c408f18a 4713 else if (!irccasecmp(react, "svsjoin"))
4714 reaction = REACT_SVSJOIN;
39c1a4ef 4715 else if (!irccasecmp(react, "svspart"))
4716 reaction = REACT_SVSPART;
0e08a8e0 4717 else if (!irccasecmp(react, "version"))
4718 reaction = REACT_VERSION;
d82cf2f0 4719 else if (!irccasecmp(react, "mark"))
4720 reaction = REACT_MARK;
d76ed9a9 4721 else {
4722 log_module(OS_LOG, LOG_ERROR, "Invalid reaction %s for alert %s.", react, key);
4723 return 0;
4724 }
4725 alert = opserv_add_user_alert(opserv, key, reaction, discrim);
4726 if (!alert) {
4727 log_module(OS_LOG, LOG_ERROR, "Unable to create alert %s from database.", key);
4728 return 0;
4729 }
4730 owner = database_get_data(alert_dict, KEY_OWNER, RECDB_QSTRING);
4731 free(alert->owner);
4732 alert->owner = strdup(owner ? owner : "<unknown>");
4733 return 0;
4734}
4735
4736static int
4737trusted_host_read(const char *host, void *data, UNUSED_ARG(void *extra))
4738{
4739 struct record_data *rd = data;
4740 const char *limit, *str, *reason, *issuer;
4741 time_t issued, expires;
4742
4743 if (rd->type == RECDB_QSTRING) {
4744 /* old style host by itself */
4745 limit = GET_RECORD_QSTRING(rd);
4746 issued = 0;
4747 issuer = NULL;
4748 expires = 0;
4749 reason = NULL;
4750 } else if (rd->type == RECDB_OBJECT) {
4751 dict_t obj = GET_RECORD_OBJECT(rd);
4752 /* new style structure */
4753 limit = database_get_data(obj, KEY_LIMIT, RECDB_QSTRING);
4754 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
4755 expires = str ? ParseInterval(str) : 0;
4756 reason = database_get_data(obj, KEY_REASON, RECDB_QSTRING);
4757 issuer = database_get_data(obj, KEY_ISSUER, RECDB_QSTRING);
4758 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
4759 issued = str ? ParseInterval(str) : 0;
4760 } else
4761 return 0;
4762
4763 if (expires && (expires < now))
4764 return 0;
4765 opserv_add_trusted_host(host, (limit ? strtoul(limit, NULL, 0) : 0), issuer, issued, expires, reason);
4766 return 0;
4767}
4768
47956fc5 4769static int
4770add_routing_plan_server(const char *name, void *data, void *rp)
4771{
4772 struct record_data *rd = data;
4773 const char *uplink, *portstr, *karma, *second, *offline;
4774
4775 dict_t obj = GET_RECORD_OBJECT(rd);
4776 if(rd->type == RECDB_OBJECT) {
4777 uplink = database_get_data(obj, KEY_UPLINK, RECDB_QSTRING);
4778 second = database_get_data(obj, KEY_SECOND, RECDB_QSTRING);
4779 portstr = database_get_data(obj, KEY_PORT, RECDB_QSTRING);
4780 karma = database_get_data(obj, KEY_KARMA, RECDB_QSTRING);
4781 offline = database_get_data(obj, KEY_OFFLINE, RECDB_QSTRING);
4782 /* create routing plan server named key, with uplink uplink. */
4783 opserv_routing_plan_add_server(rp, name, uplink, portstr ? atoi(portstr) : 0,
4784 karma ? atoi(karma) : KARMA_DEFAULT, second,
4785 offline ? atoi(offline) : 0);
4786 }
4787 return 0;
4788
4789}
4790
4791static int
4792routing_plan_set_option(const char *name, void *data, UNUSED_ARG(void *extra))
4793{
4794 struct record_data *rd = data;
4795 if(rd->type == RECDB_QSTRING)
4796 {
4797 char *value = GET_RECORD_QSTRING(rd);
4798 dict_insert(opserv_routing_plan_options, strdup(name), strdup(value));
4799 }
4800 return 0;
4801}
4802
4803static int
4804add_routing_plan(const char *name, void *data, UNUSED_ARG(void *extra))
4805{
4806 struct record_data *rd = data;
4807 struct routingPlan *rp;
4808
4809 if(rd->type == RECDB_OBJECT) {
4810 dict_t obj = GET_RECORD_OBJECT(rd);
4811 rp = opserv_add_routing_plan(name);
4812 dict_foreach(obj, add_routing_plan_server, rp);
4813 }
4814 return 0;
4815}
4816
d76ed9a9 4817static int
4818opserv_saxdb_read(struct dict *conf_db)
4819{
4820 dict_t object;
4821 struct record_data *rd;
4822 dict_iterator_t it;
4823 unsigned int nn;
4824
4825 if ((object = database_get_data(conf_db, KEY_RESERVES, RECDB_OBJECT)))
4826 dict_foreach(object, add_reserved, opserv_reserved_nick_dict);
4827 if ((rd = database_get_path(conf_db, KEY_BAD_WORDS))) {
4828 switch (rd->type) {
4829 case RECDB_STRING_LIST:
4830 /* Add words one by one just in case there are overlaps from an old DB. */
4831 for (nn=0; nn<rd->d.slist->used; ++nn)
4832 opserv_add_bad_word(NULL, NULL, rd->d.slist->list[nn]);
4833 break;
4834 case RECDB_OBJECT:
4835 for (it=dict_first(rd->d.object); it; it=iter_next(it)) {
4836 opserv_add_bad_word(NULL, NULL, iter_key(it));
4837 rd = iter_data(it);
4838 if (rd->type == RECDB_STRING_LIST)
4839 for (nn=0; nn<rd->d.slist->used; nn++)
4840 dict_insert(opserv_exempt_channels, strdup(rd->d.slist->list[nn]), NULL);
4841 }
4842 break;
4843 default:
4844 /* do nothing */;
4845 }
4846 }
4847 if ((rd = database_get_path(conf_db, KEY_EXEMPT_CHANNELS))
4848 && (rd->type == RECDB_STRING_LIST)) {
4849 for (nn=0; nn<rd->d.slist->used; ++nn)
4850 dict_insert(opserv_exempt_channels, strdup(rd->d.slist->list[nn]), NULL);
4851 }
4852 if ((object = database_get_data(conf_db, KEY_MAX_CLIENTS, RECDB_OBJECT))) {
4853 char *str;
4854 if ((str = database_get_data(object, KEY_MAX, RECDB_QSTRING)))
4855 max_clients = atoi(str);
4856 if ((str = database_get_data(object, KEY_TIME, RECDB_QSTRING)))
4857 max_clients_time = atoi(str);
4858 }
4859 if ((object = database_get_data(conf_db, KEY_TRUSTED_HOSTS, RECDB_OBJECT)))
4860 dict_foreach(object, trusted_host_read, opserv_trusted_hosts);
4861 if ((object = database_get_data(conf_db, KEY_GAGS, RECDB_OBJECT)))
4862 dict_foreach(object, add_gag_helper, NULL);
4863 if ((object = database_get_data(conf_db, KEY_ALERTS, RECDB_OBJECT)))
4864 dict_foreach(object, add_user_alert, NULL);
de9510bc 4865/*
d76ed9a9 4866 if ((object = database_get_data(conf_db, KEY_WARN, RECDB_OBJECT)))
4867 dict_foreach(object, add_chan_warn, NULL);
de9510bc 4868*/
47956fc5 4869
4870 if ((object = database_get_data(conf_db, KEY_ROUTINGPLAN, RECDB_OBJECT)))
4871 dict_foreach(object, add_routing_plan, NULL);
4872
4873 if ((object = database_get_data(conf_db, KEY_ROUTINGPLAN_OPTIONS, RECDB_OBJECT)))
4874 dict_foreach(object, routing_plan_set_option, NULL);
4875
d76ed9a9 4876 return 0;
4877}
4878
4879static int
4880opserv_saxdb_write(struct saxdb_context *ctx)
4881{
4882 struct string_list *slist;
4883 dict_iterator_t it;
4884
4885 /* reserved nicks */
4886 if (dict_size(opserv_reserved_nick_dict)) {
4887 saxdb_start_record(ctx, KEY_RESERVES, 1);
4888 for (it = dict_first(opserv_reserved_nick_dict); it; it = iter_next(it)) {
4889 struct userNode *user = iter_data(it);
4890 if (!IsPersistent(user)) continue;
4891 saxdb_start_record(ctx, iter_key(it), 0);
4892 saxdb_write_string(ctx, KEY_IDENT, user->ident);
4893 saxdb_write_string(ctx, KEY_HOSTNAME, user->hostname);
4894 saxdb_write_string(ctx, KEY_DESC, user->info);
4895 saxdb_end_record(ctx);
4896 }
4897 saxdb_end_record(ctx);
4898 }
4899 /* bad word set */
4900 if (opserv_bad_words->used) {
4901 saxdb_write_string_list(ctx, KEY_BAD_WORDS, opserv_bad_words);
4902 }
47956fc5 4903 /* routing plan options */
4904 if (dict_size(opserv_routing_plan_options)) {
4905 saxdb_start_record(ctx, KEY_ROUTINGPLAN_OPTIONS, 1);
4906 for(it = dict_first(opserv_routing_plan_options); it; it = iter_next(it)) {
4907 saxdb_write_string(ctx, iter_key(it), iter_data(it));
4908 }
4909 saxdb_end_record(ctx);
4910 }
4911 /* routing plans */
4912 if (dict_size(opserv_routing_plans)) {
4913 dict_iterator_t svrit;
4914 struct routingPlan *rp;
4915 struct routingPlanServer *rps;
4916 saxdb_start_record(ctx, KEY_ROUTINGPLAN, 1);
4917 for (it = dict_first(opserv_routing_plans); it; it = iter_next(it)) {
4918 rp = iter_data(it);
4919 saxdb_start_record(ctx, iter_key(it), 0);
4920 for(svrit = dict_first(rp->servers); svrit; svrit = iter_next(svrit)) {
4921 char buf[MAXLEN];
4922 rps = iter_data(svrit);
4923 saxdb_start_record(ctx, iter_key(svrit), 0);
4924 saxdb_write_string(ctx, KEY_UPLINK, rps->uplink);
4925 if(rps->secondaryuplink)
4926 saxdb_write_string(ctx, KEY_SECOND, rps->secondaryuplink);
4927 sprintf(buf, "%d", rps->port);
4928 saxdb_write_string(ctx, KEY_PORT, buf);
4929 sprintf(buf, "%d", rps->karma);
4930 saxdb_write_string(ctx, KEY_KARMA, buf);
4931 sprintf(buf, "%d", rps->offline);
4932 saxdb_write_string(ctx, KEY_OFFLINE, buf);
4933 saxdb_end_record(ctx);
4934 }
4935 saxdb_end_record(ctx);
4936 }
4937 saxdb_end_record(ctx);
4938 }
d76ed9a9 4939 /* insert exempt channel names */
4940 if (dict_size(opserv_exempt_channels)) {
4941 slist = alloc_string_list(dict_size(opserv_exempt_channels));
4942 for (it=dict_first(opserv_exempt_channels); it; it=iter_next(it)) {
4943 string_list_append(slist, strdup(iter_key(it)));
4944 }
4945 saxdb_write_string_list(ctx, KEY_EXEMPT_CHANNELS, slist);
4946 free_string_list(slist);
4947 }
4948 /* trusted hosts takes a little more work */
4949 if (dict_size(opserv_trusted_hosts)) {
4950 saxdb_start_record(ctx, KEY_TRUSTED_HOSTS, 1);
4951 for (it = dict_first(opserv_trusted_hosts); it; it = iter_next(it)) {
4952 struct trusted_host *th = iter_data(it);
4953 saxdb_start_record(ctx, iter_key(it), 0);
4954 if (th->limit) saxdb_write_int(ctx, KEY_LIMIT, th->limit);
4955 if (th->expires) saxdb_write_int(ctx, KEY_EXPIRES, th->expires);
4956 if (th->issued) saxdb_write_int(ctx, KEY_ISSUED, th->issued);
4957 if (th->issuer) saxdb_write_string(ctx, KEY_ISSUER, th->issuer);
4958 if (th->reason) saxdb_write_string(ctx, KEY_REASON, th->reason);
4959 saxdb_end_record(ctx);
4960 }
4961 saxdb_end_record(ctx);
4962 }
4963 /* gags */
4964 if (gagList) {
4965 struct gag_entry *gag;
4966 saxdb_start_record(ctx, KEY_GAGS, 1);
4967 for (gag = gagList; gag; gag = gag->next) {
4968 saxdb_start_record(ctx, gag->mask, 0);
4969 saxdb_write_string(ctx, KEY_OWNER, gag->owner);
4970 saxdb_write_string(ctx, KEY_REASON, gag->reason);
4971 if (gag->expires) saxdb_write_int(ctx, KEY_EXPIRES, gag->expires);
4972 saxdb_end_record(ctx);
4973 }
4974 saxdb_end_record(ctx);
4975 }
4976 /* channel warnings */
de9510bc 4977 /*
d76ed9a9 4978 if (dict_size(opserv_chan_warn)) {
4979 saxdb_start_record(ctx, KEY_WARN, 0);
4980 for (it = dict_first(opserv_chan_warn); it; it = iter_next(it)) {
4981 saxdb_write_string(ctx, iter_key(it), iter_data(it));
4982 }
4983 saxdb_end_record(ctx);
4984 }
de9510bc 4985 */
d76ed9a9 4986 /* alerts */
4987 if (dict_size(opserv_user_alerts)) {
4988 saxdb_start_record(ctx, KEY_ALERTS, 1);
4989 for (it = dict_first(opserv_user_alerts); it; it = iter_next(it)) {
4990 struct opserv_user_alert *alert = iter_data(it);
4991 const char *reaction;
4992 saxdb_start_record(ctx, iter_key(it), 0);
4993 saxdb_write_string(ctx, KEY_DISCRIM, alert->text_discrim);
4994 saxdb_write_string(ctx, KEY_OWNER, alert->owner);
4995 switch (alert->reaction) {
4996 case REACT_NOTICE: reaction = "notice"; break;
4997 case REACT_KILL: reaction = "kill"; break;
1c5f6697 4998// case REACT_SILENT: reaction = "silent"; break;
d76ed9a9 4999 case REACT_GLINE: reaction = "gline"; break;
ec311f39 5000 case REACT_TRACK: reaction = "track"; break;
d914d1cb 5001 case REACT_SHUN: reaction = "shun"; break;
c408f18a 5002 case REACT_SVSJOIN: reaction = "svsjoin"; break;
39c1a4ef 5003 case REACT_SVSPART: reaction = "svspart"; break;
0e08a8e0 5004 case REACT_VERSION: reaction = "version"; break;
d82cf2f0 5005 case REACT_MARK: reaction = "mark"; break;
d76ed9a9 5006 default:
5007 reaction = NULL;
5008 log_module(OS_LOG, LOG_ERROR, "Invalid reaction type %d for alert %s (while writing database).", alert->reaction, iter_key(it));
5009 break;
5010 }
5011 if (reaction) saxdb_write_string(ctx, KEY_REACTION, reaction);
5012 saxdb_end_record(ctx);
5013 }
5014 saxdb_end_record(ctx);
5015 }
5016 /* max clients */
5017 saxdb_start_record(ctx, KEY_MAX_CLIENTS, 0);
5018 saxdb_write_int(ctx, KEY_MAX, max_clients);
5019 saxdb_write_int(ctx, KEY_TIME, max_clients_time);
5020 saxdb_end_record(ctx);
5021 return 0;
5022}
5023
5024static int
5025query_keys_helper(const char *key, UNUSED_ARG(void *data), void *extra)
5026{
5027 send_message_type(4, extra, opserv, "$b%s$b", key);
5028 return 0;
5029}
5030
5031static MODCMD_FUNC(cmd_query)
5032{
5033 struct record_data *rd;
5034 unsigned int i;
5035 char *nodename;
5036
5037 if (argc < 2) {
258d1427 5038 reply("OSMSG_OPTION_ROOT");
5039 conf_enum_root(query_keys_helper, user);
5040 return 1;
d76ed9a9 5041 }
5042
5043 nodename = unsplit_string(argv+1, argc-1, NULL);
5044 if (!(rd = conf_get_node(nodename))) {
258d1427 5045 reply("OSMSG_UNKNOWN_OPTION", nodename);
5046 return 0;
d76ed9a9 5047 }
5048
5049 if (rd->type == RECDB_QSTRING)
258d1427 5050 reply("OSMSG_OPTION_IS", nodename, rd->d.qstring);
d76ed9a9 5051 else if (rd->type == RECDB_STRING_LIST) {
258d1427 5052 reply("OSMSG_OPTION_LIST", nodename);
5053 if (rd->d.slist->used)
5054 for (i=0; i<rd->d.slist->used; i++)
5055 send_message_type(4, user, cmd->parent->bot, "$b%s$b", rd->d.slist->list[i]);
5056 else
5057 reply("OSMSG_OPTION_LIST_EMPTY");
d76ed9a9 5058 } else if (rd->type == RECDB_OBJECT) {
258d1427 5059 reply("OSMSG_OPTION_KEYS", nodename);
5060 dict_foreach(rd->d.object, query_keys_helper, user);
d76ed9a9 5061 }
5062
5063 return 1;
5064}
5065
5066static MODCMD_FUNC(cmd_set)
5067{
5068 struct record_data *rd;
5069
5070 /* I originally wanted to be able to fully manipulate the config
5071 db with this, but i wussed out. feel free to fix this - you'll
5072 need to handle quoted strings which have been split, and likely
5073 invent a syntax for it. -Zoot */
5074
5075 if (!(rd = conf_get_node(argv[1]))) {
258d1427 5076 reply("OSMSG_SET_NOT_SET", argv[1]);
5077 return 0;
d76ed9a9 5078 }
5079
5080 if (rd->type != RECDB_QSTRING) {
258d1427 5081 reply("OSMSG_SET_BAD_TYPE", argv[1]);
5082 return 0;
d76ed9a9 5083 }
5084
5085 free(rd->d.qstring);
5086 rd->d.qstring = strdup(argv[2]);
5087 conf_call_reload_funcs();
5088 reply("OSMSG_SET_SUCCESS", argv[1], argv[2]);
5089 return 1;
5090}
5091
5092static MODCMD_FUNC(cmd_settime)
5093{
5094 const char *srv_name_mask = "*";
5095 time_t new_time = now;
5096
5097 if (argc > 1)
5098 srv_name_mask = argv[1];
5099 if (argc > 2)
5100 new_time = time(NULL);
5101 irc_settime(srv_name_mask, new_time);
5102 reply("OSMSG_SETTIME_SUCCESS", srv_name_mask);
5103 return 1;
5104}
5105
5106static discrim_t
258d1427 5107opserv_discrim_create(struct userNode *user, struct userNode *bot, unsigned int argc, char *argv[], int allow_channel)
d76ed9a9 5108{
5109 unsigned int i, j;
5110 discrim_t discrim;
5111
5112 discrim = calloc(1, sizeof(*discrim));
5113 discrim->limit = 250;
5114 discrim->max_level = ~0;
5115 discrim->max_ts = INT_MAX;
5116 discrim->domain_depth = 2;
5117 discrim->max_channels = INT_MAX;
5118 discrim->authed = -1;
5119 discrim->info_space = -1;
63665495 5120 discrim->intra_dcmp = 0;
5121 discrim->intra_scmp = 0;
1c5f6697 5122 discrim->use_regex = 0;
5123 discrim->silent = 0;
d76ed9a9 5124
5125 for (i=0; i<argc; i++) {
5126 if (irccasecmp(argv[i], "log") == 0) {
5127 discrim->option_log = 1;
5128 continue;
5129 }
5130 /* Assume all other criteria require arguments. */
5131 if (i == argc - 1) {
258d1427 5132 send_message(user, bot, "MSG_MISSING_PARAMS", argv[i]);
d76ed9a9 5133 goto fail;
5134 }
63665495 5135 if (argv[i+1][0] == '&') {
5136 /* Looking for intra-userNode matches */
5137 char *tmp = &(argv[i+1][1]);
5138 if (strcasecmp(tmp, argv[i]) != 0) { /* Don't allow "nick &nick" etc */
5139 if (!strcasecmp(tmp, "nick"))
5140 discrim->intra_dcmp = 1;
5141 else if (!strcasecmp(tmp, "ident"))
5142 discrim->intra_dcmp = 2;
5143 else if (!strcasecmp(tmp, "info"))
5144 discrim->intra_dcmp = 3;
5145 }
5146 }
258d1427 5147 if (irccasecmp(argv[i], "mask") == 0) {
5148 if (!is_ircmask(argv[++i])) {
5149 send_message(user, bot, "OSMSG_INVALID_IRCMASK", argv[i]);
5150 goto fail;
5151 }
5152 if (!split_ircmask(argv[i],
d76ed9a9 5153 &discrim->mask_nick,
5154 &discrim->mask_ident,
5155 &discrim->mask_host)) {
258d1427 5156 send_message(user, bot, "OSMSG_INVALID_IRCMASK", argv[i]);
5157 goto fail;
5158 }
5159 } else if (irccasecmp(argv[i], "nick") == 0) {
63665495 5160 i++;
5161 if (discrim->intra_dcmp > 0)
5162 discrim->intra_scmp = 1;
5163 else
5164 discrim->mask_nick = argv[i];
258d1427 5165 } else if (irccasecmp(argv[i], "ident") == 0) {
63665495 5166 i++;
5167 if (discrim->intra_dcmp > 0)
5168 discrim->intra_scmp = 2;
5169 else
5170 discrim->mask_ident = argv[i];
258d1427 5171 } else if (irccasecmp(argv[i], "host") == 0) {
5172 discrim->mask_host = argv[++i];
5173 } else if (irccasecmp(argv[i], "info") == 0) {
63665495 5174 i++;
5175 if (discrim->intra_dcmp > 0)
5176 discrim->intra_scmp = 3;
5177 else
5178 discrim->mask_info = argv[i];
0e08a8e0 5179 } else if (irccasecmp(argv[i], "version") == 0) {
5180 discrim->mask_version = argv[++i];
258d1427 5181 } else if (irccasecmp(argv[i], "server") == 0) {
5182 discrim->server = argv[++i];
5183 } else if (irccasecmp(argv[i], "ip") == 0) {
2f61d1d7 5184 j = irc_pton(&discrim->ip_mask, &discrim->ip_mask_bits, argv[++i]);
5185 if (!j) {
c092fcad 5186 send_message(user, bot, "OSMSG_BAD_IP", argv[i]);
2f61d1d7 5187 goto fail;
5188 }
d76ed9a9 5189 } else if (irccasecmp(argv[i], "account") == 0) {
5190 if (discrim->authed == 0) {
258d1427 5191 send_message(user, bot, "OSMSG_ACCOUNTMASK_AUTHED");
d76ed9a9 5192 goto fail;
5193 }
5194 discrim->accountmask = argv[++i];
5195 discrim->authed = 1;
c408f18a 5196 } else if (irccasecmp(argv[i], "chantarget") == 0) {
5197 if(!IsChannelName(argv[i+1])) {
5198 send_message(user, bot, "MSG_NOT_CHANNEL_NAME");
5199 goto fail;
5200 }
5201 discrim->chantarget = argv[++i];
5aa400d2 5202 } else if (irccasecmp(argv[i], "checkrestrictions") == 0) {
5203 i++;
5204 if (true_string(argv[i])) {
5205 discrim->checkrestrictions = 1;
5206 } else if (false_string(argv[i])) {
5207 discrim->checkrestrictions = 0;
5208 } else {
5209 send_message(user, bot, "MSG_INVALID_BINARY", argv[i]);
5210 goto fail;
5211 }
d82cf2f0 5212 } else if (irccasecmp(argv[i], "mark") == 0) {
5213 if(!is_valid_mark(argv[i+1])) {
5214 send_message(user, bot, "OSMSG_MARK_INVALID");
5215 goto fail;
5216 }
5217 discrim->mark = argv[++i];
d76ed9a9 5218 } else if (irccasecmp(argv[i], "authed") == 0) {
5219 i++; /* true_string and false_string are macros! */
5220 if (true_string(argv[i])) {
5221 discrim->authed = 1;
5222 } else if (false_string(argv[i])) {
5223 if (discrim->accountmask) {
258d1427 5224 send_message(user, bot, "OSMSG_ACCOUNTMASK_AUTHED");
d76ed9a9 5225 goto fail;
5226 }
5227 discrim->authed = 0;
5228 } else {
258d1427 5229 send_message(user, bot, "MSG_INVALID_BINARY", argv[i]);
d76ed9a9 5230 goto fail;
5231 }
5232 } else if (irccasecmp(argv[i], "info_space") == 0) {
5233 /* XXX: A hack because you can't check explicitly for a space through
5234 * any other means */
5235 i++;
5236 if (true_string(argv[i])) {
5237 discrim->info_space = 1;
5238 } else if (false_string(argv[i])) {
5239 discrim->info_space = 0;
5240 } else {
258d1427 5241 send_message(user, bot, "MSG_INVALID_BINARY", argv[i]);
d76ed9a9 5242 goto fail;
5243 }
27eaa617 5244 } else if (irccasecmp(argv[i], "regex") == 0) {
5245 i++;
5246 if (true_string(argv[i])) {
5247 discrim->use_regex = 1;
5248 } else if (false_string(argv[i])) {
5249 discrim->use_regex = 0;
5250 } else {
c092fcad 5251 send_message(user, bot, "MSG_INVALID_BINARY", argv[i]);
27eaa617 5252 goto fail;
5253 }
1c5f6697 5254 } else if (irccasecmp(argv[i], "silent") == 0) {
5255 i++;
0c0adfe0 5256 if(user != opserv && !oper_has_access(user, opserv, opserv_conf.silent_level, 0)) {
1c5f6697 5257 goto fail;
5258 } else if (true_string(argv[i])) {
5259 discrim->silent = 1;
5260 } else if (false_string(argv[i])) {
5261 discrim->silent = 0;
5262 } else {
c092fcad 5263 send_message(user, bot, "MSG_INVALID_BINARY", argv[i]);
1c5f6697 5264 goto fail;
5265 }
d76ed9a9 5266 } else if (irccasecmp(argv[i], "duration") == 0) {
5267 discrim->duration = ParseInterval(argv[++i]);
258d1427 5268 } else if (irccasecmp(argv[i], "channel") == 0) {
d76ed9a9 5269 for (j=0, i++; ; j++) {
5270 switch (argv[i][j]) {
5271 case '#':
5272 goto find_channel;
5273 case '-':
55342ce8 5274 discrim->chan_no_modes |= MODE_CHANOP | MODE_HALFOP | MODE_VOICE;
d76ed9a9 5275 break;
5276 case '+':
5277 discrim->chan_req_modes |= MODE_VOICE;
5278 discrim->chan_no_modes |= MODE_CHANOP;
55342ce8 5279 discrim->chan_no_modes |= MODE_HALFOP;
5280 break;
5281 case '%':
5282 discrim->chan_req_modes |= MODE_HALFOP;
5283 discrim->chan_no_modes |= MODE_CHANOP;
5284 discrim->chan_no_modes |= MODE_VOICE;
d76ed9a9 5285 break;
5286 case '@':
5287 discrim->chan_req_modes |= MODE_CHANOP;
5288 break;
5289 case '\0':
258d1427 5290 send_message(user, bot, "MSG_NOT_CHANNEL_NAME");
d76ed9a9 5291 goto fail;
5292 }
5293 }
5294 find_channel:
5295 discrim->chan_no_modes &= ~discrim->chan_req_modes;
258d1427 5296 if (!(discrim->channel = GetChannel(argv[i]+j))) {
d76ed9a9 5297 /* secretly "allow_channel" now means "if a channel name is
5298 * specified, require that it currently exist" */
5299 if (allow_channel) {
258d1427 5300 send_message(user, bot, "MSG_CHANNEL_UNKNOWN", argv[i]);
d76ed9a9 5301 goto fail;
5302 } else {
2aef5f4b 5303 discrim->channel = AddChannel(argv[i]+j, now, NULL, NULL, NULL);
d76ed9a9 5304 }
258d1427 5305 }
d76ed9a9 5306 LockChannel(discrim->channel);
5307 } else if (irccasecmp(argv[i], "numchannels") == 0) {
5308 discrim->min_channels = discrim->max_channels = strtoul(argv[++i], NULL, 10);
258d1427 5309 } else if (irccasecmp(argv[i], "limit") == 0) {
5310 discrim->limit = strtoul(argv[++i], NULL, 10);
d76ed9a9 5311 } else if (irccasecmp(argv[i], "reason") == 0) {
5312 discrim->reason = strdup(unsplit_string(argv+i+1, argc-i-1, NULL));
5313 i = argc;
5314 } else if (irccasecmp(argv[i], "last") == 0) {
5315 discrim->min_ts = now - ParseInterval(argv[++i]);
5316 } else if ((irccasecmp(argv[i], "linked") == 0)
5317 || (irccasecmp(argv[i], "nickage") == 0)) {
5318 const char *cmp = argv[++i];
5319 if (cmp[0] == '<') {
5320 if (cmp[1] == '=') {
5321 discrim->min_ts = now - ParseInterval(cmp+2);
5322 } else {
5323 discrim->min_ts = now - (ParseInterval(cmp+1) - 1);
5324 }
5325 } else if (cmp[0] == '>') {
5326 if (cmp[1] == '=') {
5327 discrim->max_ts = now - ParseInterval(cmp+2);
5328 } else {
5329 discrim->max_ts = now - (ParseInterval(cmp+1) - 1);
5330 }
5331 } else {
5332 discrim->min_ts = now - ParseInterval(cmp+2);
5333 }
5334 } else if (irccasecmp(argv[i], "access") == 0) {
5335 const char *cmp = argv[++i];
5336 if (cmp[0] == '<') {
5337 if (discrim->min_level == 0) discrim->min_level = 1;
5338 if (cmp[1] == '=') {
5339 discrim->max_level = strtoul(cmp+2, NULL, 0);
5340 } else {
5341 discrim->max_level = strtoul(cmp+1, NULL, 0) - 1;
5342 }
5343 } else if (cmp[0] == '=') {
5344 discrim->min_level = discrim->max_level = strtoul(cmp+1, NULL, 0);
5345 } else if (cmp[0] == '>') {
5346 if (cmp[1] == '=') {
5347 discrim->min_level = strtoul(cmp+2, NULL, 0);
5348 } else {
5349 discrim->min_level = strtoul(cmp+1, NULL, 0) + 1;
5350 }
5351 } else {
5352 discrim->min_level = strtoul(cmp+2, NULL, 0);
5353 }
5354 } else if ((irccasecmp(argv[i], "abuse") == 0)
5355 && (irccasecmp(argv[++i], "opers") == 0)) {
5356 discrim->match_opers = 1;
5357 } else if (irccasecmp(argv[i], "depth") == 0) {
5358 discrim->domain_depth = strtoul(argv[++i], NULL, 0);
5359 } else if (irccasecmp(argv[i], "clones") == 0) {
5360 discrim->min_clones = strtoul(argv[++i], NULL, 0);
5361 } else {
258d1427 5362 send_message(user, bot, "MSG_INVALID_CRITERIA", argv[i]);
d76ed9a9 5363 goto fail;
5364 }
5365 }
5366
5367 if (discrim->mask_nick && !strcmp(discrim->mask_nick, "*")) {
258d1427 5368 discrim->mask_nick = 0;
d76ed9a9 5369 }
5370 if (discrim->mask_ident && !strcmp(discrim->mask_ident, "*")) {
5371 discrim->mask_ident = 0;
5372 }
5373 if (discrim->mask_info && !strcmp(discrim->mask_info, "*")) {
258d1427 5374 discrim->mask_info = 0;
d76ed9a9 5375 }
0e08a8e0 5376 if (discrim->mask_version && !strcmp(discrim->mask_version, "*")) {
5377 discrim->mask_version = 0;
5378 }
d76ed9a9 5379 if (discrim->mask_host && !discrim->mask_host[strspn(discrim->mask_host, "*.")]) {
5380 discrim->mask_host = 0;
5381 }
27eaa617 5382
5383 if(discrim->use_regex)
5384 {
5385 if(discrim->mask_nick)
5386 {
5387 int err = regcomp(&discrim->regex_nick, discrim->mask_nick, REG_EXTENDED|REG_ICASE|REG_NOSUB);
5388 discrim->has_regex_nick = !err;
5389 if(err)
5390 {
5391 char buff[256];
5392 buff[regerror(err, &discrim->regex_nick, buff, sizeof(buff))] = 0;
5393
c092fcad 5394 send_message(user, bot, "OSMSG_INVALID_REGEX", discrim->mask_nick, buff, err);
27eaa617 5395 goto regfail;
5396 }
5397 }
5398
5399 if(discrim->mask_ident)
5400 {
5401 int err = regcomp(&discrim->regex_ident, discrim->mask_ident, REG_EXTENDED|REG_ICASE|REG_NOSUB);
5402 discrim->has_regex_ident = !err;
5403 if(err)
5404 {
5405 char buff[256];
5406 buff[regerror(err, &discrim->regex_ident, buff, sizeof(buff))] = 0;
5407
c092fcad 5408 send_message(user, bot, "OSMSG_INVALID_REGEX", discrim->mask_ident, buff, err);
27eaa617 5409 goto regfail;
5410 }
5411 }
5412
5413 if(discrim->mask_host)
5414 {
5415 int err = regcomp(&discrim->regex_host, discrim->mask_host, REG_EXTENDED|REG_ICASE|REG_NOSUB);
5416 discrim->has_regex_host = !err;
5417 if(err)
5418 {
5419 char buff[256];
5420 buff[regerror(err, &discrim->regex_host, buff, sizeof(buff))] = 0;
5421
c092fcad 5422 send_message(user, bot, "OSMSG_INVALID_REGEX", discrim->mask_host, buff, err);
27eaa617 5423 goto regfail;
5424 }
5425 }
5426
5427 if(discrim->mask_info)
5428 {
5429 int err = regcomp(&discrim->regex_info, discrim->mask_info, REG_EXTENDED|REG_ICASE|REG_NOSUB);
5430 discrim->has_regex_info = !err;
5431 if(err)
5432 {
5433 char buff[256];
5434 buff[regerror(err, &discrim->regex_info, buff, sizeof(buff))] = 0;
5435
c092fcad 5436 send_message(user, bot, "OSMSG_INVALID_REGEX", discrim->mask_info, buff, err);
27eaa617 5437 goto regfail;
5438 }
5439 }
0e08a8e0 5440
5441 if(discrim->mask_version)
5442 {
5443 int err = regcomp(&discrim->regex_version, discrim->mask_version, REG_EXTENDED|REG_ICASE|REG_NOSUB);
5444 discrim->has_regex_version = !err;
5445 if(err)
5446 {
5447 char buff[256];
5448 buff[regerror(err, &discrim->regex_version, buff, sizeof(buff))] = 0;
5449
5450 send_message(user, bot, "OSMSG_INVALID_REGEX", discrim->mask_version, buff, err);
5451 goto regfail;
5452 }
5453 }
27eaa617 5454 }
5455
d76ed9a9 5456 return discrim;
27eaa617 5457
d76ed9a9 5458 fail:
5459 free(discrim);
5460 return NULL;
27eaa617 5461
5462 regfail:
5463 if(discrim->has_regex_nick)
5464 regfree(&discrim->regex_nick);
5465 if(discrim->has_regex_ident)
5466 regfree(&discrim->regex_ident);
5467 if(discrim->has_regex_host)
5468 regfree(&discrim->regex_host);
5469 if(discrim->has_regex_info)
5470 regfree(&discrim->regex_info);
5471
5472 free(discrim);
5473 return NULL;
d76ed9a9 5474}
5475
5476static int
5477discrim_match(discrim_t discrim, struct userNode *user)
5478{
5479 unsigned int access;
63665495 5480 char *scmp=NULL, *dcmp=NULL;
d76ed9a9 5481
5482 if ((user->timestamp < discrim->min_ts)
5483 || (user->timestamp > discrim->max_ts)
5484 || (user->channels.used < discrim->min_channels)
5485 || (user->channels.used > discrim->max_channels)
5486 || (discrim->authed == 0 && user->handle_info)
5487 || (discrim->authed == 1 && !user->handle_info)
5488 || (discrim->info_space == 0 && user->info[0] == ' ')
5489 || (discrim->info_space == 1 && user->info[0] != ' ')
d76ed9a9 5490 || (discrim->server && !match_ircglob(user->uplink->name, discrim->server))
5491 || (discrim->accountmask && (!user->handle_info || !match_ircglob(user->handle_info->handle, discrim->accountmask)))
2f61d1d7 5492 || (discrim->ip_mask_bits && !irc_check_mask(&user->ip, &discrim->ip_mask, discrim->ip_mask_bits))
5493 )
5494 return 0;
5495
5496 if (discrim->channel && !GetUserMode(discrim->channel, user))
d76ed9a9 5497 return 0;
27eaa617 5498
5499 if(discrim->use_regex)
5500 {
5501 if((discrim->has_regex_nick && regexec(&discrim->regex_nick, user->nick, 0, 0, 0))
5502 || (discrim->has_regex_ident && regexec(&discrim->regex_ident, user->ident, 0, 0, 0))
5503 || (discrim->has_regex_host && regexec(&discrim->regex_host, user->hostname, 0, 0, 0))
0e08a8e0 5504 || (discrim->has_regex_info && regexec(&discrim->regex_info, user->info, 0, 0, 0))
5505 || (discrim->has_regex_version && (!user->version_reply || regexec(&discrim->regex_version, user->version_reply, 0, 0, 0)))) {
27eaa617 5506 return 0;
5507 }
5508 }
5509 else
5510 {
5511 if ((discrim->mask_nick && !match_ircglob(user->nick, discrim->mask_nick))
5512 || (discrim->mask_ident && !match_ircglob(user->ident, discrim->mask_ident))
5513 || (discrim->mask_host && !match_ircglob(user->hostname, discrim->mask_host))
0e08a8e0 5514 || (discrim->mask_info && !match_ircglob(user->info, discrim->mask_info))
5515 || (discrim->mask_version && (!user->version_reply || !match_ircglob(user->version_reply, discrim->mask_version))) ) {
27eaa617 5516 return 0;
5517 }
5518 }
5519
63665495 5520 if ((discrim->intra_scmp > 0 && discrim->intra_dcmp > 0)) {
5521 switch(discrim->intra_scmp) {
5522 case 1: scmp=user->nick; break;
5523 case 2: scmp=user->ident; break;
5524 case 3:
5525 scmp=user->info;
5526 if (discrim->info_space == 1) scmp++;
5527 break;
5528 }
5529 switch(discrim->intra_dcmp) {
5530 case 1: dcmp=user->nick; break;
5531 case 2: dcmp=user->ident; break;
5532 case 3: /* When checking INFO, and info_space is enabled
5533 * ignore the first character in a search
5534 * XXX: Should we ignore ALL leading whitespace?
5535 * Also, what about ignoring ~ in ident?
5536 */
5537 dcmp=user->info;
5538 if (discrim->info_space == 1) dcmp++;
5539 break;
5540 }
5541 if (irccasecmp(scmp,dcmp))
5542 return 0;
5543 }
5544
d76ed9a9 5545 access = user->handle_info ? user->handle_info->opserv_level : 0;
5546 if ((access < discrim->min_level)
5547 || (access > discrim->max_level)) {
5548 return 0;
5549 }
d76ed9a9 5550 if (discrim->min_clones > 1) {
2f61d1d7 5551 struct opserv_hostinfo *ohi = dict_find(opserv_hostinfo_dict, irc_ntoa(&user->ip), NULL);
5552 if (!ohi || (ohi->clients.used < discrim->min_clones))
5553 return 0;
d76ed9a9 5554 }
5555 return 1;
5556}
5557
5558static unsigned int
5559opserv_discrim_search(discrim_t discrim, discrim_search_func dsf, void *data)
5560{
5561 unsigned int nn, count;
5562 struct userList matched;
5563
5564 userList_init(&matched);
5565 /* Try most optimized search methods first */
5566 if (discrim->channel) {
5567 for (nn=0;
5568 (nn < discrim->channel->members.used)
5569 && (matched.used < discrim->limit);
5570 nn++) {
5571 struct modeNode *mn = discrim->channel->members.list[nn];
5572 if (((mn->modes & discrim->chan_req_modes) != discrim->chan_req_modes)
5573 || ((mn->modes & discrim->chan_no_modes) != 0)) {
5574 continue;
5575 }
5576 if (discrim_match(discrim, mn->user)) {
5577 userList_append(&matched, mn->user);
5578 }
5579 }
2f61d1d7 5580 } else if (discrim->ip_mask_bits == 128) {
5581 struct opserv_hostinfo *ohi = dict_find(opserv_hostinfo_dict, irc_ntoa(&discrim->ip_mask), NULL);
d76ed9a9 5582 if (!ohi) {
5583 userList_clean(&matched);
5584 return 0;
5585 }
5586 for (nn=0; (nn<ohi->clients.used) && (matched.used < discrim->limit); nn++) {
5587 if (discrim_match(discrim, ohi->clients.list[nn])) {
5588 userList_append(&matched, ohi->clients.list[nn]);
5589 }
5590 }
5591 } else {
5592 dict_iterator_t it;
5593 for (it=dict_first(clients); it && (matched.used < discrim->limit); it=iter_next(it)) {
5594 if (discrim_match(discrim, iter_data(it))) {
5595 userList_append(&matched, iter_data(it));
5596 }
5597 }
5598 }
5599
5600 if (!matched.used) {
5601 userList_clean(&matched);
5602 return 0;
5603 }
5604
5605 if (discrim->option_log) {
5606 log_module(OS_LOG, LOG_INFO, "Logging matches for search:");
5607 }
5608 for (nn=0; nn<matched.used; nn++) {
5609 struct userNode *user = matched.list[nn];
5610 if (discrim->option_log) {
5611 log_module(OS_LOG, LOG_INFO, " %s!%s@%s", user->nick, user->ident, user->hostname);
5612 }
5613 if (dsf(user, data)) {
258d1427 5614 /* If a search function returns true, it ran into a
5615 problem. Stop going through the list. */
5616 break;
5617 }
d76ed9a9 5618 }
5619 if (discrim->option_log) {
5620 log_module(OS_LOG, LOG_INFO, "End of matching users.");
5621 }
5622 count = matched.used;
5623 userList_clean(&matched);
5624 return count;
5625}
5626
5627static int
5628trace_print_func(struct userNode *match, void *extra)
5629{
5630 struct discrim_and_source *das = extra;
5631 if (match->handle_info) {
258d1427 5632 send_message_type(4, das->source, das->destination, "%-15s\002 \002%10s\002@\002%s (%s)", match->nick, match->ident, match->hostname, match->handle_info->handle);
d76ed9a9 5633 } else {
258d1427 5634 send_message_type(4, das->source, das->destination, "%-15s\002 \002%10s\002@\002%s", match->nick, match->ident, match->hostname);
d76ed9a9 5635 }
5636 return 0;
5637}
5638
5639static int
5640trace_count_func(UNUSED_ARG(struct userNode *match), UNUSED_ARG(void *extra))
5641{
5642 return 0;
5643}
5644
5645static int
5646is_oper_victim(struct userNode *user, struct userNode *target, int match_opers)
5647{
5648 return !(IsService(target)
5649 || (!match_opers && IsOper(target))
5650 || (target->handle_info
5651 && target->handle_info->opserv_level > user->handle_info->opserv_level));
5652}
5653
5654static int
5655trace_gline_func(struct userNode *match, void *extra)
5656{
5657 struct discrim_and_source *das = extra;
5658
5659 if (is_oper_victim(das->source, match, das->discrim->match_opers)) {
1c5f6697 5660 opserv_block(match, das->source->handle_info->handle, das->discrim->reason, das->discrim->duration, das->discrim->silent);
d76ed9a9 5661 }
5662
5663 return 0;
5664}
5665
d914d1cb 5666static int
5667trace_shun_func(struct userNode *match, void *extra)
5668{
5669 struct discrim_and_source *das = extra;
5670
5671 if (is_oper_victim(das->source, match, das->discrim->match_opers)) {
5672 opserv_shun(match, das->source->handle_info->handle, das->discrim->reason, das->discrim->duration);
5673 }
5674
5675 return 0;
5676}
5677
d76ed9a9 5678static int
5679trace_kill_func(struct userNode *match, void *extra)
5680{
5681 struct discrim_and_source *das = extra;
5682
5683 if (is_oper_victim(das->source, match, das->discrim->match_opers)) {
258d1427 5684 char *reason;
d76ed9a9 5685 if (das->discrim->reason) {
5686 reason = das->discrim->reason;
5687 } else {
5688 reason = alloca(strlen(OSMSG_KILL_REQUESTED)+strlen(das->source->nick)+1);
5689 sprintf(reason, OSMSG_KILL_REQUESTED, das->source->nick);
5690 }
5691 DelUser(match, opserv, 1, reason);
5692 }
5693
5694 return 0;
5695}
5696
d82cf2f0 5697static int
5698trace_mark_func(struct userNode *match, void *extra)
5699{
5700 struct discrim_and_source *das = extra;
5701 char *mark = das->discrim->mark;
5702
5703 if(!mark)
5704 return 1;
5705 irc_mark(match, mark);
5706 return 0;
5707}
5708
0e08a8e0 5709static int
5710trace_svsjoin_func(struct userNode *match, void *extra)
5711{
5712 struct discrim_and_source *das = extra;
5713
5714 char *channame = das->discrim->chantarget;
5aa400d2 5715 int checkrestrictions = das->discrim->checkrestrictions;
0e08a8e0 5716 struct chanNode *channel;
5717
a62ba70c 5718 if(!channame || !IsChannelName(channame)) {
0e08a8e0 5719 //reply("MSG_NOT_CHANNEL_NAME");
5720 return 1;
5721 }
5722
5723 if (!(channel = GetChannel(channame))) {
5724 channel = AddChannel(channame, now, NULL, NULL, NULL);
5725 }
5aa400d2 5726
5727 if (checkrestrictions) {
27fa6acf 5728 if (trace_check_bans(match, channel) == 1) {
5729 return 1; /* found on lamer list */
5aa400d2 5730 }
5731
5732 if (channel->modes & MODE_INVITEONLY) {
27fa6acf 5733 return 1; /* channel is invite only */
5aa400d2 5734 }
5735
5736 if (channel->members.used >= channel->limit) {
27fa6acf 5737 return 1; /* channel is invite on */
5aa400d2 5738 }
5739
5740 if (*channel->key) {
27fa6acf 5741 return 1; /* channel is password protected */
5aa400d2 5742 }
5743 }
5744
0e08a8e0 5745 if (GetUserMode(channel, match)) {
5746// reply("OSMSG_ALREADY_THERE", channel->name);
5747 return 1;
5748 }
5749 irc_svsjoin(opserv, match, channel);
5750 // reply("OSMSG_SVSJOIN_SENT");
5751 return 0;
5752}
5753
39c1a4ef 5754static int
5755trace_svspart_func(struct userNode *match, void *extra)
5756{
5757 struct discrim_and_source *das = extra;
39c1a4ef 5758 char *channame = das->discrim->chantarget;
5759 struct chanNode *channel;
5760
5761 if(!channame || !IsChannelName(channame))
5762 return 1;
5763
5764 if (!(channel = GetChannel(channame)))
5765 return 1;
5766
5767 if (!GetUserMode(channel, match))
5768 return 1;
5769
5770 irc_svspart(opserv, match, channel);
5771 return 0;
5772}
5773
0e08a8e0 5774static int
5775trace_version_func(struct userNode *match, UNUSED_ARG(void *extra))
5776{
5777 irc_version_user(opserv, match);
5778 return 0;
5779}
5780
d76ed9a9 5781static int
5782is_gagged(char *mask)
5783{
5784 struct gag_entry *gag;
5785
5786 for (gag = gagList; gag; gag = gag->next) {
5787 if (match_ircglobs(gag->mask, mask)) return 1;
5788 }
5789 return 0;
5790}
5791
5792static int
5793trace_gag_func(struct userNode *match, void *extra)
5794{
5795 struct discrim_and_source *das = extra;
5796
5797 if (is_oper_victim(das->source, match, das->discrim->match_opers)) {
5798 char *reason, *mask;
5799 int masksize;
5800 if (das->discrim->reason) {
5801 reason = das->discrim->reason;
5802 } else {
5803 reason = alloca(strlen(OSMSG_GAG_REQUESTED)+strlen(das->source->nick)+1);
5804 sprintf(reason, OSMSG_GAG_REQUESTED, das->source->nick);
5805 }
258d1427 5806 masksize = 5+strlen(match->hostname);
5807 mask = alloca(masksize);
d76ed9a9 5808 snprintf(mask, masksize, "*!*@%s", match->hostname);
258d1427 5809 if (!is_gagged(mask)) {
d76ed9a9 5810 gag_create(mask, das->source->handle_info->handle, reason,
5811 das->discrim->duration ? (now + das->discrim->duration) : 0);
5812 }
5813 }
5814
5815 return 0;
5816}
5817
5818static int
5819trace_domains_func(struct userNode *match, void *extra)
5820{
5821 struct discrim_and_source *das = extra;
2f61d1d7 5822 irc_in_addr_t ip;
d76ed9a9 5823 unsigned long *count;
5824 unsigned int depth;
5825 char *hostname;
2f61d1d7 5826 char ipmask[IRC_NTOP_MASK_MAX_SIZE];
d76ed9a9 5827
2f61d1d7 5828 if (irc_pton(&ip, NULL, match->hostname)) {
5829 if (irc_in_addr_is_ipv4(ip)) {
5830 unsigned long matchip = ntohl(ip.in6_32[3]);
5831 /* raw IP address.. use up to first three octets of IP */
5832 switch (das->discrim->domain_depth) {
5833 default:
5834 snprintf(ipmask, sizeof(ipmask), "%lu.%lu.%lu.*", (matchip>>24)&255, (matchip>>16)&255, (matchip>>8)&255);
5835 break;
5836 case 2:
5837 snprintf(ipmask, sizeof(ipmask), "%lu.%lu.*", (matchip>>24)&255, (matchip>>16)&255);
5838 break;
5839 case 1:
5840 snprintf(ipmask, sizeof(ipmask), "%lu.*", (matchip>>24)&255);
5841 break;
5842 }
5843 } else if (irc_in_addr_is_ipv6(ip)) {
5844 switch (das->discrim->domain_depth) {
5845 case 1: depth = 16; goto ipv6_pfx;
5846 case 2: depth = 24; goto ipv6_pfx;
5847 case 3: depth = 32; goto ipv6_pfx;
5848 default: depth = das->discrim->domain_depth;
5849 ipv6_pfx:
5850 irc_ntop_mask(ipmask, sizeof(ipmask), &ip, depth);
5851 }
5852 } else safestrncpy(ipmask, match->hostname, sizeof(ipmask));
5853 ipmask[sizeof(ipmask) - 1] = '\0';
d76ed9a9 5854 hostname = ipmask;
5855 } else {
5856 hostname = match->hostname + strlen(match->hostname);
2f61d1d7 5857 for (depth=das->discrim->domain_depth;
d76ed9a9 5858 depth && (hostname > match->hostname);
5859 depth--) {
5860 hostname--;
5861 while ((hostname > match->hostname) && (*hostname != '.')) hostname--;
5862 }
5863 if (*hostname == '.') hostname++; /* advance past last dot we saw */
5864 }
5865 if (!(count = dict_find(das->dict, hostname, NULL))) {
5866 count = calloc(1, sizeof(*count));
5867 dict_insert(das->dict, strdup(hostname), count);
5868 }
5869 (*count)++;
5870 return 0;
5871}
5872
5873static int
5874opserv_show_hostinfo(const char *key, void *data, void *extra)
5875{
5876 unsigned long *count = data;
5877 struct discrim_and_source *das = extra;
5878
258d1427 5879 send_message_type(4, das->source, das->destination, "%s %lu", key, *count);
d76ed9a9 5880 return !--das->disp_limit;
5881}
5882
5883static MODCMD_FUNC(cmd_trace)
5884{
5885 struct discrim_and_source das;
5886 discrim_search_func action;
5887 unsigned int matches;
5888 struct svccmd *subcmd;
5889 char buf[MAXLEN];
a62ba70c 5890 int ret = 1;
d76ed9a9 5891
5892 sprintf(buf, "trace %s", argv[1]);
258d1427 5893 if (!(subcmd = dict_find(opserv_service->commands, buf, NULL))) {
5894 reply("OSMSG_BAD_ACTION", argv[1]);
d76ed9a9 5895 return 0;
5896 }
258d1427 5897 if (!svccmd_can_invoke(user, opserv_service->bot, subcmd, channel, SVCCMD_NOISY))
d76ed9a9 5898 return 0;
5899 if (!irccasecmp(argv[1], "print"))
5900 action = trace_print_func;
5901 else if (!irccasecmp(argv[1], "count"))
5902 action = trace_count_func;
5903 else if (!irccasecmp(argv[1], "domains"))
5904 action = trace_domains_func;
5905 else if (!irccasecmp(argv[1], "gline"))
5906 action = trace_gline_func;
d914d1cb 5907 else if (!irccasecmp(argv[1], "shun"))
5908 action = trace_shun_func;
d76ed9a9 5909 else if (!irccasecmp(argv[1], "kill"))
5910 action = trace_kill_func;
5911 else if (!irccasecmp(argv[1], "gag"))
5912 action = trace_gag_func;
0e08a8e0 5913 else if (!irccasecmp(argv[1], "svsjoin"))
5914 action = trace_svsjoin_func;
39c1a4ef 5915 else if (!irccasecmp(argv[1], "svspart"))
5916 action = trace_svspart_func;
0e08a8e0 5917 else if (!irccasecmp(argv[1], "version"))
5918 action = trace_version_func;
d82cf2f0 5919 else if (!irccasecmp(argv[1], "mark"))
5920 action = trace_mark_func;
d76ed9a9 5921 else {
258d1427 5922 reply("OSMSG_BAD_ACTION", argv[1]);
5923 return 0;
d76ed9a9 5924 }
5925
5926 if (user->handle_info->opserv_level < subcmd->min_opserv_level) {
5927 reply("OSMSG_LEVEL_TOO_LOW");
5928 return 0;
5929 }
5930
5931 das.dict = NULL;
5932 das.source = user;
258d1427 5933 das.destination = cmd->parent->bot;
5934 das.discrim = opserv_discrim_create(user, cmd->parent->bot, argc-2, argv+2, 1);
d76ed9a9 5935 if (!das.discrim)
5936 return 0;
5937
5938 if (action == trace_print_func)
de9510bc 5939 {
8e11460f 5940 reply("OSMSG_USER_SEARCH_RESULTS");
de9510bc 5941 reply("OSMSG_USER_SEARCH_BAR");
5942 reply("OSMSG_USER_SEARCH_HEADER");
5943 reply("OSMSG_USER_SEARCH_BAR");
5944 }
d76ed9a9 5945 else if (action == trace_count_func)
258d1427 5946 das.discrim->limit = INT_MAX;
d76ed9a9 5947 else if ((action == trace_gline_func) && !das.discrim->duration)
5948 das.discrim->duration = opserv_conf.block_gline_duration;
d914d1cb 5949 else if ((action == trace_shun_func) && !das.discrim->duration)
5950 das.discrim->duration = opserv_conf.block_shun_duration;
d76ed9a9 5951 else if (action == trace_domains_func) {
5952 das.dict = dict_new();
5953 dict_set_free_data(das.dict, free);
5954 dict_set_free_keys(das.dict, free);
5955 das.disp_limit = das.discrim->limit;
5956 das.discrim->limit = INT_MAX;
5957 }
d76ed9a9 5958
a62ba70c 5959 if (action == trace_svsjoin_func && !das.discrim->chantarget) {
5960 reply("OSMSG_SVSJOIN_NO_TARGET");
5961 ret = 0;
5962 }
39c1a4ef 5963 else if (action == trace_svspart_func && !das.discrim->chantarget) {
5964 reply("OSMSG_SVSPART_NO_TARGET");
5965 ret = 0;
5966 }
d82cf2f0 5967 else if (action == trace_mark_func && !das.discrim->mark) {
5968 reply("OSMSG_MARK_NO_MARK");
5969 ret = 0;
5970 }
a62ba70c 5971 else {
5972 matches = opserv_discrim_search(das.discrim, action, &das);
d76ed9a9 5973
a62ba70c 5974 if (action == trace_domains_func)
5975 dict_foreach(das.dict, opserv_show_hostinfo, &das);
5976
5977 if (matches)
5978 {
5979 if(action == trace_print_func)
5980 reply("OSMSG_USER_SEARCH_COUNT_BAR", matches);
5981 else
5982 reply("OSMSG_USER_SEARCH_COUNT", matches);
5983 }
8e11460f 5984 else
a62ba70c 5985 reply("MSG_NO_MATCHES");
8e11460f 5986 }
d76ed9a9 5987
5988 if (das.discrim->channel)
5989 UnlockChannel(das.discrim->channel);
5990 free(das.discrim->reason);
27eaa617 5991
5992 if(das.discrim->has_regex_nick)
5993 regfree(&das.discrim->regex_nick);
5994 if(das.discrim->has_regex_ident)
5995 regfree(&das.discrim->regex_ident);
5996 if(das.discrim->has_regex_host)
5997 regfree(&das.discrim->regex_host);
5998 if(das.discrim->has_regex_info)
5999 regfree(&das.discrim->regex_info);
0e08a8e0 6000 if(das.discrim->has_regex_version)
6001 regfree(&das.discrim->regex_version);
27eaa617 6002
d76ed9a9 6003 free(das.discrim);
6004 dict_delete(das.dict);
a62ba70c 6005 return ret;
d76ed9a9 6006}
6007
258d1427 6008typedef void (*cdiscrim_search_func)(struct chanNode *match, void *data, struct userNode *bot);
d76ed9a9 6009
6010typedef struct channel_discrim {
6011 char *name, *topic;
6012
6013 unsigned int min_users, max_users;
6014 time_t min_ts, max_ts;
6015 unsigned int limit;
6016} *cdiscrim_t;
6017
258d1427 6018static cdiscrim_t opserv_cdiscrim_create(struct userNode *user, struct userNode *bot, unsigned int argc, char *argv[]);
6019static unsigned int opserv_cdiscrim_search(cdiscrim_t discrim, cdiscrim_search_func dsf, void *data, struct userNode *bot);
d76ed9a9 6020
6021static time_t
6022smart_parse_time(const char *str) {
6023 /* If an interval-style string is given, treat as time before now.
6024 * If it's all digits, treat directly as a Unix timestamp. */
6025 return str[strspn(str, "0123456789")] ? (time_t)(now - ParseInterval(str)) : (time_t)atoi(str);
6026}
6027
6028static cdiscrim_t
258d1427 6029opserv_cdiscrim_create(struct userNode *user, struct userNode *bot, unsigned int argc, char *argv[])
d76ed9a9 6030{
6031 cdiscrim_t discrim;
6032 unsigned int i;
6033
6034 discrim = calloc(1, sizeof(*discrim));
6035 discrim->limit = 25;
6036
6037 for (i = 0; i < argc; i++) {
258d1427 6038 /* Assume all criteria require arguments. */
6039 if (i == (argc - 1)) {
6040 send_message(user, bot, "MSG_MISSING_PARAMS", argv[i]);
6041 return NULL;
6042 }
6043
6044 if (!irccasecmp(argv[i], "name"))
6045 discrim->name = argv[++i];
6046 else if (!irccasecmp(argv[i], "topic"))
6047 discrim->topic = argv[++i];
6048 else if (!irccasecmp(argv[i], "users")) {
6049 const char *cmp = argv[++i];
d76ed9a9 6050 if (cmp[0] == '<') {
6051 if (cmp[1] == '=')
6052 discrim->max_users = strtoul(cmp+2, NULL, 0);
6053 else
6054 discrim->max_users = strtoul(cmp+1, NULL, 0) - 1;
6055 } else if (cmp[0] == '=') {
6056 discrim->min_users = discrim->max_users = strtoul(cmp+1, NULL, 0);
6057 } else if (cmp[0] == '>') {
6058 if (cmp[1] == '=')
6059 discrim->min_users = strtoul(cmp+2, NULL, 0);
6060 else
6061 discrim->min_users = strtoul(cmp+1, NULL, 0) + 1;
6062 } else {
6063 discrim->min_users = strtoul(cmp+2, NULL, 0);
6064 }
258d1427 6065 } else if (!irccasecmp(argv[i], "timestamp")) {
6066 const char *cmp = argv[++i];
d76ed9a9 6067 if (cmp[0] == '<') {
6068 if (cmp[1] == '=')
6069 discrim->max_ts = smart_parse_time(cmp+2);
6070 else
6071 discrim->max_ts = smart_parse_time(cmp+1)-1;
6072 } else if (cmp[0] == '=') {
6073 discrim->min_ts = discrim->max_ts = smart_parse_time(cmp+1);
6074 } else if (cmp[0] == '>') {
6075 if (cmp[1] == '=')
6076 discrim->min_ts = smart_parse_time(cmp+2);
6077 else
6078 discrim->min_ts = smart_parse_time(cmp+1)+1;
6079 } else {
6080 discrim->min_ts = smart_parse_time(cmp);
6081 }
258d1427 6082 } else if (!irccasecmp(argv[i], "limit")) {
6083 discrim->limit = strtoul(argv[++i], NULL, 10);
6084 } else {
6085 send_message(user, bot, "MSG_INVALID_CRITERIA", argv[i]);
6086 goto fail;
6087 }
d76ed9a9 6088 }
6089
6090 if (discrim->name && !strcmp(discrim->name, "*"))
258d1427 6091 discrim->name = 0;
d76ed9a9 6092 if (discrim->topic && !strcmp(discrim->topic, "*"))
258d1427 6093 discrim->topic = 0;
d76ed9a9 6094
6095 return discrim;
6096 fail:
6097 free(discrim);
6098 return NULL;
6099}
6100
6101static int
6102cdiscrim_match(cdiscrim_t discrim, struct chanNode *chan)
6103{
6104 if ((discrim->name && !match_ircglob(chan->name, discrim->name)) ||
6105 (discrim->topic && !match_ircglob(chan->topic, discrim->topic)) ||
6106 (discrim->min_users && chan->members.used < discrim->min_users) ||
6107 (discrim->max_users && chan->members.used > discrim->max_users) ||
6108 (discrim->min_ts && chan->timestamp < discrim->min_ts) ||
6109 (discrim->max_ts && chan->timestamp > discrim->max_ts)) {
258d1427 6110 return 0;
d76ed9a9 6111 }
6112 return 1;
6113}
6114
258d1427 6115static unsigned int opserv_cdiscrim_search(cdiscrim_t discrim, cdiscrim_search_func dsf, void *data, struct userNode *bot)
d76ed9a9 6116{
6117 unsigned int count = 0;
6118 dict_iterator_t it, next;
6119
6120 for (it = dict_first(channels); it && count < discrim->limit ; it = next) {
258d1427 6121 struct chanNode *chan = iter_data(it);
d76ed9a9 6122
258d1427 6123 /* Hold on to the next channel in case we decide to
6124 add actions that destructively modify the channel. */
6125 next = iter_next(it);
6126 if ((chan->members.used > 0) && cdiscrim_match(discrim, chan)) {
6127 dsf(chan, data, bot);
6128 count++;
6129 }
d76ed9a9 6130 }
6131
6132 return count;
6133}
6134
258d1427 6135void channel_count(UNUSED_ARG(struct chanNode *channel), UNUSED_ARG(void *data), UNUSED_ARG(struct userNode *bot))
d76ed9a9 6136{
6137}
6138
258d1427 6139void channel_print(struct chanNode *channel, void *data, struct userNode *bot)
d76ed9a9 6140{
6141 char modes[MAXLEN];
6142 irc_make_chanmode(channel, modes);
258d1427 6143 send_message(data, bot, "OSMSG_CSEARCH_CHANNEL_INFO", channel->name, channel->members.used, modes, channel->topic);
d76ed9a9 6144}
6145
6146static MODCMD_FUNC(cmd_csearch)
6147{
6148 cdiscrim_t discrim;
6149 unsigned int matches;
6150 cdiscrim_search_func action;
6151 struct svccmd *subcmd;
6152 char buf[MAXLEN];
6153
6154 if (!irccasecmp(argv[1], "count"))
258d1427 6155 action = channel_count;
d76ed9a9 6156 else if (!irccasecmp(argv[1], "print"))
258d1427 6157 action = channel_print;
d76ed9a9 6158 else {
258d1427 6159 reply("OSMSG_BAD_ACTION", argv[1]);
6160 return 0;
d76ed9a9 6161 }
6162
6163 sprintf(buf, "%s %s", argv[0], argv[0]);
258d1427 6164 if ((subcmd = dict_find(opserv_service->commands, buf, NULL))
6165 && !svccmd_can_invoke(user, opserv_service->bot, subcmd, channel, SVCCMD_NOISY)) {
d76ed9a9 6166 return 0;
6167 }
6168
258d1427 6169 discrim = opserv_cdiscrim_create(user, cmd->parent->bot, argc - 2, argv + 2);
d76ed9a9 6170 if (!discrim)
258d1427 6171 return 0;
d76ed9a9 6172
6173 if (action == channel_print)
258d1427 6174 reply("OSMSG_CHANNEL_SEARCH_RESULTS");
d76ed9a9 6175 else if (action == channel_count)
258d1427 6176 discrim->limit = INT_MAX;
d76ed9a9 6177
258d1427 6178 matches = opserv_cdiscrim_search(discrim, action, user, cmd->parent->bot);
d76ed9a9 6179
6180 if (matches)
258d1427 6181 reply("MSG_MATCH_COUNT", matches);
d76ed9a9 6182 else
258d1427 6183 reply("MSG_NO_MATCHES");
d76ed9a9 6184
6185 free(discrim);
6186 return 1;
6187}
6188
6189static MODCMD_FUNC(cmd_gsync)
6190{
6191 struct server *src;
6192 if (argc > 1) {
6193 src = GetServerH(argv[1]);
6194 if (!src) {
6195 reply("MSG_SERVER_UNKNOWN", argv[1]);
6196 return 0;
6197 }
6198 } else {
6199 src = self->uplink;
6200 }
6201 irc_stats(cmd->parent->bot, src, 'G');
6202 reply("OSMSG_GSYNC_RUNNING", src->name);
6203 return 1;
6204}
6205
d914d1cb 6206static MODCMD_FUNC(cmd_ssync)
6207{
6208 struct server *src;
6209 if (argc > 1) {
6210 src = GetServerH(argv[1]);
6211 if (!src) {
6212 reply("MSG_SERVER_UNKNOWN", argv[1]);
6213 return 0;
6214 }
6215 } else {
6216 src = self->uplink;
6217 }
6218 irc_stats(cmd->parent->bot, src, 'S');
6219 reply("OSMSG_SSYNC_RUNNING", src->name);
6220 return 1;
6221}
6222
d76ed9a9 6223struct gline_extra {
6224 struct userNode *user;
6225 struct string_list *glines;
258d1427 6226 struct userNode *bot;
d76ed9a9 6227};
6228
6229static void
6230gtrace_print_func(struct gline *gline, void *extra)
6231{
6232 struct gline_extra *xtra = extra;
6233 char *when_text, set_text[20];
6234 strftime(set_text, sizeof(set_text), "%Y-%m-%d", localtime(&gline->issued));
6235 when_text = asctime(localtime(&gline->expires));
6236 when_text[strlen(when_text)-1] = 0; /* strip lame \n */
258d1427 6237 send_message(xtra->user, xtra->bot, "OSMSG_GTRACE_FORMAT", gline->target, set_text, gline->issuer, when_text, gline->reason);
d76ed9a9 6238}
6239
6240static void
6241gtrace_count_func(UNUSED_ARG(struct gline *gline), UNUSED_ARG(void *extra))
6242{
6243}
6244
6245static void
6246gtrace_ungline_func(struct gline *gline, void *extra)
6247{
6248 struct gline_extra *xtra = extra;
6249 string_list_append(xtra->glines, strdup(gline->target));
6250}
6251
6252static MODCMD_FUNC(cmd_gtrace)
6253{
6254 struct gline_discrim *discrim;
6255 gline_search_func action;
6256 unsigned int matches, nn;
6257 struct gline_extra extra;
6258 struct svccmd *subcmd;
6259 char buf[MAXLEN];
6260
6261 if (!irccasecmp(argv[1], "print"))
6262 action = gtrace_print_func;
6263 else if (!irccasecmp(argv[1], "count"))
6264 action = gtrace_count_func;
6265 else if (!irccasecmp(argv[1], "ungline"))
6266 action = gtrace_ungline_func;
6267 else {
6268 reply("OSMSG_BAD_ACTION", argv[1]);
6269 return 0;
6270 }
6271 sprintf(buf, "%s %s", argv[0], argv[0]);
258d1427 6272 if ((subcmd = dict_find(opserv_service->commands, buf, NULL))
6273 && !svccmd_can_invoke(user, opserv_service->bot, subcmd, channel, SVCCMD_NOISY)) {
d76ed9a9 6274 return 0;
6275 }
6276
6277 discrim = gline_discrim_create(user, cmd->parent->bot, argc-2, argv+2);
6278 if (!discrim)
6279 return 0;
6280
6281 if (action == gtrace_print_func)
6282 reply("OSMSG_GLINE_SEARCH_RESULTS");
6283 else if (action == gtrace_count_func)
6284 discrim->limit = INT_MAX;
6285
6286 extra.user = user;
6287 extra.glines = alloc_string_list(4);
258d1427 6288 extra.bot = cmd->parent->bot;
d76ed9a9 6289 matches = gline_discrim_search(discrim, action, &extra);
6290
6291 if (action == gtrace_ungline_func)
6292 for (nn=0; nn<extra.glines->used; nn++)
6293 gline_remove(extra.glines->list[nn], 1);
6294 free_string_list(extra.glines);
6295
6296 if (matches)
6297 reply("MSG_MATCH_COUNT", matches);
6298 else
6299 reply("MSG_NO_MATCHES");
6300 free(discrim->alt_target_mask);
6301 free(discrim);
6302 return 1;
6303}
6304
d914d1cb 6305struct shun_extra {
6306 struct userNode *user;
6307 struct string_list *shuns;
6308 struct userNode *bot;
6309};
6310
6311static void
6312strace_print_func(struct shun *shun, void *extra)
6313{
6314 struct shun_extra *xtra = extra;
6315 char *when_text, set_text[20];
6316 strftime(set_text, sizeof(set_text), "%Y-%m-%d", localtime(&shun->issued));
6317 when_text = asctime(localtime(&shun->expires));
6318 when_text[strlen(when_text)-1] = 0; /* strip lame \n */
6319 send_message(xtra->user, xtra->bot, "OSMSG_STRACE_FORMAT", shun->target, set_text, shun->issuer, when_text, shun->reason);
6320}
6321
6322static void
6323strace_count_func(UNUSED_ARG(struct shun *shun), UNUSED_ARG(void *extra))
6324{
6325}
6326
6327static void
6328strace_unshun_func(struct shun *shun, void *extra)
6329{
6330 struct shun_extra *xtra = extra;
6331 string_list_append(xtra->shuns, strdup(shun->target));
6332}
6333
6334static MODCMD_FUNC(cmd_strace)
6335{
6336 struct shun_discrim *discrim;
6337 shun_search_func action;
6338 unsigned int matches, nn;
6339 struct shun_extra extra;
6340 struct svccmd *subcmd;
6341 char buf[MAXLEN];
6342
6343 if (!irccasecmp(argv[1], "print"))
6344 action = strace_print_func;
6345 else if (!irccasecmp(argv[1], "count"))
6346 action = strace_count_func;
6347 else if (!irccasecmp(argv[1], "unshun"))
6348 action = strace_unshun_func;
6349 else {
6350 reply("OSMSG_BAD_ACTION", argv[1]);
6351 return 0;
6352 }
6353 sprintf(buf, "%s %s", argv[0], argv[0]);
6354 if ((subcmd = dict_find(opserv_service->commands, buf, NULL))
6355 && !svccmd_can_invoke(user, opserv_service->bot, subcmd, channel, SVCCMD_NOISY)) {
6356 return 0;
6357 }
6358
6359 discrim = shun_discrim_create(user, cmd->parent->bot, argc-2, argv+2);
6360 if (!discrim)
6361 return 0;
6362
6363 if (action == strace_print_func)
6364 reply("OSMSG_SHUN_SEARCH_RESULTS");
6365 else if (action == strace_count_func)
6366 discrim->limit = INT_MAX;
6367
6368 extra.user = user;
6369 extra.shuns = alloc_string_list(4);
6370 extra.bot = cmd->parent->bot;
6371 matches = shun_discrim_search(discrim, action, &extra);
6372
6373 if (action == strace_unshun_func)
6374 for (nn=0; nn<extra.shuns->used; nn++)
6375 shun_remove(extra.shuns->list[nn], 1);
6376 free_string_list(extra.shuns);
6377
6378 if (matches)
6379 reply("MSG_MATCH_COUNT", matches);
6380 else
6381 reply("MSG_NO_MATCHES");
6382 free(discrim->alt_target_mask);
6383 free(discrim);
6384 return 1;
6385}
6386
d76ed9a9 6387static int
6388alert_check_user(const char *key, void *data, void *extra)
6389{
6390 struct opserv_user_alert *alert = data;
6391 struct userNode *user = extra;
6392
6393 if (!discrim_match(alert->discrim, user))
6394 return 0;
6395
6396 if ((alert->reaction != REACT_NOTICE)
6397 && IsOper(user)
6398 && !alert->discrim->match_opers) {
6399 return 0;
6400 }
6401
6402 /* The user matches the alert criteria, so trigger the reaction. */
6403 if (alert->discrim->option_log)
6404 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);
6405
6406 /* Return 1 to halt alert matching, such as when killing the user
6407 that triggered the alert. */
6408 switch (alert->reaction) {
6409 case REACT_KILL:
6410 DelUser(user, opserv, 1, alert->discrim->reason);
6411 return 1;
6412 case REACT_GLINE:
1c5f6697 6413 opserv_block(user, alert->owner, alert->discrim->reason, alert->discrim->duration, alert->discrim->silent);
d76ed9a9 6414 return 1;
d914d1cb 6415 case REACT_SHUN:
6416 opserv_shun(user, alert->owner, alert->discrim->reason, alert->discrim->duration);
6417 return 1;
c408f18a 6418 case REACT_SVSJOIN:
5aa400d2 6419 opserv_svsjoin(user, alert->owner, alert->discrim->reason, alert->discrim->chantarget, alert->discrim->checkrestrictions);
0e08a8e0 6420 break;
39c1a4ef 6421 case REACT_SVSPART:
6422 opserv_svspart(user, alert->owner, alert->discrim->reason, alert->discrim->chantarget);
6423 break;
0e08a8e0 6424 case REACT_VERSION:
6425 /* Don't auto-version a user who we already have a version on, because the version reply itself
6426 * re-triggers this check...
6427 * TODO: maybe safer if we didn't even check react_version type alerts for the 2nd check?
6428 * sort of like we only look at channel alerts on join. -Rubin
6429 */
6430 if(!user->version_reply)
6431 opserv_version(user);
6432 break;
d82cf2f0 6433 case REACT_MARK:
6434 opserv_mark(user, alert->owner, alert->discrim->reason, alert->discrim->mark);
6435 break;
d76ed9a9 6436 default:
6437 log_module(OS_LOG, LOG_ERROR, "Invalid reaction type %d for alert %s.", alert->reaction, key);
6438 /* fall through to REACT_NOTICE case */
6439 case REACT_NOTICE:
6440 opserv_alert("Alert $b%s$b triggered by user $b%s$b!%s@%s (%s).", key, user->nick, user->ident, user->hostname, alert->discrim->reason);
6441 break;
ec311f39 6442 case REACT_TRACK:
6443#ifdef HAVE_TRACK
6444 opserv_alert("Alert $b%s$b triggered by user $b%s$b!%s@%s (%s) (Tracking).", key, user->nick, user->ident, user->hostname, alert->discrim->reason);
6445 add_track_user(user);
6446#endif
6447 break;
d76ed9a9 6448 }
6449 return 0;
6450}
6451
6452static void
6453opserv_alert_check_nick(struct userNode *user, UNUSED_ARG(const char *old_nick))
6454{
6455 struct gag_entry *gag;
6456 dict_foreach(opserv_nick_based_alerts, alert_check_user, user);
6457 /* Gag them if appropriate (and only if). */
6458 user->modes &= ~FLAGS_GAGGED;
6459 for (gag = gagList; gag; gag = gag->next) {
2f61d1d7 6460 if (user_matches_glob(user, gag->mask, MATCH_USENICK)) {
d76ed9a9 6461 gag_helper_func(user, NULL);
6462 break;
6463 }
6464 }
6465}
6466
6467static void
6468opserv_staff_alert(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
6469{
6470 const char *type;
6471
6472 if (!opserv_conf.staff_auth_channel
6473 || user->uplink->burst
6474 || !user->handle_info)
6475 return;
6476 else if (user->handle_info->opserv_level)
6477 type = "OPER";
6478 else if (IsNetworkHelper(user))
6479 type = "NETWORK HELPER";
6480 else if (IsSupportHelper(user))
6481 type = "SUPPORT HELPER";
6482 else
6483 return;
6484
2f61d1d7 6485 if (irc_in_addr_is_valid(user->ip))
d76ed9a9 6486 send_channel_notice(opserv_conf.staff_auth_channel, opserv, IDENT_FORMAT" authed to %s account %s", IDENT_DATA(user), type, user->handle_info->handle);
6487 else
6488 send_channel_notice(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);
6489}
6490
6491static MODCMD_FUNC(cmd_log)
6492{
6493 struct logSearch *discrim;
6494 unsigned int matches;
6495 struct logReport report;
6496
6497 discrim = log_discrim_create(cmd->parent->bot, user, argc, argv);
6498 if (!discrim)
6499 return 0;
6500
6501 reply("OSMSG_LOG_SEARCH_RESULTS");
6502 report.reporter = opserv;
6503 report.user = user;
6504 matches = log_entry_search(discrim, log_report_entry, &report);
6505
6506 if (matches)
258d1427 6507 reply("MSG_MATCH_COUNT", matches);
d76ed9a9 6508 else
258d1427 6509 reply("MSG_NO_MATCHES");
d76ed9a9 6510
6511 free(discrim);
6512 return 1;
6513}
6514
6515static int
6516gag_helper_func(struct userNode *match, UNUSED_ARG(void *extra))
6517{
6518 if (IsOper(match) || IsLocal(match))
6519 return 0;
6520 match->modes |= FLAGS_GAGGED;
6521 return 0;
6522}
6523
6524static MODCMD_FUNC(cmd_gag)
6525{
6526 struct gag_entry *gag;
6527 unsigned int gagged;
6528 unsigned long duration;
6529 char *reason;
6530
6531 reason = unsplit_string(argv + 3, argc - 3, NULL);
6532
6533 if (!is_ircmask(argv[1])) {
258d1427 6534 reply("OSMSG_INVALID_IRCMASK", argv[1]);
d76ed9a9 6535 return 0;
6536 }
6537
6538 for (gag = gagList; gag; gag = gag->next)
258d1427 6539 if (match_ircglobs(gag->mask, argv[1]))
d76ed9a9 6540 break;
6541
6542 if (gag) {
258d1427 6543 reply("OSMSG_REDUNDANT_GAG", argv[1]);
6544 return 0;
d76ed9a9 6545 }
6546
6547 duration = ParseInterval(argv[2]);
6548 gagged = gag_create(argv[1], user->handle_info->handle, reason, (duration?now+duration:0));
6549
6550 if (gagged)
258d1427 6551 reply("OSMSG_GAG_APPLIED", argv[1], gagged);
d76ed9a9 6552 else
258d1427 6553 reply("OSMSG_GAG_ADDED", argv[1]);
d76ed9a9 6554 return 1;
6555}
6556
6557static int
6558ungag_helper_func(struct userNode *match, UNUSED_ARG(void *extra))
6559{
6560 match->modes &= ~FLAGS_GAGGED;
6561 return 0;
6562}
6563
6564static MODCMD_FUNC(cmd_ungag)
6565{
6566 struct gag_entry *gag;
6567 unsigned int ungagged;
6568
6569 for (gag = gagList; gag; gag = gag->next)
258d1427 6570 if (!strcmp(gag->mask, argv[1]))
d76ed9a9 6571 break;
6572
6573 if (!gag) {
258d1427 6574 reply("OSMSG_GAG_NOT_FOUND", argv[1]);
6575 return 0;
d76ed9a9 6576 }
6577
6578 timeq_del(gag->expires, gag_expire, gag, 0);
6579 ungagged = gag_free(gag);
6580
6581 if (ungagged)
258d1427 6582 reply("OSMSG_UNGAG_APPLIED", argv[1], ungagged);
d76ed9a9 6583 else
258d1427 6584 reply("OSMSG_UNGAG_ADDED", argv[1]);
d76ed9a9 6585 return 1;
6586}
6587
6588static MODCMD_FUNC(cmd_addalert)
6589{
6590 opserv_alert_reaction reaction;
6591 struct svccmd *subcmd;
6592 const char *name;
6593 char buf[MAXLEN];
6594
6595 name = argv[1];
6596 sprintf(buf, "addalert %s", argv[2]);
258d1427 6597 if (!(subcmd = dict_find(opserv_service->commands, buf, NULL))) {
6598 reply("OSMSG_UNKNOWN_REACTION", argv[2]);
6599 return 0;
d76ed9a9 6600 }
6601 if (!irccasecmp(argv[2], "notice"))
6602 reaction = REACT_NOTICE;
6603 else if (!irccasecmp(argv[2], "kill"))
6604 reaction = REACT_KILL;
6605 else if (!irccasecmp(argv[2], "gline"))
6606 reaction = REACT_GLINE;
ec311f39 6607 else if (!irccasecmp(argv[2], "track")) {
6608#ifndef HAVE_TRACK
c092fcad 6609 reply("OSMSG_TRACK_DISABLED");
ec311f39 6610 return 0;
6611#else
6612 reaction = REACT_TRACK;
6613#endif
6614 } else if (!irccasecmp(argv[2], "shun"))
d914d1cb 6615 reaction = REACT_SHUN;
c408f18a 6616 else if(!irccasecmp(argv[2], "svsjoin"))
6617 reaction = REACT_SVSJOIN;
39c1a4ef 6618 else if(!irccasecmp(argv[2], "svspart"))
6619 reaction = REACT_SVSPART;
0e08a8e0 6620 else if(!irccasecmp(argv[2], "version"))
6621 reaction = REACT_VERSION;
d82cf2f0 6622 else if(!irccasecmp(argv[2], "mark"))
6623 reaction = REACT_MARK;
d76ed9a9 6624 else {
258d1427 6625 reply("OSMSG_UNKNOWN_REACTION", argv[2]);
6626 return 0;
d76ed9a9 6627 }
258d1427 6628 if (!svccmd_can_invoke(user, opserv_service->bot, subcmd, channel, SVCCMD_NOISY)
a62ba70c 6629 || !opserv_add_user_alert(user, name, reaction, unsplit_string(argv + 3, argc - 3, NULL))) {
6630 reply("OSMSG_ALERT_ADD_FAILED");
d76ed9a9 6631 return 0;
a62ba70c 6632 }
d76ed9a9 6633 reply("OSMSG_ADDED_ALERT", name);
6634 return 1;
6635}
6636
6637static MODCMD_FUNC(cmd_delalert)
6638{
6639 unsigned int i;
6640 for (i=1; i<argc; i++) {
6641 dict_remove(opserv_nick_based_alerts, argv[i]);
6642 dict_remove(opserv_channel_alerts, argv[i]);
258d1427 6643 if (dict_remove(opserv_user_alerts, argv[i]))
6644 reply("OSMSG_REMOVED_ALERT", argv[i]);
d76ed9a9 6645 else
258d1427 6646 reply("OSMSG_NO_SUCH_ALERT", argv[i]);
d76ed9a9 6647 }
6648 return 1;
6649}
6650
6651static void
6652opserv_conf_read(void)
6653{
7637f48f 6654 struct chanNode *chan;
6655 unsigned int i;
d76ed9a9 6656 struct record_data *rd;
6657 dict_t conf_node, child;
6658 const char *str, *str2;
6659 struct policer_params *pp;
6660 dict_iterator_t it;
6661
6662 rd = conf_get_node(OPSERV_CONF_NAME);
6663 if (!rd || rd->type != RECDB_OBJECT) {
258d1427 6664 log_module(OS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", OPSERV_CONF_NAME);
6665 return;
d76ed9a9 6666 }
6667 conf_node = rd->d.object;
6668 str = database_get_data(conf_node, KEY_DEBUG_CHANNEL, RECDB_QSTRING);
6669 if (opserv && str) {
6670 str2 = database_get_data(conf_node, KEY_DEBUG_CHANNEL_MODES, RECDB_QSTRING);
6671 if (!str2)
6672 str2 = "+tinms";
258d1427 6673 opserv_conf.debug_channel = AddChannel(str, now, str2, NULL, NULL);
d76ed9a9 6674 AddChannelUser(opserv, opserv_conf.debug_channel)->modes |= MODE_CHANOP;
6675 } else {
258d1427 6676 opserv_conf.debug_channel = NULL;
d76ed9a9 6677 }
6678 str = database_get_data(conf_node, KEY_ALERT_CHANNEL, RECDB_QSTRING);
6679 if (opserv && str) {
6680 str2 = database_get_data(conf_node, KEY_ALERT_CHANNEL_MODES, RECDB_QSTRING);
6681 if (!str2)
6682 str2 = "+tns";
258d1427 6683 opserv_conf.alert_channel = AddChannel(str, now, str2, NULL, NULL);
d76ed9a9 6684 AddChannelUser(opserv, opserv_conf.alert_channel)->modes |= MODE_CHANOP;
6685 } else {
258d1427 6686 opserv_conf.alert_channel = NULL;
d76ed9a9 6687 }
6688 str = database_get_data(conf_node, KEY_STAFF_AUTH_CHANNEL, RECDB_QSTRING);
6689 if (opserv && str) {
6690 str2 = database_get_data(conf_node, KEY_STAFF_AUTH_CHANNEL_MODES, RECDB_QSTRING);
6691 if (!str2)
6692 str2 = "+timns";
2aef5f4b 6693 opserv_conf.staff_auth_channel = AddChannel(str, now, str2, NULL, NULL);
d76ed9a9 6694 AddChannelUser(opserv, opserv_conf.staff_auth_channel)->modes |= MODE_CHANOP;
6695 } else {
6696 opserv_conf.staff_auth_channel = NULL;
6697 }
5a1daaab 6698
6699 str = database_get_data(conf_node, KEY_ADMIN_LEVEL, RECDB_QSTRING);
6700 opserv_conf.admin_level = str ? strtoul(str, NULL, 0): 800;
6701
1c5f6697 6702 str = database_get_data(conf_node, KEY_SILENT_LEVEL, RECDB_QSTRING);
6703 opserv_conf.silent_level = str ? strtoul(str, NULL, 0): 700;
6704
d76ed9a9 6705 str = database_get_data(conf_node, KEY_UNTRUSTED_MAX, RECDB_QSTRING);
6706 opserv_conf.untrusted_max = str ? strtoul(str, NULL, 0) : 5;
6707 str = database_get_data(conf_node, KEY_PURGE_LOCK_DELAY, RECDB_QSTRING);
6708 opserv_conf.purge_lock_delay = str ? strtoul(str, NULL, 0) : 60;
6709 str = database_get_data(conf_node, KEY_JOIN_FLOOD_MODERATE, RECDB_QSTRING);
6710 opserv_conf.join_flood_moderate = str ? strtoul(str, NULL, 0) : 1;
6711 str = database_get_data(conf_node, KEY_JOIN_FLOOD_MODERATE_THRESH, RECDB_QSTRING);
6712 opserv_conf.join_flood_moderate_threshold = str ? strtoul(str, NULL, 0) : 50;
6713 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
6714 if (opserv && str)
6715 NickChange(opserv, str, 0);
d914d1cb 6716
d76ed9a9 6717 str = database_get_data(conf_node, KEY_CLONE_GLINE_DURATION, RECDB_QSTRING);
6718 opserv_conf.clone_gline_duration = str ? ParseInterval(str) : 3600;
6719 str = database_get_data(conf_node, KEY_BLOCK_GLINE_DURATION, RECDB_QSTRING);
6720 opserv_conf.block_gline_duration = str ? ParseInterval(str) : 3600;
6721
7637f48f 6722 free_string_list(autojoin_channels);
6723 autojoin_channels = database_get_data(conf_node, KEY_AUTOJOIN_CHANNELS, RECDB_STRING_LIST);
6724
6725 if(autojoin_channels)
6726 autojoin_channels = string_list_copy(autojoin_channels);
6727
6728 if (autojoin_channels && opserv) {
6729 for (i = 0; i < autojoin_channels->used; i++) {
6730 chan = AddChannel(autojoin_channels->list[i], now, "+nt", NULL, NULL);
6731 AddChannelUser(opserv, chan)->modes |= MODE_CHANOP;
6732 }
6733 }
6734
d914d1cb 6735 str = database_get_data(conf_node, KEY_BLOCK_SHUN_DURATION, RECDB_QSTRING);
6736 opserv_conf.block_shun_duration = str ? ParseInterval(str) : 3600;
6737
d76ed9a9 6738 if (!opserv_conf.join_policer_params)
6739 opserv_conf.join_policer_params = policer_params_new();
6740 policer_params_set(opserv_conf.join_policer_params, "size", "20");
6741 policer_params_set(opserv_conf.join_policer_params, "drain-rate", "1");
6742 if ((child = database_get_data(conf_node, KEY_JOIN_POLICER, RECDB_OBJECT)))
258d1427 6743 dict_foreach(child, set_policer_param, opserv_conf.join_policer_params);
d76ed9a9 6744
6745 for (it = dict_first(channels); it; it = iter_next(it)) {
6746 struct chanNode *cNode = iter_data(it);
6747 cNode->join_policer.params = opserv_conf.join_policer_params;
6748 }
6749
6750 if (opserv_conf.new_user_policer.params)
6751 pp = opserv_conf.new_user_policer.params;
6752 else
6753 pp = opserv_conf.new_user_policer.params = policer_params_new();
6754 policer_params_set(pp, "size", "200");
6755 policer_params_set(pp, "drain-rate", "3");
6756 if ((child = database_get_data(conf_node, KEY_NEW_USER_POLICER, RECDB_OBJECT)))
258d1427 6757 dict_foreach(child, set_policer_param, pp);
08895577 6758
6759 /* Defcon configuration */
6760 DefCon[0] = 0;
6761 str = database_get_data(conf_node, KEY_DEFCON1, RECDB_QSTRING);
6762 DefCon[1] = str ? atoi(str) : 415;
6763 str = database_get_data(conf_node, KEY_DEFCON2, RECDB_QSTRING);
6764 DefCon[2] = str ? atoi(str) : 159;
6765 str = database_get_data(conf_node, KEY_DEFCON3, RECDB_QSTRING);
6766 DefCon[3] = str ? atoi(str) : 31;
6767 str = database_get_data(conf_node, KEY_DEFCON4, RECDB_QSTRING);
6768 DefCon[4] = str? atoi(str) : 23;
6769 DefCon[5] = 0;
6770
6771 str = database_get_data(conf_node, KEY_DEFCON_LEVEL, RECDB_QSTRING);
6772 DefConLevel = str ? atoi(str) : 5;
6773
6774 str = database_get_data(conf_node, KEY_DEFCON_CHANMODES, RECDB_QSTRING);
6775 DefConChanModes = str ? strdup(str) : "+r";
6776
6777 str = database_get_data(conf_node, KEY_DEFCON_SESSION_LIMIT, RECDB_QSTRING);
6778 DefConSessionLimit = str ? atoi(str) : 2;
6779
6780 str = database_get_data(conf_node, KEY_DEFCON_TIMEOUT, RECDB_QSTRING);
6781 DefConTimeOut = str ? ParseInterval(str) : 900;
6782
6783 str = database_get_data(conf_node, KEY_DEFCON_GLINE_DURATION, RECDB_QSTRING);
6784 DefConGlineExpire = str ? ParseInterval(str) : 300;
6785
08895577 6786 str = database_get_data(conf_node, KEY_DEFCON_GLOBAL, RECDB_QSTRING);
6787 GlobalOnDefcon = str ? atoi(str) : 0;
6788
6789 str = database_get_data(conf_node, KEY_DEFCON_GLOBAL_MORE, RECDB_QSTRING);
6790 GlobalOnDefconMore = str ? atoi(str) : 0;
6791
6792 str = database_get_data(conf_node, KEY_DEFCON_MESSAGE, RECDB_QSTRING);
6793 DefConMessage = str ? strdup(str) : "Put your message to send your users here. Dont forget to uncomment GlobalOnDefconMore";
6794
6795 str = database_get_data(conf_node, KEY_DEFCON_OFF_MESSAGE, RECDB_QSTRING);
6796 DefConOffMessage = str? strdup(str) : "Services are now back to normal, sorry for any inconvenience";
6797
6798 str = database_get_data(conf_node, KEY_DEFCON_GLINE_REASON, RECDB_QSTRING);
6799 DefConGlineReason = str ? strdup(str) : "This network is currently not accepting connections, please try again later";
d76ed9a9 6800}
6801
5a1daaab 6802/* lame way to export opserv_conf value to nickserv.c ... */
6803unsigned int
6804opserv_conf_admin_level()
6805{
6806 return(opserv_conf.admin_level);
6807}
6808
d76ed9a9 6809static void
6810opserv_db_init(void) {
6811 /* set up opserv_trusted_hosts dict */
6812 dict_delete(opserv_trusted_hosts);
6813 opserv_trusted_hosts = dict_new();
6814 dict_set_free_data(opserv_trusted_hosts, free_trusted_host);
47956fc5 6815
6816 opserv_routing_plan_options = dict_new();
6817
6818 opserv_routing_plans = dict_new();
6819 dict_set_free_data(opserv_routing_plans, free_routing_plan);
d76ed9a9 6820 /* set up opserv_chan_warn dict */
de9510bc 6821
6822/* alert trace notice channel #x replaces warnings
d76ed9a9 6823 dict_delete(opserv_chan_warn);
6824 opserv_chan_warn = dict_new();
6825 dict_set_free_keys(opserv_chan_warn, free);
6826 dict_set_free_data(opserv_chan_warn, free);
de9510bc 6827*/
d76ed9a9 6828 /* set up opserv_user_alerts */
6829 dict_delete(opserv_channel_alerts);
6830 opserv_channel_alerts = dict_new();
6831 dict_delete(opserv_nick_based_alerts);
6832 opserv_nick_based_alerts = dict_new();
6833 dict_delete(opserv_user_alerts);
6834 opserv_user_alerts = dict_new();
6835 dict_set_free_keys(opserv_user_alerts, free);
6836 dict_set_free_data(opserv_user_alerts, opserv_free_user_alert);
6837 /* set up opserv_bad_words */
6838 free_string_list(opserv_bad_words);
6839 opserv_bad_words = alloc_string_list(4);
6840 /* and opserv_exempt_channels */
6841 dict_delete(opserv_exempt_channels);
6842 opserv_exempt_channels = dict_new();
6843 dict_set_free_keys(opserv_exempt_channels, free);
6844}
6845
6846static void
6847opserv_db_cleanup(void)
6848{
6849 unsigned int nn;
6850
de9510bc 6851/* dict_delete(opserv_chan_warn); */
d76ed9a9 6852 dict_delete(opserv_reserved_nick_dict);
6853 free_string_list(opserv_bad_words);
6854 dict_delete(opserv_exempt_channels);
6855 dict_delete(opserv_trusted_hosts);
6856 unreg_del_user_func(opserv_user_cleanup);
6857 dict_delete(opserv_hostinfo_dict);
6858 dict_delete(opserv_nick_based_alerts);
6859 dict_delete(opserv_channel_alerts);
6860 dict_delete(opserv_user_alerts);
6861 for (nn=0; nn<ArrayLength(level_strings); ++nn)
6862 free(level_strings[nn]);
6863 while (gagList)
6864 gag_free(gagList);
6865 policer_params_delete(opserv_conf.join_policer_params);
6866 policer_params_delete(opserv_conf.new_user_policer.params);
6867}
6868
6869void
6870init_opserv(const char *nick)
6871{
6872 OS_LOG = log_register_type("OpServ", "file:opserv.log");
a32da4c7 6873 if (nick) {
6874 const char *modes = conf_get_data("services/opserv/modes", RECDB_QSTRING);
6875 opserv = AddService(nick, modes ? modes : NULL, "Oper Services", NULL);
6876 }
d76ed9a9 6877 conf_register_reload(opserv_conf_read);
6878
6879 memset(level_strings, 0, sizeof(level_strings));
6880 opserv_module = module_register("OpServ", OS_LOG, "opserv.help", opserv_help_expand);
6881 opserv_define_func("ACCESS", cmd_access, 0, 0, 0);
6882 opserv_define_func("ADDALERT", cmd_addalert, 800, 0, 4);
6883 opserv_define_func("ADDALERT NOTICE", NULL, 0, 0, 0);
9a75756e 6884 opserv_define_func("ADDALERT SILENT", NULL, 900, 0, 0);
d76ed9a9 6885 opserv_define_func("ADDALERT GLINE", NULL, 900, 0, 0);
d914d1cb 6886 opserv_define_func("ADDALERT SHUN", NULL, 900, 0, 0);
ec311f39 6887 opserv_define_func("ADDALERT TRACK", NULL, 900, 0, 0);
d76ed9a9 6888 opserv_define_func("ADDALERT KILL", NULL, 900, 0, 0);
c408f18a 6889 opserv_define_func("ADDALERT SVSJOIN", NULL, 999, 0, 0);
39c1a4ef 6890 opserv_define_func("ADDALERT SVSPART", NULL, 999, 0, 0);
0e08a8e0 6891 opserv_define_func("ADDALERT VERSION", NULL, 999, 0, 0);
d82cf2f0 6892 opserv_define_func("ADDALERT MARK", NULL, 999, 0, 0);
d76ed9a9 6893 opserv_define_func("ADDBAD", cmd_addbad, 800, 0, 2);
6894 opserv_define_func("ADDEXEMPT", cmd_addexempt, 800, 0, 2);
6895 opserv_define_func("ADDTRUST", cmd_addtrust, 800, 0, 5);
6896 opserv_define_func("BAN", cmd_ban, 100, 2, 2);
6897 opserv_define_func("BLOCK", cmd_block, 100, 0, 2);
6898 opserv_define_func("CHANINFO", cmd_chaninfo, 0, 3, 0);
6899 opserv_define_func("CLEARBANS", cmd_clearbans, 300, 2, 0);
6900 opserv_define_func("CLEARMODES", cmd_clearmodes, 400, 2, 0);
6901 opserv_define_func("CLONE", cmd_clone, 999, 0, 3);
6902 opserv_define_func("COLLIDE", cmd_collide, 800, 0, 5);
6903 opserv_define_func("CSEARCH", cmd_csearch, 100, 0, 3);
6904 opserv_define_func("CSEARCH COUNT", cmd_csearch, 0, 0, 0);
6905 opserv_define_func("CSEARCH PRINT", cmd_csearch, 0, 0, 0);
6906 opserv_define_func("DELALERT", cmd_delalert, 800, 0, 2);
6907 opserv_define_func("DELBAD", cmd_delbad, 800, 0, 2);
6908 opserv_define_func("DELEXEMPT", cmd_delexempt, 800, 0, 2);
6909 opserv_define_func("DELTRUST", cmd_deltrust, 800, 0, 2);
6910 opserv_define_func("DEOP", cmd_deop, 100, 2, 2);
6911 opserv_define_func("DEOPALL", cmd_deopall, 400, 2, 0);
08895577 6912 opserv_define_func("DEFCON", cmd_defcon, 900, 0, 0);
55342ce8 6913 opserv_define_func("DEHOP", cmd_dehop, 100, 2, 2);
6914 opserv_define_func("DEHOPALL", cmd_dehopall, 400, 2, 0);
d76ed9a9 6915 opserv_define_func("DEVOICEALL", cmd_devoiceall, 300, 2, 0);
6916 opserv_define_func("DIE", cmd_die, 900, 0, 2);
6917 opserv_define_func("DUMP", cmd_dump, 999, 0, 2);
6918 opserv_define_func("EDITTRUST", cmd_edittrust, 800, 0, 5);
6919 opserv_define_func("GAG", cmd_gag, 600, 0, 4);
6920 opserv_define_func("GLINE", cmd_gline, 600, 0, 4);
6921 opserv_define_func("GSYNC", cmd_gsync, 600, 0, 0);
6922 opserv_define_func("GTRACE", cmd_gtrace, 100, 0, 3);
6923 opserv_define_func("GTRACE COUNT", NULL, 0, 0, 0);
6924 opserv_define_func("GTRACE PRINT", NULL, 0, 0, 0);
d914d1cb 6925 opserv_define_func("SBLOCK", cmd_sblock, 100, 0, 2);
6926 opserv_define_func("SHUN", cmd_shun, 600, 0, 4);
6927 opserv_define_func("SSYNC", cmd_ssync, 600, 0, 0);
6928 opserv_define_func("STRACE", cmd_strace, 100, 0, 3);
6929 opserv_define_func("STRACE COUNT", NULL, 0, 0, 0);
6930 opserv_define_func("STRACE PRINT", NULL, 0, 0, 0);
d76ed9a9 6931 opserv_define_func("INVITE", cmd_invite, 100, 2, 0);
6932 opserv_define_func("INVITEME", cmd_inviteme, 100, 0, 0);
6933 opserv_define_func("JOIN", cmd_join, 601, 0, 2);
c408f18a 6934 opserv_define_func("SVSJOIN", cmd_svsjoin, 999, 0, 3);
39c1a4ef 6935 opserv_define_func("SVSPART", cmd_svspart, 999, 0, 3);
d76ed9a9 6936 opserv_define_func("JUMP", cmd_jump, 900, 0, 2);
6937 opserv_define_func("JUPE", cmd_jupe, 900, 0, 4);
6938 opserv_define_func("KICK", cmd_kick, 100, 2, 2);
6939 opserv_define_func("KICKALL", cmd_kickall, 400, 2, 0);
6940 opserv_define_func("KICKBAN", cmd_kickban, 100, 2, 2);
6941 opserv_define_func("KICKBANALL", cmd_kickbanall, 450, 2, 0);
6942 opserv_define_func("LOG", cmd_log, 900, 0, 2);
6943 opserv_define_func("MODE", cmd_mode, 100, 2, 2);
d82cf2f0 6944 opserv_define_func("MARK", cmd_mark, 900, 0, 3);
d76ed9a9 6945 opserv_define_func("OP", cmd_op, 100, 2, 2);
6946 opserv_define_func("OPALL", cmd_opall, 400, 2, 0);
55342ce8 6947 opserv_define_func("HOP", cmd_hop, 100, 2, 2);
6948 opserv_define_func("HOPALL", cmd_hopall, 400, 2, 0);
47956fc5 6949 opserv_define_func("MAP", cmd_stats_links, 0, 0, 0);
6c34bb5a 6950 opserv_define_func("PRIVSET", cmd_privset, 900, 0, 3);
d76ed9a9 6951 opserv_define_func("PART", cmd_part, 601, 0, 2);
6952 opserv_define_func("QUERY", cmd_query, 0, 0, 0);
6953 opserv_define_func("RAW", cmd_raw, 999, 0, 2);
6954 opserv_define_func("RECONNECT", cmd_reconnect, 900, 0, 0);
6955 opserv_define_func("REFRESHG", cmd_refreshg, 600, 0, 0);
d914d1cb 6956 opserv_define_func("REFRESHS", cmd_refreshs, 600, 0, 0);
d76ed9a9 6957 opserv_define_func("REHASH", cmd_rehash, 900, 0, 0);
6958 opserv_define_func("REOPEN", cmd_reopen, 900, 0, 0);
7637f48f 6959 opserv_define_func("RESETMAX", cmd_resetmax, 900, 0, 0);
d76ed9a9 6960 opserv_define_func("RESERVE", cmd_reserve, 800, 0, 5);
6961 opserv_define_func("RESTART", cmd_restart, 900, 0, 2);
47956fc5 6962 opserv_define_func("ROUTING ADDPLAN", cmd_routing_addplan, 800, 0, 2);
6963 opserv_define_func("ROUTING DELPLAN", cmd_routing_delplan, 800, 0, 2);
6964 opserv_define_func("ROUTING ADDSERVER", cmd_routing_addserver, 800, 0, 4);
6965 opserv_define_func("ROUTING DELSERVER", cmd_routing_delserver, 800, 0, 3);
6966 opserv_define_func("ROUTING MAP", cmd_routing_map, 800, 0, 0);
6967 opserv_define_func("ROUTING SET", cmd_routing_set, 800, 0, 0);
6968 opserv_define_func("REROUTE", cmd_reroute, 800, 0, 2);
d76ed9a9 6969 opserv_define_func("SET", cmd_set, 900, 0, 3);
6970 opserv_define_func("SETTIME", cmd_settime, 901, 0, 0);
6971 opserv_define_func("STATS ALERTS", cmd_stats_alerts, 0, 0, 0);
6972 opserv_define_func("STATS BAD", cmd_stats_bad, 0, 0, 0);
6973 opserv_define_func("STATS GAGS", cmd_stats_gags, 0, 0, 0);
6974 opserv_define_func("STATS GLINES", cmd_stats_glines, 0, 0, 0);
d914d1cb 6975 opserv_define_func("STATS SHUNS", cmd_stats_shuns, 0, 0, 0);
d76ed9a9 6976 opserv_define_func("STATS LINKS", cmd_stats_links, 0, 0, 0);
6977 opserv_define_func("STATS MAX", cmd_stats_max, 0, 0, 0);
6978 opserv_define_func("STATS NETWORK", cmd_stats_network, 0, 0, 0);
6979 opserv_define_func("STATS NETWORK2", cmd_stats_network2, 0, 0, 0);
6980 opserv_define_func("STATS RESERVED", cmd_stats_reserved, 0, 0, 0);
47956fc5 6981 opserv_define_func("STATS ROUTING", cmd_stats_routing_plans, 0, 0, 0);
d76ed9a9 6982 opserv_define_func("STATS TIMEQ", cmd_stats_timeq, 0, 0, 0);
6983 opserv_define_func("STATS TRUSTED", cmd_stats_trusted, 0, 0, 0);
6984 opserv_define_func("STATS UPLINK", cmd_stats_uplink, 0, 0, 0);
6985 opserv_define_func("STATS UPTIME", cmd_stats_uptime, 0, 0, 0);
de9510bc 6986/* opserv_define_func("STATS WARN", cmd_stats_warn, 0, 0, 0); */
f14e4f83 6987#if defined(WITH_MALLOC_X3) || defined(WITH_MALLOC_SLAB)
ec1a68c8 6988 opserv_define_func("STATS MEMORY", cmd_stats_memory, 0, 0, 0);
6989#endif
d76ed9a9 6990 opserv_define_func("TRACE", cmd_trace, 100, 0, 3);
6991 opserv_define_func("TRACE PRINT", NULL, 0, 0, 0);
6992 opserv_define_func("TRACE COUNT", NULL, 0, 0, 0);
6993 opserv_define_func("TRACE DOMAINS", NULL, 0, 0, 0);
6994 opserv_define_func("TRACE GLINE", NULL, 600, 0, 0);
d914d1cb 6995 opserv_define_func("TRACE SHUN", NULL, 600, 0, 0);
d76ed9a9 6996 opserv_define_func("TRACE GAG", NULL, 600, 0, 0);
6997 opserv_define_func("TRACE KILL", NULL, 600, 0, 0);
0e08a8e0 6998 opserv_define_func("TRACE VERSION", NULL, 999, 0, 0);
6999 opserv_define_func("TRACE SVSJOIN", NULL, 999, 0, 0);
39c1a4ef 7000 opserv_define_func("TRACE SVSPART", NULL, 999, 0, 0);
d82cf2f0 7001 opserv_define_func("TRACE MARK", NULL, 999, 0, 0);
d76ed9a9 7002 opserv_define_func("UNBAN", cmd_unban, 100, 2, 2);
7003 opserv_define_func("UNGAG", cmd_ungag, 600, 0, 2);
7004 opserv_define_func("UNGLINE", cmd_ungline, 600, 0, 2);
7005 modcmd_register(opserv_module, "GTRACE UNGLINE", NULL, 0, 0, "template", "ungline", NULL);
d914d1cb 7006 opserv_define_func("UNSHUN", cmd_unshun, 600, 0, 2);
7007 modcmd_register(opserv_module, "GTRACE UNSHUN", NULL, 0, 0, "template", "unshun", NULL);
d76ed9a9 7008 opserv_define_func("UNJUPE", cmd_unjupe, 900, 0, 2);
7009 opserv_define_func("UNRESERVE", cmd_unreserve, 800, 0, 2);
de9510bc 7010/* opserv_define_func("UNWARN", cmd_unwarn, 800, 0, 0); */
d76ed9a9 7011 opserv_define_func("VOICEALL", cmd_voiceall, 300, 2, 0);
de9510bc 7012/* opserv_define_func("WARN", cmd_warn, 800, 0, 2); */
d76ed9a9 7013 opserv_define_func("WHOIS", cmd_whois, 0, 0, 2);
7014
7015 opserv_reserved_nick_dict = dict_new();
7016 opserv_hostinfo_dict = dict_new();
47956fc5 7017
d76ed9a9 7018 dict_set_free_keys(opserv_hostinfo_dict, free);
7019 dict_set_free_data(opserv_hostinfo_dict, opserv_free_hostinfo);
7020
47956fc5 7021 opserv_waiting_connections = dict_new();
7022 dict_set_free_data(opserv_waiting_connections, opserv_free_waiting_connection);
7023
d76ed9a9 7024 reg_new_user_func(opserv_new_user_check);
7025 reg_nick_change_func(opserv_alert_check_nick);
7026 reg_del_user_func(opserv_user_cleanup);
c52666c6 7027 reg_new_channel_func(opserv_channel_check);
d76ed9a9 7028 reg_del_channel_func(opserv_channel_delete);
7029 reg_join_func(opserv_join_check);
7030 reg_auth_func(opserv_staff_alert);
0e08a8e0 7031 reg_notice_func(opserv, opserv_notice_handler);
d76ed9a9 7032
7033 opserv_db_init();
7034 saxdb_register("OpServ", opserv_saxdb_read, opserv_saxdb_write);
7035 if (nick)
258d1427 7036 {
7037 opserv_service = service_register(opserv);
7038 opserv_service->trigger = '?';
7039 }
d76ed9a9 7040
47956fc5 7041 /* start auto-routing system */
9079d26c 7042 /* this cant be done here, because the routing system isnt marked active yet. */
7043 /* reroute_timer(NULL); */
7044
47956fc5 7045 /* start the karma timer, using the saved one if available */
7046 routing_karma_timer(dict_find(opserv_routing_plan_options, "KARMA_TIMER", NULL));
7047
d76ed9a9 7048 reg_exit_func(opserv_db_cleanup);
7049 message_register_table(msgtab);
7050}