ほげノート(5)
はて前回の更新部分、コメントで気付いたんですが .htaccess とか好きにできちゃう。
http://d.hatena.ne.jp/hogelog/comment?date=20070404#c
とりあえずは .htaccess のパーミッションを 404 にして対処しました。
さらに問題はそれだけでもなかった。page.cgiに直接アクセスすれば、「hoge<>」みたいな、普通にはアクセスできないファイルもつくれたり。
ここまできて「.htaccessにアクセスされたら困るからパーミッション変えておく」とか「変なファイル名がなあ」とかにいちいち対処するのではなく、そもそもデータファイルのファイル名を(encodeURI()でエスケープされない範囲では)利用者が好きに決められる仕組みがそもそも間違ってたのだ、と考えた。
タイトルの全部をエスケープすりゃいいかな、と思いそうしてみた。
http://konbu.s13.xrea.com/lib/ajax/page4/page.html
今回の更新にあたり、高度な JavaScript 技集の、UTF-8 , UTF16 変換ライブラリを参考にさせていただきました。
page.html
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <link rel="stylesheet" type="text/css" href="page.css" media="screen" /> <title> めもちょう。 </title> <script type="text/javascript" src="jquery.js"></script> <script type="text/javascript"><!-- function escapeUTF8(page_name){ var escaped_name = String(), c; for(i=0;i<page_name.length;++i){ c = page_name.charCodeAt(i); if ((c >= 0x0001) && (c <= 0x007F)) { escaped_name += "%"+c.toString(16); } else if (c > 0x07FF){ escaped_name += "%"+(0xE0 | ((c >> 12) & 0x0F)).toString(16); escaped_name += "%"+(0x80 | ((c >> 6) & 0x3F)).toString(16); escaped_name += "%"+(0x80 | ((c >> 0) & 0x3F)).toString(16); } else { escaped_name += "%"+(0xC0 | ((c >> 6) & 0x1F)).toString(16); escaped_name += "%"+(0x80 | ((c >> 0) & 0x3F)).toString(16); } } return escaped_name; } function loadpage(page_name){ if(!page_name) page_name = $("#page_name").text(); $("#httpdump").load(datadir+encodeURI(escapeUTF8(page_name)), function(text, status){ $("#page")[0].value = text; $("#page_name").text(page_name); document.title = page_name+" - メモ帳"; print_flush("status", "loaded."); }); } function savepage(page_name){ var txtdata = $("#page")[0].value; $("#httpdump").load("page.cgi", {mode: "save", page_name: page_name, txtdata: txtdata}, function(text, status){ print_flush("status", "saved."); }); } function editevent(event,tid){ var page_name = $("#page_name").text(); $("#status").text("text edited..."); clearTimeout(tid); return window.setTimeout('savepage("'+page_name+'")',2000); } function openindexbox(){ var indexbox; $("#httpdump").load("page.cgi",{mode: "index"}, function(text, status){ indexbox = decodeURIComponent(text); indexbox = indexbox.replace(/&/g, "&"); indexbox = indexbox.replace(/</g, "<"); indexbox = indexbox.replace(/>/g, ">"); indexbox = indexbox.replace(/"/g, """); indexbox = indexbox.replace(/(.+)\n/g,"<a onclick='selectpage(\"$1\");' href='#'>$1</a><br>"); $("#indexlist").html(indexbox); $("#indexbox").show(); $("#status").text(""); } ); } function selectpage(page_name){ loadpage(page_name); $("#indexbox").hide(); } function savepageas(){ var page_name = $("#savepagename")[0].value; savepage(page_name); $("#page_name").text(page_name); document.title = page_name; $("#savepagename")[0].value = ""; $("#savebox").hide(); } function print_flush(id, str){ document.getElementById(id).innerHTML = str; window.setTimeout('document.getElementById("'+id+'").innerHTML = ""', 2000); } var tid; var datadir = "data/"; var xbegin, ybegin, xmove, ymove, moveflag; var xpoint = 100, ypoint = 100, notex = 100, notey = 100; $(function(){ $("#openindexbox").click(function(){openindexbox()}); $("#closeindexbox").click(function(){$("#indexbox").hide()}); $("#opensavebox").click(function(){$("#savebox").show()}); $("#closesavebox").click(function(){$("#savebox").hide()}); $("#page").keypress(function(event){tid=editevent(event,tid)}); $("#httpdump").ajaxStart(function(){$("#status").text("Wait...")}); $(".closewindow").click(function(){ $("#note").hide(); $("#note_mini").show(); notex = xpoint; notey = ypoint; xpoint = ypoint = 0; $("#note_mini")[0].style["left"] = "0px"; $("#note_mini")[0].style["top"] = "0px"; }); $(".maxwindow").click(function(){ $("#note").show(); $("#note_mini").hide(); xpoint = notex; ypoint = notey; $("#note")[0].style["left"] = xpoint+"px"; $("#note")[0].style["top"] = ypoint+"px"; }); $(".titlebar").mousedown(function(event){ xbegin=event.pageX; ybegin=event.pageY; movewindow=$(this).parent().parent(); moveflag=true; }); $().mousemove(function(event){if(moveflag){ xmove = (event.pageX-xbegin); ymove = (event.pageY-ybegin); movewindow[0].style["left"] = xpoint+xmove+"px"; movewindow[0].style["top"] = ypoint+ymove+"px"; }}); $().mouseup(function(event){if(moveflag){ xmove = (event.pageX-xbegin); ymove = (event.pageY-ybegin); movewindow[0].style["left"] = xpoint+xmove+"px"; movewindow[0].style["top"] = ypoint+ymove+"px"; xpoint += xmove; ypoint += ymove; moveflag = false; }}); }); // --> </script> </head> <body> <table id="note"> <tr class="titlebar"> <td class="titlebar_title"> <span id="page_name">無題</span> - メモ帳 </td> <td class="titlebar_button"> <input type="submit" value="□" class="maxwindow"> <input type="submit" value="×" class="closewindow"> </td> </tr> <tr class="note_menu"><td colspan="2"> <a class="menu_item" id="openindexbox" href="#">他のページ開く</a> <div class="childbox" id="indexbox"> <div id="indexlist"></div> <input type="submit" id="closeindexbox" value="やっぱやめる"> </div> <a class="menu_item" id="opensavebox" href="#">別名で保存</a> <div class="childbox" id="savebox"> <input type="text" id="savepagename"> <input type="submit" onclick="savepageas()" value="保存"><br> <input type="submit" id="closesavebox" value="やっぱやめる"> </div> </td></tr> <tr class="note_txt"><td colspan="2"> <textarea id="page" rows="15" cols="80"></textarea> </td></tr> <tr class="note_status"><td colspan="2"><span id="status"></span></td></tr> </table> <table id="note_mini"> <tr class="titlebar"> <td class="minititlebar_title">メモ帳</td> <td class="titlebar_button"> <input type="submit" value="□" class="maxwindow"> <input type="submit" value="×" class="closewindow"> </td> </tr> </table> <textarea id="httpdump"></textarea> <script type="text/javascript"><!-- $(document).ready(function(){ loadpage(); }); // --> </script> </body> </html>
ちなみに、htmlな中身のテキストデータを読み込ませると、ちゃんと読めなかったのは
<pre id="httpdump"></pre>
なんて部分があったせい。textareaにしておいた。
page.cgi
#!/usr/bin/perl -w use strict; use CGI; my $cgi = new CGI; print "Content-Type: text/plain; charset=utf-8\n\n"; my $datadir = "data"; my $mode = $cgi->param('mode'); my $page_name = $cgi->param('page_name'); my $txtdata = $cgi->param('txtdata'); mkdir $datadir if(!(-e $datadir)); chdir $datadir; if(!defined $mode){ print "Bye."; } elsif($mode eq 'save' && defined $page_name && defined $txtdata){ $page_name = escapeUTF8($page_name); open PAGE, ">$page_name"; print PAGE $txtdata; close PAGE; print "saved."; } elsif($mode eq 'index'){ opendir DIR, "."; for(readdir DIR){ if(-T $_ && ($_ ne ".htaccess" and $_ ne ".htpasswd") != 0){ print "$_\n" } } closedir DIR; } sub escapeUTF8{ my $page_name = shift; $page_name =~ s/(.)/"%" . unpack("H2", $1)/eg; return $page_name; }
page.css
@charset "utf-8"; /* ほげほげ */ body{ background-color:#efeff0; color:#004624; } a:link,a:visited{ color:#004624; } a:active,a:hover{ font-weight:bold; } #page{ } #pageindex{ } #status{ } #httpdump{ display:none; } #note{ background:#c9c9c9; border:solid 1px; width:42em; position:absolute; left:100px; top:100px; } #note_mini{ background:#c9c9c9; border:solid 1px; width:10em; display:none; position:absolute; left:0px; top:0px; } .childbox{ position:absolute; z-index:1; border:solid 1px; background:#c9c9c9; display:none; padding:2px 2em 5px 2px; } .titlebar{ background:#3939a0; color:white; text-align:left; border: 0px; } .titlebar_title{ width:39em; } .minititlebar_title{ width:6em; } .titlebar_button{ color:black; font:bold; text-align:right; width:45px; background:#c9c9c9; } .titlebar_button input{ width:20px; height:20px; } .note_status{ height:1.5em; }