Source Code Cross Referenced for httpd.java in  » Search-Engine » yacy » de » anomic » http » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Search Engine » yacy » de.anomic.http 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


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>&lt;form&gt;</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) == '#') { // &#1234; 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:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.