#!/usr/bin/perl # # ColdSync Mail sync conduit # # For each Outbox message in the Palm database: # - try to send # - if sent, delete from Palm outbox # # $Id: send-mail,v 1.3 2003/06/05 03:09:59 cpb Exp $ # use strict; use Text::Wrap; use ColdSync; use ColdSync::SPC; use Palm::Mail; use Palm::StdAppInfo; # Default values for headers %HEADERS = ( # XXX - This ought to be determined at configure-time "Sendmail" => "/usr/sbin/sendmail", "My-Address" => (getpwuid($>))[0], "Wrap" => 74, "Outbox-name" => "Outbox", # Name of category for outgoing mail "Paragraph" => "", # Paragraph indent "Line-Indent" => "", # Line indentation ); my $VERSION = (qw( $Revision: 1.3 $ ))[1]; # Conduit version ####################################################################### # send a Palm MailDB record via sendmail or compatible MTA sub sendmail_record { my $record = shift; return unless defined $record->{'to'} or defined $record->{'cc'} or defined $record->{'bcc'}; # print_header # Print a valid mail header. Headers with empty values are ignored. # Newlines are replaced with newline-whitespace, per RFC822. sub print_header { my ($fh,$header_name, $header_content) = @_; # empty header? return if !defined($header_content) or $header_content eq ""; $header_content =~ s/\n(?!\s)/\n\t/mg; print $fh "$header_name: $header_content\n"; } my $whoami = $HEADERS{"My-Address"}; $whoami = $record->{'from'} if defined $record->{'from'} and $record->{'from'} ne ""; # Sendmail command-line arguments my @sm_args = ("-t", "-i"); # -t: get addressee from the # body of the message # -i: ignore lines consisting of # a single dot. if ($record->{confirm_delivery}) { push @sm_args, "-N", "success,failure"; } open SENDMAIL, "| $HEADERS{Sendmail} @sm_args" or die "401 Can't run $HEADERS{Sendmail}: $!\n"; # Print header fields so that they conform to RFC822 (a continuation # line must start with a whitespace or tab character). &print_header(*SENDMAIL,"From", $whoami); &print_header(*SENDMAIL,"To", $record->{"to"}); &print_header(*SENDMAIL,"Cc", $record->{"cc"}); &print_header(*SENDMAIL,"Bcc", $record->{bcc}); &print_header(*SENDMAIL,"Reply-To", $record->{reply_to}) &print_header(*SENDMAIL,"X-Sent-To", $record->{send_to}); &print_header(*SENDMAIL,"X-Mailer", "$HEADERS{Daemon} $HEADERS{Version}/send-mail conduit $VERSION"); &print_header(*SENDMAIL,"Subject", $record->{subject}); if ($record->{confirm_read}) { &print_header(*SENDMAIL,"Disposition-Notification-To", $whoami); } my $body = $record->{body}; $body .= "\n" if $body !~ /\n$/m; # Make sure message ends in \n # Wrap the text if requested $Text::Wrap::columns = $HEADERS{Wrap}; $body = wrap($HEADERS{"Paragraph"}, $HEADERS{"Line-Indent"}, $record->{body}) if $HEADERS{Wrap} > 0; print SENDMAIL "\n", $body; # only return success if the pipe doesn't return an error close SENDMAIL or return 0; return 1; } # snarfed from std-categories. It's probably best to check there # for an explanation of what's happening. The gist is that we have # a remote database open and we want to know a) does the named category # exist and b) what is the category id. sub find_category { my ($dbh,$catname) = @_; use vars qw( $Pappinfo_raw ); use vars qw( %Pappinfo ); $Pappinfo_raw = dlp_ReadAppBlock($dbh); &Palm::StdAppInfo::parse_StdAppInfo(\%Pappinfo, $Pappinfo_raw); for( my $i = 0; $i < Palm::StdAppInfo::numCategories; $i ++ ) { my $category = $Pappinfo{categories}[$i]; return $category->{id} if $category->{name} eq $catname; } return undef; } ####################################################################### StartConduit("sync"); # check this before we start messing with databases die "401 Can't run $HEADERS{Sendmail}\n" unless -x $HEADERS{Sendmail}; my $dbinfo = spc_get_dbinfo; die "501 Error reading database info" unless defined $dbinfo; my $dbh = &dlp_OpenDB($dbinfo->{name}, 0x40|0x80); die "501 Can't open database on Palm" unless defined $dbh; # Figure out which category (numeric index) is the Outbox my $catno = find_category( $dbh, $HEADERS{"Outbox-name"} ); die "401 Can't find $HEADERS{'Outbox-name'} category" unless defined $catno; my $odbinfo = &dlp_ReadOpenDBInfo( $dbh ); die "501 Can't get number of records" unless defined $odbinfo; my $nrecs = $odbinfo->{numrecords}; for( my $rnum = $nrecs - 1; $rnum >= 0; $rnum -- ) { my $recordraw = &dlp_ReadRecordByIndex( $dbh, $rnum, 0, -1 ); die "501 Can't get record $rnum" unless defined $recordraw; next unless $recordraw->{'category'} == $catno; # turn it into a Mail record my $record = Palm::Mail->ParseRecord(%$recordraw); die "501 Record $rnum isn't mail!" unless defined $record; unless( sendmail_record( $record ) ) { print "301 sendmail failed: $!"; next; } # delete it from the palm. Note that we won't get here if the send failed. &dlp_DeleteRecord( $dbh, $recordraw->{'id'}, 0 ); } &dlp_CloseDB( $dbh ); EndConduit; __END__ =head1 NAME send-mail - ColdSync conduit to send Palm mail =head1 SYNOPSIS conduit sync { type: mail/DATA; path: "<...>/send-mail"; arguments: Sendmail: /path/to/sendmail; My-Address: user@my.dom.ain; Outbox-name: Outbox; } =head1 DESCRIPTION The C conduit reads the Palm Mail database, finds the outgoing messages in the specified outbox, and passes them on to the C program for further processing. Once each message has been successfully passed along to C, it is deleted. =head1 OPTIONS =over 4 =item C Specifies the path to the C executable. The default is F. =item C Specifies the return address to put on outgoing messages. If omitted, defaults to your username (or, more precisely, the username of the first user with your uid). If using Gable Watts' simplified mail application eMail with the sender address correctly configured in the Preferences, that address will be used instead (and My-Address left out). Other mail client apps that allow the user to specify the sender address on the Palm itself might also work correctly. =item C Specifies the column at which to wrap long lines. Defaults to 74. A value of 0 specifies that text should not be wrapped. =item C Specifies the category where outgoing messages are found. If omitted, the default F is used. For German use F. =head1 BUGS Most preferences on the Palm are ignored. In theory, confirm read and delivery are handled because they involve flags in the messages themselves. This conduit will only pull messages from the Outbox for sending. It will not perform any other synching of the mail database. You might want to ensure that a non-default [generic] sync is done for the F database in order to sync other sections of the database. Some F implementations (Postfix) don't return a usable exit code if the message couldn't be sent due to local errors (i.e. mail subsystem not running). We can't detect this, so the conduit will assume all is well and delete the original from the Outbox. The lesson being to either use a correct sendmail or keep your mail server running. =head1 AUTHOR Andrew Arensburger Earensb@ooblick.comE sync version by Echristophe.beauregard@sympatico.caE =head1 SEE ALSO coldsync(8) sendmail(1) F, in the ColdSync documentation.