[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[coldsync-hackers] fetch-mail conduit and deleting records



Hi there,

first of all, I wrote an fetch-mail conduit, which puts an local
mbox (or only the unread mails of it) into the Inbox of the Palm
and deletes mails in the Inbox of the Palm, which have been read
already.

2 Problems:

   1) Even with my md5summing and explicit comparing, I still
      have duplicates for unknown reasons :-(

   2) Read mails do not get deleted sometimes. So I'm not sure,
      whether I do this in the right stage. 
      
      I've tried to solve this, by writing an second conduit only
      deleting unread messages. But this doesn't seem to help :-(

I'm using coldsync 2.1.3.

Both scripts appended for review.

Thanks & Regards

Ingo Oeser
#!/usr/bin/perl -w
#
# ColdSync Mail fetching conduit, by Ingo Oeser <cs@rameria.de>
#
# Distributed under Artistic License
#
# $Id$
#
use strict;
use Digest::MD5;
use Palm::Mail;
use ColdSync;
use Mail::Internet;
use Mail::Util;
use Mail::Header;
use MIME::QuotedPrint;

# Default values for headers
%HEADERS = (
	"Local-Inbox"	=> "",
	"Inbox-name"	=> "Inbox",	# Name of category for incoming mail
	"Truncate-After"	=> 8000,	# Truncate body after that amount of bytes
);

my $VERSION = (qw( $Revision: 0.2 $ ))[1];	# Conduit version

my %cat_name2index;		# Maps category names to indices
my %rec_hash;

StartConduit("fetch");

$HEADERS{'Truncate-After'}+=0; # Mark it a number
$HEADERS{'Truncate-After'}-=50 if $HEADERS{'Truncate-After'} gt 1000;
$HEADERS{'Truncate-After'}=100 if $HEADERS{'Truncate-After'} lt 100;
$HEADERS{'Truncate-After'}=7950 if $HEADERS{'Truncate-After'} gt 7950;

&map_cats();
# map category number
my $cat = $cat_name2index{$HEADERS{'Inbox-name'}};

&hash_recs();

