Search in sources :

Example 1 with ClientApp

use of net.i2p.app.ClientApp in project i2p.i2p by i2p.

the class I2PTunnelConnectClient method clientConnectionRun.

protected void clientConnectionRun(Socket s) {
    InputStream in = null;
    OutputStream out = null;
    String targetRequest = null;
    boolean usingWWWProxy = false;
    String currentProxy = null;
    // local outproxy plugin
    boolean usingInternalOutproxy = false;
    Outproxy outproxy = null;
    long requestId = __requestId.incrementAndGet();
    I2PSocket i2ps = null;
    try {
        s.setSoTimeout(INITIAL_SO_TIMEOUT);
        out = s.getOutputStream();
        in = s.getInputStream();
        String line, method = null, host = null, destination = null, restofline = null;
        StringBuilder newRequest = new StringBuilder();
        String authorization = null;
        int remotePort = 443;
        while (true) {
            // Use this rather than BufferedReader because we can't have readahead,
            // since we are passing the stream on to I2PTunnelRunner
            line = DataHelper.readLine(in);
            if (line == null) {
                break;
            }
            line = line.trim();
            if (_log.shouldLog(Log.DEBUG))
                _log.debug(getPrefix(requestId) + "Line=[" + line + "]");
            if (method == null) {
                // first line CONNECT blah.i2p:80 HTTP/1.1
                int pos = line.indexOf(' ');
                // empty first line
                if (pos == -1)
                    break;
                method = line.substring(0, pos);
                String request = line.substring(pos + 1);
                pos = request.indexOf(':');
                if (pos == -1) {
                    pos = request.indexOf(' ');
                } else {
                    int spos = request.indexOf(' ');
                    if (spos > 0) {
                        try {
                            remotePort = Integer.parseInt(request.substring(pos + 1, spos));
                        } catch (NumberFormatException nfe) {
                            break;
                        } catch (IndexOutOfBoundsException ioobe) {
                            break;
                        }
                    }
                }
                if (pos == -1) {
                    host = request;
                    restofline = "";
                } else {
                    host = request.substring(0, pos);
                    // ":80 HTTP/1.1" or " HTTP/1.1"
                    restofline = request.substring(pos);
                }
                if (host.toLowerCase(Locale.US).endsWith(".i2p")) {
                    // Destination gets the host name
                    destination = host;
                } else if (host.contains(".") || host.startsWith("[")) {
                    if (Boolean.parseBoolean(getTunnel().getClientOptions().getProperty(PROP_USE_OUTPROXY_PLUGIN, "true"))) {
                        ClientAppManager mgr = _context.clientAppManager();
                        if (mgr != null) {
                            ClientApp op = mgr.getRegisteredApp(Outproxy.NAME);
                            if (op != null) {
                                outproxy = (Outproxy) op;
                                usingInternalOutproxy = true;
                                if (host.startsWith("[")) {
                                    host = host.substring(1);
                                    if (host.endsWith("]"))
                                        host = host.substring(0, host.length() - 1);
                                }
                            }
                        }
                    }
                    if (!usingInternalOutproxy) {
                        // The request must be forwarded to a outproxy
                        currentProxy = selectProxy();
                        if (currentProxy == null) {
                            if (_log.shouldLog(Log.WARN))
                                _log.warn(getPrefix(requestId) + "Host wants to be outproxied, but we dont have any!");
                            writeErrorMessage(ERR_NO_OUTPROXY, out);
                            return;
                        }
                        destination = currentProxy;
                        usingWWWProxy = true;
                        // HTTP spec
                        newRequest.append("CONNECT ").append(host).append(restofline).append("\r\n");
                    }
                } else if (host.toLowerCase(Locale.US).equals("localhost")) {
                    writeErrorMessage(ERR_LOCALHOST, out);
                    return;
                } else {
                    // full b64 address (hopefully)
                    destination = host;
                }
                targetRequest = host;
                if (_log.shouldLog(Log.DEBUG)) {
                    _log.debug(getPrefix(requestId) + "METHOD:" + method + ":\n" + "HOST  :" + host + ":\n" + "PORT  :" + remotePort + ":\n" + "REST  :" + restofline + ":\n" + "DEST  :" + destination + ":\n" + "www proxy? " + usingWWWProxy + " internal proxy? " + usingInternalOutproxy);
                }
            } else if (line.toLowerCase(Locale.US).startsWith("proxy-authorization: ")) {
                // strip Proxy-Authenticate from the response in HTTPResponseOutputStream
                // save for auth check below
                // "proxy-authorization: ".length()
                authorization = line.substring(21);
                line = null;
            } else if (line.length() > 0) {
                // Additional lines - shouldn't be too many. Firefox sends:
                // User-Agent: blabla
                // Proxy-Connection: keep-alive
                // Host: blabla.i2p
                // 
                // We could send these (filtered like in HTTPClient) on to the outproxy,
                // but for now just chomp them all.
                line = null;
            } else {
                // Add Proxy-Authentication header for next hop (outproxy)
                if (usingWWWProxy && Boolean.parseBoolean(getTunnel().getClientOptions().getProperty(PROP_OUTPROXY_AUTH))) {
                    // specific for this proxy
                    String user = getTunnel().getClientOptions().getProperty(PROP_OUTPROXY_USER_PREFIX + currentProxy);
                    String pw = getTunnel().getClientOptions().getProperty(PROP_OUTPROXY_PW_PREFIX + currentProxy);
                    if (user == null || pw == null) {
                        // if not, look at default user and pw
                        user = getTunnel().getClientOptions().getProperty(PROP_OUTPROXY_USER);
                        pw = getTunnel().getClientOptions().getProperty(PROP_OUTPROXY_PW);
                    }
                    if (user != null && pw != null) {
                        newRequest.append("Proxy-Authorization: Basic ").append(// true = use standard alphabet
                        Base64.encode(DataHelper.getUTF8(user + ':' + pw), true)).append("\r\n");
                    }
                }
                // HTTP spec
                newRequest.append("\r\n");
                s.setSoTimeout(0);
                // do it
                break;
            }
        }
        if (method == null || !"CONNECT".equals(method.toUpperCase(Locale.US))) {
            writeErrorMessage(ERR_BAD_PROTOCOL, out);
            return;
        }
        // no destination, going to outproxy plugin
        if (usingInternalOutproxy) {
            Socket outSocket = outproxy.connect(host, remotePort);
            OnTimeout onTimeout = new OnTimeout(s, s.getOutputStream(), targetRequest, usingWWWProxy, currentProxy, requestId);
            byte[] response = SUCCESS_RESPONSE.getBytes("UTF-8");
            Thread t = new I2PTunnelOutproxyRunner(s, outSocket, sockLock, null, response, onTimeout);
            // we are called from an unlimited thread pool, so run inline
            t.run();
            return;
        }
        if (destination == null) {
            writeErrorMessage(ERR_BAD_PROTOCOL, out);
            return;
        }
        // Authorization
        AuthResult result = authorize(s, requestId, method, authorization);
        if (result != AuthResult.AUTH_GOOD) {
            if (_log.shouldLog(Log.WARN)) {
                if (authorization != null)
                    _log.warn(getPrefix(requestId) + "Auth failed, sending 407 again");
                else
                    _log.warn(getPrefix(requestId) + "Auth required, sending 407");
            }
            out.write(DataHelper.getASCII(getAuthError(result == AuthResult.AUTH_STALE)));
            return;
        }
        Destination clientDest = _context.namingService().lookup(destination);
        if (clientDest == null) {
            String header;
            if (usingWWWProxy)
                header = getErrorPage("dnfp", ERR_DESTINATION_UNKNOWN);
            else
                header = getErrorPage("dnfh", ERR_DESTINATION_UNKNOWN);
            writeErrorMessage(header, out, targetRequest, usingWWWProxy, destination);
            return;
        }
        I2PSocketOptions sktOpts = getDefaultOptions();
        if (!usingWWWProxy && remotePort > 0)
            sktOpts.setPort(remotePort);
        i2ps = createI2PSocket(clientDest, sktOpts);
        byte[] data = null;
        byte[] response = null;
        if (usingWWWProxy)
            data = newRequest.toString().getBytes("ISO-8859-1");
        else
            response = SUCCESS_RESPONSE.getBytes("UTF-8");
        OnTimeout onTimeout = new OnTimeout(s, s.getOutputStream(), targetRequest, usingWWWProxy, currentProxy, requestId);
        Thread t = new I2PTunnelRunner(s, i2ps, sockLock, data, response, mySockets, onTimeout);
        // we are called from an unlimited thread pool, so run inline
        // t.start();
        t.run();
    } catch (IOException ex) {
        _log.info(getPrefix(requestId) + "Error trying to connect", ex);
        handleClientException(ex, out, targetRequest, usingWWWProxy, currentProxy, requestId);
    } catch (I2PException ex) {
        _log.info("getPrefix(requestId) + Error trying to connect", ex);
        handleClientException(ex, out, targetRequest, usingWWWProxy, currentProxy, requestId);
    } catch (OutOfMemoryError oom) {
        IOException ex = new IOException("OOM");
        _log.info("getPrefix(requestId) + Error trying to connect", ex);
        handleClientException(ex, out, targetRequest, usingWWWProxy, currentProxy, requestId);
    } finally {
        // only because we are running it inline
        closeSocket(s);
        if (i2ps != null)
            try {
                i2ps.close();
            } catch (IOException ioe) {
            }
    }
}
Also used : Destination(net.i2p.data.Destination) ClientApp(net.i2p.app.ClientApp) OutputStream(java.io.OutputStream) Outproxy(net.i2p.app.Outproxy) I2PException(net.i2p.I2PException) InputStream(java.io.InputStream) I2PSocket(net.i2p.client.streaming.I2PSocket) I2PSocketOptions(net.i2p.client.streaming.I2PSocketOptions) IOException(java.io.IOException) ClientAppManager(net.i2p.app.ClientAppManager) Socket(java.net.Socket) I2PSocket(net.i2p.client.streaming.I2PSocket)

