/[notice-sender]/jifty-dbi/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 /jifty-dbi/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 80 by dpavlin, Fri Aug 26 05:38:00 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_messages(
713            list => 'My list',
714            email => "john.doe@example.com",
715            from_date => '2005-01-01 10:15:00',
716            to_date => '2005-01-01 12:00:00',
717            message => 0,
718     );
719    
720    If don't specify C<list> or C<email> it will return all received messages.
721    Results will be sorted by received date, oldest first.
722    
723    Other optional parametars include:
724    
725    =over 10
726    
727    =item from_date
728    
729    Date (in ISO format) for lower limit of dates received
730    
731    =item to_date
732    
733    Return just messages older than this date
734    
735    =item message
736    
737    Include whole received message in result. This will probably make result
738    array very large. Use with care.
739    
740    =back
741    
742    Each element in returned array will have following structure:
743    
744     my $row = {
745            id => 42,                       # unique ID of received message
746            list => 'My list',              # useful if filtering by email
747            ext_id => 9999,                 # ext_id from message sender
748            email => 'jdoe@example.com',    # e-mail of message sender
749            bounced => 0,                   # true if message is bounce
750            date => '2005-08-24 18:57:24',  # date of receival in ISO format
751     }
752    
753    If you specified C<message> option, this hash will also have C<message> key
754    which will contain whole received message.
755    
756    =cut
757    
758    sub received_messages {
759            my $self = shift;
760    
761            my $arg = {@_} if (@_);
762    
763    #       croak "need list name or email" unless ($arg->{'list'} || $arg->{'email'});
764    
765            my $sql = qq{
766                            select
767                                    received.id as id,
768                                    lists.name as list,
769                                    users.ext_id as ext_id,
770                                    users.email as email,
771            };
772            $sql .= qq{             message,} if ($arg->{'message'});
773            $sql .= qq{
774                                    bounced,received.date as date
775                            from received
776                            join lists on lists.id = list_id
777                            join users on users.id = user_id
778            };
779    
780            my $order = qq{ order by date desc };
781    
782            my $where;
783    
784            $where->{'lists.name'} = lc($arg->{'list'}) if ($arg->{'list'});
785            $where->{'users.email'} = lc($arg->{'email'}) if ($arg->{'email'});
786            $where->{'received.date'} = { '>=', $arg->{'date_from'} } if ($arg->{'date_from'});
787            $where->{'received.date'} = { '<=', $arg->{'date_to'} } if ($arg->{'date_to'});
788    
789            # hum, yammy one-liner
790            my($stmt, @bind)  = SQL::Abstract->new->where($where);
791    
792            my $dbh = $self->{'loader'}->find_class('received')->db_Main;
793    
794            my $sth = $dbh->prepare($sql . $stmt . $order);
795            $sth->execute(@bind);
796            return $sth->fetchall_hash;
797    }
798    
799    
800  =head1 INTERNAL METHODS  =head1 INTERNAL METHODS
801    
# Line 694  Beware of dragons! You shouldn't need to Line 804  Beware of dragons! You shouldn't need to
804    
805  =head2 _add_aliases  =head2 _add_aliases
806    
807  Add new list to C</etc/aliases> (or equivavlent) file  Add or update alias in C</etc/aliases> (or equivalent) file for selected list
808    
809   my $ok = $nos->add_aliases(   my $ok = $nos->add_aliases(
810          list => 'My list',          list => 'My list',
# Line 715  sub _add_aliases { Line 825  sub _add_aliases {
825    
826          my $arg = {@_};          my $arg = {@_};
827    
828          croak "need list and email options" unless ($arg->{'list'} && $arg->{'email'});          foreach my $o (qw/list email aliases/) {
829                    croak "need $o option" unless ($arg->{$o});
830            }
831    
832          my $aliases = $arg->{'aliases'} || croak "need aliases";          my $aliases = $arg->{'aliases'};
833            my $email = $arg->{'email'};
834            my $list = $arg->{'list'};
835    
836          unless (-e $aliases) {          unless (-e $aliases) {
837                  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 840  sub _add_aliases {
840                  chmod 0777, $aliases || warn "can't change permission to 0777";                  chmod 0777, $aliases || warn "can't change permission to 0777";
841          }          }
842    
843            die "FATAL: aliases file $aliases is not writable\n" unless (-w $aliases);
844    
845          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: $!";
846    
847          my $target = '';          my $target = '';
# Line 747  sub _add_aliases { Line 863  sub _add_aliases {
863          $self_path =~ s#/[^/]+$##;          $self_path =~ s#/[^/]+$##;
864          $self_path =~ s#/t/*$#/#;          $self_path =~ s#/t/*$#/#;
865    
866          $target .= qq#| cd $self_path && ./sender.pl --inbox="$arg->{'list'}"#;          $target .= qq#| cd $self_path && ./sender.pl --inbox="$list"#;
867    
868          unless ($a->append($arg->{'email'}, $target)) {          if ($a->exists($email)) {
869                  croak "can't add alias ".$a->error_check;                  $a->update($email, $target) or croak "can't update alias ".$a->error_check;
870            } else {
871                    $a->append($email, $target) or croak "can't add alias ".$a->error_check;
872          }          }
873    
874            #$a->write($aliases) or croak "can't save aliases $aliases ".$a->error_check;
875    
876          return 1;          return 1;
877  }  }
878    
# Line 793  sub _add_list { Line 913  sub _add_list {
913                  list => $name,                  list => $name,
914                  email => $email,                  email => $email,
915                  aliases => $aliases,                  aliases => $aliases,
916          ) || croak "can't add alias $email for list $name";          ) || warn "can't add alias $email for list $name";
917    
918          my $l = $lists->find_or_create({          my $l = $lists->find_or_create({
919                  name => $name,                  name => $name,
# Line 835  sub _get_list { Line 955  sub _get_list {
955          return $lists->search({ name => lc($name) })->first;          return $lists->search({ name => lc($name) })->first;
956  }  }
957    
958    
959    =head2 _remove_alias
960    
961    Remove list alias
962    
963     my $ok = $nos->_remove_alias(
964            email => 'mylist@example.com',
965            aliases => '/etc/mail/mylist',
966     );
967    
968    Returns true if list is removed or false if list doesn't exist. Dies in case of error.
969    
970    =cut
971    
972    sub _remove_alias {
973            my $self = shift;
974    
975            my $arg = {@_};
976    
977            my $email = lc($arg->{'email'}) || confess "can't remove alias without email";
978            my $aliases = lc($arg->{'aliases'}) || confess "can't remove alias without list";
979    
980            my $a = new Mail::Alias($aliases) || croak "can't open aliases file $aliases: $!";
981    
982            if ($a->exists($email)) {
983                    $a->delete($email) || croak "can't remove alias $email";
984            } else {
985                    return 0;
986            }
987    
988            return 1;
989    
990    }
991    
992  ###  ###
993  ### SOAP  ### SOAP
994  ###  ###
# Line 874  Create new SOAP object Line 1028  Create new SOAP object
1028          aliases => '/etc/aliases',          aliases => '/etc/aliases',
1029   );   );
1030    
1031    If you are writing SOAP server (like C<soap.cgi> example), you will need to
1032    call this method once to make new instance of Nos::SOAP and specify C<dsn>
1033    and options for it.
1034    
1035  =cut  =cut
1036    
1037  sub new {  sub new {
# Line 890  sub new { Line 1048  sub new {
1048  }  }
1049    
1050    
1051  =head2 NewList  =head2 CreateList
1052    
1053   $message_id = NewList(   $message_id = CreateList(
1054          list => 'My list',          list => 'My list',
1055          from => 'Name of my list',          from => 'Name of my list',
1056          email => 'my-list@example.com'          email => 'my-list@example.com'
# Line 900  sub new { Line 1058  sub new {
1058    
1059  =cut  =cut
1060    
1061  sub NewList {  sub CreateList {
1062          my $self = shift;          my $self = shift;
1063    
1064          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";  
1065    
1066          if ($_[0] !~ m/^HASH/) {          if ($_[0] !~ m/^HASH/) {
1067                  return $nos->new_list(                  return $nos->create_list(
1068                          list => $_[0], from => $_[1], email => $_[2],                          list => $_[0], from => $_[1], email => $_[2],
1069                          aliases => $aliases,                          aliases => $aliases,
1070                  );                  );
1071          } else {          } else {
1072                  return $nos->new_list( %{ shift @_ }, aliases => $aliases );                  return $nos->create_list( %{ shift @_ }, aliases => $aliases );
1073          }          }
1074  }  }
1075    
1076    
1077  =head2 DeleteList  =head2 DropList
1078    
1079   $ok = DeleteList(   $ok = DropList(
1080          list => 'My list',          list => 'My list',
1081   );   );
1082    
1083  =cut  =cut
1084    
1085  sub DeleteList {  sub DropList {
1086          my $self = shift;          my $self = shift;
1087    
1088            my $aliases = $nos->{'aliases'} || croak "need 'aliases' argument to new constructor";
1089    
1090          if ($_[0] !~ m/^HASH/) {          if ($_[0] !~ m/^HASH/) {
1091                  return $nos->delete_list(                  return $nos->drop_list(
1092                          list => $_[0],                          list => $_[0],
1093                            aliases => $aliases,
1094                  );                  );
1095          } else {          } else {
1096                  return $nos->delete_list( %{ shift @_ } );                  return $nos->drop_list( %{ shift @_ }, aliases => $aliases );
1097          }          }
1098  }  }
1099    
# Line 970  sub AddMemberToList { Line 1129  sub AddMemberToList {
1129    
1130  Returns array of hashes with user informations, see C<list_members>.  Returns array of hashes with user informations, see C<list_members>.
1131    
 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.  
   
1132  =cut  =cut
1133    
1134  sub ListMembers {  sub ListMembers {
# Line 1034  sub AddMessageToList { Line 1189  sub AddMessageToList {
1189          }          }
1190  }  }
1191    
1192    =head2 MessagesReceived
1193    
1194    Return statistics about received messages.
1195    
1196     my @result = MessagesReceived(
1197            list => 'My list',
1198            email => 'jdoe@example.com',
1199            from_date => '2005-01-01 10:15:00',
1200            to_date => '2005-01-01 12:00:00',
1201            message => 0,
1202     );
1203    
1204    You must specify C<list> or C<email> or any combination of those two. Other
1205    parametars are optional.
1206    
1207    For format of returned array element see C<received_messages>.
1208    
1209    =cut
1210    
1211    sub MessagesReceived {
1212            my $self = shift;
1213    
1214            if ($_[0] !~ m/^HASH/) {
1215                    die "need at least list or email" unless (scalar @_ < 2);
1216                    return $nos->received_messages(
1217                            list => $_[0], email => $_[1],
1218                            from_date => $_[2], to_date => $_[3],
1219                            message => $_[4]
1220                    );
1221            } else {
1222                    my $arg = shift;
1223                    die "need list or email argument" unless ($arg->{'list'} || $arg->{'email'});
1224                    return $nos->received_messages( $arg );
1225            }
1226    }
1227    
1228  ###  ###
1229    
1230    =head1 UNIMPLEMENTED SOAP FUNCTIONS
1231    
1232    This is a stub for documentation of unimplemented functions.
1233    
1234    =head2 MessagesReceivedByDate
1235    
1236    =head2 MessagesReceivedByDateWithContent
1237    
1238    =head2 ReceivedMessageContent
1239    
1240    Return content of received message.
1241    
1242     my $mail_body = ReceivedMessageContent( id => 42 );
1243    
1244    
1245    
1246    
1247    =head1 NOTE ON ARRAYS IN SOAP
1248    
1249    Returning arrays from SOAP calls is somewhat fuzzy (at least to me). It
1250    seems that SOAP::Lite client thinks that it has array with one element which
1251    is array of hashes with data.
1252    
1253  =head1 EXPORT  =head1 EXPORT
1254    
1255  Nothing.  Nothing.

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

  ViewVC Help
Powered by ViewVC 1.1.26