# append all mail as new records
# TODO: Do this optionally only for unread mail 
my @refs=Mail::Util::read_mbox($HEADERS{'Local-Inbox'});
while (($#refs > 0)) {
   my $mail=new Mail::Internet(shift @refs);
   my $head=$mail->head();
   my %headers;
   my $body;
   my $record;
   
   # clean the mail a little
   $mail->remove_sig(); $mail->tidy_body();
   
   my @tags=('From','To','Reply-To','Subject','Cc','Status','Date');
   my $key;

   foreach $key (@tags) {
      my $s=$head->get($key);
      $headers{$key}='', next if !(defined($s));
      $s=~ s/\n//gm ;
      $s=~ s/(\s{1,})/ /gm ;
      if ($s =~ /=\?iso-8859-1\?Q\?/i ) { 
         $s=~s/=\?iso-8859-1\?Q\?(.*?)\?=/$1/i ;
         $s=decode_qp($s);
      }
      $s=~ s/^\s(.*)\s$/$1/ ;
      $headers{$key}=$s;
   }

   # ignore mail already read
   next if (defined($headers{'Status:'}) and ($headers{'Status:'} eq 'RO'));
   
   # evaluate the body
   $body=join '',@{$mail->body()};
   if (length($body) > $HEADERS{'Truncate-After'}) {
      $body=substr $body,0,$HEADERS{'Truncate-After'};
      $body.="<<<<<<<<<<<<< truncated!!";
   }

   # build up the actual record
	$record = $PDB->new_Record;
	$record->{from}='';
	$record->{to}='';
	$record->{cc}='';
	$record->{subject}='';
	$record->{replyTo}='';
	$record->{sentTo}='';
   $record->{from}=$headers{'From'} if $headers{'From'} ne "";
	$record->{to}=$headers{'To'} if $headers{'To'} ne "";
	$record->{cc}=$headers{'Cc'} if $headers{'Cc'} ne "";
	$record->{replyTo}=$headers{'Reply-To'} if $headers{'Reply-To'} ne "";
	$record->{subject}=$headers{'Subject'} if $headers{'Subject'} ne "";

	$record->{body}=$body, if $body ne '';
   $record->{category} = $cat;
   
   # ignore records already in database
   next if (defined(&unique_record($record)));
   
   $PDB->append_Record($record);
}

&delete_read_mail();

EndConduit;

sub delete_read_mail() {
   my $record;
   foreach $record (@{$PDB->{records}})
   {
      # Skip everything except the Inbox folder
      next if ($record->{category} != $cat);
      # Delete read records
      $PDB->delete_Record($record, 1) if ($record->{is_read});
   }
}

# hash contents of database
sub hash_recs() {
   my $record;

   foreach $record (@{$PDB->{records}})
   {
      # Skip everything except the Inbox folder
      next if ($record->{category} != $cat);

      $rec_hash{$record->{id}} = &md5record($record); 
   }
}

# map category names to category ids
sub map_cats() {
   my $i;

   for ($i = 0; $i <= $#{$PDB->{appinfo}{categories}}; $i++)
   {
      $cat_name2index{$PDB->{appinfo}{categories}[$i]{name}} = $i;
   }
}

# check, if this record is unique and return undef, if true or the (first)
# duplicate, if false

sub unique_record() {
   my $rec = shift;
   my $rec2;
   my $k; my $v;
   my $hash = &md5record($rec);

   while (($k,$v) = each %rec_hash) {
      next if ($v ne $hash);

      $rec2 = $PDB->findRecordByID($k);
      next if (!defined($rec2));
      next if (!defined($rec2->{from}) or !defined($rec->{from}));
      next if (!defined($rec2->{to}) or !defined($rec->{to}));
      next if (!defined($rec2->{cc}) or !defined($rec->{cc}));
      next if (!defined($rec2->{replyTo}) or !defined($rec->{replyTo}));
      next if (!defined($rec2->{subject}) or !defined($rec->{subject}));
      next if (!defined($rec2->{body}) or !defined($rec->{body}));
      next if ($rec2->{from} ne $rec->{from});
      next if ($rec2->{to} ne $rec->{to});
      next if ($rec2->{cc} ne $rec->{cc});
      next if ($rec2->{replyTo} ne $rec->{replyTo});
      next if ($rec2->{subject} ne $rec->{subject});
      next if ($rec2->{body} ne $rec->{body});

      # yes, this is the first duplicate
      return $rec2;
   }

   # no duplicate item found
   return undef;
}

sub md5record() {
   my $rec = shift;
   my $md5 = Digest::MD5->new;
   $md5->reset;

   $md5->add($rec->{from}) if (defined($rec->{from}) and ($rec->{from} ne ''));
	$md5->add($rec->{to}) if (defined($rec->{to}) and ($rec->{to} ne ''));
	$md5->add($rec->{cc}) if (defined($rec->{cc}) and ($rec->{cc} ne ''));
	$md5->add($rec->{replyTo}) if (defined($rec->{replyTo}) and ($rec->{replyTo} ne ''));
	$md5->add($rec->{subject}) if (defined($rec->{subject}) and ($rec->{subject} ne ''));
	$md5->add($rec->{body}) if (defined($rec->{body}) and ($rec->{body} ne ''));
   
   return $md5->hexdigest;
}

__END__
#!/usr/bin/perl
#
# ColdSync Mail conduit
#
# $Id: send-mail,v 1.7 2001/03/16 03:47:53 arensb Exp $
#
use strict;
use Palm::Mail;
use ColdSync;

# Default values for headers
%HEADERS = (
	"Outbox-name"	=> "Outbox",	# Name of category for outgoing mail
	"Inbox-name"	=> "Inbox",	# Name of category for incoming mail
);

my $VERSION = (qw( $Revision: 1.0 $ ))[1];	# Conduit version

StartConduit("sync");

my $record;
foreach $record (@{$PDB->{records}})
{
	# Skip everything except the Outbox folder
	next unless (($PDB->{appinfo}{categories}[$record->{category}]{name}
         eq $HEADERS{"Outbox-name"}) 
      or
      ($PDB->{appinfo}{categories}[$record->{category}]{name}
		   eq $HEADERS{"Inbox-name"}));

   # Delete read records
   $PDB->delete_Record($record, 1) if ($record->{is_read});
}

EndConduit;

__END__
listen serial {
       device: /dev/pilot;
       speed: 57600;
}

pda "My Visor" {
        snum: *Visor*;
        directory: /home/ioe/.visor;
        default;
}

conduit dump {
       path: /home/ioe/bin/conduits/send-mail;
       type: mail/DATA;
   arguments:
       Sendmail: /usr/sbin/sendmail;
       Signature: /home/ioe/.signature;
       My-Address: ioe@bertha.s4u;
       DSN: return-receipt;
}

conduit fetch {
       path: /home/ioe/bin/conduits/fetch-mail;
       type: mail/DATA;
   arguments:
       Local-Inbox: /var/spool/mail/ioe;
#       Truncate-After: 8000;
       Inbox-name: Inbox;

}
# conduit sync {
#        path: /home/ioe/bin/conduits/delete-mail;
#        type: mail/DATA;
#    arguments:
#        Inbox-name:     Inbox;
#        Outbox-name:     Outbox;
# 
# }
conduit fetch {
       path: /home/ioe/bin/conduits/add-memo.pl;
       type: memo/DATA;
   arguments:
       Where: /home/ioe/tmp/New-Memos;
       Category: Temp;
}

conduit dump {
       path: /home/ioe/bin/conduits/memo-text;
       type: memo/DATA;
   arguments:
       Where: /home/ioe/Memos-by-cat;
}

conduit sync {
       path: /home/ioe/bin/conduits/sync-time.pl;
       type: date/DATA;
}

# Plucker
# conduit fetch {
#        path: /home/ioe/bin/conduits/sync-plucker;
#        type: Plkr/Data;
# }

conduit sync {
       path: /bin/true;
       type: psys/smfr;
       final;
}