Example 2 with ClientApp

use of net.i2p.app.ClientApp in project i2p.i2p by i2p.

the class I2PTunnelHTTPClient method clientConnectionRun.

/**
 *  Note: This does not handle RFC 2616 header line splitting,
 *  which is obsoleted in RFC 7230.
 */
protected void clientConnectionRun(Socket s) {
    OutputStream out = null;
    /**
     * The URL after fixup, always starting with http:// or https://
     */
    String targetRequest = null;
    // in-net outproxy
    boolean usingWWWProxy = false;
    // local outproxy plugin
    boolean usingInternalOutproxy = false;
    Outproxy outproxy = null;
    boolean usingInternalServer = false;
    String internalPath = null;
    String internalRawQuery = null;
    String currentProxy = null;
    long requestId = __requestId.incrementAndGet();
    boolean shout = false;
    I2PSocket i2ps = null;
    try {
        s.setSoTimeout(INITIAL_SO_TIMEOUT);
        out = s.getOutputStream();
        InputReader reader = new InputReader(s.getInputStream());
        String line, method = null, protocol = null, host = null, destination = null;
        StringBuilder newRequest = new StringBuilder();
        boolean ahelperPresent = false;
        boolean ahelperNew = false;
        String ahelperKey = null;
        String userAgent = null;
        String authorization = null;
        int remotePort = 0;
        String referer = null;
        URI origRequestURI = null;
        while ((line = reader.readLine(method)) != null) {
            line = line.trim();
            if (_log.shouldLog(Log.DEBUG)) {
                _log.debug(getPrefix(requestId) + "Line=[" + line + "]");
            }
            String lowercaseLine = line.toLowerCase(Locale.US);
            if (lowercaseLine.startsWith("connection: ") || lowercaseLine.startsWith("keep-alive: ") || lowercaseLine.startsWith("proxy-connection: ")) {
                continue;
            }
            if (method == null) {
                // first line (GET /base64/realaddr)
                if (_log.shouldLog(Log.DEBUG)) {
                    _log.debug(getPrefix(requestId) + "First line [" + line + "]");
                }
                String[] params = DataHelper.split(line, " ", 3);
                if (params.length != 3) {
                    break;
                }
                String request = params[1];
                // various obscure fixups
                if (request.startsWith("/") && getTunnel().getClientOptions().getProperty("i2ptunnel.noproxy") != null) {
                    // what is this for ???
                    request = "http://i2p" + request;
                } else if (request.startsWith("/eepproxy/")) {
                    // Deprecated
                    // /eepproxy/foo.i2p/bar/baz.html
                    String subRequest = request.substring("/eepproxy/".length());
                    if (subRequest.indexOf('/') == -1) {
                        subRequest += '/';
                    }
                    request = "http://" + subRequest;
                /**
                 **
                 *                    } else if (request.toLowerCase(Locale.US).startsWith("http://i2p/")) {
                 *                    // http://i2p/b64key/bar/baz.html
                 *                    // we can't do this now by setting the URI host to the b64key, as
                 *                    // it probably contains '=' and '~' which are illegal,
                 *                    // and a host may not include escaped octets
                 *                    // This will get undone below.
                 *                    String subRequest = request.substring("http://i2p/".length());
                 *                    if (subRequest.indexOf("/") == -1)
                 *                    subRequest += "/";
                 *                    "http://" + "b64key/bar/baz.html"
                 *                    request = "http://" + subRequest;
                 *                    } else if (request.toLowerCase(Locale.US).startsWith("http://")) {
                 *                    // Unsupported
                 *                    // http://$b64key/...
                 *                    // This probably used to work, rewrite it so that
                 *                    // we can create a URI without illegal characters
                 *                    // This will get undone below.
                 *                    String  oldPath = request.substring(7);
                 *                    int slash = oldPath.indexOf("/");
                 *                    if (slash < 0)
                 *                    slash = oldPath.length();
                 *                    if (slash >= 516 && !oldPath.substring(0, slash).contains("."))
                 *                    request = "http://i2p/" + oldPath;
                 ***
                 */
                }
                method = params[0];
                if (method.toUpperCase(Locale.US).equals("CONNECT")) {
                    // this makes things easier later, by spoofing a
                    // protocol so the URI parser find the host and port
                    // For in-net outproxy, will be fixed up below
                    request = "https://" + request + '/';
                }
                // Now use the Java URI parser
                // This will be the incoming URI but will then get modified
                // to be the outgoing URI (with http:// if going to outproxy, otherwise without)
                URI requestURI = null;
                try {
                    try {
                        requestURI = new URI(request);
                    } catch (URISyntaxException use) {
                        // fixup []| in path/query not escaped by browsers, see ticket #2130
                        boolean error = true;
                        // find 3rd /
                        int idx = 0;
                        for (int i = 0; i < 2; i++) {
                            idx = request.indexOf('/', idx);
                            if (idx < 0)
                                break;
                            idx++;
                        }
                        if (idx > 0) {
                            String schemeHostPort = request.substring(0, idx);
                            String rest = request.substring(idx);
                            rest = rest.replace("[", "%5B");
                            rest = rest.replace("]", "%5D");
                            rest = rest.replace("|", "%7C");
                            String testRequest = schemeHostPort + rest;
                            if (!testRequest.equals(request)) {
                                try {
                                    requestURI = new URI(testRequest);
                                    request = testRequest;
                                    error = false;
                                } catch (URISyntaxException use2) {
                                // didn't work, give up
                                }
                            }
                        }
                        // guess it wasn't []|
                        if (error)
                            throw use;
                    }
                    origRequestURI = requestURI;
                    if (requestURI.getRawUserInfo() != null || requestURI.getRawFragment() != null) {
                        // these should never be sent to the proxy in the request line
                        if (_log.shouldLog(Log.WARN)) {
                            _log.warn(getPrefix(requestId) + "Removing userinfo or fragment [" + request + "]");
                        }
                        requestURI = changeURI(requestURI, null, 0, null);
                    }
                    if (requestURI.getPath() == null || requestURI.getPath().length() <= 0) {
                        // Add a path
                        if (_log.shouldLog(Log.WARN)) {
                            _log.warn(getPrefix(requestId) + "Adding / path to [" + request + "]");
                        }
                        requestURI = changeURI(requestURI, null, 0, "/");
                    }
                } catch (URISyntaxException use) {
                    if (_log.shouldLog(Log.WARN)) {
                        _log.warn(getPrefix(requestId) + "Bad request [" + request + "]", use);
                    }
                    try {
                        out.write(getErrorPage("baduri", ERR_BAD_URI).getBytes("UTF-8"));
                        String msg = use.getLocalizedMessage();
                        if (msg != null) {
                            out.write(DataHelper.getASCII("<p>\n"));
                            out.write(DataHelper.getUTF8(DataHelper.escapeHTML(msg)));
                            out.write(DataHelper.getASCII("</p>\n"));
                        }
                        out.write(DataHelper.getASCII("</div>\n"));
                        writeFooter(out);
                        reader.drain();
                    } catch (IOException ioe) {
                    // ignore
                    }
                    return;
                }
                String protocolVersion = params[2];
                protocol = requestURI.getScheme();
                host = requestURI.getHost();
                if (protocol == null || host == null) {
                    _log.warn("Null protocol or host: " + request + ' ' + protocol + ' ' + host);
                    method = null;
                    break;
                }
                int port = requestURI.getPort();
                // Go through the various types of host names, set
                // the host and destination variables accordingly,
                // and transform the first line.
                // For all i2p network hosts, ensure that the host is a
                // Base 32 hostname so that we do not reveal our name for it
                // in our addressbook (all naming is local),
                // and it is removed from the request line.
                String hostLowerCase = host.toLowerCase(Locale.US);
                if (hostLowerCase.equals(LOCAL_SERVER)) {
                    // so we don't do any naming service lookups
                    destination = host;
                    usingInternalServer = true;
                    internalPath = requestURI.getPath();
                    internalRawQuery = requestURI.getRawQuery();
                } else if (hostLowerCase.equals("i2p")) {
                    // pull the b64 _dest out of the first path element
                    String oldPath = requestURI.getPath().substring(1);
                    int slash = oldPath.indexOf('/');
                    if (slash < 0) {
                        slash = oldPath.length();
                        oldPath += '/';
                    }
                    String _dest = oldPath.substring(0, slash);
                    if (slash >= 516 && !_dest.contains(".")) {
                        // possible alternative:
                        // redirect to b32
                        destination = _dest;
                        host = getHostName(destination);
                        targetRequest = requestURI.toASCIIString();
                        String newURI = oldPath.substring(slash);
                        String query = requestURI.getRawQuery();
                        if (query != null) {
                            newURI += '?' + query;
                        }
                        try {
                            requestURI = new URI(newURI);
                        } catch (URISyntaxException use) {
                            // shouldnt happen
                            _log.warn(request, use);
                            method = null;
                            break;
                        }
                    } else {
                        _log.warn("Bad http://i2p/b64dest " + request);
                        host = null;
                        break;
                    }
                } else if (hostLowerCase.endsWith(".i2p")) {
                    // Destination gets the host name
                    destination = host;
                    // Host becomes the destination's "{b32}.b32.i2p" string, or "i2p" on lookup failure
                    host = getHostName(destination);
                    int rPort = requestURI.getPort();
                    if (rPort > 0) {
                        // Save it to put in the I2PSocketOptions,
                        remotePort = rPort;
                    /**
                     ******
                     *                            // but strip it from the URL
                     *                            if(_log.shouldLog(Log.WARN)) {
                     *                                _log.warn(getPrefix(requestId) + "Removing port from [" + request + "]");
                     *                            }
                     *                            try {
                     *                                requestURI = changeURI(requestURI, null, -1, null);
                     *                            } catch(URISyntaxException use) {
                     *                                _log.warn(request, use);
                     *                                method = null;
                     *                                break;
                     *                            }
                     *****
                     */
                    } else if ("https".equals(protocol) || method.toUpperCase(Locale.US).equals("CONNECT")) {
                        remotePort = 443;
                    } else {
                        remotePort = 80;
                    }
                    String query = requestURI.getRawQuery();
                    if (query != null) {
                        boolean ahelperConflict = false;
                        // Try to find an address helper in the query
                        String[] helperStrings = removeHelper(query);
                        if (helperStrings != null && !Boolean.parseBoolean(getTunnel().getClientOptions().getProperty(PROP_DISABLE_HELPER))) {
                            query = helperStrings[0];
                            if (query.equals("")) {
                                query = null;
                            }
                            try {
                                requestURI = replaceQuery(requestURI, query);
                            } catch (URISyntaxException use) {
                                // shouldn't happen
                                _log.warn(request, use);
                                method = null;
                                break;
                            }
                            ahelperKey = helperStrings[1];
                            // Key contains data, lets not ignore it
                            if (ahelperKey.length() > 0) {
                                if (ahelperKey.endsWith(".i2p")) {
                                    // allow i2paddresshelper=<b32>.b32.i2p syntax.
                                    /*
                                        also i2paddresshelper=name.i2p for aliases
                                        i.e. on your eepsite put
                                        <a href="?i2paddresshelper=name.i2p">This is the name I want to be called.</a>
                                         */
                                    Destination _dest = _context.namingService().lookup(ahelperKey);
                                    if (_dest == null) {
                                        if (_log.shouldLog(Log.WARN)) {
                                            _log.warn(getPrefix(requestId) + "Could not find destination for " + ahelperKey);
                                        }
                                        String header = getErrorPage("ahelper-notfound", ERR_AHELPER_NOTFOUND);
                                        try {
                                            out.write(header.getBytes("UTF-8"));
                                            out.write(("<p>" + _t("This seems to be a bad destination:") + " " + ahelperKey + " " + _t("i2paddresshelper cannot help you with a destination like that!") + "</p>").getBytes("UTF-8"));
                                            writeFooter(out);
                                            reader.drain();
                                        } catch (IOException ioe) {
                                        // ignore
                                        }
                                        return;
                                    }
                                    ahelperKey = _dest.toBase64();
                                }
                                ahelperPresent = true;
                                // ahelperKey will be validated later
                                if (host == null || "i2p".equals(host)) {
                                    // Host lookup failed - resolvable only with addresshelper
                                    // Store in local HashMap unless there is conflict
                                    String old = addressHelpers.putIfAbsent(destination.toLowerCase(Locale.US), ahelperKey);
                                    ahelperNew = old == null;
                                    // inr address helper links without trailing '=', so omit from comparison
                                    if ((!ahelperNew) && !old.replace("=", "").equals(ahelperKey.replace("=", ""))) {
                                        // Conflict: handle when URL reconstruction done
                                        ahelperConflict = true;
                                        if (_log.shouldLog(Log.WARN)) {
                                            _log.warn(getPrefix(requestId) + "Addresshelper key conflict for site [" + destination + "], trusted key [" + old + "], specified key [" + ahelperKey + "].");
                                        }
                                    }
                                } else {
                                    // If the host is resolvable from database, verify addresshelper key
                                    // Silently bypass correct keys, otherwise alert
                                    Destination hostDest = _context.namingService().lookup(destination);
                                    if (hostDest != null) {
                                        String destB64 = hostDest.toBase64();
                                        if (destB64 != null && !destB64.equals(ahelperKey)) {
                                            // Conflict: handle when URL reconstruction done
                                            ahelperConflict = true;
                                            if (_log.shouldLog(Log.WARN)) {
                                                _log.warn(getPrefix(requestId) + "Addresshelper key conflict for site [" + destination + "], trusted key [" + destB64 + "], specified key [" + ahelperKey + "].");
                                            }
                                        }
                                    }
                                }
                            }
                        // ahelperKey
                        }
                        // Did addresshelper key conflict?
                        if (ahelperConflict) {
                            try {
                                // convert ahelperKey to b32
                                String alias = getHostName(ahelperKey);
                                if (alias.equals("i2p")) {
                                    // bad ahelperKey
                                    String header = getErrorPage("dnfb", ERR_DESTINATION_UNKNOWN);
                                    writeErrorMessage(header, out, targetRequest, false, destination);
                                } else {
                                    String trustedURL = requestURI.toASCIIString();
                                    URI conflictURI;
                                    try {
                                        conflictURI = changeURI(requestURI, alias, 0, null);
                                    } catch (URISyntaxException use) {
                                        // shouldn't happen
                                        _log.warn(request, use);
                                        method = null;
                                        break;
                                    }
                                    String conflictURL = conflictURI.toASCIIString();
                                    String header = getErrorPage("ahelper-conflict", ERR_AHELPER_CONFLICT);
                                    out.write(header.getBytes("UTF-8"));
                                    out.write("<p>".getBytes("UTF-8"));
                                    out.write(_t("To visit the destination in your address book, click <a href=\"{0}\">here</a>. To visit the conflicting addresshelper destination, click <a href=\"{1}\">here</a>.", trustedURL, conflictURL).getBytes("UTF-8"));
                                    out.write("</p>".getBytes("UTF-8"));
                                    Hash h1 = ConvertToHash.getHash(requestURI.getHost());
                                    Hash h2 = ConvertToHash.getHash(ahelperKey);
                                    if (h1 != null && h2 != null) {
                                        String conURL = _context.portMapper().getConsoleURL();
                                        out.write(("\n<table class=\"conflict\"><tr><th align=\"center\">" + "<a href=\"" + trustedURL + "\">").getBytes("UTF-8"));
                                        out.write(_t("Destination for {0} in address book", requestURI.getHost()).getBytes("UTF-8"));
                                        out.write(("</a></th>\n<th align=\"center\">" + "<a href=\"" + conflictURL + "\">").getBytes("UTF-8"));
                                        out.write(_t("Conflicting address helper destination").getBytes("UTF-8"));
                                        out.write(("</a></th></tr>\n").getBytes("UTF-8"));
                                        if (_context.portMapper().getPort(PortMapper.SVC_IMAGEGEN) > 0) {
                                            out.write(("<tr><td align=\"center\">" + "<a href=\"" + trustedURL + "\">" + "<img src=\"" + conURL + "imagegen/id?s=160&amp;c=" + h1.toBase64().replace("=", "%3d") + "\" width=\"160\" height=\"160\"></a>\n" + "</td>\n<td align=\"center\">" + "<a href=\"" + conflictURL + "\">" + "<img src=\"" + conURL + "imagegen/id?s=160&amp;c=" + h2.toBase64().replace("=", "%3d") + "\" width=\"160\" height=\"160\"></a>\n" + "</td></tr>").getBytes("UTF-8"));
                                        }
                                        out.write("</table>".getBytes("UTF-8"));
                                    }
                                    out.write("</div>".getBytes("UTF-8"));
                                    writeFooter(out);
                                }
                                reader.drain();
                            } catch (IOException ioe) {
                            // ignore
                            }
                            return;
                        }
                    }
                    // end query processing
                    String addressHelper = addressHelpers.get(destination);
                    if (addressHelper != null) {
                        host = getHostName(addressHelper);
                    }
                    // now strip everything but path and query from URI
                    targetRequest = requestURI.toASCIIString();
                    String newURI = requestURI.getRawPath();
                    if (query != null) {
                        newURI += '?' + query;
                    }
                    try {
                        requestURI = new URI(newURI);
                    } catch (URISyntaxException use) {
                        // shouldnt happen
                        _log.warn(request, use);
                        method = null;
                        break;
                    }
                // end of (host endsWith(".i2p"))
                } else if (hostLowerCase.equals("localhost") || host.equals("127.0.0.1") || host.startsWith("192.168.") || host.equals("[::1]")) {
                    // if somebody is trying to get to 192.168.example.com, oh well
                    try {
                        out.write(getErrorPage("localhost", ERR_LOCALHOST).getBytes("UTF-8"));
                        writeFooter(out);
                        reader.drain();
                    } catch (IOException ioe) {
                    // ignore
                    }
                    return;
                } else if (host.contains(".") || host.startsWith("[")) {
                    if (Boolean.parseBoolean(getTunnel().getClientOptions().getProperty(PROP_USE_OUTPROXY_PLUGIN, "true"))) {
                        ClientAppManager mgr = _context.clientAppManager();
                        if (mgr != null) {
                            ClientApp op = mgr.getRegisteredApp(Outproxy.NAME);
                            if (op != null) {
                                outproxy = (Outproxy) op;
                                int rPort = requestURI.getPort();
                                if (rPort > 0)
                                    remotePort = rPort;
                                else if ("https".equals(protocol) || method.toUpperCase(Locale.US).equals("CONNECT"))
                                    remotePort = 443;
                                else
                                    remotePort = 80;
                                usingInternalOutproxy = true;
                                targetRequest = requestURI.toASCIIString();
                                if (_log.shouldLog(Log.DEBUG))
                                    _log.debug(getPrefix(requestId) + " [" + host + "]: outproxy!");
                            }
                        }
                    }
                    if (!usingInternalOutproxy) {
                        if (port >= 0) {
                            host = host + ':' + port;
                        }
                        // The request must be forwarded to a WWW proxy
                        if (_log.shouldLog(Log.DEBUG)) {
                            _log.debug("Before selecting outproxy for " + host);
                        }
                        if ("https".equals(protocol) || method.toUpperCase(Locale.US).equals("CONNECT"))
                            currentProxy = selectSSLProxy();
                        else
                            currentProxy = selectProxy();
                        if (_log.shouldLog(Log.DEBUG)) {
                            _log.debug("After selecting outproxy for " + host + ": " + currentProxy);
                        }
                        if (currentProxy == null) {
                            if (_log.shouldLog(Log.WARN)) {
                                _log.warn(getPrefix(requestId) + "Host wants to be outproxied, but we dont have any!");
                            }
                            l.log("No outproxy found for the request.");
                            try {
                                out.write(getErrorPage("noproxy", ERR_NO_OUTPROXY).getBytes("UTF-8"));
                                writeFooter(out);
                                reader.drain();
                            } catch (IOException ioe) {
                            // ignore
                            }
                            return;
                        }
                        destination = currentProxy;
                        usingWWWProxy = true;
                        targetRequest = requestURI.toASCIIString();
                        if (_log.shouldLog(Log.DEBUG)) {
                            _log.debug(getPrefix(requestId) + " [" + host + "]: wwwProxy!");
                        }
                    }
                } else {
                    // Rather than look it up, just bail out.
                    if (_log.shouldLog(Log.WARN)) {
                        _log.warn("NODOTS, NOI2P: " + request);
                    }
                    try {
                        out.write(getErrorPage("denied", ERR_REQUEST_DENIED).getBytes("UTF-8"));
                        writeFooter(out);
                        reader.drain();
                    } catch (IOException ioe) {
                    // ignore
                    }
                    return;
                }
                // end host name processing
                boolean isValid = usingInternalOutproxy || usingWWWProxy || usingInternalServer || isSupportedAddress(host, protocol);
                if (!isValid) {
                    if (_log.shouldLog(Log.INFO)) {
                        _log.info(getPrefix(requestId) + "notValid(" + host + ")");
                    }
                    method = null;
                    destination = null;
                    break;
                }
                if (method.toUpperCase(Locale.US).equals("CONNECT")) {
                    // fix up the change to requestURI above to get back to the original host:port
                    line = method + ' ' + requestURI.getHost() + ':' + requestURI.getPort() + ' ' + protocolVersion;
                } else {
                    line = method + ' ' + requestURI.toASCIIString() + ' ' + protocolVersion;
                }
                if (_log.shouldLog(Log.DEBUG)) {
                    _log.debug(getPrefix(requestId) + "NEWREQ: \"" + line + "\"");
                    _log.debug(getPrefix(requestId) + "HOST  : \"" + host + "\"");
                    _log.debug(getPrefix(requestId) + "DEST  : \"" + destination + "\"");
                }
            // end first line processing
            } else {
                if (lowercaseLine.startsWith("host: ") && !usingWWWProxy && !usingInternalOutproxy) {
                    // Note that we only pass the original Host: line through to the outproxy
                    // But we don't create a Host: line if it wasn't sent to us
                    line = "Host: " + host;
                    if (_log.shouldLog(Log.INFO)) {
                        _log.info(getPrefix(requestId) + "Setting host = " + host);
                    }
                } else if (lowercaseLine.startsWith("user-agent: ")) {
                    // save for deciding whether to offer address book form
                    userAgent = lowercaseLine.substring(12);
                    if (!Boolean.parseBoolean(getTunnel().getClientOptions().getProperty(PROP_USER_AGENT))) {
                        line = null;
                        continue;
                    }
                } else if (lowercaseLine.startsWith("accept: ")) {
                    if (!Boolean.parseBoolean(getTunnel().getClientOptions().getProperty(PROP_ACCEPT))) {
                        // Replace with a standard one if possible
                        boolean html = lowercaseLine.indexOf("text/html") > 0;
                        boolean css = lowercaseLine.indexOf("text/css") > 0;
                        boolean img = lowercaseLine.indexOf("image") > 0;
                        if (html && !img && !css) {
                            // firefox, tor browser
                            line = "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";
                        } else if (img && !html && !css) {
                            // chrome
                            line = "Accept: image/webp,image/apng,image/*,*/*;q=0.8";
                        } else if (css && !html && !img) {
                            // chrome, firefox
                            line = "Accept: text/css,*/*;q=0.1";
                        }
                    // else allow as-is
                    }
                } else if (lowercaseLine.startsWith("accept")) {
                    // But allow Accept-Encoding: gzip, deflate
                    if (!lowercaseLine.startsWith("accept-encoding: ") && !Boolean.parseBoolean(getTunnel().getClientOptions().getProperty(PROP_ACCEPT))) {
                        line = null;
                        continue;
                    }
                } else if (lowercaseLine.startsWith("referer: ")) {
                    // save for address helper form below
                    referer = line.substring(9);
                    if (!Boolean.parseBoolean(getTunnel().getClientOptions().getProperty(PROP_REFERER))) {
                        try {
                            // Either strip or rewrite the referer line
                            URI refererURI = new URI(referer);
                            String refererHost = refererURI.getHost();
                            if (refererHost != null) {
                                String origHost = origRequestURI.getHost();
                                if (!refererHost.equals(origHost) || refererURI.getPort() != origRequestURI.getPort() || !DataHelper.eq(refererURI.getScheme(), origRequestURI.getScheme())) {
                                    line = null;
                                    // completely strip the line if everything doesn't match
                                    continue;
                                }
                                // Strip to a relative URI, to hide the original host name
                                StringBuilder buf = new StringBuilder();
                                buf.append("Referer: ");
                                String refererPath = refererURI.getRawPath();
                                buf.append(refererPath != null ? refererPath : "/");
                                String refererQuery = refererURI.getRawQuery();
                                if (refererQuery != null)
                                    buf.append('?').append(refererQuery);
                                line = buf.toString();
                            }
                        // else relative URI, leave in
                        } catch (URISyntaxException use) {
                            line = null;
                            // completely strip the line
                            continue;
                        }
                    }
                // else allow
                } else if (lowercaseLine.startsWith("via: ") && !Boolean.parseBoolean(getTunnel().getClientOptions().getProperty(PROP_VIA))) {
                    // line = "Via: i2p";
                    line = null;
                    // completely strip the line
                    continue;
                } else if (lowercaseLine.startsWith("from: ")) {
                    // line = "From: i2p";
                    line = null;
                    // completely strip the line
                    continue;
                } else if (lowercaseLine.startsWith("authorization: ntlm ")) {
                    // Block Windows NTLM after 401
                    line = null;
                    continue;
                } else if (lowercaseLine.startsWith("proxy-authorization: ")) {
                    // This should be for us. It is a
                    // hop-by-hop header, and we definitely want to block Windows NTLM after a far-end 407.
                    // Response to far-end shouldn't happen, as we
                    // strip Proxy-Authenticate from the response in HTTPResponseOutputStream
                    // "proxy-authorization: ".length()
                    authorization = line.substring(21);
                    line = null;
                    continue;
                } else if (lowercaseLine.startsWith("icy")) {
                    // icecast/shoutcast, We need to leave the user-agent alone.
                    shout = true;
                }
            }
            if (line.length() == 0) {
                // No more headers, add our own and break out of the loop
                String ok = getTunnel().getClientOptions().getProperty("i2ptunnel.gzip");
                boolean gzip = DEFAULT_GZIP;
                if (ok != null) {
                    gzip = Boolean.parseBoolean(ok);
                }
                if (gzip && !usingInternalServer && !method.toUpperCase(Locale.US).equals("CONNECT")) {
                    // newRequest.append("Accept-Encoding: \r\n");
                    if (!usingInternalOutproxy)
                        newRequest.append("X-Accept-Encoding: x-i2p-gzip;q=1.0, identity;q=0.5, deflate;q=0, gzip;q=0, *;q=0\r\n");
                }
                if (!shout && !method.toUpperCase(Locale.US).equals("CONNECT")) {
                    if (!Boolean.parseBoolean(getTunnel().getClientOptions().getProperty(PROP_USER_AGENT))) {
                        // let's not advertise to external sites that we are from I2P
                        if (usingWWWProxy || usingInternalOutproxy) {
                            newRequest.append(UA_CLEARNET);
                        } else {
                            newRequest.append(UA_I2P);
                        }
                    }
                }
                // Add Proxy-Authentication header for next hop (outproxy)
                if (usingWWWProxy && Boolean.parseBoolean(getTunnel().getClientOptions().getProperty(PROP_OUTPROXY_AUTH))) {
                    // specific for this proxy
                    String user = getTunnel().getClientOptions().getProperty(PROP_OUTPROXY_USER_PREFIX + currentProxy);
                    String pw = getTunnel().getClientOptions().getProperty(PROP_OUTPROXY_PW_PREFIX + currentProxy);
                    if (user == null || pw == null) {
                        // if not, look at default user and pw
                        user = getTunnel().getClientOptions().getProperty(PROP_OUTPROXY_USER);
                        pw = getTunnel().getClientOptions().getProperty(PROP_OUTPROXY_PW);
                    }
                    if (user != null && pw != null) {
                        newRequest.append("Proxy-Authorization: Basic ").append(// true = use standard alphabet
                        Base64.encode((user + ':' + pw).getBytes("UTF-8"), true)).append("\r\n");
                    }
                }
                newRequest.append("Connection: close\r\n\r\n");
                s.setSoTimeout(0);
                break;
            } else {
                // HTTP spec
                newRequest.append(line).append("\r\n");
            }
        }
        if (_log.shouldLog(Log.DEBUG)) {
            _log.debug(getPrefix(requestId) + "NewRequest header: [" + newRequest.toString() + "]");
        }
        if (method == null || (destination == null && !usingInternalOutproxy)) {
            // l.log("No HTTP method found in the request.");
            try {
                if (protocol != null && "http".equals(protocol.toLowerCase(Locale.US))) {
                    out.write(getErrorPage("denied", ERR_REQUEST_DENIED).getBytes("UTF-8"));
                } else {
                    out.write(getErrorPage("protocol", ERR_BAD_PROTOCOL).getBytes("UTF-8"));
                }
                writeFooter(out);
            } catch (IOException ioe) {
            // ignore
            }
            return;
        }
        if (_log.shouldLog(Log.DEBUG)) {
            _log.debug(getPrefix(requestId) + "Destination: " + destination);
        }
        // Authorization
        AuthResult result = authorize(s, requestId, method, authorization);
        if (result != AuthResult.AUTH_GOOD) {
            if (_log.shouldLog(Log.WARN)) {
                if (authorization != null) {
                    _log.warn(getPrefix(requestId) + "Auth failed, sending 407 again");
                } else {
                    _log.warn(getPrefix(requestId) + "Auth required, sending 407");
                }
            }
            try {
                out.write(getAuthError(result == AuthResult.AUTH_STALE).getBytes("UTF-8"));
                writeFooter(out);
            } catch (IOException ioe) {
            // ignore
            }
            return;
        }
        // Ignore all the headers
        if (usingInternalServer) {
            try {
                // disable the add form if address helper is disabled
                if (internalPath.equals("/add") && Boolean.parseBoolean(getTunnel().getClientOptions().getProperty(PROP_DISABLE_HELPER))) {
                    out.write(ERR_HELPER_DISABLED.getBytes("UTF-8"));
                } else {
                    LocalHTTPServer.serveLocalFile(out, method, internalPath, internalRawQuery, _proxyNonce);
                }
            } catch (IOException ioe) {
            // ignore
            }
            return;
        }
        // no destination, going to outproxy plugin
        if (usingInternalOutproxy) {
            Socket outSocket = outproxy.connect(host, remotePort);
            OnTimeout onTimeout = new OnTimeout(s, s.getOutputStream(), targetRequest, usingWWWProxy, currentProxy, requestId);
            byte[] data;
            byte[] response;
            if (method.toUpperCase(Locale.US).equals("CONNECT")) {
                data = null;
                response = SUCCESS_RESPONSE.getBytes("UTF-8");
            } else {
                data = newRequest.toString().getBytes("ISO-8859-1");
                response = null;
            }
            Thread t = new I2PTunnelOutproxyRunner(s, outSocket, sockLock, data, response, onTimeout);
            // we are called from an unlimited thread pool, so run inline
            // t.start();
            t.run();
            return;
        }
        // LOOKUP
        // If the host is "i2p", the getHostName() lookup failed, don't try to
        // look it up again as the naming service does not do negative caching
        // so it will be slow.
        Destination clientDest = null;
        String addressHelper = addressHelpers.get(destination.toLowerCase(Locale.US));
        if (addressHelper != null) {
            clientDest = _context.namingService().lookup(addressHelper);
            if (clientDest == null) {
                // remove bad entries
                addressHelpers.remove(destination.toLowerCase(Locale.US));
                if (_log.shouldLog(Log.WARN)) {
                    _log.warn(getPrefix(requestId) + "Could not find destination for " + addressHelper);
                }
                String header = getErrorPage("ahelper-notfound", ERR_AHELPER_NOTFOUND);
                try {
                    writeErrorMessage(header, out, targetRequest, false, destination);
                } catch (IOException ioe) {
                // ignore
                }
                return;
            }
        } else if ("i2p".equals(host)) {
            clientDest = null;
        } else if (destination.length() == 60 && destination.toLowerCase(Locale.US).endsWith(".b32.i2p")) {
            // use existing session to look up for efficiency
            verifySocketManager();
            I2PSession sess = sockMgr.getSession();
            if (!sess.isClosed()) {
                byte[] hData = Base32.decode(destination.substring(0, 52));
                if (hData != null) {
                    if (_log.shouldLog(Log.INFO)) {
                        _log.info("lookup in-session " + destination);
                    }
                    Hash hash = Hash.create(hData);
                    clientDest = sess.lookupDest(hash, 20 * 1000);
                }
            } else {
                clientDest = _context.namingService().lookup(destination);
            }
        } else {
            clientDest = _context.namingService().lookup(destination);
        }
        if (clientDest == null) {
            // l.log("Could not resolve " + destination + ".");
            if (_log.shouldLog(Log.WARN)) {
                _log.warn("Unable to resolve " + destination + " (proxy? " + usingWWWProxy + ", request: " + targetRequest);
            }
            String header;
            String jumpServers = null;
            String extraMessage = null;
            if (usingWWWProxy) {
                header = getErrorPage("dnfp", ERR_DESTINATION_UNKNOWN);
            } else if (ahelperPresent) {
                header = getErrorPage("dnfb", ERR_DESTINATION_UNKNOWN);
            } else if (destination.length() == 60 && destination.toLowerCase(Locale.US).endsWith(".b32.i2p")) {
                header = getErrorPage("nols", ERR_DESTINATION_UNKNOWN);
                extraMessage = _t("Destination lease set not found");
            } else {
                header = getErrorPage("dnfh", ERR_DESTINATION_UNKNOWN);
                jumpServers = getTunnel().getClientOptions().getProperty(PROP_JUMP_SERVERS);
                if (jumpServers == null) {
                    jumpServers = DEFAULT_JUMP_SERVERS;
                }
                int jumpDelay = 400 + _context.random().nextInt(256);
                try {
                    Thread.sleep(jumpDelay);
                } catch (InterruptedException ie) {
                }
            }
            try {
                writeErrorMessage(header, extraMessage, out, targetRequest, usingWWWProxy, destination, jumpServers);
            } catch (IOException ioe) {
            // ignore
            }
            return;
        }
        if (method.toUpperCase(Locale.US).equals("CONNECT") && !usingWWWProxy && !Boolean.parseBoolean(getTunnel().getClientOptions().getProperty(PROP_INTERNAL_SSL))) {
            try {
                writeErrorMessage(ERR_INTERNAL_SSL, out, targetRequest, false, destination);
            } catch (IOException ioe) {
            // ignore
            }
            if (_log.shouldLog(Log.WARN))
                _log.warn("SSL to i2p destinations denied by configuration: " + targetRequest);
            return;
        }
        // Don't do this for eepget, which uses a user-agent of "Wget"
        if (ahelperNew && "GET".equals(method) && (userAgent == null || !userAgent.startsWith("Wget")) && !Boolean.parseBoolean(getTunnel().getClientOptions().getProperty(PROP_DISABLE_HELPER))) {
            try {
                writeHelperSaveForm(out, destination, ahelperKey, targetRequest, referer);
            } catch (IOException ioe) {
            // ignore
            }
            return;
        }
        // Syndie can't handle a redirect of a POST
        if (ahelperPresent && !"POST".equals(method)) {
            String uri = targetRequest;
            if (_log.shouldLog(Log.DEBUG)) {
                _log.debug("Auto redirecting to " + uri);
            }
            try {
                out.write(("HTTP/1.1 301 Address Helper Accepted\r\n" + "Location: " + uri + "\r\n" + "Connection: close\r\n" + "Proxy-Connection: close\r\n" + "\r\n").getBytes("UTF-8"));
            } catch (IOException ioe) {
            // ignore
            }
            return;
        }
        Properties opts = new Properties();
        // opts.setProperty("i2p.streaming.inactivityTimeout", ""+120*1000);
        // 1 == disconnect.  see ConnectionOptions in the new streaming lib, which i
        // dont want to hard link to here
        // opts.setProperty("i2p.streaming.inactivityTimeoutAction", ""+1);
        I2PSocketOptions sktOpts = getDefaultOptions(opts);
        if (remotePort > 0)
            sktOpts.setPort(remotePort);
        i2ps = createI2PSocket(clientDest, sktOpts);
        OnTimeout onTimeout = new OnTimeout(s, s.getOutputStream(), targetRequest, usingWWWProxy, currentProxy, requestId);
        Thread t;
        if (method.toUpperCase(Locale.US).equals("CONNECT")) {
            byte[] data;
            byte[] response;
            if (usingWWWProxy) {
                data = newRequest.toString().getBytes("ISO-8859-1");
                response = null;
            } else {
                data = null;
                response = SUCCESS_RESPONSE.getBytes("UTF-8");
            }
            t = new I2PTunnelRunner(s, i2ps, sockLock, data, response, mySockets, onTimeout);
        } else {
            byte[] data = newRequest.toString().getBytes("ISO-8859-1");
            t = new I2PTunnelHTTPClientRunner(s, i2ps, sockLock, data, mySockets, onTimeout);
        }
        // we are called from an unlimited thread pool, so run inline
        // t.start();
        t.run();
    } catch (IOException ex) {
        if (_log.shouldLog(Log.INFO)) {
            _log.info(getPrefix(requestId) + "Error trying to connect", ex);
        }
        handleClientException(ex, out, targetRequest, usingWWWProxy, currentProxy, requestId);
    } catch (I2PException ex) {
        if (_log.shouldLog(Log.INFO)) {
            _log.info("getPrefix(requestId) + Error trying to connect", ex);
        }
        handleClientException(ex, out, targetRequest, usingWWWProxy, currentProxy, requestId);
    } catch (OutOfMemoryError oom) {
        IOException ex = new IOException("OOM");
        _log.error("getPrefix(requestId) + Error trying to connect", oom);
        handleClientException(ex, out, targetRequest, usingWWWProxy, currentProxy, requestId);
    } finally {
        // only because we are running it inline
        closeSocket(s);
        if (i2ps != null)
            try {
                i2ps.close();
            } catch (IOException ioe) {
            }
    }
}
Also used : Destination(net.i2p.data.Destination) ClientApp(net.i2p.app.ClientApp) OutputStream(java.io.OutputStream) Outproxy(net.i2p.app.Outproxy) URISyntaxException(java.net.URISyntaxException) Hash(net.i2p.data.Hash) ConvertToHash(net.i2p.util.ConvertToHash) Properties(java.util.Properties) URI(java.net.URI) I2PSession(net.i2p.client.I2PSession) I2PException(net.i2p.I2PException) I2PSocket(net.i2p.client.streaming.I2PSocket) I2PSocketOptions(net.i2p.client.streaming.I2PSocketOptions) IOException(java.io.IOException) ClientAppManager(net.i2p.app.ClientAppManager) Socket(java.net.Socket) I2PSocket(net.i2p.client.streaming.I2PSocket)

