/usr/share/perl5/Cal/DAV.pm is in libcal-dav-perl 0.6-2.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 | package Cal::DAV;
use strict;
use Data::ICal;
use HTTP::DAV;
our $VERSION="0.6";
=head1 NAME
Cal::DAV - a CalDAV client
=head1 SYNOPSIS
my $cal = Cal::DAV->new( user => $user, pass => $pass, url => $url);
# the ics data will be fetched automatically if it's there
# ... or you can parse some ics
$cal->parse(filename => $data);
# cal now has all the methods of Data::ICal
# you can now monkey around with the object
# saves the updated calendar
$cal->save;
# deletes the calendar
$cal->delete;
# lock the file on the server
$cal->lock;
# unlock the file on the server
$cal->unlock
# steal the lock
$cal->steal_lock;
# also
$cal->forcefully_unlock_all
# and
$cal->lockdiscovery
# resyncs it with the server
$cal->get;
# Get the underlying HTTP::DAV object
my $dav = $cal->dav;
=head1 DESCRIPTION
C<Cal::DAV> is actually a very thin wrapper round C<HTTP::DAV> and
C<Data::ICal> but it may gain more functionality later and, in the mean
time, serves as something that
=head1 TESTING
In order to test you need to define three environment variables:
C<CAL_DAV_USER>, C<CAL_DAV_PASS> and C<CAL_DAV_URL_BASE> which
points to a DAV collection that the user supplied has write
permissions for.
It should be noted that, at the moment, I'm having problems finding
a CalDAV server that allows me to create files and so I can't run all
the tests.
=head1 METHODS
=cut
=head2 new <arg[s]>
Must have at least C<user>, C<pass> and C<url> args where
C<url> is the url of a remote, DAV accessible C<.ics> file.
Can optionally take an C<auto_commit> option. See C<auto_commit()> method below.
=cut
# TODO if we remove the option to do operations with other urls
# we could then cache the resource object
sub new {
my $class = shift;
my %args = @_;
my %opts;
for (qw(user pass url)) {
die "You must pass in a $_ param\n" unless defined $args{$_};
$opts{"-${_}"} = $args{$_};
}
my $dav = HTTP::DAV->new;
$dav->credentials(%opts);
return bless { _dav => $dav, url => $args{url}, _auto_commit => $args{auto_commit} }, $class;
}
=head2 parse <arg[s]>
Make a new calendar object using same arguments as C<Data::ICal>'s C<new()> or C<parse()> methods.
Does not auto save for you.
Returns 1 on success and 0 on failure.
=cut
sub parse {
my $self = shift;
my %args = @_;
$self->{_cal} = Data::ICal->new(%args);
return (defined $self->{_cal}) ?
$self->dav->ok("Loaded data successfully") :
$self->dav->err('ERR_GENERIC', "Failed to load calendar: parse error $@");
}
=head2 save [url]
Save the calendar back to the server (or optionally to another path).
Returns 1 on success and 0 on failure.
=cut
sub save {
my $self = shift;
my $url = shift || $self->{url};
my $cal = $self->{_cal}; # TODO should this be cal()
return 1 unless defined $cal;
my $res = $self->dav->new_resource( -uri => $url );
#unless ($self->{_fetched}) {
#my $ret = $res->mkcol;
#unless ($ret->is_success) {
# return $self->dav->err( 'ERR_RESP_FAIL',"mkcol in put failed ".$ret->message(), $url);
#}
#$self->{_fetched} = 1;
#}
my $data = $cal->as_string;
my $ret = $res->put($data);
if ($ret->is_success) {
return $self->dav->ok( "put $url (" . length($data) ." bytes)",$url );
} else {
return $self->dav->err( 'ERR_RESP_FAIL',"put failed ".$ret->message(), $url);
}
}
=head2 delete [url]
Delete the file on the server or optionally another url.
Returns 1 on success and 0 on failure.
=cut
sub delete {
my $self = shift;
my $url = shift || $self->{url};
my $res = $self->dav->new_resource( -uri => $url );
my $ret = $res->delete();
if ($ret->is_success) {
return $self->dav->ok( "deleted $url successfully", $url );
} else {
return $self->dav->err( 'ERR_RESP_FAIL',$ret->message(), $url);
}
}
=head2 get [url]
Refetch the file from the sever to sync it -
Alternatively fetch an alternative url.
These will lose any local changes.
=cut
sub get {
my $self = shift;
my $url = shift || $self->{url};
my $res = $self->dav->new_resource( -uri => $url );
my $ret = $res->get();
if ($ret->is_success) {
$self->{_fetched} = 1;
#return $self->dav->ok("get $url", $url, $ret->content_length() );
} else {
return $self->dav->err('ERR_GENERIC', "get $url failed: ". $ret->message, $url);
}
my $data = $res->get_content();
return $self->dav->err('ERR_GENERIC', "Couldn't get data from $url", $url) unless defined $data;
return $self->parse(data => $data);
}
=head2 lock
Same options as C<HTTP::DAV>'s C<unlock>.
=cut
sub lock {
my $self = shift;
my $resp = $self->_do_on_dav('lock', @_);
if ( $resp->is_success() ) {
return $self->dav->ok( "lock $self->{url} succeeded",$self->{url} );
} else {
return $self->dav->err( 'ERR_RESP_FAIL',$resp->message, $self->{url} );
}
}
=head2 unlock
Same options as C<HTTP::DAV>'s C<unlock>.
=cut
sub unlock {
my $self = shift;
my $resp = $self->_do_on_dav('unlock', @_);
if ( $resp->is_success ) {
return $self->dav->ok( "unlock $self->{url} succeeded",$self->{url} );
} else {
# The Resource.pm::lock routine has a hack
# where if it doesn't know the locktoken, it will
# just return an empty response with message "Client Error".
# Make a custom message for this case.
my $msg = $resp->message;
if ( $msg=~ /Client error/i ) {
$msg = "No locks found. Try steal";
return $self->dav->err( 'ERR_GENERIC',$msg,$self->{url} );
} else {
return $self->dav->err( 'ERR_RESP_FAIL',$msg,$self->{url} );
}
}
}
=head2 steal_lock
Same options as C<HTTP::DAV>'s C<steal_lock>.
=cut
sub steal_lock {
my $self = shift;
my $resp = $self->_do_on_dav('steal_lock', @_);
if ( $resp->is_success() ) {
return $self->dav->ok( "steal succeeded",$self->{url} );
} else {
return $self->dav->err( 'ERR_RESP_FAIL',$resp->message(),$self->{url} );
}
}
=head2 lockdiscovery
Same options as C<HTTP::DAV::Response>'s C<lockdiscovery>.
=cut
sub lockdiscovery {
my $self = shift;
my $resp = $self->_do_on_dav('lockdiscovery', @_);
}
=head2 forcefully_unlock_all
Same options as C<HTTP::DAV::Response>'s C<forcefully_unlock_all>.
=cut
sub forcefully_unlock_all {
my $self = shift;
$self->_do_on_dav('forcefully_unlock_all', @_);
}
sub _do_on_dav {
my $self = shift;
my $meth = shift;
my $url = $self->{url};
my $res = $self->dav->new_resource( -uri => $url );
$res->$meth(@_);
}
=head2 dav [HTTP::DAV]
Get the underlying C<HTTP::DAV> object or, alterntively, replace it with
a a new one.
=cut
sub dav {
my $self = shift;
if (@_) {
$self->{_dav} = shift;
}
return $self->{_dav};
}
=head2 cal
Get the underlying cal object
=cut
sub cal {
my $self = shift;
if (!defined $self->{_cal}) {
my $ret = $self->get || die "Couldn't autofetch calendar: ".$self->dav->message;
}
return $self->{_cal};
}
=head2 auto_commit [boolean]
Whether to auto save on desctruction or not.
Defaults to 0.
=cut
sub auto_commit {
my $self = shift;
if (@_) {
$self->{_auto_commit} = shift;
}
return $self->{_auto_commit};
}
=head2 message
Same as C<HTTP::DAV>'s C<message> function.
=cut
sub message {
my $self = shift;
return $self->dav->message;
}
=head2 errors
Same as C<HTTP::DAV>'s C<errors> function.
=cut
sub errors {
my $self = shift;
return $self->dav->errors;
}
use Carp qw(croak confess cluck);
our $AUTOLOAD;
sub AUTOLOAD {
my $self = shift;
my $method = $AUTOLOAD;
$method =~ s/.*://; # strip fully-qualified portion
# TODO should we cache this in a glob?
$self->cal->$method(@_)
}
sub DESTROY {
my $self = shift;
$self->save if $self->auto_commit;
}
=head1 AUTHOR
Simon Wistow <simon@thegestalt.org>
=head1 COPYRIGHT
Copyright 2007, Simon Wistow
Released under the same terms as Perl itself.
=head1 SEE ALSO
L<HTTP::DAV>
L<Data::ICal>
http://tools.ietf.org/html/rfc4791
=cut
1;
|