iCalでカレンダーを公開/公開中止/照会するためのCGIを書いてみた
CalDAVって、あんなにでっかいソースで何をしているんだろうか←ソースを読め
とりあえず、mod_actionsが有効ならば使えるはず。RFC2445はざっと読んだだけなので、細かいところは修正が必要かも。iCalのicsフィアルは扱えるはず。
確認したのは、Apache 2.2.8, Ruby 1.8.6, iCal 3.0.4。
calcgi.rb
#!/usr/bin/ruby # $Id$ require "cgi" class CalCGI < CGI attr("put_content") attr("calendar") def cal_initialize_query() if ("POST" == env_table['REQUEST_METHOD']) and %r|\Amultipart/form-data.*boundary=\"?([^\";,]+)\"?|n.match(env_table['CONTENT_TYPE']) boundary = $1.dup @multipart = true @params = read_multipart(boundary, Integer(env_table['CONTENT_LENGTH'])) elsif ("PUT" == env_table['REQUEST_METHOD']) # @put_content = read_from_cmdline @put_content = readlines if ("text/calendar" == env_table['CONTENT_TYPE']) @calendar = rfc2445parse(@put_content) end else @multipart = false @params = CGI::parse( case env_table['REQUEST_METHOD'] when "GET", "HEAD" if defined?(MOD_RUBY) Apache::request.args or "" else env_table['QUERY_STRING'] or "" end when "POST" stdinput.binmode if defined? stdinput.binmode stdinput.read(Integer(env_table['CONTENT_LENGTH'])) or '' else read_from_cmdline end ) end @cookies = CGI::Cookie::parse((env_table['HTTP_COOKIE'] or env_table['COOKIE'])) end private :cal_initialize_query def rfc2445parse(content) cal_data = Hash.new([].freeze) target_data = Hash.new() stacked_data = [] stacked_name = [] target_name = "" content = connect_content_line(content) content.each do |line| key, value, param_hash = split_content_line(line) if "BEGIN" == key if target_name != "" # top stacked_data.push(target_data.dup) stacked_name.push(target_name) end target_data = {value => Hash.new()} target_name = value elsif "END" == key if stacked_data.size > 0 if stacked_data[-1][stacked_name[-1]].include?(value) stacked_data[-1][stacked_name[-1]][value].push(target_data[value].dup) else stacked_data[-1][stacked_name[-1]][value] = [target_data[value].dup] end target_data = stacked_data.pop target_name = stacked_name.pop end else target_data[target_name][key] = [value, param_hash] end end cal_data = target_data end private :rfc2445parse def connect_content_line(content) connected_content = [] content.each do |line| #double quoteが複数にまたがったら嫌なので、先につなげておく line.chomp! if /^(\s)(.*)$/ =~ line connected_content[-1] += $2 # spaceは、一文字だけ取り除くのでstripは不可 else connected_content.push(line) end end connected_content end private :connect_content_line def split_content_line(line) key = "" value = "" param_list = Array.new() param_hash = Hash.new() in_quote = false key_gotten = false setting_param = false line.split(//).each do |c| if key_gotten value += c elsif in_quote if c == '"' in_quote = false end else if c == ":" key_gotten = true next elsif c == '"' in_quote = true elsif c == ',' param_list.push("") elsif c == ';' setting_param = true param_list.push("") else if setting_param param_list[-1] += c else key += c end end end end param_list.each do |a_param| pkey, pvalue = a_param.split("=",2) param_hash[pkey] = pvalue end param_hash = nil if param_hash.size == 0 [key, value, param_hash] end private :split_content_line def initialize() if defined?(MOD_RUBY) && !ENV.key?("GATEWAY_INTERFACE") Apache.request.setup_cgi_env end extend QueryExtension @multipart = false cal_initialize_query() # set @params, @cookies end end
calendar.cgi
#!/usr/bin/ruby # $Id$ require "calcgi" Ok_result_header = {"nph" => false, "status" => "OK", "type" => "text/calendar", "charset" => "UTF-8"} Not_found_result_header = {"nph" => false, "status" => "NOT_FOUND", "type" => "text/calendar", "charset" => "UTF-8"} Base_dir = "/var/db/apache2/calendar" def result_put(cgi) file = File.open(Base_dir + ENV["PATH_INFO"], "w") file.puts(cgi.put_content) file.close cgi.out(Ok_result_header){""} end def result_get(cgi) if File.exist?(Base_dir + ENV["PATH_INFO"]) file = File.open(Base_dir + ENV["PATH_INFO"], "r") cgi.out(Ok_result_header) do file.gets(nil) end file.close else cgi.out(Not_found_result_header) {""} end end def result_delete(cgi) if File.exist?(Base_dir + ENV["PATH_INFO"]) File.delete(Base_dir + ENV["PATH_INFO"]) cgi.out(Ok_result_header) {""} else cgi.out(Not_found_result_header) {""} end end cgi = CalCGI.new() case cgi.request_method when "GET" result_get(cgi) when "PUT" result_put(cgi) when "DELETE" result_delete(cgi) end
以下、Leopardでの話なので、別環境の人は適当に読み替えて。
/etc/apache2/other/calendar.confを、以下の内容で作成。
<IfModule actions_module> Action text/calendar cgi-bin/calendar.cgi </IfModule>
ディレクトリ、/var/db/apache2/calendar を作成し、ownerをwww:wwwに変更。
/Library/WebServer/CGI-Executables の下に、calcgi.rb, calendar.cgi を置く。これもownerはwww:wwwで。
iCalでのカレンダーの公開では、公開先を「個人用サーバ」にして、ベース URLを http://your.web.server/cgi-bin/calendar.cgi にする。認証については、詳しくないしいい加減な答えになるかもしれないので、別の人にきいてみて。とりあえず、ローカルで使う限りは何もしなくてもいけるはず。
iCalでのカレンダーの照会は、URLを http://your.web.server/cgi-bin/calendar.cgi/calendar_name.ics にすればいけるはず。
と、こんなかんじで。