#!/usr/bin/env ruby # $Id: zical.rb,v 1.16 2003/09/14 02:25:30 root Exp $ # Copyright (C) 2003 Hiroyuki KUROSAKI # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # $KCODE = 'U' require 'qte' require 'qpe' require "qtexml" include Qte include Qpe include Qtexml require 'thread' require 'socket' require 'jcode' class Time def Time.from_iso8601(str) year = month = day = nil hour = min = sec = 0 is_utc = false if /^(\d{4})(\d{2})(\d{2})(T(\d{2})(\d{2})(\d{2})(Z?))?$/ =~ str then year = $1; month = $2; day = $3 if $4 then hour = $5; min = $6; sec = $7 is_utc = $8 end end if year && month && day then if is_utc then Time.utc(year, month, day, hour, min, sec) else Time.local(year, month, day, hour, min, sec) end else nil end end def to_iso8601(date_part_only = false) formatstr = "%Y%m%d" t = self if date_part_only then if self.utc? then t = self.localtime end else formatstr << "T%H%M%S" if self.utc? then formatstr << "Z" end end t.strftime(formatstr) end def to_iso8601_date to_iso8601(true) end end def ary_ary_to_ary_hash(ary_ary) rows = Array.new hashkeys = Array.new first_line = true ary_ary.each do |row_ary| if first_line then hashkeys = row_ary first_line = false else row_hash = Hash.new for i in (0 .. (row_ary.size - 1)) do row_hash[hashkeys[i]] = row_ary[i] end rows.push(row_hash) end end rows end class SimpleCSV def SimpleCSV.split_line(line) sep = "\x1c" s = line.dup + ',' s.gsub(/("([^"]|"")*"|[^,]*),/, "\\1#{sep}"). gsub(/"([^#{sep}]*)"#{sep}/, "\\1#{sep}"). gsub(/""/, '"').split(sep) end def SimpleCSV.split_lines(lines) rows = Array.new copied_lines = lines.dup while copied_lines.size > 0 do row = copied_lines.shift while ((row.length - row.delete('"').length) % 2) != 0 do #" row << copied_lines.shift end rows.push(SimpleCSV.split_line(row.chomp.sub(/\r$/, ''))) end rows end def SimpleCSV.parse(lines) ary_ary_to_ary_hash(SimpleCSV.split_lines(lines)) end def SimpleCSV.join(row) str = '' sep = '' row.each do |col| str << sep + col.gsub(/"/, '""').sub(/^(.*[",\r\n].*)$/, '"\1"') sep = ',' end str end end module ZiCal PROGNAME = 'ZiCal' VERSION = '0.0.1' PRODID = "-//noir//#{PROGNAME} #{VERSION}//EN" DEFAULT_PORT = 8990 ConvTable_SlA300Event_to_VEvent = { 'SUMMARY' => [:make_summary, ['description', 'location']], 'LOCATION' => [nil, ['location']], 'DESCRIPTION' => [nil, ['note']], 'DTSTART' => [:make_dtstart, ['type', 'start']], 'DTEND' => [:make_dtend, ['type', 'end', 'rtype', 'rfreq', 'rhasenddate', 'enddt']], 'RRULE' => [:make_rrule, ['type', 'rtype', 'rposition', 'rfreq', 'start', 'rweekdays', 'rhasenddate', 'enddt']] } ConvTable_SlA300ToDo_to_VToDo = { 'SUMMARY' => [nil, ['Description']], 'DTSTART' => [:ymd, ['StartDateYear', 'StartDateMonth', 'StartDateDay']], 'DUE' => [:ymd, ['DateYear', 'DateMonth', 'DateDay']], 'COMPLETED' => [:ymd, ['EndDateYear', 'EndDateMonth', 'EndDateDay']], 'STATUS' => [:make_status, ['Completed']], 'PRIORITY' => [nil, ['Priority']] } ConvTable_SlEvent_to_VEvent = { 'SUMMARY' => [:make_summary, ['DSRP', 'PLCE']], 'LOCATION' => [nil, ['PLCE']], 'DESCRIPTION' => [nil, ['MEM1']], 'DTSTART' => [:make_dtstart2,['ADAY', 'TIM1']], 'DTEND' => [:make_dtend2, ['ADAY', 'TIM2', 'RTYP', 'RFRQ', 'REND', 'REDT']], 'RRULE' => [:make_rrule2, ['ADAY', 'RTYP', 'RPOS', 'RFRQ', 'TIM1', 'RDYS', 'REND', 'REDT']] } ConvTable_SlToDo_to_VToDo = { 'SUMMARY' => [nil, ['TITL']], 'DTSTART' => [:iso8601local, ['ETDY']], 'DUE' => [:iso8601local, ['LTDY']], 'COMPLETED' => [:make_completed, ['FNDY', 'MARK']], 'STATUS' => [:make_status2, ['MARK']], 'PRIORITY' => [nil, ['PRTY']] } Sl_to_SlA300 = { 'type' => { '1' => 'AllDay' }, 'rtype' => { '0' => 'Daily', '1' => 'Weekly', '2' => 'MonthlyDay', '3' => 'MonthlyDate', '4' => 'Yearly' } } WDAYS = %w(SU MO TU WE TH FR SA) WDAYS_MO = WDAYS[1, 6] << WDAYS[0] class ICalendarItemConv def initialize(tab, itemName) @tab = tab @itemName = itemName end def make_summary(description, location) if location && location.size > 0 then description + '@' + location else description end end def ut2iso8601(ut) if ut then Time.at(ut.to_i).to_iso8601 else nil end end def ut2iso8601_date(ut) if ut then Time.at(ut.to_i).to_iso8601_date else nil end end def make_dtstart(type0, start) if type0 && type0 == 'AllDay' then ut2iso8601_date(start) else ut2iso8601(start) end end def make_dtstart2(aday, start) make_dtstart(Sl_to_SlA300['type'][aday], Time.from_iso8601(start).localtime.to_i) end def allday_daily_hasenddate?(type0, rtype, rfreq, rhasenddate, enddt) if type0 && type0 == 'AllDay' && rtype && rtype == 'Daily' && rfreq && rfreq == "1" && rhasenddate && rhasenddate == "1" && enddt && !enddt.empty? then true else false end end def make_dtend(type0, end0, rtype, rfreq, rhasenddate, enddt) if allday_daily_hasenddate?(type0, rtype, rfreq, rhasenddate, enddt) then Time.at(enddt.to_i + 60 * 60 * 24).to_iso8601_date elsif end0 && !end0.empty? then if type0 && type0 == 'AllDay' then ut2iso8601_date(end0) else ut2iso8601(end0) end else nil end end def is_valid_enddt?(rhasenddate, enddt) if enddt && !enddt.empty? && rhasenddate && rhasenddate == '1' then true else false end end def make_dtend2(type0, end0, rtype, rfreq, rhasenddate, enddt) enddt_sec = nil if is_valid_enddt?(rhasenddate, enddt) then enddt_sec = Time.from_iso8601(enddt).to_i.to_s end make_dtend(Sl_to_SlA300['type'][type0], (end0 && !end0.empty? ? Time.from_iso8601(end0).to_i.to_s : nil), Sl_to_SlA300['rtype'][rtype], rfreq, rhasenddate, enddt_sec) end def make_rrule(type0, rtype, rposition, rfreq, start, rweekdays, rhasenddate, enddt) rrule = nil if rtype then rrule = Hash.new case rtype when 'Daily', 'Weekly', 'Yearly' rrule['FREQ'] = rtype.upcase when 'MonthlyDate' rrule['FREQ'] = 'MONTHLY' end if rfreq then rrule['INTERVAL'] = rfreq end if rtype && rtype == 'MonthlyDay' then if rrule['INTERVAL'] == "12" then rrule['FREQ'] = 'YEARLY' rrule['INTERVAL'] = "1" else rrule['FREQ'] = 'MONTHLY' end start_time = Time.at(start.to_i) rrule['BYDAY'] = (((start_time.mday - 1) / 7) + 1).to_s + WDAYS[start_time.wday] if rposition && !rposition.empty? then rrule['BYSETPOS'] = rposition else rrule['BYMONTH'] = start_time.month.to_s end end if rtype && rtype == 'Weekly' then wdaybits = rweekdays.to_i.chr.unpack('b7')[0].split(//) byday = [] WDAYS_MO.each_with_index {|wd, i| if wdaybits[i] == "1" then byday.push(wd) end } rrule['BYDAY'] = byday.join(',') end if rhasenddate && rhasenddate == "1" then rrule['UNTIL'] = ut2iso8601_date(enddt) + "T235960" end if allday_daily_hasenddate?(type0, rtype, rfreq, rhasenddate, enddt) then nil else rrule.to_a.collect {|key, val| "#{key}=#{val}" }.join(';') end else nil end end def make_rrule2(type0, rtype, rposition, rfreq, start, rweekdays, rhasenddate, enddt) enddt_sec = nil if is_valid_enddt?(rhasenddate, enddt) then enddt_sec = Time.from_iso8601(enddt).to_i.to_s end make_rrule(Sl_to_SlA300['type'][type0], Sl_to_SlA300['rtype'][rtype], rfreq, Time.from_iso8601(start).to_i.to_s, rweekdays, rhasenddate, enddt_sec) end def ymd(y, m, d) if y && m && d then sprintf("%04d%02d%02d", y.to_i, m.to_i, d.to_i) else nil end end def iso8601local(iso8601utc) Time.from_iso8601(iso8601utc).localtime.to_iso8601_date end def make_completed(iso8601utc, mark) if mark && mark == '0' then iso8601local(iso8601utc) else nil end end def make_status(completed) if completed && completed == '1' then 'COMPLETED' else nil end end def make_status2(mark) if mark && mark == '0' then 'COMPLETED' else nil end end def convert(obj) ret = ICalendarItem.new(@itemName) @tab.each {|key, func| if !func[0] then ret[key] = obj[func[1][0]] else args = func[1].collect {|xkey| obj[xkey]} ret[key] = send(func[0], *args) end } ret end end class ICalendarItem 0 then fold("#{key}:#{val_s}") else nil end }.compact) ret.push("END:#{@itemName}") ret.join("\r\n") + "\r\n" end end class ICalendar 0 then attrs_h[key] = val_str end end } @itemVisited.sendWith(@conv.convert(attrs_h)) end true end def endElement(namespaceURI, localname, qName) true end end class SlA300PIMParser 404 Not Found

404 Not Found

\r\n") soc.close else $stderr.print "OK.\n" soc.write("HTTP/1.1 200 OK\r\n") soc.write("Date: " + Time.now.utc.strftime("%a, %d %b %Y %H:%M:%S %Z") + "\r\n") soc.write("Server: #{PROGNAME} #{VERSION}\r\n") soc.write("Connection: close\r\n") # soc.write("Last-Modified: " + Time.now.utc.strftime("%a, %d %b %Y %H:%M:%S %Z") + "\r\n") soc.write("Content-Length: " + (@icalendar_lines.join("\r\n") + "\r\n").to_s.size.to_s + "\r\n") soc.write("Content-Type: text/calendar\r\n\r\n") @icalendar_lines.each {|line| soc.write(line + "\r\n") } soc.close end rescue soc.close end end end # class GUIServer