#!/usr/bin/perl # Flexop: some higher level operations for obexftp. # Copyright 2005 Timo Korvola. # Currently only directory downloads and sensible (i.e. non-XML) directory # listings are implemented. # Tested only with Siemens ME45. More standards-compatible # phones hopefully support better tools and less compatible ones # are hopeless anyway. # On the ME45 timestamps in telecom/cal are not modification dates but # dates of the calendar events. For reliable results empty the # destination directory before using flexop -d telecom/cal. use strict; use warnings; use XML::Parser; use XML::Parser::Expat; use Getopt::Std; use Date::Manip; my %options; my $obexftp = "obexftp"; # Start tag handler sub lselem { my ($expat, $elem, %attr) = @_; for ($elem) { if ($options{l}) { my $foo; { /^file$/ and $foo = '-', last; /^folder$/ and $foo = 'd', last; return; } $foo .= sprintf ' %-3s %-3s ', lc $attr{'user-perm'}, lc $attr{'group-perm'}; $foo .= $attr{modified} || " " x 15; print "$foo $attr{name}\n"; } else { /^file$/ and print( "$attr{name}\n"), last; /^folder$/ and print( "$attr{name}/\n"), last; } } } # parser dir -> sub parsedir( $$) { my $parser = shift; my $dir = $_[0] || "."; $parser->parsefile( "$obexftp -l '$dir' |" . "sed -n -e '//p' |"); } sub ls( ;$) { parsedir new XML::Parser( Handlers => {Start => \&lselem}), $_[0]; } # %hash -> start tag handler which gathers mtimes into %hash. # Only files are processed, not folders. sub getmtimes_handler( \%) { my $hashref = shift; sub { my ($expat, $elem, %attr) = @_; $$hashref{$attr{name}} = $attr{modified} if $elem =~ /^file$/; }; } # file timestamp -> true if file is older than timestamp. # False on error. sub older($$) { my ($file, $ts) = @_; my @stats = stat $file or warn( "Failed to stat $file: $!\n"), return 0; my $date = ParseDate $ts or warn( "Failed to parse date: $ts\n"), return 0; $stats[9] < UnixDate $date, "%s"; } # dir -> # Downloads all files from dir to current directory. Existing files are # overwritten only if they are regular files and the version on the phone # is newer. sub download( $) { my $dir = shift; my %mtimes = (); parsedir( new XML::Parser( Handlers => {Start => getmtimes_handler %mtimes}), $dir); while (my ($file, $mtime) = each %mtimes) { warn( "$file: not a regular file\n"), next if -e $file && !-f $file; next if -e $file && defined( $mtime) && !older $file, $mtime; system "$obexftp -c '$dir' -g '$file'"; } } sub usage() { print "Usage: flexop [-l] [directory ...] Simplifiled (non-XML) directory listing. With -l a pathetic attempt at a format resembling ls -l is made. flexop -d directory Download the regular files in directory (not recursive). If a destination file already exists timestamps are compared and the remote file is only downloaded if it is newer. "; die "\n"; } # Main getopts 'dl', \%options or usage; if ($options{d}) { die "One dir at a time, sorry!\n" unless @ARGV == 1; download $ARGV[0]; } elsif (@ARGV == 0) { ls; } elsif (@ARGV == 1) { ls $ARGV[0]; } else { foreach (@ARGV) { print "\n$_:\n"; ls $_; } }