alt_si_Mail.pm 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  1. ##- Package modified by sigi <pss@zweiernet.ch>
  2. ##- for usage with Yasma
  3. ##- sigi, 2006
  4. package Parse::Syslog::Mail;
  5. use strict;
  6. use Carp;
  7. use Parse::Syslog;
  8. { no strict;
  9. $VERSION = '0.09';
  10. }
  11. =head1 NAME
  12. Parse::Syslog::Mail - Parse mailer logs from syslog
  13. =head1 VERSION
  14. Version 0.09
  15. =head1 SYNOPSIS
  16. use Parse::Syslog::Mail;
  17. my $maillog = Parse::Syslog::Mail->new('/var/log/syslog');
  18. while(my $log = $maillog->next) {
  19. # do something with $log
  20. # ...
  21. }
  22. =head1 DESCRIPTION
  23. As its names implies, C<Parse::Syslog::Mail> presents a simple interface
  24. to gather mail information from a syslog file. It uses C<Parse::Syslog> for
  25. reading the syslog, and offer the same simple interface. Currently supported
  26. log formats are: Sendmail, Postfix, Qmail.
  27. =head1 METHODS
  28. =over 4
  29. =item B<new()>
  30. Creates and returns a new C<Parse::Syslog::Mail> object.
  31. A file path or a C<File::Tail> object is expected as first argument.
  32. Options can follow as a hash. Most are the same as for C<Parse::Syslog->new()>.
  33. B<Options>
  34. =over 4
  35. =item *
  36. C<year> - Syslog files usually do store the time of the event without
  37. year. With this option you can specify the start-year of this log. If
  38. not specified, it will be set to the current year.
  39. =item *
  40. C<GMT> - If this option is set, the time in the syslog will be converted
  41. assuming it is GMT time instead of local time.
  42. =item *
  43. C<repeat> - C<Parse::Syslog> will by default repeat xx times events that
  44. are followed by messages like C<"last message repeated xx times">. If you
  45. set this option to false, it won't do that.
  46. =item *
  47. C<locale> - Specifies an additional locale name or the array of locale
  48. names for the parsing of log files with national characters.
  49. =item *
  50. C<allow_future> - If true will allow for timestamps in the future.
  51. Otherwise timestamps of one day in the future and more will not be returned
  52. (as a safety measure against wrong configurations, bogus --year arguments,
  53. etc.)
  54. =back
  55. B<Example>
  56. my $syslog = new Parse::Syslog::Mail '/var/log/syslog', allow_future => 1;
  57. =cut
  58. sub new {
  59. my $self = {
  60. syslog => undef,
  61. };
  62. my $class = ref $_[0] ? ref shift : shift;
  63. bless $self, $class;
  64. my $file = shift;
  65. my %args = @_;
  66. croak "fatal: Expected an argument"
  67. unless defined $file;
  68. croak "fatal: First argument of new() must be a file path of a File::Tail object"
  69. unless -f $file or $file->isa('File::Tail') or $file->isa('IO::Handle');
  70. eval { $self->{syslog} = new Parse::Syslog $file, %args };
  71. if($@) {
  72. $@ =~ s/ at .*$//;
  73. croak "fatal: Can't create new Parse::Syslog object: $@";
  74. }
  75. return $self
  76. }
  77. =item B<next()>
  78. Returns the next line of the syslog as a hashref, or C<undef> when there
  79. is no more lines. The hashref contains at least the following keys:
  80. =over 4
  81. =item *
  82. C<host> - hostname of the machine.
  83. =item *
  84. C<program> - name of the program.
  85. =item *
  86. C<timestamp> - Unix timestamp for the event.
  87. =item *
  88. C<id> - Local transient mail identifier.
  89. =item *
  90. C<text> - text description.
  91. =back
  92. Other available keys:
  93. =over 4
  94. =item *
  95. C<from> - Email address of the sender.
  96. =item *
  97. C<to> - Email addresses of the recipients, coma-separated.
  98. =item *
  99. C<msgid> - Message ID.
  100. =item *
  101. C<relay> - MTA host used for relaying the mail.
  102. =item *
  103. C<status> - Status of the transaction.
  104. =item *
  105. C<delivery_type> - I<(Qmail only)> type of the delivery: C<"local"> or C<"remote">.
  106. =item *
  107. C<delivery_id> - I<(Qmail only)> id number of the delivery.
  108. =back
  109. B<Example>
  110. while(my $log = $syslog->next) {
  111. # do something with $log
  112. }
  113. =cut
  114. sub next {
  115. my $self = shift;
  116. my %mail = ();
  117. my @fields = qw(host program timestamp text);
  118. my %delivery2id = (); # used to map delivery id with msg id (Qmail)
  119. my $si_id = time();
  120. LINE: {
  121. my $log = $self->{syslog}->next;
  122. return undef unless defined $log;
  123. @mail{@fields} = @$log{@fields};
  124. my $text = $log->{text};
  125. # Sendmail & Postfix format parsing ------------------------------------
  126. if($log->{program} =~ /^(?:sendmail|sm-mta|postfix)/) {
  127. redo LINE if $text =~ /^(?:NOQUEUE|STARTTLS|TLS:)/;
  128. redo LINE if $text =~ /prescan: (?:token too long|too many tokens|null leading token) *$/;
  129. my $id;
  130. $text =~ s/^(\w+): *// and $id = $1; # gather the MTA transient id
  131. if ( $text =~ /^ruleset=/ && not $id ) {
  132. $id = "si_" . $si_id++; # set id for msgs w/o msgid (by sigi)
  133. }
  134. redo LINE unless $id;
  135. redo LINE if $text =~ /^\s*(?:[<-]--|[Mm]ilter|SYSERR)/; # we don't treat these
  136. $text =~ s/stat=/status=/; # renaming 'stat' field to 'status'
  137. $text =~ s/^\s*([^=]+)\s*$/status=$1/; # format other status messages
  138. $text =~ s/^\s*([^=]+)\s*;\s*/status=$1, /; # format other status messages (2)
  139. $text =~ s/collect: /collect=/; # treat collect messages as field identifiers
  140. $text =~ s/(\S+),\s+([\w-]+)=/$1\t$2=/g; # replace fields seperator with tab character
  141. %mail = (%mail, map {
  142. s/,$//; s/^ +//; s/ +$//; # cleaning spaces
  143. s/.*\s+([\w-]+=)/$1/; # cleaning up field names
  144. split /=/, $_, 2 # no more than 2 elements
  145. } split /\t/, $text);
  146. my $tmpt = $mail{to};
  147. $mail{to} = split /\s/, $tmpt, 1; print "gaga\n";
  148. if(exists $mail{ruleset} and exists $mail{arg1}) {
  149. $mail{ruleset} eq 'check_mail' and $mail{from} = $mail{arg1};
  150. $mail{ruleset} eq 'check_rcpt' and $mail{to} = $mail{arg1};
  151. $mail{ruleset} eq 'check_relay' and $mail{relay} = $mail{arg1};
  152. unless(exists $mail{status}) {
  153. $mail{reject} and $mail{status} = "reject: $mail{reject}";
  154. $mail{quarantine} and $mail{status} = "quarantine: $mail{quarantine}";
  155. }
  156. }
  157. $mail{id} = $id;
  158. # Courier ESMTP -------------------------------------------------------
  159. } elsif($log->{program} =~ /^courier/) {
  160. redo LINE if $text =~ /^(?:NOQUEUE|STARTTLS|TLS:)/;
  161. $text =~ s/,status: /,status=/; # treat status as a field
  162. $text =~ s/,(\w+)=/\t$1=/g; # replace fields separator with tab character
  163. %mail = (%mail, map {
  164. split /=/, $_, 2
  165. } split /\t/, $text);
  166. # Qmail format parsing -------------------------------------------------
  167. } elsif($log->{program} =~ /^qmail/) {
  168. $text =~ s/^(\d+\.\d+) // and $mail{qmail_timestamp} = $1; # Qmail timestamp
  169. redo LINE if $text =~ /^(?:status|bounce|warning)/;
  170. # record 'new' and 'end' events in the status
  171. $text =~ s/^(new|end) msg (\d+)$//
  172. and $mail{status} = "$1 message" and $mail{id} = $2 and last;
  173. # record 'triple bounce' events in the status
  174. $text =~ s/^(triple bounce: discarding bounce)\/(\d+)$//
  175. and $mail{status} = $1 and $mail{id} = $2 and last;
  176. # mail id and its size
  177. $text =~ s/^info msg (\d+): bytes (\d+) from (<[^>]*>) //
  178. and $mail{id} = $1 and $mail{size} = $2 and $mail{from} = $3;
  179. # begining of the delivery
  180. $text =~ s/^(starting delivery (\d+)): msg (\d+) to (local|remote) (.+)$//
  181. and $mail{status} = $1 and $mail{id} = $3 and $delivery2id{$2} = $3
  182. and $mail{delivery_id} = $2 and $mail{delivery_type} = $4 and $mail{to} = $5;
  183. $text =~ s/^delivery (\d+): +//
  184. and $mail{delivery_id} = $1 and $mail{id} = $delivery2id{$1} || '';
  185. # status of the delivery
  186. $text =~ s/^(success|deferral|failure): +(\S+)//
  187. and $mail{status} = "$1: $2" and $mail{status} =~ tr/_/ /;
  188. # in case of missing mail id, generate one
  189. $mail{id} ||= 'psm' . time;
  190. } else {
  191. redo LINE
  192. }
  193. }
  194. return \%mail
  195. }
  196. =back
  197. =head1 DIAGNOSTICS
  198. =over 4
  199. =item Can't create new %s object: %s
  200. B<(F)> Occurs in C<new()>. As the message says, we were unable to create
  201. a new object of the given class. The rest of the error may give more information.
  202. =item Expected an argument
  203. B<(F)> You tried to call C<new()> with no argument.
  204. =item First argument of new() must be a file path of a File::Tail object
  205. B<(F)> As the message says, you must give to C<new()> a valid (and readable)
  206. file path or a C<File::Tail> object as first argument.
  207. =back
  208. =head1 SEE ALSO
  209. L<Parse::Syslog>
  210. =head1 TODO
  211. Add support for other mailer daemons (Exim, Courier, Qpsmtpd).
  212. Send me logs or, even better, patches, if you want support for your
  213. favorite mailer daemon.
  214. =head1 AUTHOR
  215. SE<eacute>bastien Aperghis-Tramoni E<lt>sebastien@aperghis.netE<gt>
  216. =head1 BUGS
  217. Please report any bugs or feature requests to
  218. C<bug-parse-syslog-mail@rt.cpan.org>, or through the web interface at
  219. L<https://rt.cpan.org/NoAuth/Bugs.html?Dist=Parse-Syslog-Mail>.
  220. I will be notified, and then you'll automatically be notified
  221. of progress on your bug as I make changes.
  222. =head1 CAVEATS
  223. Most probably the same as C<Parse::Syslog>, see L<Parse::Syslog/"BUGS">
  224. =head1 COPYRIGHT & LICENSE
  225. Copyright 2005 SE<eacute>bastien Aperghis-Tramoni, All Rights Reserved.
  226. This program is free software; you can redistribute it and/or modify it
  227. under the same terms as Perl itself.
  228. =cut
  229. 1; # End of Parse::Syslog::Mail