| 28 | | my $ua = LWP::UserAgent->new(); |
|---|
| 29 | | |
|---|
| 30 | | my $res = $ua->post($cfg->{cybozu_url} . '?page=SyncCalendar', { |
|---|
| 31 | | _System => 'login', |
|---|
| 32 | | _Login => '1', |
|---|
| 33 | | defined $cfg->{username} ? (_Account => $cfg->{username}) : (), |
|---|
| 34 | | defined $cfg->{userid} ? (_Id => $cfg->{userid}) : (), |
|---|
| 35 | | Password => $cfg->{password} || '', |
|---|
| 36 | | 'csv' => 1, |
|---|
| 37 | | 'notimecard' => 1, |
|---|
| 38 | | }); |
|---|
| 39 | | |
|---|
| 40 | | die "Cannot access Cybozu Office 6 (" . $cfg->{cybozu_url} . ")." |
|---|
| 41 | | unless $res->is_success; |
|---|
| 42 | | |
|---|
| 43 | | my $content = $res->content; |
|---|
| 44 | | from_to($content, $cfg->{input_encoding} || 'shiftjis', 'utf8'); |
|---|
| 45 | | my @lines = grep /^\d+,ts\.\d+,/, split(/\r?\n/, $content); |
|---|
| 46 | | |
|---|
| 47 | | my $tzid = $cfg->{time_zone} || 'Asia/Tokyo'; |
|---|
| 48 | | my $tz = DateTime::TimeZone->new(name => $tzid); |
|---|
| | 28 | my $time_zone = $cfg->{time_zone} || 'Asia/Tokyo'; |
|---|
| 70 | | |
|---|
| 71 | | if (my $freq = $fields[7]) { |
|---|
| 72 | | # TODO: handle recurrent events |
|---|
| 73 | | warn "Recurrent events[$freq] cannot be handled."; |
|---|
| | 42 | my %args = ( |
|---|
| | 43 | summary => decode_utf8($item->{summary}), |
|---|
| | 44 | description => decode_utf8($item->{description}), |
|---|
| | 45 | created => to_icaldate($item->{created}), |
|---|
| | 46 | dtstamp => to_icaldate($item->{modified}) |
|---|
| | 47 | ); |
|---|
| | 48 | |
|---|
| | 49 | if ($item->{is_full_day}) { |
|---|
| | 50 | $args{dtstart} = [to_icaldate($item->{start}, 1), { VALUE => 'DATE' }]; |
|---|
| | 51 | $args{dtend} = [to_icaldate($item->{end}, 1), { VALUE => 'DATE' }]; |
|---|
| 75 | | my ($dtstart, $dtend); |
|---|
| 76 | | my $dt0 = cydate2dt($fields[3], $fields[5], $tz); |
|---|
| 77 | | my $dt1 = cydate2dt($fields[4], $fields[6], $tz); |
|---|
| 78 | | if ($fields[5] =~ /^:$/) { |
|---|
| 79 | | $dtstart = $dt0->ymd(''); |
|---|
| 80 | | $dtend = $dt1->add(days => 1)->ymd(''); |
|---|
| 81 | | } else { |
|---|
| 82 | | $dtstart = dt2ical($dt0); |
|---|
| 83 | | $dtend = dt2ical(($fields[6] =~ /^:$/) ? |
|---|
| 84 | | $dt0->add(minutes => 10) : $dt1); |
|---|
| 85 | | } |
|---|
| 86 | | $vevent->add_properties( |
|---|
| 87 | | dtstart => [$dtstart, { TZID => $tzid }], |
|---|
| 88 | | dtend => [$dtend, { TZID => $tzid }] |
|---|
| 89 | | ); |
|---|
| | 53 | $args{dtstart} = [to_icaldate($item->{start}, 0), { TZID => $time_zone }]; |
|---|
| | 54 | $args{dtend} = [to_icaldate($item->{end}, 0), { TZID => $time_zone }]; |
|---|
| 92 | | my($epoch) = $fields[1] =~ m/ts\.(\d+)/; |
|---|
| 93 | | my $created = dt2ical(DateTime->from_epoch(epoch => $epoch)); |
|---|
| 94 | | |
|---|
| 95 | | my $summary = $fields[11] || ''; |
|---|
| 96 | | $summary .= ': ' if $summary; |
|---|
| 97 | | $summary .= $fields[12] || ''; |
|---|
| 98 | | $vevent->add_properties( |
|---|
| 99 | | summary => decode_utf8($summary), |
|---|
| 100 | | description => decode_utf8($fields[13] || $summary), |
|---|
| 101 | | dtstamp => $dtstamp, |
|---|
| 102 | | created => $created |
|---|
| 103 | | ); |
|---|
| 104 | | |
|---|
| | 57 | # handle frequency |
|---|
| | 58 | if ($item->{frequency}) { |
|---|
| | 59 | my $freq = $item->{frequency}; |
|---|
| | 60 | my %rrules = $freq ne 'WEEKDAYS' ? |
|---|
| | 61 | ( FREQ => $freq ) : |
|---|
| | 62 | ( FREQ => 'WEEKLY', WKST => 'SU', BYDAY => 'MO,TU,WE,TH,FR' ); |
|---|
| | 63 | $rrules{UNTIL} = to_icaldate($item->{until}, 1) if $item->{until}; |
|---|
| | 64 | $args{rrule} = join ';', map { $_ . '=' . $rrules{$_} } keys %rrules; |
|---|
| | 65 | } |
|---|
| | 66 | |
|---|
| | 67 | $vevent->add_properties(%args); |
|---|
| 114 | | my $dt = cydate2dt("1970/01/01", "00:00:00", $tz); |
|---|
| 115 | | my $offset = DateTime::TimeZone::offset_as_string($dt->offset) || '+0900'; |
|---|
| 116 | | my $tzname = $cfg->{tzname} || $tz->short_name_for_datetime($dt) || 'JST'; |
|---|
| | 77 | my $std = DateTime->new(year => 1970, month => 1, day => 1, |
|---|
| | 78 | hour => 0, minute => 0, second => 0, |
|---|
| | 79 | time_zone => $time_zone); |
|---|
| | 80 | my $offset = DateTime::TimeZone::offset_as_string($std->offset) || '+0900'; |
|---|
| | 81 | my $tzname = $cfg->{tzname} || 'JST'; |
|---|
| 128 | | my $enc = $cfg->{output_encoding} || 'utf8'; |
|---|
| 129 | | print $enc eq 'ncr' ? |
|---|
| 130 | | encode_ncr($vcalendar->as_string) : |
|---|
| 131 | | encode($enc, $vcalendar->as_string); |
|---|
| 132 | | |
|---|
| 133 | | sub encode_ncr { |
|---|
| 134 | | my $text = shift; |
|---|
| 135 | | $text =~ s/(\P{ASCII})/sprintf("&#%d;", ord($1))/eg; |
|---|
| | 93 | print encode_($cfg->{output_encoding} || 'utf8', $vcalendar->as_string); |
|---|
| | 94 | |
|---|
| | 95 | sub to_icaldate { |
|---|
| | 96 | my($dt, $is_full_day) = @_; |
|---|
| | 97 | $is_full_day ? |
|---|
| | 98 | $dt->ymd('') : |
|---|
| | 99 | $dt->ymd('') . 'T' . $dt->hms('') . ($dt->time_zone->is_utc ? 'Z' : ''); |
|---|
| | 100 | } |
|---|
| | 101 | |
|---|
| | 102 | sub encode_ { |
|---|
| | 103 | my($enc, $text) = @_; |
|---|
| | 104 | if ($enc eq 'ncr') { |
|---|
| | 105 | $text =~ s/(\P{ASCII})/sprintf("&#%d;", ord($1))/eg; |
|---|
| | 106 | } else { |
|---|
| | 107 | $text = encode($enc, $text); |
|---|
| | 108 | } |
|---|
| 139 | | sub cydate2dt { |
|---|
| 140 | | my($date, $time, $tz) = @_; |
|---|
| 141 | | my @d = split("/", $date); |
|---|
| 142 | | my @t = split(":", $time); |
|---|
| 143 | | |
|---|
| 144 | | my $dt = DateTime->new( |
|---|
| 145 | | year => $d[0], month => $d[1], day => $d[2], |
|---|
| 146 | | hour => $t[0] || 0, minute => $t[1] || 0, second => $t[2] || 0, |
|---|
| 147 | | time_zone => $tz || 'Asia/Tokyo' |
|---|
| 148 | | ); |
|---|
| 149 | | return $dt; |
|---|
| 150 | | } |
|---|
| 151 | | |
|---|
| 152 | | sub dt2ical { |
|---|
| 153 | | my($dt) = @_; |
|---|
| 154 | | $dt->ymd('') . 'T' . $dt->hms('') . ($dt->time_zone->is_utc ? 'Z' : ''); |
|---|
| 155 | | } |
|---|
| 156 | | |
|---|