/[notice-sender]/trunk/Nos.pm
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Diff of /trunk/Nos.pm

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 67 by dpavlin, Fri Jul 8 17:00:20 2005 UTC revision 77 by dpavlin, Thu Aug 25 00:37:48 2005 UTC
# Line 16  our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all' Line 16  our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'
16  our @EXPORT = qw(  our @EXPORT = qw(
17  );  );
18    
19  our $VERSION = '0.6';  our $VERSION = '0.8';
20    
21  use Class::DBI::Loader;  use Class::DBI::Loader;
22  use Email::Valid;  use Email::Valid;
# Line 27  use Email::Simple; Line 27  use Email::Simple;
27  use Email::Address;  use Email::Address;
28  use Mail::DeliveryStatus::BounceParser;  use Mail::DeliveryStatus::BounceParser;
29  use Class::DBI::AbstractSearch;  use Class::DBI::AbstractSearch;
30    use SQL::Abstract;
31  use Mail::Alias;  use Mail::Alias;
32  use Cwd qw(abs_path);  use Cwd qw(abs_path);
33    
# Line 62  encoded) or anything else. Line 63  encoded) or anything else.
63  It will just queue your e-mail message to particular list (sending it to  It will just queue your e-mail message to particular list (sending it to
64  possibly remote Notice Sender SOAP server just once), send it out at  possibly remote Notice Sender SOAP server just once), send it out at
65  reasonable rate (so that it doesn't flood your e-mail infrastructure) and  reasonable rate (so that it doesn't flood your e-mail infrastructure) and
66  track replies.  keep track replies.
67    
68  It is best used to send smaller number of messages to more-or-less fixed  It is best used to send small number of messages to more-or-less fixed
69  list of recipients while allowing individual responses to be examined.  list of recipients while allowing individual responses to be examined.
70  Tipical use include replacing php e-mail sending code with SOAP call to  Tipical use include replacing php e-mail sending code with SOAP call to
71  Notice Sender. It does support additional C<ext_id> field for each member  Notice Sender. It does support additional C<ext_id> field for each member
# Line 72  which can be used to track some unique i Line 73  which can be used to track some unique i
73  particular user.  particular user.
74    
75  It comes with command-line utility C<sender.pl> which can be used to perform  It comes with command-line utility C<sender.pl> which can be used to perform
76  all available operation from scripts (see C<perldoc sender.pl>).  all available operation from scripts (see C<sender.pl --man>).
77  This command is also useful for debugging while writing client SOAP  This command is also useful for debugging while writing client SOAP
78  application.  application.
79    
# Line 120  sub new { Line 121  sub new {
121  }  }
122    
123    
124  =head2 new_list  =head2 create_list
125    
126  Create new list. Required arguments are name of C<list>, C<email> address  Create new list. Required arguments are name of C<list>, C<email> address
127  and path to C<aliases> file.  and path to C<aliases> file.
128    
129   $nos->new_list(   $nos->create_list(
130          list => 'My list',          list => 'My list',
131          from => 'Outgoing from comment',          from => 'Outgoing from comment',
132          email => 'my-list@example.com',          email => 'my-list@example.com',
# Line 139  Calls internally C<_add_list>, see detai Line 140  Calls internally C<_add_list>, see detai
140    
141  =cut  =cut
142    
143  sub new_list {  sub create_list {
144          my $self = shift;          my $self = shift;
145    
146          my $arg = {@_};          my $arg = {@_};
# Line 158  sub new_list { Line 159  sub new_list {
159  }  }
160    
161    
162  =head2 delete_list  =head2 drop_list
163    
164  Delete list from database.  Delete list from database.
165    
166   my $ok = delete_list(   my $ok = drop_list(
167          list => 'My list'          list => 'My list'
168            aliases => '/etc/mail/mylist',
169   );   );
170    
171  Returns false if list doesn't exist.  Returns false if list doesn't exist.
172    
173  =cut  =cut
174    
175  sub delete_list {  sub drop_list {
176          my $self = shift;          my $self = shift;
177    
178          my $args = {@_};          my $args = {@_};
# Line 179  sub delete_list { Line 181  sub delete_list {
181    
182          $args->{'list'} = lc($args->{'list'});          $args->{'list'} = lc($args->{'list'});
183    
184            my $aliases = $args->{'aliases'} || croak "need path to aliases file";
185    
186          my $lists = $self->{'loader'}->find_class('lists');          my $lists = $self->{'loader'}->find_class('lists');
187    
188          my $this_list = $lists->search( name => $args->{'list'} )->first || return;          my $this_list = $lists->search( name => $args->{'list'} )->first || return;
189    
190            $self->_remove_alias( email => $this_list->email, aliases => $aliases);
191    
192          $this_list->delete || croak "can't delete list\n";          $this_list->delete || croak "can't delete list\n";
193    
194          return $lists->dbi_commit || croak "can't commit";          return $lists->dbi_commit || croak "can't commit";
# Line 262  List all members of some list. Line 268  List all members of some list.
268          list => 'My list',          list => 'My list',
269   );   );
270    
271  Returns array of hashes with user informations like this:  Returns array of hashes with user information like this:
272    
273   $member = {   $member = {
274          name => 'Dobrica Pavlinusic',          name => 'Dobrica Pavlinusic',
# Line 470  Send e-mail using SMTP server at 127.0.0 Line 476  Send e-mail using SMTP server at 127.0.0
476    
477  =back  =back
478    
479    Any other driver name will try to use C<Email::Send::that_driver> module.
480    
481  Default sleep wait between two messages is 3 seconds.  Default sleep wait between two messages is 3 seconds.
482    
483    This method will return number of succesfully sent messages.
484    
485  =cut  =cut
486    
487  sub send_queued_messages {  sub send_queued_messages {
# Line 484  sub send_queued_messages { Line 494  sub send_queued_messages {
494          my $sleep = $arg->{'sleep'};          my $sleep = $arg->{'sleep'};
495          $sleep ||= 3 unless defined($sleep);          $sleep ||= 3 unless defined($sleep);
496    
497            # number of messages sent o.k.
498            my $ok = 0;
499    
500          my $email_send_driver = 'Email::Send::IO';          my $email_send_driver = 'Email::Send::IO';
501          my @email_send_options;          my @email_send_options;
502    
503          if (lc($driver) eq 'smtp') {          if (lc($driver) eq 'smtp') {
504                  $email_send_driver = 'Email::Send::SMTP';                  $email_send_driver = 'Email::Send::SMTP';
505                  @email_send_options = ['127.0.0.1'];                  @email_send_options = ['127.0.0.1'];
506            } elsif ($driver && $driver ne '') {
507                    $email_send_driver = 'Email::Send::' . $driver;
508          } else {          } else {
509                  warn "dumping all messages to STDERR\n";                  warn "dumping all messages to STDERR\n";
510          }          }
# Line 559  sub send_queued_messages { Line 574  sub send_queued_messages {
574                                  }                                  }
575    
576                                  croak "can't send e-mail: $sent_status\n\nOriginal e-mail follows:\n".$m_obj->as_string unless ($sent_status);                                  croak "can't send e-mail: $sent_status\n\nOriginal e-mail follows:\n".$m_obj->as_string unless ($sent_status);
577                                  my @bad = @{ $sent_status->prop('bad') };                                  my @bad;
578                                    @bad = @{ $sent_status->prop('bad') } if (eval { $sent_status->can('prop') });
579                                  croak "failed sending to ",join(",",@bad) if (@bad);                                  croak "failed sending to ",join(",",@bad) if (@bad);
580    
581                                  if ($sent_status) {                                  if ($sent_status) {
# Line 573  sub send_queued_messages { Line 589  sub send_queued_messages {
589    
590                                          print " - $sent_status\n";                                          print " - $sent_status\n";
591    
592                                            $ok++;
593                                  } else {                                  } else {
594                                          warn "ERROR: $sent_status\n";                                          warn "ERROR: $sent_status\n";
595                                  }                                  }
# Line 588  sub send_queued_messages { Line 605  sub send_queued_messages {
605                  $m->dbi_commit;                  $m->dbi_commit;
606          }          }
607    
608            return $ok;
609    
610  }  }
611    
612  =head2 inbox_message  =head2 inbox_message
# Line 686  sub inbox_message { Line 705  sub inbox_message {
705  #       print "message_id: ",($message_id || "not found")," -- $is_bounce\n";  #       print "message_id: ",($message_id || "not found")," -- $is_bounce\n";
706  }  }
707    
708    =head2 received_messages
709    
710    Returns all received messages for given list or user.
711    
712     my @received = $nos->received_message(
713            list => 'My list',
714            email => "john.doe@example.com",
715     );
716    
717    Each element in returned array will have following structure:
718    
719     {
720            id => 42,                       # unique ID of received message
721            list => 'My list',              # useful only of filtering by email
722            ext_id => 9999,                 # ext_id from message user
723            email => 'jdoe@example.com',    # e-mail of user
724            bounced => 0,                   # true value if message is bounce
725            date => '2005-08-24 18:57:24',  # date of recival in ISO format
726     }
727    
728    
729    =cut
730    
731    sub received_messages {
732            my $self = shift;
733    
734            my $arg = {@_} if (@_);
735    
736    #       croak "need list name or email" unless ($arg->{'list'} || $arg->{'email'});
737    
738            my $sql = qq{
739                            select
740                                    received.id as id,
741                                    lists.name as list,
742                                    users.ext_id as ext_id,
743                                    users.email as email,
744                                    bounced,received.date as date
745                            from received
746                            join lists on lists.id = list_id
747                            join users on users.id = user_id
748            };
749    
750            my $where;
751    
752            $where->{'lists.name'} = lc($arg->{'list'}) if ($arg->{'list'});
753            $where->{'users.email'} = lc($arg->{'email'}) if ($arg->{'email'});
754    
755            # hum, yammy one-liner
756            my($stmt, @bind)  = SQL::Abstract->new->where($where);
757    
758            my $dbh = $self->{'loader'}->find_class('received')->db_Main;
759    
760            my $sth = $dbh->prepare($sql . $stmt);
761            $sth->execute(@bind);
762            return $sth->fetchall_hash;
763    }
764    
765    
766  =head1 INTERNAL METHODS  =head1 INTERNAL METHODS
767    
# Line 694  Beware of dragons! You shouldn't need to Line 770  Beware of dragons! You shouldn't need to
770    
771  =head2 _add_aliases  =head2 _add_aliases
772    
773  Add new list to C</etc/aliases> (or equivavlent) file  Add or update alias in C</etc/aliases> (or equivalent) file for selected list
774    
775   my $ok = $nos->add_aliases(   my $ok = $nos->add_aliases(
776          list => 'My list',          list => 'My list',
# Line 715  sub _add_aliases { Line 791  sub _add_aliases {
791    
792          my $arg = {@_};          my $arg = {@_};
793    
794          croak "need list and email options" unless ($arg->{'list'} && $arg->{'email'});          foreach my $o (qw/list email aliases/) {
795                    croak "need $o option" unless ($arg->{$o});
796            }
797    
798          my $aliases = $arg->{'aliases'} || croak "need aliases";          my $aliases = $arg->{'aliases'};
799            my $email = $arg->{'email'};
800            my $list = $arg->{'list'};
801    
802          unless (-e $aliases) {          unless (-e $aliases) {
803                  warn "aliases file $aliases doesn't exist, creating empty\n";                  warn "aliases file $aliases doesn't exist, creating empty\n";
# Line 726  sub _add_aliases { Line 806  sub _add_aliases {
806                  chmod 0777, $aliases || warn "can't change permission to 0777";                  chmod 0777, $aliases || warn "can't change permission to 0777";
807          }          }
808    
809            die "FATAL: aliases file $aliases is not writable\n" unless (-w $aliases);
810    
811          my $a = new Mail::Alias($aliases) || croak "can't open aliases file $aliases: $!";          my $a = new Mail::Alias($aliases) || croak "can't open aliases file $aliases: $!";
812    
813          my $target = '';          my $target = '';
# Line 747  sub _add_aliases { Line 829  sub _add_aliases {
829          $self_path =~ s#/[^/]+$##;          $self_path =~ s#/[^/]+$##;
830          $self_path =~ s#/t/*$#/#;          $self_path =~ s#/t/*$#/#;
831    
832          $target .= qq#| cd $self_path && ./sender.pl --inbox="$arg->{'list'}"#;          $target .= qq#| cd $self_path && ./sender.pl --inbox="$list"#;
833    
834          unless ($a->append($arg->{'email'}, $target)) {          if ($a->exists($email)) {
835                  croak "can't add alias ".$a->error_check;                  $a->update($email, $target) or croak "can't update alias ".$a->error_check;
836            } else {
837                    $a->append($email, $target) or croak "can't add alias ".$a->error_check;
838          }          }
839    
840            #$a->write($aliases) or croak "can't save aliases $aliases ".$a->error_check;
841    
842          return 1;          return 1;
843  }  }
844    
# Line 793  sub _add_list { Line 879  sub _add_list {
879                  list => $name,                  list => $name,
880                  email => $email,                  email => $email,
881                  aliases => $aliases,                  aliases => $aliases,
882          ) || croak "can't add alias $email for list $name";          ) || warn "can't add alias $email for list $name";
883    
884          my $l = $lists->find_or_create({          my $l = $lists->find_or_create({
885                  name => $name,                  name => $name,
# Line 835  sub _get_list { Line 921  sub _get_list {
921          return $lists->search({ name => lc($name) })->first;          return $lists->search({ name => lc($name) })->first;
922  }  }
923    
924    
925    =head2 _remove_alias
926    
927    Remove list alias
928    
929     my $ok = $nos->_remove_alias(
930            email => 'mylist@example.com',
931            aliases => '/etc/mail/mylist',
932     );
933    
934    Returns true if list is removed or false if list doesn't exist. Dies in case of error.
935    
936    =cut
937    
938    sub _remove_alias {
939            my $self = shift;
940    
941            my $arg = {@_};
942    
943            my $email = lc($arg->{'email'}) || confess "can't remove alias without email";
944            my $aliases = lc($arg->{'aliases'}) || confess "can't remove alias without list";
945    
946            my $a = new Mail::Alias($aliases) || croak "can't open aliases file $aliases: $!";
947    
948            if ($a->exists($email)) {
949                    $a->delete($email) || croak "can't remove alias $email";
950            } else {
951                    return 0;
952            }
953    
954            return 1;
955    
956    }
957    
958  ###  ###
959  ### SOAP  ### SOAP
960  ###  ###
# Line 874  Create new SOAP object Line 994  Create new SOAP object
994          aliases => '/etc/aliases',          aliases => '/etc/aliases',
995   );   );
996    
997    If you are writing SOAP server (like C<soap.cgi> example), you will need to
998    call this method once to make new instance of Nos::SOAP and specify C<dsn>
999    and options for it.
1000    
1001  =cut  =cut
1002    
1003  sub new {  sub new {
# Line 890  sub new { Line 1014  sub new {
1014  }  }
1015    
1016    
1017  =head2 NewList  =head2 CreateList
1018    
1019   $message_id = NewList(   $message_id = CreateList(
1020          list => 'My list',          list => 'My list',
1021          from => 'Name of my list',          from => 'Name of my list',
1022          email => 'my-list@example.com'          email => 'my-list@example.com'
# Line 900  sub new { Line 1024  sub new {
1024    
1025  =cut  =cut
1026    
1027  sub NewList {  sub CreateList {
1028          my $self = shift;          my $self = shift;
1029    
1030          croak "self is not Nos::SOAP object" unless (ref($self) eq 'Nos::SOAP');          my $aliases = $nos->{'aliases'} || croak "need 'aliases' argument to new constructor";
   
         my $aliases = $self->{'aliases'} || croak "need 'aliases' argument to new constructor";  
1031    
1032          if ($_[0] !~ m/^HASH/) {          if ($_[0] !~ m/^HASH/) {
1033                  return $nos->new_list(                  return $nos->create_list(
1034                          list => $_[0], from => $_[1], email => $_[2],                          list => $_[0], from => $_[1], email => $_[2],
1035                          aliases => $aliases,                          aliases => $aliases,
1036                  );                  );
1037          } else {          } else {
1038                  return $nos->new_list( %{ shift @_ }, aliases => $aliases );                  return $nos->create_list( %{ shift @_ }, aliases => $aliases );
1039          }          }
1040  }  }
1041    
1042    
1043  =head2 DeleteList  =head2 DropList
1044    
1045   $ok = DeleteList(   $ok = DropList(
1046          list => 'My list',          list => 'My list',
1047   );   );
1048    
1049  =cut  =cut
1050    
1051  sub DeleteList {  sub DropList {
1052          my $self = shift;          my $self = shift;
1053    
1054            my $aliases = $nos->{'aliases'} || croak "need 'aliases' argument to new constructor";
1055    
1056          if ($_[0] !~ m/^HASH/) {          if ($_[0] !~ m/^HASH/) {
1057                  return $nos->delete_list(                  return $nos->drop_list(
1058                          list => $_[0],                          list => $_[0],
1059                            aliases => $aliases,
1060                  );                  );
1061          } else {          } else {
1062                  return $nos->delete_list( %{ shift @_ } );                  return $nos->drop_list( %{ shift @_ }, aliases => $aliases );
1063          }          }
1064  }  }
1065    
# Line 970  sub AddMemberToList { Line 1095  sub AddMemberToList {
1095    
1096  Returns array of hashes with user informations, see C<list_members>.  Returns array of hashes with user informations, see C<list_members>.
1097    
 Returning arrays from SOAP calls is somewhat fuzzy (at least to me). It  
 seems that SOAP::Lite client thinks that it has array with one element which  
 is array of hashes with data.  
   
1098  =cut  =cut
1099    
1100  sub ListMembers {  sub ListMembers {
# Line 1034  sub AddMessageToList { Line 1155  sub AddMessageToList {
1155          }          }
1156  }  }
1157    
1158    =head1 UNIMPLEMENTED FUNCTIONS
1159    
1160    This is a stub for documentation of unimplemented functions.
1161    
1162    =head2 MessagesReceived
1163    
1164     my @result = MessagesReceived(
1165            list => 'My list',
1166            email => 'jdoe@example.com',
1167     );
1168    
1169    You can specify just C<list> or C<email> or any combination of those.
1170    
1171    For format of returned array element see C<received_messages>.
1172    
1173    =head2 MessagesReceivedByDate
1174    
1175    =head2 MessagesReceivedByDateWithContent
1176    
1177    =head2 ReceivedMessasgeContent
1178    
1179    Return content of received message.
1180    
1181     my $mail_body = ReceivedMessageContent( id => 42 );
1182    
1183    =cut
1184    
1185    
1186    
1187    
1188  ###  ###
1189    
1190    =head1 NOTE ON ARRAYS IN SOAP
1191    
1192    Returning arrays from SOAP calls is somewhat fuzzy (at least to me). It
1193    seems that SOAP::Lite client thinks that it has array with one element which
1194    is array of hashes with data.
1195    
1196  =head1 EXPORT  =head1 EXPORT
1197    
1198  Nothing.  Nothing.

Legend:
Removed from v.67  
changed lines
  Added in v.77

  ViewVC Help
Powered by ViewVC 1.1.26