Example 3 with ClientApp

use of net.i2p.app.ClientApp in project i2p.i2p by i2p.

the class PluginStarter method runClientApps.

/**
 *  @param action "start" or "stop" or "uninstall"
 *  @throws Exception just about anything if an app has a delay less than zero, caller would be wise to catch Throwable
 *  If no apps have a delay less than zero, it shouldn't throw anything
 */
private static void runClientApps(RouterContext ctx, File pluginDir, List<ClientAppConfig> apps, String action) throws Exception {
    Log log = ctx.logManager().getLog(PluginStarter.class);
    // initialize pluginThreadGroup and _pendingPluginClients
    String pluginName = pluginDir.getName();
    if (!pluginThreadGroups.containsKey(pluginName))
        pluginThreadGroups.put(pluginName, new ThreadGroup(pluginName));
    ThreadGroup pluginThreadGroup = pluginThreadGroups.get(pluginName);
    if (action.equals("start"))
        _pendingPluginClients.put(pluginName, new ConcurrentHashSet<SimpleTimer2.TimedEvent>());
    for (ClientAppConfig app : apps) {
        // bypass all the logic below.
        if (action.equals("stop")) {
            String[] argVal = LoadClientAppsJob.parseArgs(app.args);
            // Do this after parsing so we don't need to worry about quoting
            for (int i = 0; i < argVal.length; i++) {
                if (argVal[i].indexOf('$') >= 0) {
                    argVal[i] = argVal[i].replace("$I2P", ctx.getBaseDir().getAbsolutePath());
                    argVal[i] = argVal[i].replace("$CONFIG", ctx.getConfigDir().getAbsolutePath());
                    argVal[i] = argVal[i].replace("$PLUGIN", pluginDir.getAbsolutePath());
                }
            }
            ClientApp ca = ctx.routerAppManager().getClientApp(app.className, argVal);
            if (ca != null) {
                // even if (ca.getState() != ClientAppState.RUNNING), we do this, we don't want to fall thru
                try {
                    ca.shutdown(LoadClientAppsJob.parseArgs(app.stopargs));
                } catch (Throwable t) {
                    throw new Exception(t);
                }
                continue;
            }
        }
        if (action.equals("start") && app.disabled)
            continue;
        String[] argVal;
        if (action.equals("start")) {
            // start
            argVal = LoadClientAppsJob.parseArgs(app.args);
        } else {
            String args;
            if (action.equals("stop"))
                args = app.stopargs;
            else if (action.equals("uninstall"))
                args = app.uninstallargs;
            else
                throw new IllegalArgumentException("bad action");
            // args must be present
            if (args == null || args.length() <= 0)
                continue;
            argVal = LoadClientAppsJob.parseArgs(args);
        }
        // do this after parsing so we don't need to worry about quoting
        for (int i = 0; i < argVal.length; i++) {
            if (argVal[i].indexOf('$') >= 0) {
                argVal[i] = argVal[i].replace("$I2P", ctx.getBaseDir().getAbsolutePath());
                argVal[i] = argVal[i].replace("$CONFIG", ctx.getConfigDir().getAbsolutePath());
                argVal[i] = argVal[i].replace("$PLUGIN", pluginDir.getAbsolutePath());
            }
        }
        ClassLoader cl = null;
        if (app.classpath != null) {
            String cp = app.classpath;
            if (cp.indexOf('$') >= 0) {
                cp = cp.replace("$I2P", ctx.getBaseDir().getAbsolutePath());
                cp = cp.replace("$CONFIG", ctx.getConfigDir().getAbsolutePath());
                cp = cp.replace("$PLUGIN", pluginDir.getAbsolutePath());
            }
            // Old way - add for the whole JVM
            // addToClasspath(cp, app.clientName, log);
            // New way - add only for this client
            // We cache the ClassLoader we start the client with, so
            // we can reuse it for stopping and uninstalling.
            // If we don't, the client won't be able to find its
            // static members.
            String clCacheKey = pluginName + app.className + app.args;
            if (!action.equals("start"))
                cl = _clCache.get(clCacheKey);
            if (cl == null) {
                URL[] urls = classpathToURLArray(cp, app.clientName, log);
                if (urls != null) {
                    cl = new URLClassLoader(urls, ClassLoader.getSystemClassLoader());
                    if (action.equals("start"))
                        _clCache.put(clCacheKey, cl);
                }
            }
        }
        if (app.delay < 0 && action.equals("start")) {
            // this will throw exceptions
            LoadClientAppsJob.runClientInline(app.className, app.clientName, argVal, log, cl);
        } else if (app.delay == 0 || !action.equals("start")) {
            // quick check, will throw ClassNotFoundException on error
            LoadClientAppsJob.testClient(app.className, cl);
            // run this guy now
            LoadClientAppsJob.runClient(app.className, app.clientName, argVal, ctx, log, pluginThreadGroup, cl);
        } else {
            // If it bombs after that, then we throw the ClassNotFoundException.
            try {
                // quick check
                LoadClientAppsJob.testClient(app.className, cl);
            } catch (ClassNotFoundException ex) {
                // Under normal circumstances there will be no delay at all.
                try {
                    if (app.delay > 1) {
                        Thread.sleep(2000);
                    } else {
                        Thread.sleep(1000);
                    }
                } catch (InterruptedException ie) {
                }
                // quick check, will throw ClassNotFoundException on error
                LoadClientAppsJob.testClient(app.className, cl);
            }
            // wait before firing it up
            SimpleTimer2.TimedEvent evt = new TrackedDelayedClient(pluginName, ctx.simpleTimer2(), ctx, app.className, app.clientName, argVal, pluginThreadGroup, cl);
            evt.schedule(app.delay);
        }
    }
}
Also used : Log(net.i2p.util.Log) ClientApp(net.i2p.app.ClientApp) IOException(java.io.IOException) URL(java.net.URL) ConcurrentHashSet(net.i2p.util.ConcurrentHashSet) URLClassLoader(java.net.URLClassLoader) ClientAppConfig(net.i2p.router.startup.ClientAppConfig) URLClassLoader(java.net.URLClassLoader) SimpleTimer2(net.i2p.util.SimpleTimer2)

