argius note

プログラミング関連

ローカル用ブックマーク

http://d.hatena.ne.jp/hyuki/20071220#bookmark
乗り遅れすぎですが、自分でもこういうの欲しかったので、作ってみました。とりあえず試作品。指定したURLのファイルしか取ってきません。文字コードによっては題名がちゃんと読めません。
簡易Webサーバを立てる方式にしてます。Javaで書きました。非常に書きづらいと分かっていたものの、自分で手っ取り早くサーバを書けるのがJavaしか無かったので。(Rubyだと、WEBrickというのを使えばできそうですね。)

起動は

$ java Bukma 80

で、ポート番号は可変になってます。※セキュリティ注意してください。

ブックマークレットで、

javascript:window.location='http://localhost:80/edit'

を設定。ここもポートを上のに合わせて。


ブックマークレットをたたくと、画像の上の画面が出ます。入力して送信すると、下の一覧が出ます。
リストはローカルのURLで読み直して使うようにしています。

import java.io.*;
import java.net.*;
import java.text.*;
import java.util.*;

public final class Bukma {

    private static final File FILE = new File("bookmarklist.html");
    private static final String CHARSET = "Shift_JIS";

    private static void outputForm(PrintWriter out, String url) throws IOException {
        out.print("<html><body>input comment.");
        out.print("<form method=\"get\" action=\"/register\">");
        out.printf("<input type=\"text\" name=\"title\" value=\"%s\" size=\"60\">",
                   getTitle(url));
        out.print("<br><input type=\"text\" name=\"comment\" size=\"60\">");
        out.printf("<input type=\"hidden\" name=\"url\" value=\"%s\">", url);
        out.print("<input type=\"submit\" value=\" REGISTER \">");
        out.print("</form></body></html>");
    }

    private static String getTitle(String url) throws IOException {
        HttpURLConnection conn = (HttpURLConnection)new URL(url).openConnection();
        String contentType = conn.getContentType();
        String enc;
        if (contentType.startsWith("text/html;charset=")) {
            enc = contentType.substring(18);
        } else {
            enc = CHARSET;
        }
        try {
            BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream(),
                                                                             enc));
            // あとでHTMLParserに書き換え
            StringBuffer buffer = new StringBuffer();
            for (String line; (line = reader.readLine()) != null;) {
                buffer.append(line);
                int index = line.toLowerCase().lastIndexOf("</title>");
                if (index >= 0) {
                    String s = buffer.toString().toLowerCase();
                    int index1 = s.indexOf("<title>") + 7;
                    int index2 = s.indexOf("</title>");
                    return buffer.substring(index1, index2);
                }
            }
            return "(unknown title)";
        } catch (UnsupportedOperationException ex) {
            ex.printStackTrace();
            throw new IOException(ex.toString());
        }
    }

    private static Properties getRemoteFiles(String url) throws IOException {
        Date now = new Date();
        String id = new SimpleDateFormat("yyyyMMdd-HHmmss-SSS").format(now);
        HttpURLConnection conn = (HttpURLConnection)new URL(url).openConnection();
        int httpStatus = conn.getResponseCode();
        if (httpStatus != HttpURLConnection.HTTP_OK) {
            throw new IOException("HTTP status returned not OK but "
                                  + httpStatus);
        }
        InputStream is = conn.getInputStream();
        File file = new File(id + ".html");
        if (file.exists()) {
            throw new IllegalStateException("already exists : " + file);
        }
        OutputStream os = new FileOutputStream(file);
        try {
            byte[] buffer = new byte[8192];
            for (int length; (length = is.read(buffer)) >= 0;) {
                os.write(buffer, 0, length);
            }
        } finally {
            os.close();
        }
        Properties props = new Properties();
        props.setProperty("id", id);
        props.setProperty("time",
                          new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(now));
        return props;
    }

    private static void outputResult(PrintWriter out) throws IOException {
        BufferedReader reader = new BufferedReader(new FileReader(FILE));
        try {
            for (String line; (line = reader.readLine()) != null;) {
                out.println(line);
            }
        } finally {
            reader.close();
        }
    }

    private static void appendRecord(Properties props) throws IOException {
        List<String> list = new ArrayList<String>();
        if (!FILE.exists()) {
            FILE.createNewFile();
        }
        BufferedReader reader = new BufferedReader(new FileReader(FILE));
        try {
            for (String line; (line = reader.readLine()) != null;) {
                if (line.startsWith("<ul")) {
                    list.add(line);
                }
            }
            System.out.println(list);
        } finally {
            reader.close();
        }
        PrintWriter out = new PrintWriter(new FileWriter(FILE));
        try {
            out.println("<html><body>");
            out.println("<style>.time { font-size: 75%; }</style>");
            out.println("<h1>Local Bookmarks</h1>");
            out.print("<ul><li>");
            out.printf("<a href=\"%s\">%s</a>",
                       new File(".", props.getProperty("id") + ".html").getPath(),
                       props.getProperty("title"));
            out.printf(" (<a href=\"%s\">source</a>)", props.getProperty("url"));
            out.printf(" <span class=\"time\">%s</span>",
                       props.getProperty("time"));
            out.print("</li>");
            out.print("<ul><li> ");
            out.print(props.getProperty("comment", ""));
            out.print("</li></ul>");
            out.println("</ul>");
            for (String record : list) {
                out.println(record);
            }
            out.println("</body></html>");
            out.flush();
        } finally {
            out.close();
        }
    }

    private static Properties parseQuery(String query) throws UnsupportedEncodingException {
        Properties props = new Properties();
        String[] a = query.split("\\?")[1].split("&");
        for (int i = 0; i < a.length; i++) {
            String[] kv = a[i].split("=");
            if (kv.length >= 2) {
                String value = URLDecoder.decode(kv[1], CHARSET);
                props.setProperty(kv[0], value);
            }
        }
        System.out.println(props);
        return props;
    }

    public static void main(String[] args) throws IOException {
        int port = (args.length > 0) ? Integer.parseInt(args[0]) : 80;
        ServerSocket serverSocket = new ServerSocket(port);
        while (true) {
            Socket socket = serverSocket.accept();
            try {
                InputStream is = socket.getInputStream();
                BufferedReader reader = new BufferedReader(new InputStreamReader(is));
                String query = "";
                String referer = "";
                while (true) {
                    String line = reader.readLine();
                    if (line == null || line.length() == 0) {
                        break;
                    }
                    System.out.println(line);
                    if (line.startsWith("GET")) {
                        query = line.split(" ")[1];
                    } else if (line.startsWith("Referer:")) {
                        referer = line.split(" ")[1];
                    }
                }
                PrintWriter out = new PrintWriter(socket.getOutputStream());
                out.println("HTTP/1.1 200 OK");
                out.println("Content-type: text/html;charset=" + CHARSET);
                out.println();
                if (query.startsWith("/register")) {
                    Properties params = parseQuery(query);
                    Properties props = getRemoteFiles(params.getProperty("url"));
                    props.putAll(params);
                    appendRecord(props);
                    outputResult(out);
                } else if (query.startsWith("/edit")) {
                    out.println();
                    outputForm(out, referer);
                } else {
                    outputResult(out);
                }
                out.flush();
            } finally {
                socket.close();
            }
        }
    }

}