/* * Copyright (c) 1996, 2004, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.net.www.protocol.gopher; import java.io.*; import java.util.*; import java.net.*; import sun.net.www.*; import sun.net.NetworkClient; import java.net.URL; import java.net.URLStreamHandler; import sun.security.action.GetBooleanAction; /** Class to maintain the state of a gopher fetch and handle the protocol */ public class GopherClient extends NetworkClient implements Runnable { /* The following three data members are left in for binary * backwards-compatibility. Unfortunately, HotJava sets them directly * when it wants to change the settings. The new design has us not * cache these, so this is unnecessary, but eliminating the data members * would break HJB 1.1 under JDK 1.2. * * These data members are not used, and their values are meaningless. * REMIND: Take them out for JDK 2.0! */ /** * @deprecated */ @Deprecated public static boolean useGopherProxy; /** * @deprecated */ @Deprecated public static String gopherProxyHost; /** * @deprecated */ @Deprecated public static int gopherProxyPort; static { useGopherProxy = java.security.AccessController.doPrivileged( new sun.security.action.GetBooleanAction("gopherProxySet")) .booleanValue(); gopherProxyHost = java.security.AccessController.doPrivileged( new sun.security.action.GetPropertyAction("gopherProxyHost")); gopherProxyPort = java.security.AccessController.doPrivileged( new sun.security.action.GetIntegerAction("gopherProxyPort", 80)) .intValue(); } PipedOutputStream os; URL u; int gtype; String gkey; sun.net.www.URLConnection connection; GopherClient(sun.net.www.URLConnection connection) { this.connection = connection; } /** * @return true if gopher connections should go through a proxy, according * to system properties. */ public static boolean getUseGopherProxy() { return java.security.AccessController.doPrivileged( new GetBooleanAction("gopherProxySet")).booleanValue(); } /** * @return the proxy host to use, or null if nothing is set. */ public static String getGopherProxyHost() { String host = java.security.AccessController.doPrivileged( new sun.security.action.GetPropertyAction("gopherProxyHost")); if ("".equals(host)) { host = null; } return host; } /** * @return the proxy port to use. Will default reasonably. */ public static int getGopherProxyPort() { return java.security.AccessController.doPrivileged( new sun.security.action.GetIntegerAction("gopherProxyPort", 80)) .intValue(); } /** Given a url, setup to fetch the gopher document it refers to */ InputStream openStream(URL u) throws IOException { this.u = u; this.os = os; int i = 0; String s = u.getFile(); int limit = s.length(); int c = '1'; while (i < limit && (c = s.charAt(i)) == '/') i++; gtype = c == '/' ? '1' : c; if (i < limit) i++; gkey = s.substring(i); openServer(u.getHost(), u.getPort() <= 0 ? 70 : u.getPort()); MessageHeader msgh = new MessageHeader(); switch (gtype) { case '0': case '7': msgh.add("content-type", "text/plain"); break; case '1': msgh.add("content-type", "text/html"); break; case 'g': case 'I': msgh.add("content-type", "image/gif"); break; default: msgh.add("content-type", "content/unknown"); break; } if (gtype != '7') { serverOutput.print(decodePercent(gkey) + "\r\n"); serverOutput.flush(); } else if ((i = gkey.indexOf('?')) >= 0) { serverOutput.print(decodePercent(gkey.substring(0, i) + "\t" + gkey.substring(i + 1) + "\r\n")); serverOutput.flush(); msgh.add("content-type", "text/html"); } else { msgh.add("content-type", "text/html"); } connection.setProperties(msgh); if (msgh.findValue("content-type") == "text/html") { os = new PipedOutputStream(); PipedInputStream ret = new PipedInputStream(); ret.connect(os); new Thread(this).start(); return ret; } return new GopherInputStream(this, serverInput); } /** Translate all the instances of %NN into the character they represent */ private String decodePercent(String s) { if (s == null || s.indexOf('%') < 0) return s; int limit = s.length(); char d[] = new char[limit]; int dp = 0; for (int sp = 0; sp < limit; sp++) { int c = s.charAt(sp); if (c == '%' && sp + 2 < limit) { int s1 = s.charAt(sp + 1); int s2 = s.charAt(sp + 2); if ('0' <= s1 && s1 <= '9') s1 = s1 - '0'; else if ('a' <= s1 && s1 <= 'f') s1 = s1 - 'a' + 10; else if ('A' <= s1 && s1 <= 'F') s1 = s1 - 'A' + 10; else s1 = -1; if ('0' <= s2 && s2 <= '9') s2 = s2 - '0'; else if ('a' <= s2 && s2 <= 'f') s2 = s2 - 'a' + 10; else if ('A' <= s2 && s2 <= 'F') s2 = s2 - 'A' + 10; else s2 = -1; if (s1 >= 0 && s2 >= 0) { c = (s1 << 4) | s2; sp += 2; } } d[dp++] = (char) c; } return new String(d, 0, dp); } /** Turn special characters into the %NN form */ private String encodePercent(String s) { if (s == null) return s; int limit = s.length(); char d[] = null; int dp = 0; for (int sp = 0; sp < limit; sp++) { int c = s.charAt(sp); if (c <= ' ' || c == '"' || c == '%') { if (d == null) d = s.toCharArray(); if (dp + 3 >= d.length) { char nd[] = new char[dp + 10]; System.arraycopy(d, 0, nd, 0, dp); d = nd; } d[dp] = '%'; int dig = (c >> 4) & 0xF; d[dp + 1] = (char) (dig < 10 ? '0' + dig : 'A' - 10 + dig); dig = c & 0xF; d[dp + 2] = (char) (dig < 10 ? '0' + dig : 'A' - 10 + dig); dp += 3; } else { if (d != null) { if (dp >= d.length) { char nd[] = new char[dp + 10]; System.arraycopy(d, 0, nd, 0, dp); d = nd; } d[dp] = (char) c; } dp++; } } return d == null ? s : new String(d, 0, dp); } /** This method is run as a seperate thread when an incoming gopher document requires translation to html */ public void run() { int qpos = -1; try { if (gtype == '7' && (qpos = gkey.indexOf('?')) < 0) { PrintStream ps = new PrintStream(os, false, encoding); ps.print("