Example 4 with ClientApp

use of net.i2p.app.ClientApp in project i2p.i2p by i2p.

the class ConfigClientsHandler method stopClient.

/**
 *  @since Implemented in 0.9.6 using ClientAppManager
 */
private void stopClient(int i) {
    List<ClientAppConfig> clients = ClientAppConfig.getClientApps(_context);
    if (i >= clients.size()) {
        addFormError(_t("Bad client index."));
        return;
    }
    ClientAppConfig ca = clients.get(i);
    ClientApp clientApp = _context.routerAppManager().getClientApp(ca.className, LoadClientAppsJob.parseArgs(ca.args));
    if (clientApp != null && clientApp.getState() == ClientAppState.RUNNING) {
        try {
            // todo parseArgs(ca.stopArgs) ?
            clientApp.shutdown(null);
            addFormNotice(_t("Client {0} stopped", ca.clientName));
            // Give a chance for status to update
            try {
                Thread.sleep(1000);
            } catch (InterruptedException ie) {
            }
        } catch (Throwable t) {
            addFormError("Cannot stop client " + ca.className + ": " + t);
            _log.error("Error stopping client " + ca.className, t);
        }
    } else {
        addFormError("Cannot stop client " + i + ": " + ca.className);
    }
}
Also used : ClientApp(net.i2p.app.ClientApp) ClientAppConfig(net.i2p.router.startup.ClientAppConfig)

