0001: // httpd.java
0002: // -----------------------
0003: // (C) by Michael Peter Christen; mc@anomic.de
0004: // first published on http://www.anomic.de
0005: // Frankfurt, Germany, 2004
0006: //
0007: // last major change: $LastChangedDate: 2008-01-28 18:21:08 +0000 (Mo, 28 Jan 2008) $ by $LastChangedBy: orbiter $
0008: // Revision: $LastChangedRevision: 4411 $
0009: //
0010: // This program is free software; you can redistribute it and/or modify
0011: // it under the terms of the GNU General Public License as published by
0012: // the Free Software Foundation; either version 2 of the License, or
0013: // (at your option) any later version.
0014: //
0015: // This program is distributed in the hope that it will be useful,
0016: // but WITHOUT ANY WARRANTY; without even the implied warranty of
0017: // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
0018: // GNU General Public License for more details.
0019: //
0020: // You should have received a copy of the GNU General Public License
0021: // along with this program; if not, write to the Free Software
0022: // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
0023: //
0024: // Using this software in any meaning (reading, learning, copying, compiling,
0025: // running) means that you agree that the Author(s) is (are) not responsible
0026: // for cost, loss of data or any harm that may be caused directly or indirectly
0027: // by usage of this softare or this documentation. The usage of this software
0028: // is on your own risk. The installation and usage (starting/running) of this
0029: // software may allow other people or application to access your computer and
0030: // any attached devices and is highly dependent on the configuration of the
0031: // software which must be done by the user of the software; the author(s) is
0032: // (are) also not responsible for proper configuration and usage of the
0033: // software, even if provoked by documentation provided together with
0034: // the software.
0035: //
0036: // Any changes to this file according to the GPL as documented in the file
0037: // gpl.txt aside this file in the shipment you received can be done to the
0038: // lines that follows this copyright notice here, but changes must not be
0039: // done inside the copyright notive above. A re-distribution must contain
0040: // the intact and unchanged copyright notice.
0041: // Contributions and changes to the program code must be marked as such.
0042:
0043: package de.anomic.http;
0044:
0045: import java.io.ByteArrayOutputStream;
0046: import java.io.CharArrayWriter;
0047: import java.io.File;
0048: import java.io.FileInputStream;
0049: import java.io.IOException;
0050: import java.io.InputStream;
0051: import java.io.OutputStream;
0052: import java.io.PrintStream;
0053: import java.io.UnsupportedEncodingException;
0054: import java.net.InetAddress;
0055: import java.net.MalformedURLException;
0056: import java.net.URLDecoder;
0057: import java.net.URLEncoder;
0058: import java.util.Arrays;
0059: import java.util.Date;
0060: import java.util.HashMap;
0061: import java.util.HashSet;
0062: import java.util.Iterator;
0063: import java.util.Properties;
0064: import java.util.StringTokenizer;
0065:
0066: import de.anomic.data.htmlTools;
0067: import de.anomic.data.userDB;
0068: import de.anomic.kelondro.kelondroBase64Order;
0069: import de.anomic.plasma.plasmaSwitchboard;
0070: import de.anomic.server.serverByteBuffer;
0071: import de.anomic.server.serverCodings;
0072: import de.anomic.server.serverCore;
0073: import de.anomic.server.serverDomains;
0074: import de.anomic.server.serverFileUtils;
0075: import de.anomic.server.serverHandler;
0076: import de.anomic.server.serverObjects;
0077: import de.anomic.server.serverSwitch;
0078: import de.anomic.server.logging.serverLog;
0079: import de.anomic.yacy.yacyCore;
0080: import de.anomic.yacy.yacySeed;
0081: import de.anomic.yacy.yacyURL;
0082:
0083: /**
0084: * Instances of this class can be passed as argument to the serverCore.
0085: * The generic server dispatches HTTP commands and calls the
0086: * method GET, HEAD or POST in this class
0087: * these methods parse the command line and decide wether to call
0088: * a proxy servlet or a file server servlet
0089: */
0090: public final class httpd implements serverHandler {
0091:
0092: /**
0093: * <p><code>public static final String <strong>ADMIN_ACCOUNT_B64MD5</strong> = "adminAccountBase64MD5"</code></p>
0094: * <p>Name of the setting holding the authentification hash for the static <code>admin</code>-account. It is calculated
0095: * by first encoding <code>username:password</code> as Base64 and hashing it using {@link serverCodings#encodeMD5Hex(String)}.</p>
0096: */
0097: public static final String ADMIN_ACCOUNT_B64MD5 = "adminAccountBase64MD5";
0098:
0099: public static final int ERRORCASE_MESSAGE = 4;
0100: public static final int ERRORCASE_FILE = 5;
0101:
0102: /**
0103: * A hashset containing extensions that indicate content that should not be transported
0104: * using zipped content encoding
0105: * @see #shallTransportZipped(String)
0106: */
0107:
0108: //TODO: Load this from a file
0109: private static final HashSet<String> disallowZippedContentEncoding = new HashSet<String>(
0110: Arrays.asList(new String[] { ".gz", ".tgz", ".jpg",
0111: ".jpeg", ".gif", ".zip", ".rar", ".bz2", ".lha",
0112: ".jar", ".rpm", ".arc", ".arj", ".wmv", ".png",
0113: ".ico", ".bmp" }));
0114:
0115: // static objects
0116: public static final String vDATE = "<<REPL>>";
0117: public static final String copyright = "[ HTTP SERVER: AnomicHTTPD v"
0118: + vDATE + " by Michael Christen / www.anomic.de ]";
0119: public static final String hline = "-------------------------------------------------------------------------------";
0120:
0121: public static HashMap<String, String> reverseMappingCache = new HashMap<String, String>();
0122: private static plasmaSwitchboard switchboard = null;
0123: private static String virtualHost = null;
0124:
0125: public static boolean keepAliveSupport = false;
0126: private static HashMap<String, Long> YaCyHopAccessRequester = new HashMap<String, Long>();
0127: private static HashMap<String, Long> YaCyHopAccessTargets = new HashMap<String, Long>();
0128:
0129: // class objects
0130: private serverCore.Session session; // holds the session object of the calling class
0131: private InetAddress userAddress; // the address of the client
0132:
0133: // for authentication
0134: private boolean use_proxyAccounts = false;
0135: private boolean proxyAccounts_init = false; // is use_proxyAccounts set?
0136: private String serverAccountBase64MD5;
0137: private String clientIP;
0138: private boolean allowProxy;
0139: private boolean allowServer;
0140: private boolean allowYaCyHop;
0141:
0142: // the connection properties
0143: private final Properties prop = new Properties();
0144:
0145: private int emptyRequestCount = 0;
0146: private int keepAliveRequestCount = 0;
0147:
0148: // needed for logging
0149: private final serverLog log = new serverLog("HTTPD");
0150:
0151: // class methods
0152: public httpd(serverSwitch s) {
0153: // handler info
0154: httpd.switchboard = (plasmaSwitchboard) s;
0155: httpd.virtualHost = switchboard.getConfig("fileHost",
0156: "localhost");
0157:
0158: // authentication: by default none
0159: this .proxyAccounts_init = false;
0160: this .serverAccountBase64MD5 = null;
0161: this .clientIP = null;
0162:
0163: // configuring keep alive support
0164: keepAliveSupport = Boolean.valueOf(
0165: switchboard.getConfig("connectionKeepAliveSupport",
0166: "false")).booleanValue();
0167: }
0168:
0169: public Properties getConProp() {
0170: return this .prop;
0171: }
0172:
0173: /**
0174: * Can be used to reset this {@link serverHandler} oject so that
0175: * it can be reused for further connections
0176: * @see de.anomic.server.serverHandler#reset()
0177: */
0178: public void reset() {
0179: this .session = null;
0180: this .userAddress = null;
0181: this .allowProxy = false;
0182: this .allowServer = false;
0183: this .allowYaCyHop = false;
0184: this .proxyAccounts_init = false;
0185: this .serverAccountBase64MD5 = null;
0186: this .clientIP = null;
0187: this .prop.clear();
0188:
0189: this .emptyRequestCount = 0;
0190: this .keepAliveRequestCount = 0;
0191: }
0192:
0193: /**
0194: * Must be called at least once, but can be called again to re-use the object.
0195: * @see de.anomic.server.serverHandler#initSession(de.anomic.server.serverCore.Session)
0196: */
0197: public void initSession(serverCore.Session newsession)
0198: throws IOException {
0199: this .session = newsession;
0200: this .userAddress = session.userAddress; // client InetAddress
0201: this .clientIP = this .userAddress.getHostAddress();
0202: if (this .userAddress.isAnyLocalAddress())
0203: this .clientIP = "localhost";
0204: if (this .clientIP.equals("0:0:0:0:0:0:0:1"))
0205: this .clientIP = "localhost";
0206: if (this .clientIP.equals("127.0.0.1"))
0207: this .clientIP = "localhost";
0208: String proxyClient = switchboard.getConfig("proxyClient", "*");
0209: String serverClient = switchboard
0210: .getConfig("serverClient", "*");
0211:
0212: this .allowProxy = (proxyClient.equals("*")) ? true : match(
0213: this .clientIP, proxyClient);
0214: this .allowServer = (serverClient.equals("*")) ? true : match(
0215: this .clientIP, serverClient);
0216: this .allowYaCyHop = switchboard.getConfigBool("YaCyHop", false);
0217:
0218: // check if we want to allow this socket to connect us
0219: if (!(this .allowProxy || this .allowServer || this .allowYaCyHop)) {
0220: String errorMsg = "CONNECTION FROM " + this .clientIP
0221: + " FORBIDDEN";
0222: this .log.logWarning(errorMsg);
0223: throw new IOException(errorMsg);
0224: }
0225:
0226: this .proxyAccounts_init = false;
0227: this .serverAccountBase64MD5 = null;
0228: }
0229:
0230: private static boolean match(String key, String latch) {
0231: // the latch is a comma-separated list of patterns
0232: // each pattern may contain one wildcard-character '*' which matches anything
0233: StringTokenizer st = new StringTokenizer(latch, ",");
0234: String pattern;
0235: while (st.hasMoreTokens()) {
0236: pattern = st.nextToken();
0237: if (key.matches(pattern))
0238: return true;
0239: /*
0240: pos = pattern.indexOf("*");
0241: if (pos < 0) {
0242: // no wild card: exact match
0243: if (key.equals(pattern)) return true;
0244: } else {
0245: // wild card: match left and right side of pattern
0246: if ((key.startsWith(pattern.substring(0, pos))) &&
0247: (key.endsWith(pattern.substring(pos + 1)))) return true;
0248: }
0249: */
0250: }
0251: return false;
0252: }
0253:
0254: public String greeting() { // OBLIGATORIC FUNCTION
0255: // a response line upon connection is send to client
0256: // if no response line is wanted, return "" or null
0257: return null;
0258: }
0259:
0260: public String error(Throwable e) { // OBLIGATORIC FUNCTION
0261: // return string in case of any error that occurs during communication
0262: // is always (but not only) called if an IO-dependent exception occurrs.
0263: this .log.logSevere("Unexpected Error. "
0264: + e.getClass().getName(), e);
0265: String message = e.getMessage();
0266: if (message.indexOf("heap space") > 0)
0267: e.printStackTrace();
0268: return "501 Exception occurred: " + message;
0269: }
0270:
0271: /**
0272: * This funciton is used to determine if a persistent connection was requested by the
0273: * client.
0274: * @param header the received http-headers
0275: * @return <code>true</code> if a persistent connection was requested or <code>false</code> otherwise
0276: */
0277: private boolean handlePersistentConnection(httpHeader header) {
0278:
0279: if (!keepAliveSupport) {
0280: this .prop.put(httpHeader.CONNECTION_PROP_PERSISTENT,
0281: "close");
0282: return false;
0283: }
0284:
0285: // getting the http version that is used by the client
0286: String httpVersion = this .prop.getProperty(
0287: httpHeader.CONNECTION_PROP_HTTP_VER, "HTTP/0.9");
0288:
0289: // managing keep-alive: in HTTP/0.9 and HTTP/1.0 every connection is closed
0290: // afterwards. In HTTP/1.1 (and above, in the future?) connections are
0291: // persistent by default, but closed with the "Connection: close"
0292: // property.
0293: boolean persistent = !(httpVersion
0294: .equals(httpHeader.HTTP_VERSION_0_9) || httpVersion
0295: .equals(httpHeader.HTTP_VERSION_1_0));
0296: if (((String) header.get(httpHeader.CONNECTION, "keep-alive"))
0297: .toLowerCase().indexOf("close") != -1
0298: || ((String) header.get(httpHeader.PROXY_CONNECTION,
0299: "keep-alive")).toLowerCase().indexOf("close") != -1) {
0300: persistent = false;
0301: }
0302:
0303: String transferEncoding = (String) header.get(
0304: httpHeader.TRANSFER_ENCODING, "identity");
0305: boolean isPostRequest = this .prop.getProperty(
0306: httpHeader.CONNECTION_PROP_METHOD).equals(
0307: httpHeader.METHOD_POST);
0308: boolean hasContentLength = header
0309: .containsKey(httpHeader.CONTENT_LENGTH);
0310: boolean hasTransferEncoding = header
0311: .containsKey(httpHeader.TRANSFER_ENCODING)
0312: && !transferEncoding.equalsIgnoreCase("identity");
0313:
0314: // if the request does not contain a content-length we have to close the connection
0315: // independently of the value of the connection header
0316: if (persistent && isPostRequest
0317: && !(hasContentLength || hasTransferEncoding))
0318: this .prop.put(httpHeader.CONNECTION_PROP_PERSISTENT,
0319: "close");
0320: else
0321: this .prop.put(httpHeader.CONNECTION_PROP_PERSISTENT,
0322: persistent ? "keep-alive" : "close");
0323:
0324: return persistent;
0325: }
0326:
0327: public static int staticAdminAuthenticated(String authorization,
0328: serverSwitch sw) {
0329: if (authorization == null)
0330: return 1;
0331: //if (authorization.length() < 6) return 1; // no authentication information given
0332: //authorization = authorization.trim().substring(6);
0333: String adminAccountBase64MD5 = sw.getConfig(
0334: ADMIN_ACCOUNT_B64MD5, "");
0335: if (adminAccountBase64MD5.length() == 0)
0336: return 2; // no passwrd stored
0337: if (adminAccountBase64MD5.equals(serverCodings
0338: .encodeMD5Hex(authorization)))
0339: return 4; // hard-authenticated, all ok
0340: return 1;
0341: }
0342:
0343: private boolean handleServerAuthentication(httpHeader header)
0344: throws IOException {
0345: // getting the http version that is used by the client
0346: String httpVersion = this .prop.getProperty(
0347: httpHeader.CONNECTION_PROP_HTTP_VER, "HTTP/0.9");
0348:
0349: // reading the authentication settings from switchboard
0350: if (this .serverAccountBase64MD5 == null)
0351: this .serverAccountBase64MD5 = switchboard.getConfig(
0352: "serverAccountBase64MD5", "");
0353:
0354: if (this .serverAccountBase64MD5.length() > 0) {
0355: String auth = (String) header.get(httpHeader.AUTHORIZATION);
0356: if (auth == null) {
0357: // authorization requested, but no authorizeation given in header. Ask for authenticate:
0358: this .session.out
0359: .write((httpVersion + " 401 log-in required"
0360: + serverCore.CRLF_STRING
0361: + httpHeader.WWW_AUTHENTICATE
0362: + ": Basic realm=\"log-in\""
0363: + serverCore.CRLF_STRING + serverCore.CRLF_STRING)
0364: .getBytes());
0365: this .session.out
0366: .write((httpHeader.CONTENT_LENGTH + ": 0\r\n")
0367: .getBytes());
0368: this .session.out.write("\r\n".getBytes());
0369: return false;
0370: } else if (!this .serverAccountBase64MD5
0371: .equals(serverCodings.encodeMD5Hex(auth.trim()
0372: .substring(6)))) {
0373: // wrong password given: ask for authenticate again
0374: serverLog.logInfo("HTTPD",
0375: "Wrong log-in for account 'server' in HTTPD.GET "
0376: + this .prop.getProperty("PATH")
0377: + " from IP " + this .clientIP);
0378: this .session.out
0379: .write((httpVersion + " 401 log-in required"
0380: + serverCore.CRLF_STRING
0381: + httpHeader.WWW_AUTHENTICATE
0382: + ": Basic realm=\"log-in\"" + serverCore.CRLF_STRING)
0383: .getBytes());
0384: this .session.out
0385: .write((httpHeader.CONTENT_LENGTH + ": 0\r\n")
0386: .getBytes());
0387: this .session.out.write("\r\n".getBytes());
0388: return false;
0389: }
0390: }
0391: return true;
0392: }
0393:
0394: private boolean handleYaCyHopAuthentication(httpHeader header) {
0395: // check if the user has allowed that his/her peer is used for hops
0396: if (!this .allowYaCyHop)
0397: return false;
0398:
0399: // proxy hops must identify with 4 criteria:
0400:
0401: // the accessed port must not be port 80
0402: String host = this .prop
0403: .getProperty(httpHeader.CONNECTION_PROP_HOST);
0404: if (host == null)
0405: return false;
0406: int pos;
0407: if ((pos = host.indexOf(":")) < 0) {
0408: // default port 80
0409: return false; // not allowed
0410: } else {
0411: if (Integer.parseInt(host.substring(pos + 1)) == 80)
0412: return false;
0413: }
0414:
0415: // the access path must be into the yacy protocol path; it must start with 'yacy'
0416: if (!(this .prop
0417: .getProperty(httpHeader.CONNECTION_PROP_PATH, "")
0418: .startsWith("/yacy/")))
0419: return false;
0420:
0421: // the accessing client must identify with user:password, where
0422: // user = addressed peer name
0423: // pw = addressed peer hash (b64-hash)
0424: String auth = (String) header.get(
0425: httpHeader.PROXY_AUTHORIZATION, "xxxxxx");
0426: String test = kelondroBase64Order.standardCoder
0427: .encodeString(yacyCore.seedDB.mySeed().getName() + ":"
0428: + yacyCore.seedDB.mySeed().hash);
0429: if (!test.equals(auth.trim().substring(6)))
0430: return false;
0431:
0432: // the accessing client must use a yacy user-agent
0433: if (!(((String) header.get(httpHeader.USER_AGENT, ""))
0434: .startsWith("yacy")))
0435: return false;
0436:
0437: // furthermore, YaCy hops must not exceed a specific access frequency
0438:
0439: // check access requester frequency: protection against DoS against this peer
0440: String requester = this .prop
0441: .getProperty(httpHeader.CONNECTION_PROP_CLIENTIP);
0442: if (requester == null)
0443: return false;
0444: if (lastAccessDelta(YaCyHopAccessRequester, requester) < 10000)
0445: return false;
0446: YaCyHopAccessRequester.put(requester, new Long(System
0447: .currentTimeMillis()));
0448:
0449: // check access target frequecy: protection against DoS from a single peer by several different requesters
0450: if (lastAccessDelta(YaCyHopAccessTargets, host) < 3000)
0451: return false;
0452: YaCyHopAccessTargets.put(host, new Long(System
0453: .currentTimeMillis()));
0454:
0455: // passed all tests
0456: return true;
0457: }
0458:
0459: private static long lastAccessDelta(
0460: HashMap<String, Long> accessTable, String domain) {
0461: Long lastAccess = accessTable.get(domain);
0462: if (lastAccess == null)
0463: return Long.MAX_VALUE; // never accessed
0464: return System.currentTimeMillis() - lastAccess.longValue();
0465: }
0466:
0467: private boolean handleProxyAuthentication(httpHeader header)
0468: throws IOException {
0469: // getting the http version that is used by the client
0470: String httpVersion = this .prop.getProperty("HTTP", "HTTP/0.9");
0471:
0472: // reading the authentication settings from switchboard
0473: if (this .proxyAccounts_init == false) {
0474: this .use_proxyAccounts = (switchboard.getConfig(
0475: "use_proxyAccounts", "false").equals("true") ? true
0476: : false);
0477: this .proxyAccounts_init = true; // is initialised
0478: }
0479:
0480: if (this .use_proxyAccounts) {
0481: String auth = (String) header.get(
0482: httpHeader.PROXY_AUTHORIZATION, "xxxxxx");
0483: userDB.Entry entry = switchboard.userDB
0484: .ipAuth(this .clientIP);
0485: if (entry == null) {
0486: entry = switchboard.userDB.proxyAuth(auth,
0487: this .clientIP);
0488: }
0489: if (entry != null) {
0490: int returncode = entry.surfRight();
0491: if (returncode == userDB.Entry.PROXY_ALLOK) {
0492: return true;
0493: }
0494: serverObjects tp = new serverObjects();
0495: if (returncode == userDB.Entry.PROXY_TIMELIMIT_REACHED) {
0496: tp.put("limit", "1");//time per day
0497: tp.put("limit_timelimit", entry.getTimeLimit());
0498: sendRespondError(this .prop, this .session.out, 403,
0499: "Internet-Timelimit reached", new File(
0500: "proxymsg/proxylimits.inc"), tp,
0501: null);
0502: } else if (returncode == userDB.Entry.PROXY_NORIGHT) {
0503: tp.put("limit", "0");
0504: sendRespondError(this .prop, this .session.out, 403,
0505: "Proxy use forbidden", new File(
0506: "proxymsg/proxylimits.inc"), tp,
0507: null);
0508: }
0509: return false;
0510: }
0511: // ask for authenticate
0512: this .session.out
0513: .write((httpVersion
0514: + " 407 Proxy Authentication Required"
0515: + serverCore.CRLF_STRING
0516: + httpHeader.PROXY_AUTHENTICATE
0517: + ": Basic realm=\"log-in\"" + serverCore.CRLF_STRING)
0518: .getBytes());
0519: this .session.out
0520: .write((httpHeader.CONTENT_LENGTH + ": 0\r\n")
0521: .getBytes());
0522: this .session.out.write("\r\n".getBytes());
0523: return false;
0524: }
0525:
0526: return true;
0527: }
0528:
0529: public Boolean UNKNOWN(String requestLine) throws IOException {
0530:
0531: int pos;
0532: String unknownCommand = null, args = null;
0533: if ((pos = requestLine.indexOf(" ")) > 0) {
0534: unknownCommand = requestLine.substring(0, pos);
0535: args = requestLine.substring(pos + 1);
0536: } else {
0537: unknownCommand = requestLine;
0538: args = "";
0539: }
0540:
0541: parseRequestLine(unknownCommand, args);
0542: //String httpVersion = this.prop.getProperty(httpHeader.CONNECTION_PROP_HTTP_VER,"HTTP/0.9");
0543:
0544: sendRespondError(this .prop, this .session.out, 0, 501, null,
0545: unknownCommand + " method not implemented", null);
0546: return serverCore.TERMINATE_CONNECTION;
0547: }
0548:
0549: public Boolean EMPTY(String arg) throws IOException {
0550: if (++this .emptyRequestCount > 10)
0551: return serverCore.TERMINATE_CONNECTION;
0552: return serverCore.RESUME_CONNECTION;
0553: }
0554:
0555: public Boolean TRACE(String arg) throws IOException {
0556: sendRespondError(this .prop, this .session.out, 0, 501, null,
0557: "TRACE method not implemented", null);
0558: return serverCore.TERMINATE_CONNECTION;
0559: }
0560:
0561: public Boolean OPTIONS(String arg) throws IOException {
0562: sendRespondError(this .prop, this .session.out, 0, 501, null,
0563: "OPTIONS method not implemented", null);
0564: return serverCore.TERMINATE_CONNECTION;
0565: }
0566:
0567: public Boolean GET(String arg) {
0568: try {
0569: // parsing the http request line
0570: parseRequestLine(httpHeader.METHOD_GET, arg);
0571:
0572: // we now know the HTTP version. depending on that, we read the header
0573: String httpVersion = this .prop.getProperty(
0574: httpHeader.CONNECTION_PROP_HTTP_VER,
0575: httpHeader.HTTP_VERSION_0_9);
0576: httpHeader header = (httpVersion
0577: .equals(httpHeader.HTTP_VERSION_0_9)) ? new httpHeader(
0578: reverseMappingCache)
0579: : httpHeader.readHeader(this .prop, this .session);
0580:
0581: // handling transparent proxy support
0582: httpHeader.handleTransparentProxySupport(header, this .prop,
0583: virtualHost, httpdProxyHandler.isTransparentProxy);
0584:
0585: // determines if the connection should be kept alive
0586: handlePersistentConnection(header);
0587:
0588: if (this .prop.getProperty(httpHeader.CONNECTION_PROP_HOST)
0589: .equals(virtualHost)) {
0590: // pass to server
0591: if (this .allowServer) {
0592: if (this .handleServerAuthentication(header)) {
0593: httpdFileHandler.doGet(this .prop, header,
0594: this .session.out);
0595: }
0596: } else {
0597: // not authorized through firewall blocking (ip does not match filter)
0598: this .session.out
0599: .write((httpVersion
0600: + " 403 refused (IP not granted)"
0601: + serverCore.CRLF_STRING
0602: + serverCore.CRLF_STRING
0603: + "you are not allowed to connect to this server, because you are using the non-granted IP "
0604: + clientIP
0605: + ". allowed are only connections that match with the following filter: "
0606: + switchboard.getConfig(
0607: "serverClient", "*") + serverCore.CRLF_STRING)
0608: .getBytes());
0609: return serverCore.TERMINATE_CONNECTION;
0610: }
0611: } else {
0612: // pass to proxy
0613: if (((this .allowYaCyHop) && (handleYaCyHopAuthentication(header)))
0614: || ((this .allowProxy) && (handleProxyAuthentication(header)))) {
0615: httpdProxyHandler.doGet(this .prop, header,
0616: this .session.out);
0617: } else {
0618: // not authorized through firewall blocking (ip does not match filter)
0619: this .session.out
0620: .write((httpVersion
0621: + " 403 refused (IP not granted)"
0622: + serverCore.CRLF_STRING
0623: + serverCore.CRLF_STRING
0624: + "you are not allowed to connect to this proxy, because you are using the non-granted IP "
0625: + clientIP
0626: + ". allowed are only connections that match with the following filter: "
0627: + switchboard.getConfig(
0628: "proxyClient", "*") + serverCore.CRLF_STRING)
0629: .getBytes());
0630: return serverCore.TERMINATE_CONNECTION;
0631: }
0632: }
0633:
0634: return this .prop.getProperty(
0635: httpHeader.CONNECTION_PROP_PERSISTENT).equals(
0636: "keep-alive") ? serverCore.RESUME_CONNECTION
0637: : serverCore.TERMINATE_CONNECTION;
0638: } catch (Exception e) {
0639: logUnexpectedError(e);
0640: return serverCore.TERMINATE_CONNECTION;
0641: } finally {
0642: this .doUserAccounting(this .prop);
0643: }
0644: }
0645:
0646: private void logUnexpectedError(Exception e) {
0647: if (e instanceof InterruptedException) {
0648: this .log.logInfo("Interruption detected");
0649: } else {
0650: String errorMsg = e.getMessage();
0651: if (errorMsg != null) {
0652: if (errorMsg.startsWith("Socket closed")) {
0653: this .log.logInfo("httpd shutdown detected ...");
0654: } else if ((errorMsg.startsWith("Broken pipe") || errorMsg
0655: .startsWith("Connection reset"))) {
0656: // client closed the connection, so we just end silently
0657: this .log
0658: .logInfo("Client unexpectedly closed connection");
0659: } else if (errorMsg.equals("400 Bad request")) {
0660: this .log.logInfo("Bad client request.");
0661: } else {
0662: this .log.logSevere("Unexpected Error. "
0663: + e.getClass().getName() + ": "
0664: + e.getMessage(), e);
0665: }
0666: } else {
0667: this .log.logSevere("Unexpected Error. "
0668: + e.getClass().getName(), e);
0669: }
0670: }
0671: }
0672:
0673: public Boolean HEAD(String arg) {
0674: try {
0675: parseRequestLine(httpHeader.METHOD_HEAD, arg);
0676:
0677: // we now know the HTTP version. depending on that, we read the header
0678: httpHeader header;
0679: String httpVersion = this .prop.getProperty(
0680: httpHeader.CONNECTION_PROP_HTTP_VER,
0681: httpHeader.HTTP_VERSION_0_9);
0682: if (httpVersion.equals(httpHeader.HTTP_VERSION_0_9))
0683: header = new httpHeader(reverseMappingCache);
0684: else
0685: header = httpHeader.readHeader(this .prop, this .session);
0686:
0687: // handle transparent proxy support
0688: httpHeader.handleTransparentProxySupport(header, this .prop,
0689: virtualHost, httpdProxyHandler.isTransparentProxy);
0690:
0691: // determines if the connection should be kept alive
0692: handlePersistentConnection(header);
0693:
0694: // return multi-line message
0695: if (this .prop.getProperty(httpHeader.CONNECTION_PROP_HOST)
0696: .equals(virtualHost)) {
0697: // pass to server
0698: if (allowServer) {
0699: if (handleServerAuthentication(header)) {
0700: httpdFileHandler.doHead(prop, header,
0701: this .session.out);
0702: }
0703: } else {
0704: // not authorized through firewall blocking (ip does not match filter)
0705: session.out
0706: .write((httpVersion
0707: + " 403 refused (IP not granted)" + serverCore.CRLF_STRING)
0708: .getBytes());
0709: return serverCore.TERMINATE_CONNECTION;
0710: }
0711: } else {
0712: // pass to proxy
0713: if (((this .allowYaCyHop) && (handleYaCyHopAuthentication(header)))
0714: || ((this .allowProxy) && (handleProxyAuthentication(header)))) {
0715: httpdProxyHandler.doHead(prop, header,
0716: this .session.out);
0717: } else {
0718: // not authorized through firewall blocking (ip does not match filter)
0719: session.out
0720: .write((httpVersion
0721: + " 403 refused (IP not granted)" + serverCore.CRLF_STRING)
0722: .getBytes());
0723: return serverCore.TERMINATE_CONNECTION;
0724: }
0725: }
0726: return this .prop.getProperty(
0727: httpHeader.CONNECTION_PROP_PERSISTENT).equals(
0728: "keep-alive") ? serverCore.RESUME_CONNECTION
0729: : serverCore.TERMINATE_CONNECTION;
0730: } catch (Exception e) {
0731: logUnexpectedError(e);
0732: return serverCore.TERMINATE_CONNECTION;
0733: } finally {
0734: this .doUserAccounting(this .prop);
0735: }
0736: }
0737:
0738: public Boolean POST(String arg) {
0739: try {
0740: parseRequestLine(httpHeader.METHOD_POST, arg);
0741:
0742: // we now know the HTTP version. depending on that, we read the header
0743: httpHeader header;
0744: String httpVersion = this .prop.getProperty(
0745: httpHeader.CONNECTION_PROP_HTTP_VER,
0746: httpHeader.HTTP_VERSION_0_9);
0747: if (httpVersion.equals(httpHeader.HTTP_VERSION_0_9))
0748: header = new httpHeader(reverseMappingCache);
0749: else
0750: header = httpHeader.readHeader(this .prop, this .session);
0751:
0752: // handle transparent proxy support
0753: httpHeader.handleTransparentProxySupport(header, this .prop,
0754: virtualHost, httpdProxyHandler.isTransparentProxy);
0755:
0756: // determines if the connection should be kept alive
0757: handlePersistentConnection(header);
0758:
0759: // return multi-line message
0760: if (prop.getProperty(httpHeader.CONNECTION_PROP_HOST)
0761: .equals(virtualHost)) {
0762: // pass to server
0763: if (allowServer) {
0764: if (handleServerAuthentication(header)) {
0765: httpdFileHandler.doPost(prop, header,
0766: this .session.out, this .session.in);
0767: }
0768: } else {
0769: // not authorized through firewall blocking (ip does not match filter)
0770: session.out
0771: .write((httpVersion
0772: + " 403 refused (IP not granted)"
0773: + serverCore.CRLF_STRING
0774: + serverCore.CRLF_STRING
0775: + "you are not allowed to connect to this server, because you are using the non-granted IP "
0776: + clientIP
0777: + ". allowed are only connections that match with the following filter: "
0778: + switchboard.getConfig(
0779: "serverClient", "*") + serverCore.CRLF_STRING)
0780: .getBytes());
0781: return serverCore.TERMINATE_CONNECTION;
0782: }
0783: } else {
0784: // pass to proxy
0785: if (((this .allowYaCyHop) && (handleYaCyHopAuthentication(header)))
0786: || ((this .allowProxy) && (handleProxyAuthentication(header)))) {
0787: httpdProxyHandler.doPost(prop, header,
0788: this .session.out, this .session.in);
0789: } else {
0790: // not authorized through firewall blocking (ip does not match filter)
0791: session.out
0792: .write((httpVersion
0793: + " 403 refused (IP not granted)"
0794: + serverCore.CRLF_STRING
0795: + serverCore.CRLF_STRING
0796: + "you are not allowed to connect to this proxy, because you are using the non-granted IP "
0797: + clientIP
0798: + ". allowed are only connections that match with the following filter: "
0799: + switchboard.getConfig(
0800: "proxyClient", "*") + serverCore.CRLF_STRING)
0801: .getBytes());
0802: return serverCore.TERMINATE_CONNECTION;
0803: }
0804: }
0805: //return serverCore.RESUME_CONNECTION;
0806: return this .prop.getProperty(
0807: httpHeader.CONNECTION_PROP_PERSISTENT).equals(
0808: "keep-alive") ? serverCore.RESUME_CONNECTION
0809: : serverCore.TERMINATE_CONNECTION;
0810: } catch (Exception e) {
0811: logUnexpectedError(e);
0812: return serverCore.TERMINATE_CONNECTION;
0813: } finally {
0814: this .doUserAccounting(this .prop);
0815: }
0816: }
0817:
0818: public Boolean CONNECT(String arg) throws IOException {
0819: // establish a ssh-tunneled http connection
0820: // this is to support https
0821:
0822: // parse HTTP version
0823: int pos = arg.indexOf(" ");
0824: String httpVersion = "HTTP/1.0";
0825: if (pos >= 0) {
0826: httpVersion = arg.substring(pos + 1);
0827: arg = arg.substring(0, pos);
0828: }
0829: prop.setProperty(httpHeader.CONNECTION_PROP_HTTP_VER,
0830: httpVersion);
0831:
0832: // parse hostname and port
0833: prop.setProperty(httpHeader.CONNECTION_PROP_HOST, arg);
0834: pos = arg.indexOf(":");
0835: int port = 443;
0836: if (pos >= 0) {
0837: port = Integer.parseInt(arg.substring(pos + 1));
0838: arg = arg.substring(0, pos);
0839: }
0840:
0841: // setting other connection properties
0842: prop.setProperty(httpHeader.CONNECTION_PROP_CLIENTIP,
0843: this .clientIP);
0844: prop.setProperty(httpHeader.CONNECTION_PROP_METHOD,
0845: httpHeader.METHOD_CONNECT);
0846: prop.setProperty(httpHeader.CONNECTION_PROP_PATH, "/");
0847: prop.setProperty(httpHeader.CONNECTION_PROP_EXT, "");
0848: prop.setProperty(httpHeader.CONNECTION_PROP_URL, "");
0849:
0850: // parse remaining lines
0851: httpHeader header = httpHeader.readHeader(this .prop,
0852: this .session);
0853:
0854: if (!(allowProxy)) {
0855: // not authorized through firewall blocking (ip does not match filter)
0856: session.out
0857: .write((httpVersion
0858: + " 403 refused (IP not granted)"
0859: + serverCore.CRLF_STRING
0860: + serverCore.CRLF_STRING
0861: + "you are not allowed to connect to this proxy, because you are using the non-granted IP "
0862: + clientIP
0863: + ". allowed are only connections that match with the following filter: "
0864: + switchboard.getConfig("proxyClient", "*") + serverCore.CRLF_STRING)
0865: .getBytes());
0866: return serverCore.TERMINATE_CONNECTION;
0867: }
0868:
0869: if (port != 443
0870: && switchboard.getConfig("secureHttps", "true").equals(
0871: "true")) {
0872: // security: connection only to ssl port
0873: // we send a 403 (forbidden) error back
0874: session.out.write((httpVersion
0875: + " 403 Connection to non-443 forbidden"
0876: + serverCore.CRLF_STRING + serverCore.CRLF_STRING)
0877: .getBytes());
0878: return serverCore.TERMINATE_CONNECTION;
0879: }
0880:
0881: // pass to proxy
0882: if (((this .allowYaCyHop) && (handleYaCyHopAuthentication(header)))
0883: || ((this .allowProxy) && (this
0884: .handleProxyAuthentication(header)))) {
0885: httpdProxyHandler.doConnect(prop, header, this .session.in,
0886: this .session.out);
0887: } else {
0888: // not authorized through firewall blocking (ip does not match filter)
0889: session.out
0890: .write((httpVersion
0891: + " 403 refused (IP not granted)"
0892: + serverCore.CRLF_STRING
0893: + serverCore.CRLF_STRING
0894: + "you are not allowed to connect to this proxy, because you are using the non-granted IP "
0895: + clientIP
0896: + ". allowed are only connections that match with the following filter: "
0897: + switchboard.getConfig("proxyClient", "*") + serverCore.CRLF_STRING)
0898: .getBytes());
0899: }
0900:
0901: return serverCore.TERMINATE_CONNECTION;
0902: }
0903:
0904: private final void parseRequestLine(String cmd, String s) {
0905:
0906: // parsing the header
0907: httpHeader.parseRequestLine(cmd, s, this .prop, virtualHost);
0908:
0909: // track the request
0910: String path = this .prop
0911: .getProperty(httpHeader.CONNECTION_PROP_URL);
0912: String args = this .prop.getProperty(
0913: httpHeader.CONNECTION_PROP_ARGS, "");
0914: switchboard.track(this .userAddress.getHostName(), (args
0915: .length() > 0) ? path + "?" + args : path);
0916:
0917: // reseting the empty request counter
0918: this .emptyRequestCount = 0;
0919:
0920: // counting the amount of received requests within this permanent conneciton
0921: this .prop.setProperty(
0922: httpHeader.CONNECTION_PROP_KEEP_ALIVE_COUNT, Integer
0923: .toString(++this .keepAliveRequestCount));
0924:
0925: // setting the client-IP
0926: this .prop.setProperty(httpHeader.CONNECTION_PROP_CLIENTIP,
0927: this .clientIP);
0928: }
0929:
0930: // some static methods that needs to be used from any CGI
0931: // and also by the httpdFileHandler
0932: // but this belongs to the protocol handler, this class.
0933:
0934: public static int parseArgs(serverObjects args, InputStream in,
0935: int length) throws IOException {
0936: // this is a quick hack using a previously coded parseMultipart based on a buffer
0937: // should be replaced sometime by a 'right' implementation
0938: byte[] buffer = null;
0939:
0940: // parsing post request bodies with a given length
0941: if (length != -1) {
0942: buffer = new byte[length];
0943: in.read(buffer);
0944: // parsing post request bodies which are gzip content-encoded
0945: } else {
0946: ByteArrayOutputStream bout = new ByteArrayOutputStream();
0947: serverFileUtils.copy(in, bout);
0948: buffer = bout.toByteArray();
0949: bout.close();
0950: bout = null;
0951: }
0952:
0953: int argc = parseArgs(args, new String(buffer, "UTF-8"));
0954: buffer = null;
0955: return argc;
0956: }
0957:
0958: public static int parseArgs(serverObjects args, String argsString) {
0959: // this parses a arg string that can either be attached to a URL query
0960: // or can be given as result of a post method
0961: // the String argsString is supposed to be constructed as
0962: // <key1>=<value1>'&'<key2>=<value2>'&'<key3>=<value3>
0963: // the calling function must strip off a possible leading '?' char
0964: if (argsString.length() == 0)
0965: return 0;
0966: argsString = argsString + "&"; // for technical reasons
0967: int sep;
0968: int eqp;
0969: int argc = 0;
0970: // Textfield1=default+value+Textfield+1&Textfield2=default+value+Textfield+2&selection1=sel1&selection2=othervalue1&selection2=sel2&selection3=sel3&Menu1=SubEnry11&radio1=button1&check1=button2&check1=button3&hidden1=&sButton1=enter+%281%29
0971: while (argsString.length() > 0) {
0972: eqp = argsString.indexOf("=");
0973: sep = argsString.indexOf("&");
0974: if ((eqp <= 0) || (sep <= 0))
0975: break;
0976: // resulting equations are inserted into the property args with leading '&'
0977: args.put(parseArg(argsString.substring(0, eqp)),
0978: parseArg(argsString.substring(eqp + 1, sep)));
0979: argsString = argsString.substring(sep + 1);
0980: argc++;
0981: }
0982: // we return the number of parsed arguments
0983: return argc;
0984: }
0985:
0986: /**
0987: * <p>This method basically does the same as {@link URLDecoder#decode(String, String) URLDecoder.decode(s, "UTF-8")}
0988: * would do with the exception of more lazyness in regard to current browser implementations as they do not
0989: * always comply with the standards.</p>
0990: * <p>The following replacements are performed on the input-<code>String</code>:</p>
0991: * <ul>
0992: * <li>'<code>+</code>'-characters are replaced by space
0993: * <li>(supbsequent (in the case of encoded unicode-chars)) '<code>%HH</code>'-entities are replaced by their
0994: * respective <code>char</code>-representation</li>
0995: * <li>'<code>%uHHHH</code>'-entities (sent by IE although rejected by the W3C) are replaced by their respective
0996: * <code>char</code>-representation</li>
0997: * <li><strong>TODO</strong>: <code>chars</code> already encoded in UTF-8 are url-encoded and re-decoded due to internal restrictions,
0998: * which slows down this method unnecessarily</li>
0999: * </ul>
1000: *
1001: * @param s the URL-encoded <code>String</code> to decode, note that the encoding used to URL-encode the original
1002: * <code>String</code> has to be UTF-8 (i.e. the "<code>accept-charset</code>"-property of HTML
1003: * <code><form></code>-elements)
1004: * @return the "normal" Java-<code>String</code> (UTF-8) represented by the input or <code>null</code>
1005: * if the passed argument <code>encoding</code> is not supported
1006: */
1007: private static String parseArg(String s) {
1008: int pos = 0;
1009: ByteArrayOutputStream baos = new ByteArrayOutputStream(s
1010: .length());
1011:
1012: while (pos < s.length()) {
1013: if (s.charAt(pos) == '+') {
1014: baos.write(' ');
1015: pos++;
1016: } else if (s.charAt(pos) == '%') {
1017: try {
1018: if (s.length() >= pos + 6
1019: && (s.charAt(pos + 1) == 'u' || s
1020: .charAt(pos + 1) == 'U')) {
1021: // non-standard encoding of IE for unicode-chars
1022: int bh = Integer.parseInt(s.substring(pos + 2,
1023: pos + 4), 16);
1024: int bl = Integer.parseInt(s.substring(pos + 4,
1025: pos + 6), 16);
1026: // TODO: needs conversion from UTF-16 to UTF-8
1027: baos.write(bh);
1028: baos.write(bl);
1029: pos += 6;
1030: } else if (s.length() >= pos + 3) {
1031: baos.write(Integer.parseInt(s.substring(
1032: pos + 1, pos + 3), 16));
1033: pos += 3;
1034: } else {
1035: baos.write(s.charAt(pos++));
1036: }
1037: } catch (NumberFormatException e) {
1038: baos.write(s.charAt(pos++));
1039: }
1040: } else if (s.charAt(pos) > 127) {
1041: // Unicode chars sent by client, see http://www.w3.org/International/O-URL-code.html
1042: try {
1043: // don't write anything but url-encode the unicode char
1044: s = s.substring(0, pos)
1045: + URLEncoder.encode(s.substring(pos,
1046: pos + 1), "UTF-8")
1047: + s.substring(pos + 1);
1048: } catch (UnsupportedEncodingException e) {
1049: return null;
1050: }
1051: } else {
1052: baos.write(s.charAt(pos++));
1053: }
1054: }
1055:
1056: try {
1057: return new String(baos.toByteArray(), "UTF-8");
1058: } catch (UnsupportedEncodingException e) {
1059: return null;
1060: }
1061: }
1062:
1063: // 06.01.2007: decode HTML entities by [FB]
1064: public static String decodeHtmlEntities(String s) {
1065: // replace all entities defined in wikiCode.characters and htmlentities
1066: s = htmlTools.decodeHtml2Unicode(s);
1067:
1068: // replace all other
1069: CharArrayWriter b = new CharArrayWriter(s.length());
1070: int end;
1071: for (int i = 0; i < s.length(); i++) {
1072: if (s.charAt(i) == '&' && (end = s.indexOf(';', i + 1)) > i) {
1073: if (s.charAt(i + 1) == '#') { // Ӓ symbols
1074: b.write(Integer.parseInt(s.substring(i + 2, end)));
1075: i += end - i;
1076: } else { // 'named' smybols
1077: serverLog.logFine("HTTPD",
1078: "discovered yet unimplemented HTML entity '"
1079: + s.substring(i, end + 1) + "'");
1080: b.write(s.charAt(i));
1081: }
1082: } else {
1083: b.write(s.charAt(i));
1084: }
1085: }
1086: return b.toString();
1087: }
1088:
1089: public static HashMap<String, byte[]> parseMultipart(
1090: httpHeader header, serverObjects args, InputStream in,
1091: int length) throws IOException {
1092: // this is a quick hack using a previously coded parseMultipart based on a buffer
1093: // should be replaced sometime by a 'right' implementation
1094:
1095: byte[] buffer = null;
1096:
1097: // parsing post request bodies with a given length
1098: if (length != -1) {
1099: buffer = new byte[length];
1100: int c, a = 0;
1101: while (a < length) {
1102: c = in.read(buffer, a, length - a);
1103: if (c <= 0)
1104: break;
1105: a += c;
1106: }
1107: // parsing post request bodies which are gzip content-encoded
1108: } else {
1109: serverByteBuffer bout = new serverByteBuffer();
1110: serverFileUtils.copy(in, bout);
1111: buffer = bout.getBytes();
1112: bout.close();
1113: bout = null;
1114: }
1115:
1116: //System.out.println("MULTIPART-BUFFER=" + new String(buffer));
1117: HashMap<String, byte[]> files = parseMultipart(header, args,
1118: buffer);
1119: buffer = null;
1120: return files;
1121: }
1122:
1123: public static HashMap<String, byte[]> parseMultipart(
1124: httpHeader header, serverObjects args, byte[] buffer)
1125: throws IOException {
1126: // we parse a multipart message and put results into the properties
1127: // find/identify boundary marker
1128: //System.out.println("DEBUG parseMultipart = <<" + new String(buffer) + ">>");
1129: String s = (String) header.get(httpHeader.CONTENT_TYPE);
1130: if (s == null)
1131: return null;
1132: int q;
1133: int p = s.toLowerCase().indexOf("boundary=");
1134: if (p < 0)
1135: throw new IOException(
1136: "boundary marker in multipart not found");
1137: // boundaries start with additional leading "--", see RFC1867
1138: byte[] boundary = ("--" + s.substring(p + 9)).getBytes();
1139:
1140: // eat up first boundary
1141: // the buffer must start with a boundary
1142: byte[] line = readLine(0, buffer);
1143: int pos = nextPos;
1144: if ((line == null)
1145: || (!(equals(line, 0, boundary, 0, boundary.length))))
1146: throw new IOException("boundary not recognized: "
1147: + ((line == null) ? "NULL" : new String(line,
1148: "UTF-8")) + ", boundary = "
1149: + new String(boundary));
1150:
1151: // we need some constants
1152: byte[] namec = (new String("name=")).getBytes();
1153: byte[] filenamec = (new String("filename=")).getBytes();
1154: //byte[] semicolonc = (new String(";")).getBytes();
1155: byte[] quotec = new byte[] { (byte) '"' };
1156:
1157: // now loop over boundaries
1158: byte[] name;
1159: byte[] filename;
1160: HashMap<String, byte[]> files = new HashMap<String, byte[]>();
1161: int argc = 0;
1162: //System.out.println("DEBUG: parsing multipart body:" + new String(buffer));
1163: while (pos < buffer.length) { // boundary enumerator
1164: // here the 'pos' marker points to the first line in a section after a boundary line
1165: line = readLine(pos, buffer);
1166: pos = nextPos;
1167: // termination if line is empty
1168: if (line.length == 0)
1169: break;
1170: // find name tag in line
1171: p = indexOf(0, line, namec);
1172: if (p < 0)
1173: throw new IOException(
1174: "tag name in marker section not found: '"
1175: + new String(line, "UTF-8") + "'"); // a name tag must always occur
1176: p += namec.length + 1; // first position of name value
1177: q = indexOf(p, line, quotec);
1178: if (q < 0)
1179: throw new IOException("missing quote in name tag: '"
1180: + new String(line, "UTF-8") + "'");
1181: name = new byte[q - p];
1182: java.lang.System.arraycopy(line, p, name, 0, q - p);
1183: // if this line has also a filename attribute, read it
1184: p = indexOf(q, line, filenamec);
1185: if (p > 0) {
1186: p += filenamec.length + 1; // first position of name value
1187: q = indexOf(p, line, quotec);
1188: if (q < 0)
1189: throw new IOException(
1190: "missing quote in filename tag: '"
1191: + new String(line) + "'");
1192: filename = new byte[q - p];
1193: java.lang.System.arraycopy(line, p, filename, 0, q - p);
1194: } else
1195: filename = null;
1196: // we have what we need. more information lines may follow, but we omit parsing them
1197: // we just skip until an empty line is reached
1198: while (pos < buffer.length) { // line skiping
1199: line = readLine(pos, buffer);
1200: pos = nextPos;
1201: if ((line == null) || (line.length == 0))
1202: break;
1203: }
1204: // depending on the filename tag exsistence, read now either a value for the name
1205: // or a complete uploaded file
1206: // to know the exact length of the value, we must identify the next boundary
1207: p = indexOf(pos, buffer, boundary);
1208:
1209: // if we can't find another boundary, then this is an error in the input
1210: if (p < 0) {
1211: serverLog
1212: .logSevere("HTTPD",
1213: "ERROR in PUT body: no ending boundary. probably missing values");
1214: break;
1215: }
1216:
1217: // we don't know if the value is terminated by LF, CR or CRLF
1218: // (it's suppose to be CRLF, but we want to be lazy about wrong terminations)
1219: if (buffer[p - 2] == serverCore.CR) // ERROR: IndexOutOfBounds: -2
1220: /* CRLF */q = p - 2;
1221: else
1222: /* CR or LF only */q = p - 1;
1223: // the above line is wrong if we uploaded a file that has a CR as it's last byte
1224: // and the client's line termination symbol is only a CR or LF (which would be incorrect)
1225: // the value is between 'pos' and 'q', while the next marker is 'p'
1226: line = new byte[q - pos];
1227: java.lang.System.arraycopy(buffer, pos, line, 0, q - pos);
1228: // in the 'line' variable we have now either a normal value or an uploadef file
1229: if (filename == null) {
1230: args.put(new String(name, "UTF-8"), new String(line,
1231: "UTF-8"));
1232: } else {
1233: // we store the file in a hashtable.
1234: // we use the same key to address the file in the hashtable as we
1235: // use to address the filename in the properties, but without leading '&'
1236: args.put(new String(name, "UTF-8"), new String(
1237: filename, "UTF-8"));
1238: files.put(new String(name, "UTF-8"), line);
1239: }
1240: argc++;
1241: // finally, read the next boundary line
1242: line = readLine(p, buffer);
1243: pos = nextPos;
1244: }
1245: header.put("ARGC", Integer.toString(argc)); // store argument count
1246: return files;
1247: }
1248:
1249: /*
1250: ------------1090358578442
1251: Content-Disposition: form-data; name="youare"
1252:
1253: Ty2F86ekSWM5
1254: ------------1090358578442
1255: Content-Disposition: form-data; name="key"
1256:
1257: 6EkPPOl7
1258: ------------1090358578442
1259: Content-Disposition: form-data; name="iam"
1260:
1261: HnTvzwV7SCJR
1262: ------------1090358578442
1263: Content-Disposition: form-data; name="process"
1264:
1265: permission
1266: ------------1090358578442
1267:
1268: */
1269:
1270: static int nextPos = -1;
1271:
1272: private static byte[] readLine(int start, byte[] array) {
1273: // read a string from an array; line ending is always CRLF
1274: // but we are also fuzzy with that: may also be only CR or LF
1275: // if no remaining CR, CRLF or LF can be found, return null
1276: if (start > array.length)
1277: return null;
1278: int pos = indexOf(start, array, serverCore.CRLF);
1279: nextPos = pos + 2;
1280: if (pos < 0) {
1281: pos = indexOf(start, array, new byte[] { serverCore.CR });
1282: nextPos = pos + 1;
1283: }
1284: if (pos < 0) {
1285: pos = indexOf(start, array, new byte[] { serverCore.LF });
1286: nextPos = pos + 1;
1287: }
1288: if (pos < 0) {
1289: nextPos = start;
1290: return null;
1291: }
1292: byte[] result = new byte[pos - start];
1293: java.lang.System
1294: .arraycopy(array, start, result, 0, pos - start);
1295: return result;
1296: }
1297:
1298: public static int indexOf(int start, byte[] array, byte[] pattern) {
1299: // return a position of a pattern in an array
1300: if (start > array.length - pattern.length)
1301: return -1;
1302: if (pattern.length == 0)
1303: return start;
1304: for (int pos = start; pos <= array.length - pattern.length; pos++)
1305: if ((array[pos] == pattern[0])
1306: && (equals(array, pos, pattern, 0, pattern.length)))
1307: return pos;
1308: return -1;
1309: }
1310:
1311: public static boolean equals(byte[] a, int aoff, byte[] b,
1312: int boff, int len) {
1313: //System.out.println("equals: a = " + new String(a) + ", aoff = " + aoff + ", b = " + new String(b) + ", boff = " + boff + ", length = " + len);
1314: if ((aoff + len > a.length) || (boff + len > b.length))
1315: return false;
1316: for (int i = 0; i < len; i++)
1317: if (a[aoff + i] != b[boff + i])
1318: return false;
1319: //System.out.println("TRUE!");
1320: return true;
1321: }
1322:
1323: public Object clone() {
1324: return new httpd(switchboard);
1325: }
1326:
1327: public static final void sendRespondBody(Properties conProp,
1328: OutputStream respond, byte[] body) throws IOException {
1329: respond.write(body);
1330: respond.flush();
1331: }
1332:
1333: public static final void sendRespondError(Properties conProp,
1334: OutputStream respond, int errorcase, int httpStatusCode,
1335: String httpStatusText, String detailedErrorMsg,
1336: Throwable stackTrace) throws IOException {
1337: sendRespondError(conProp, respond, errorcase, httpStatusCode,
1338: httpStatusText, detailedErrorMsg, null, null,
1339: stackTrace, null);
1340: }
1341:
1342: public static final void sendRespondError(Properties conProp,
1343: OutputStream respond, int httpStatusCode,
1344: String httpStatusText, File detailedErrorMsgFile,
1345: serverObjects detailedErrorMsgValues, Throwable stackTrace)
1346: throws IOException {
1347: sendRespondError(conProp, respond, 5, httpStatusCode,
1348: httpStatusText, null, detailedErrorMsgFile,
1349: detailedErrorMsgValues, stackTrace, null);
1350: }
1351:
1352: public static final void sendRespondError(Properties conProp,
1353: OutputStream respond, int errorcase, int httpStatusCode,
1354: String httpStatusText, String detailedErrorMsgText,
1355: Object detailedErrorMsgFile,
1356: serverObjects detailedErrorMsgValues, Throwable stackTrace,
1357: httpHeader header) throws IOException {
1358:
1359: FileInputStream fis = null;
1360: ByteArrayOutputStream o = null;
1361: try {
1362: // setting the proper http status message
1363: String httpVersion = conProp.getProperty(
1364: httpHeader.CONNECTION_PROP_HTTP_VER, "HTTP/1.1");
1365: if ((httpStatusText == null)
1366: || (httpStatusText.length() == 0)) {
1367: if (httpVersion.equals("HTTP/1.0")
1368: && httpHeader.http1_0.containsKey(Integer
1369: .toString(httpStatusCode)))
1370: httpStatusText = (String) httpHeader.http1_0
1371: .get(Integer.toString(httpStatusCode));
1372: else if (httpVersion.equals("HTTP/1.1")
1373: && httpHeader.http1_1.containsKey(Integer
1374: .toString(httpStatusCode)))
1375: httpStatusText = (String) httpHeader.http1_1
1376: .get(Integer.toString(httpStatusCode));
1377: else
1378: httpStatusText = "Unknown";
1379: }
1380:
1381: // generating the desired request url
1382: String host = conProp
1383: .getProperty(httpHeader.CONNECTION_PROP_HOST);
1384: String path = conProp.getProperty(
1385: httpHeader.CONNECTION_PROP_PATH, "/");
1386: String args = conProp
1387: .getProperty(httpHeader.CONNECTION_PROP_ARGS);
1388: String method = conProp
1389: .getProperty(httpHeader.CONNECTION_PROP_METHOD);
1390:
1391: int port = 80, pos = host.indexOf(":");
1392: if (pos != -1) {
1393: port = Integer.parseInt(host.substring(pos + 1));
1394: host = host.substring(0, pos);
1395: }
1396:
1397: String urlString;
1398: try {
1399: urlString = (new yacyURL((method
1400: .equals(httpHeader.METHOD_CONNECT) ? "https"
1401: : "http"), host, port, (args == null) ? path
1402: : path + "?" + args)).toString();
1403: } catch (MalformedURLException e) {
1404: urlString = "invalid URL";
1405: }
1406:
1407: // set rewrite values
1408: serverObjects tp = new serverObjects();
1409:
1410: // tp.put("host", serverCore.publicIP().getHostAddress());
1411: // tp.put("port", switchboard.getConfig("port", "8080"));
1412:
1413: String clientIP = conProp.getProperty(
1414: httpHeader.CONNECTION_PROP_CLIENTIP, "127.0.0.1");
1415:
1416: // check if ip is local ip address
1417: InetAddress hostAddress = serverDomains
1418: .dnsResolve(clientIP);
1419: if (hostAddress == null) {
1420: tp.put("host", serverDomains.myPublicLocalIP()
1421: .getHostAddress());
1422: tp.put("port", serverCore.getPortNr(switchboard
1423: .getConfig("port", "8080")));
1424: } else if (hostAddress.isSiteLocalAddress()
1425: || hostAddress.isLoopbackAddress()) {
1426: tp.put("host", serverDomains.myPublicLocalIP()
1427: .getHostAddress());
1428: tp.put("port", serverCore.getPortNr(switchboard
1429: .getConfig("port", "8080")));
1430: } else {
1431: tp.put("host", serverDomains.myPublicIP());
1432: tp
1433: .put(
1434: "port",
1435: (serverCore.portForwardingEnabled && (serverCore.portForwarding != null)) ? Integer
1436: .toString(serverCore.portForwarding
1437: .getPort())
1438: : Integer
1439: .toString(serverCore
1440: .getPortNr(switchboard
1441: .getConfig(
1442: "port",
1443: "8080"))));
1444: }
1445:
1446: // if peer has public address it will be used
1447: if (yacyCore.seedDB.mySeed().getPublicAddress() != null) {
1448: tp.put("extAddress", yacyCore.seedDB.mySeed()
1449: .getPublicAddress());
1450: }
1451: // otherwise the local ip address will be used
1452: else {
1453: tp.put("extAddress", tp.get("host", "127.0.0.1") + ":"
1454: + tp.get("port", "8080"));
1455: }
1456:
1457: tp.put("peerName", yacyCore.seedDB.mySeed().getName());
1458: tp.put("errorMessageType", errorcase);
1459: tp.put("httpStatus", Integer.toString(httpStatusCode) + " "
1460: + httpStatusText);
1461: tp.put("requestMethod", conProp
1462: .getProperty(httpHeader.CONNECTION_PROP_METHOD));
1463: tp.put("requestURL", urlString);
1464:
1465: switch (errorcase) {
1466: case ERRORCASE_MESSAGE:
1467: tp.put("errorMessageType_detailedErrorMsg",
1468: (detailedErrorMsgText == null) ? ""
1469: : detailedErrorMsgText.replaceAll("\n",
1470: "<br />"));
1471: break;
1472: case ERRORCASE_FILE:
1473: tp.put("errorMessageType_file",
1474: (detailedErrorMsgFile == null) ? ""
1475: : detailedErrorMsgFile.toString());
1476: if ((detailedErrorMsgValues != null)
1477: && (detailedErrorMsgValues.size() > 0)) {
1478: // rewriting the value-names and add the proper name prefix:
1479: Iterator<String> nameIter = detailedErrorMsgValues
1480: .keySet().iterator();
1481: while (nameIter.hasNext()) {
1482: String name = nameIter.next();
1483: tp.put("errorMessageType_" + name,
1484: detailedErrorMsgValues.get(name));
1485: }
1486: }
1487: break;
1488: default:
1489: break;
1490: }
1491:
1492: // building the stacktrace
1493: if (stackTrace != null) {
1494: tp.put("printStackTrace", 1);
1495: serverByteBuffer errorMsg = new serverByteBuffer(100);
1496: stackTrace.printStackTrace(new PrintStream(errorMsg));
1497: tp.put("printStackTrace_exception", stackTrace
1498: .toString());
1499: tp.put("printStackTrace_stacktrace", new String(
1500: errorMsg.getBytes(), "UTF-8"));
1501: } else {
1502: tp.put("printStackTrace", 0);
1503: }
1504:
1505: // Generated Tue, 23 Aug 2005 11:19:14 GMT by brain.wg (squid/2.5.STABLE3)
1506: // adding some system information
1507: String systemDate = httpc.dateString(httpc.nowDate());
1508: tp.put("date", systemDate);
1509:
1510: // rewrite the file
1511: File htRootPath = new File(switchboard.getRootPath(),
1512: switchboard.getConfig("htRootPath", "htroot"));
1513:
1514: httpTemplate.writeTemplate(fis = new FileInputStream(
1515: new File(htRootPath, "/proxymsg/error.html")),
1516: o = new ByteArrayOutputStream(), tp,
1517: "-UNRESOLVED_PATTERN-".getBytes());
1518: byte[] result = o.toByteArray();
1519: o.close();
1520: o = null;
1521:
1522: if (header == null)
1523: header = new httpHeader();
1524: header.put(httpHeader.DATE, systemDate);
1525: header.put(httpHeader.CONTENT_TYPE, "text/html");
1526: header.put(httpHeader.CONTENT_LENGTH, Integer
1527: .toString(result.length));
1528: header.put(httpHeader.PRAGMA, "no-cache");
1529: sendRespondHeader(conProp, respond, httpVersion,
1530: httpStatusCode, httpStatusText, header);
1531:
1532: if (!method.equals(httpHeader.METHOD_HEAD)) {
1533: // write the array to the client
1534: serverFileUtils.write(result, respond);
1535: }
1536: respond.flush();
1537: } catch (Exception e) {
1538: throw new IOException(e.getMessage());
1539: } finally {
1540: if (fis != null)
1541: try {
1542: fis.close();
1543: } catch (Exception e) {
1544: }
1545: if (o != null)
1546: try {
1547: o.close();
1548: } catch (Exception e) {
1549: }
1550: }
1551: }
1552:
1553: public static final void sendRespondHeader(Properties conProp,
1554: OutputStream respond, String httpVersion,
1555: int httpStatusCode, String httpStatusText,
1556: long contentLength) throws IOException {
1557: sendRespondHeader(conProp, respond, httpVersion,
1558: httpStatusCode, httpStatusText, null, contentLength,
1559: null, null, null, null, null);
1560: }
1561:
1562: public static final void sendRespondHeader(Properties conProp,
1563: OutputStream respond, String httpVersion,
1564: int httpStatusCode, String httpStatusText,
1565: String contentType, long contentLength, Date moddate,
1566: Date expires, httpHeader headers, String contentEnc,
1567: String transferEnc) throws IOException {
1568: sendRespondHeader(conProp, respond, httpVersion,
1569: httpStatusCode, httpStatusText, contentType,
1570: contentLength, moddate, expires, headers, contentEnc,
1571: transferEnc, true);
1572: }
1573:
1574: public static final void sendRespondHeader(Properties conProp,
1575: OutputStream respond, String httpVersion,
1576: int httpStatusCode, String httpStatusText,
1577: String contentType, long contentLength, Date moddate,
1578: Date expires, httpHeader headers, String contentEnc,
1579: String transferEnc, boolean nocache) throws IOException {
1580:
1581: String reqMethod = conProp
1582: .getProperty(httpHeader.CONNECTION_PROP_METHOD);
1583:
1584: if ((transferEnc != null)
1585: && !httpVersion.equals(httpHeader.HTTP_VERSION_1_1)) {
1586: throw new IllegalArgumentException(
1587: "Transfer encoding is only supported for http/1.1 connections. The current connection version is "
1588: + httpVersion);
1589: }
1590:
1591: if (!reqMethod.equals(httpHeader.METHOD_HEAD)) {
1592: if (!conProp.getProperty(
1593: httpHeader.CONNECTION_PROP_PERSISTENT, "close")
1594: .equals("close")) {
1595: if (transferEnc == null && contentLength < 0) {
1596: throw new IllegalArgumentException(
1597: "Message MUST contain a Content-Length or a non-identity transfer-coding header field.");
1598: }
1599: }
1600: if (transferEnc != null && contentLength >= 0) {
1601: throw new IllegalArgumentException(
1602: "Messages MUST NOT include both a Content-Length header field and a non-identity transfer-coding.");
1603: }
1604: }
1605:
1606: if (headers == null)
1607: headers = new httpHeader();
1608: Date now = new Date(System.currentTimeMillis());
1609:
1610: headers.put(httpHeader.SERVER, "AnomicHTTPD (www.anomic.de)");
1611: headers.put(httpHeader.DATE, httpc.dateString(now));
1612: if (moddate.after(now))
1613: moddate = now;
1614: headers
1615: .put(httpHeader.LAST_MODIFIED, httpc
1616: .dateString(moddate));
1617:
1618: if (nocache) {
1619: if (httpVersion.toUpperCase().equals(
1620: httpHeader.HTTP_VERSION_1_1))
1621: headers.put(httpHeader.CACHE_CONTROL, "no-cache");
1622: else
1623: headers.put(httpHeader.PRAGMA, "no-cache");
1624: }
1625:
1626: if (contentType == null)
1627: contentType = "text/html; charset=UTF-8";
1628: else if (contentType.startsWith("text/")
1629: && contentType.toLowerCase().indexOf("charset=") == -1)
1630: contentType += "; charset=UTF-8";
1631: headers.put(httpHeader.CONTENT_TYPE, contentType);
1632: if (contentLength > 0)
1633: headers.put(httpHeader.CONTENT_LENGTH, Long
1634: .toString(contentLength));
1635: //if (cookie != null) headers.put(httpHeader.SET_COOKIE, cookie);
1636: if (expires != null)
1637: headers.put(httpHeader.EXPIRES, httpc.dateString(expires));
1638: if (contentEnc != null)
1639: headers.put(httpHeader.CONTENT_ENCODING, contentEnc);
1640: if (transferEnc != null)
1641: headers.put(httpHeader.TRANSFER_ENCODING, transferEnc);
1642:
1643: sendRespondHeader(conProp, respond, httpVersion,
1644: httpStatusCode, httpStatusText, headers);
1645: }
1646:
1647: public static final void sendRespondHeader(Properties conProp,
1648: OutputStream respond, String httpVersion,
1649: int httpStatusCode, httpHeader header) throws IOException {
1650: sendRespondHeader(conProp, respond, httpVersion,
1651: httpStatusCode, null, header);
1652: }
1653:
1654: public static final void sendRespondHeader(Properties conProp,
1655: OutputStream respond, String httpVersion,
1656: int httpStatusCode, String httpStatusText, httpHeader header)
1657: throws IOException {
1658:
1659: if (respond == null)
1660: throw new NullPointerException(
1661: "The outputstream must not be null.");
1662: if (conProp == null)
1663: throw new NullPointerException(
1664: "The connection property structure must not be null.");
1665: if (httpVersion == null)
1666: httpVersion = conProp.getProperty(
1667: httpHeader.CONNECTION_PROP_HTTP_VER,
1668: httpHeader.HTTP_VERSION_1_1);
1669: if (header == null)
1670: header = new httpHeader();
1671:
1672: try {
1673: if ((httpStatusText == null)
1674: || (httpStatusText.length() == 0)) {
1675: if (httpVersion.equals(httpHeader.HTTP_VERSION_1_0)
1676: && httpHeader.http1_0.containsKey(Integer
1677: .toString(httpStatusCode)))
1678: httpStatusText = (String) httpHeader.http1_0
1679: .get(Integer.toString(httpStatusCode));
1680: else if (httpVersion
1681: .equals(httpHeader.HTTP_VERSION_1_1)
1682: && httpHeader.http1_1.containsKey(Integer
1683: .toString(httpStatusCode)))
1684: httpStatusText = (String) httpHeader.http1_1
1685: .get(Integer.toString(httpStatusCode));
1686: else
1687: httpStatusText = "Unknown";
1688: }
1689:
1690: StringBuffer headerStringBuffer = new StringBuffer(560);
1691:
1692: // "HTTP/0.9" does not have a status line or header in the response
1693: if (!httpVersion.toUpperCase().equals(
1694: httpHeader.HTTP_VERSION_0_9)) {
1695: // write status line
1696: headerStringBuffer.append(httpVersion).append(" ")
1697: .append(Integer.toString(httpStatusCode))
1698: .append(" ").append(httpStatusText).append(
1699: "\r\n");
1700:
1701: // prepare header
1702: if (!header.containsKey(httpHeader.DATE))
1703: header.put(httpHeader.DATE, httpc.dateString(httpc
1704: .nowDate()));
1705: if (!header.containsKey(httpHeader.CONTENT_TYPE))
1706: header.put(httpHeader.CONTENT_TYPE,
1707: "text/html; charset=UTF-8"); // fix this
1708: if (!header.containsKey(httpHeader.CONNECTION)
1709: && conProp
1710: .containsKey(httpHeader.CONNECTION_PROP_PERSISTENT))
1711: header
1712: .put(
1713: httpHeader.CONNECTION,
1714: conProp
1715: .getProperty(httpHeader.CONNECTION_PROP_PERSISTENT));
1716: if (!header.containsKey(httpHeader.PROXY_CONNECTION)
1717: && conProp
1718: .containsKey(httpHeader.CONNECTION_PROP_PERSISTENT))
1719: header
1720: .put(
1721: httpHeader.PROXY_CONNECTION,
1722: conProp
1723: .getProperty(httpHeader.CONNECTION_PROP_PERSISTENT));
1724:
1725: if (conProp
1726: .containsKey(httpHeader.CONNECTION_PROP_PERSISTENT)
1727: && conProp.getProperty(
1728: httpHeader.CONNECTION_PROP_PERSISTENT)
1729: .equals("keep-alive")
1730: && !header
1731: .containsKey(httpHeader.TRANSFER_ENCODING)
1732: && !header
1733: .containsKey(httpHeader.CONTENT_LENGTH))
1734: header.put(httpHeader.CONTENT_LENGTH, "0");
1735:
1736: // adding some yacy specific headers
1737: header
1738: .put(
1739: httpHeader.X_YACY_KEEP_ALIVE_REQUEST_COUNT,
1740: conProp
1741: .getProperty(httpHeader.CONNECTION_PROP_KEEP_ALIVE_COUNT));
1742: header
1743: .put(
1744: httpHeader.X_YACY_ORIGINAL_REQUEST_LINE,
1745: conProp
1746: .getProperty(httpHeader.CONNECTION_PROP_REQUESTLINE));
1747: header
1748: .put(
1749: httpHeader.X_YACY_PREVIOUS_REQUEST_LINE,
1750: conProp
1751: .getProperty(httpHeader.CONNECTION_PROP_PREV_REQUESTLINE));
1752:
1753: //read custom headers
1754: /*
1755: if (requestProperties != null)
1756: {
1757: httpHeader outgoingHeader=requestProperties.getOutgoingHeader();
1758: if (outgoingHeader!=null)
1759: {*/
1760: Iterator<httpHeader.Entry> it = header.getCookies();
1761: while (it.hasNext()) {
1762: //Append user properties to the main String
1763: //TODO: Should we check for user properites. What if they intersect properties that are already in header?
1764: httpHeader.Entry e = it.next();
1765: headerStringBuffer.append(e.getKey()).append(": ")
1766: .append(e.getValue()).append("\r\n");
1767: }
1768:
1769: /*
1770: }
1771: }*/
1772:
1773: // write header
1774: Iterator<String> i = header.keySet().iterator();
1775: String key;
1776: char tag;
1777: int count;
1778: //System.out.println("vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv");
1779: while (i.hasNext()) {
1780: key = i.next();
1781: tag = key.charAt(0);
1782: if ((tag != '*') && (tag != '#')) { // '#' in key is reserved for proxy attributes as artificial header values
1783: count = header.keyCount(key);
1784: for (int j = 0; j < count; j++) {
1785: headerStringBuffer.append(key).append(": ")
1786: .append(
1787: (String) header.getSingle(
1788: key, j)).append(
1789: "\r\n");
1790: }
1791: //System.out.println("#" + key + ": " + value);
1792: }
1793: }
1794:
1795: // end header
1796: headerStringBuffer.append("\r\n");
1797:
1798: // sending headers to the client
1799: respond.write(headerStringBuffer.toString().getBytes());
1800:
1801: // flush stream
1802: respond.flush();
1803: }
1804:
1805: conProp.put(
1806: httpHeader.CONNECTION_PROP_PROXY_RESPOND_HEADER,
1807: header);
1808: conProp.put(
1809: httpHeader.CONNECTION_PROP_PROXY_RESPOND_STATUS,
1810: Integer.toString(httpStatusCode));
1811: } catch (Exception e) {
1812: // any interruption may be caused be network error or because the user has closed
1813: // the windows during transmission. We simply pass it as IOException
1814: throw new IOException(e.getMessage());
1815: }
1816: }
1817:
1818: public static boolean shallTransportZipped(String path) {
1819: if ((path == null) || (path.length() == 0))
1820: return true;
1821:
1822: int pos;
1823: if ((pos = path.lastIndexOf(".")) != -1) {
1824: return !disallowZippedContentEncoding.contains(path
1825: .substring(pos).toLowerCase());
1826: }
1827: return true;
1828: }
1829:
1830: public void doUserAccounting(Properties conProps) {
1831: // TODO: validation of conprop fields
1832: // httpHeader.CONNECTION_PROP_USER
1833: // httpHeader.CONNECTION_PROP_CLIENTIP
1834: // httpHeader.CONNECTION_PROP_PROXY_RESPOND_SIZE
1835: // httpHeader.CONNECTION_PROP_PROXY_RESPOND_STATUS
1836: }
1837:
1838: public static boolean isThisHostPortForwardingIP(String hostName) {
1839: if ((hostName == null) || (hostName.length() == 0))
1840: return false;
1841: if ((!serverCore.portForwardingEnabled)
1842: || (serverCore.portForwarding == null))
1843: return false;
1844:
1845: boolean isThisHostIP = false;
1846: try {
1847: //InetAddress hostAddress = InetAddress.getByName(hostName);
1848: InetAddress hostAddress = serverDomains
1849: .dnsResolve(hostName);
1850: //InetAddress forwardingAddress = InetAddress.getByName(serverCore.portForwarding.getHost());
1851: InetAddress forwardingAddress = serverDomains
1852: .dnsResolve(serverCore.portForwarding.getHost());
1853:
1854: if ((hostAddress == null) || (forwardingAddress == null))
1855: return false;
1856: if (hostAddress.equals(forwardingAddress))
1857: return true;
1858: } catch (Exception e) {
1859: }
1860: return isThisHostIP;
1861: }
1862:
1863: public static boolean isThisSeedIP(String hostName) {
1864: if ((hostName == null) || (hostName.length() == 0))
1865: return false;
1866:
1867: // getting ip address and port of this seed
1868: yacySeed this Seed = yacyCore.seedDB.mySeed();
1869: String this SeedIP = this Seed.get(yacySeed.IP, null);
1870: String this SeedPort = this Seed.get(yacySeed.PORT, null);
1871:
1872: // resolve ip addresses
1873: if (this SeedIP == null || this SeedPort == null)
1874: return false;
1875: InetAddress seedInetAddress = serverDomains
1876: .dnsResolve(this SeedIP);
1877: InetAddress hostInetAddress = serverDomains
1878: .dnsResolve(hostName);
1879: if (seedInetAddress == null || hostInetAddress == null)
1880: return false;
1881:
1882: // if it's equal, the hostname points to this seed
1883: return (seedInetAddress.equals(hostInetAddress));
1884: }
1885:
1886: public static boolean isThisHostIP(String hostName) {
1887: if ((hostName == null) || (hostName.length() == 0))
1888: return false;
1889:
1890: boolean isThisHostIP = false;
1891: try {
1892: // final InetAddress clientAddress = InetAddress.getByName(hostName);
1893: final InetAddress clientAddress = serverDomains
1894: .dnsResolve(hostName);
1895: if (clientAddress == null)
1896: return false;
1897:
1898: if (clientAddress.isAnyLocalAddress()
1899: || clientAddress.isLoopbackAddress())
1900: return true;
1901:
1902: final InetAddress[] localAddress = InetAddress
1903: .getAllByName(InetAddress.getLocalHost()
1904: .getHostName());
1905: for (int i = 0; i < localAddress.length; i++) {
1906: if (localAddress[i].equals(clientAddress)) {
1907: isThisHostIP = true;
1908: break;
1909: }
1910: }
1911: } catch (Exception e) {
1912: }
1913: return isThisHostIP;
1914: }
1915:
1916: public static boolean isThisHostIP(InetAddress clientAddress) {
1917: if (clientAddress == null)
1918: return false;
1919:
1920: boolean isThisHostIP = false;
1921: try {
1922: if (clientAddress.isAnyLocalAddress()
1923: || clientAddress.isLoopbackAddress())
1924: return true;
1925:
1926: final InetAddress[] localAddress = InetAddress
1927: .getAllByName(InetAddress.getLocalHost()
1928: .getHostName());
1929: for (int i = 0; i < localAddress.length; i++) {
1930: if (localAddress[i].equals(clientAddress)) {
1931: isThisHostIP = true;
1932: break;
1933: }
1934: }
1935: } catch (Exception e) {
1936: }
1937: return isThisHostIP;
1938: }
1939:
1940: public static boolean isThisHostName(String hostName) {
1941: if ((hostName == null) || (hostName.length() == 0))
1942: return false;
1943:
1944: try {
1945: final int idx = hostName.indexOf(":");
1946: final String dstHost = (idx != -1) ? hostName.substring(0,
1947: idx).trim() : hostName.trim();
1948: final Integer dstPort = (idx != -1) ? Integer
1949: .valueOf(hostName.substring(idx + 1).trim())
1950: : new Integer(80);
1951:
1952: // if the hostname endswith thisPeerName.yacy ...
1953: if (dstHost.endsWith(yacyCore.seedDB.mySeed().getName()
1954: + ".yacy")) {
1955: return true;
1956: /*
1957: * If the port number is equal to the yacy port and the IP address is an address of this host ...
1958: * Please note that yacy is listening to all interfaces of this host
1959: */
1960: } else if (
1961: // check if the destination port is equal to the port yacy is listening to
1962: dstPort.equals(new Integer(serverCore.getPortNr(switchboard
1963: .getConfig("port", "8080"))))
1964: && (
1965: // check if the destination host is our local IP address
1966: isThisHostIP(dstHost) ||
1967: // check if the destination host is our seed ip address
1968: isThisSeedIP(dstHost))) {
1969: return true;
1970: } else if ((serverCore.portForwardingEnabled)
1971: && (serverCore.portForwarding != null)
1972: && (dstPort.intValue() == serverCore.portForwarding
1973: .getPort())
1974: && isThisHostPortForwardingIP(dstHost)) {
1975: return true;
1976: }
1977: } catch (Exception e) {
1978: }
1979: return false;
1980: }
1981: }
|