Example 5 with ClientApp

use of net.i2p.app.ClientApp in project i2p.i2p by i2p.

the class ConfigClientsHelper method getForm1.

/**
 * clients
 */
public String getForm1() {
    StringBuilder buf = new StringBuilder(1024);
    buf.append("<table id=\"clientconfig\">\n" + "<tr><th align=\"right\">").append(_t("Client")).append("</th><th>").append(_t("Run at Startup?")).append("</th><th>").append(_t("Control")).append("</th><th align=\"left\">").append(_t("Class and arguments")).append("</th></tr>\n");
    boolean allowEdit = isClientChangeEnabled();
    List<ClientAppConfig> clients = ClientAppConfig.getClientApps(_context);
    List<CAC> cacs = new ArrayList<CAC>(clients.size());
    for (int cur = 0; cur < clients.size(); cur++) {
        ClientAppConfig ca = clients.get(cur);
        String xname = ca.clientName;
        if (xname.length() > 0)
            xname = _t(xname);
        cacs.add(new CAC(cur, ca, xname));
    }
    Collections.sort(cacs, new CACComparator());
    for (CAC cac : cacs) {
        ClientAppConfig ca = cac.config;
        int cur = cac.index;
        boolean isConsole = ca.className.equals("net.i2p.router.web.RouterConsoleRunner");
        boolean showStart;
        boolean showStop;
        if (isConsole) {
            showStart = false;
            showStop = false;
        } else {
            ClientApp clientApp = _context.routerAppManager().getClientApp(ca.className, LoadClientAppsJob.parseArgs(ca.args));
            showStart = clientApp == null;
            showStop = clientApp != null && clientApp.getState() == ClientAppState.RUNNING;
        }
        renderForm(buf, "" + cur, ca.clientName, // urlify, enabled
        false, !ca.disabled, // "webConsole".equals(ca.clientName) || "Web console".equals(ca.clientName),
        false, RouterConsoleRunner.class.getName().equals(ca.className), // description
        DataHelper.escapeHTML(ca.className + ((ca.args != null) ? " " + ca.args : "")), // edit
        allowEdit && ("" + cur).equals(_edit), // Don't allow edit if it's running, or else we would lose the "handle" to the ClientApp to stop it.
        allowEdit && !showStop, false, // show stop button
        showStop, // show delete button, show start button
        allowEdit && !isConsole, showStart);
    }
    if (allowEdit && "new".equals(_edit))
        renderForm(buf, "" + clients.size(), "", false, false, false, false, "", true, false, false, false, false, false);
    buf.append("</table>\n");
    return buf.toString();
}
Also used : ClientApp(net.i2p.app.ClientApp) ClientAppConfig(net.i2p.router.startup.ClientAppConfig) ArrayList(java.util.ArrayList)

Aggregations

ClientApp (net.i2p.app.ClientApp)6 IOException (java.io.IOException)3 ClientAppManager (net.i2p.app.ClientAppManager)3 ClientAppConfig (net.i2p.router.startup.ClientAppConfig)3 OutputStream (java.io.OutputStream)2 Socket (java.net.Socket)2 I2PException (net.i2p.I2PException)2 Outproxy (net.i2p.app.Outproxy)2 I2PSocket (net.i2p.client.streaming.I2PSocket)2 I2PSocketOptions (net.i2p.client.streaming.I2PSocketOptions)2 Destination (net.i2p.data.Destination)2 InputStream (java.io.InputStream)1 URI (java.net.URI)1 URISyntaxException (java.net.URISyntaxException)1 URL (java.net.URL)1 URLClassLoader (java.net.URLClassLoader)1 ArrayList (java.util.ArrayList)1 Properties (java.util.Properties)1 I2PSession (net.i2p.client.I2PSession)1 Hash (net.i2p.data.Hash)1