2 =head1 NAME
4 mailqueue.pm
6 =head1 SYNOPSIS
8 use GOSA::GosaSupportDaemon;
10 =head1 DESCRIPTION
12 This module contains all GOsa-SI-client processing instructions concerning the mailqueue in GOsa.
14 =head1 VERSION
16 Version 1.0
18 =head1 AUTHOR
20 Andreas Rettenberger <rettenberger at gonicus dot de>
22 =head1 FUNCTIONS
24 =cut
27 package mailqueue;
30 use strict;
31 use warnings;
33 use Exporter;
34 use GOSA::GosaSupportDaemon;
35 use MIME::Base64;
37 @ISA = qw(Exporter);
38 my @events = (
39 "get_events",
40 "mailqueue_query",
41 "mailqueue_hold",
42 "mailqueue_unhold",
43 "mailqueue_requeue",
44 "mailqueue_del",
45 "mailqueue_header",
46 );
47 @EXPORT = @events;
49 BEGIN {}
51 END {}
54 ###############################################################################
55 =over
57 =item B<get_events ()>
59 =over
61 =item description
63 Reports all provided functions.
65 =item parameter
67 None.
69 =item return
71 \@events - ARRAYREF - array containing all functions
73 =back
75 =back
77 =cut
78 ###############################################################################
79 sub get_events { return \@events; }
82 ###############################################################################
83 =over
85 =item B<mailqueue_query ($$)>
87 =over
89 =item description
91 Executes /usr/sbin/mailq, parse the informations and return them
93 =item parameter
95 $msg - STRING - complete GOsa-si message
96 $msg_hash - HASHREF - content of GOsa-si message in a hash
98 =item GOsa-si message xml content
100 None.
102 =item return
104 $out_msg - STRING - GOsa-SI valid xml message containing msg_id, msg_hold, msg_size, arrival_time, sender and recipient.
106 =back
108 =back
110 =cut
111 ###############################################################################
112 sub mailqueue_query {
113 my ($msg, $msg_hash) = @_;
114 my $header = @{$msg_hash->{'header'}}[0];
115 my $source = @{$msg_hash->{'source'}}[0];
116 my $target = @{$msg_hash->{'target'}}[0];
117 my $session_id = @{$msg_hash->{'session_id'}}[0];
118 # q_tag can be: msg_id | msg_hold | msg_size | arrival_time | sender | recipient
119 my $q_tag = exists $msg_hash->{'q_tag'} ? @{$msg_hash->{'q_tag'}}[0] : undef ;
120 # q_operator can be: eq | gt | lt
121 my $q_operator = exists $msg_hash->{'q_operator'} ? @{$msg_hash->{'q_operator'}}[0] : undef ;
122 my $q_value = exists $msg_hash->{'q_value'} ? @{$msg_hash->{'q_value'}}[0] : undef ;
123 my $error = 0;
124 my $error_string;
125 my $msg_id;
126 #my $msg_hold;
127 #my $msg_size;
128 #my $arrival_time;
129 my $sender;
130 my $recipient;
131 #my $status_message;
132 my $out_hash;
133 my $out_msg;
135 &main::daemon_log("DEBUG: run /usr/bin/mailq\n", 7);
136 my $result = qx("/usr/bin/mailq");
137 my @result_l = split(/([0-9A-Z]{10,12})/, $result);
139 if (length($result) == 0) {
140 $error = 1;
141 $error_string = "/usr/bin/mailq has no result";
142 &main::daemon_log("ERROR: $error_string : $msg", 1);
143 }
145 my $result_collection = {};
146 if (not $error) {
147 # parse information
148 my $result_length = @result_l;
149 my $j = 0;
150 for (my $i = 1; $i < $result_length; $i+=2) {
152 # Fetch and prepare all information
153 my $act_result;
154 $act_result->{'msg_id'} = $result_l[$i];
155 $result_l[$i+1] =~ /^([\!| ])\s+(\d+)\s+(\w{3}\s+\w{3}\s+\d+\s+\d+:\d+:\d+)\s+([\w.-]+@[\w.-]+)\s+/ ;
156 $act_result->{'msg_hold'} = $1 eq "!" ? 1 : 0 ;
157 $act_result->{'msg_size'} = $2;
158 $act_result->{'arrival_time'} = $3;
159 $act_result->{'sender'} = $4;
160 my @info_l = split(/\n/, $result_l[$i+1]);
161 $act_result->{'recipient'} = $info_l[2] =~ /([\w.-]+@[\w.-]+)/ ? $1 : 'unknown' ;
162 $act_result->{'msg_status'} = $info_l[1] =~ /^([\s\S]*)$/ ? $1 : 'unknown' ;
164 # If all query tags exists, perform the selection
165 my $query_positiv = 0;
166 if (defined $q_tag && defined $q_operator && defined $q_value) {
168 # Query for message id
169 if ( $q_tag eq 'msg_id') {
170 if (not $q_operator eq 'eq') {
171 &main::daemon_log("$session_id WARNING: query option '$q_operator' is not allowed with query tag '$q_tag'".
172 ", return return complete mail queue as fallback", 3);
173 &main::daemon_log("$session_id DEBUG: \n$msg", 9);
174 $query_positiv++;
175 } else {
176 if ( &_exec_op($act_result->{'msg_id'}, $q_operator, $q_value) ) {
177 $query_positiv++;
178 }
179 }
181 # Query for message size
182 } elsif ($q_tag eq 'msg_size') {
183 my $result_size = int($act_result->{'msg_size'});
184 my $query_size = int($q_value);
185 if ( &_exec_op($result_size, $q_operator, $query_size) ) {
186 $query_positiv++;
187 }
189 # Query for arrival time
190 } elsif ($q_tag eq 'arrival_time') {
191 my $result_time = int(&_parse_mailq_time($act_result->{'arrival_time'}));
192 my $query_time = int($q_value);
194 if ( &_exec_op($result_time, $q_operator, $query_time) ) {
195 $query_positiv++;
196 }
198 # Query for sender
199 }elsif ($q_tag eq 'sender') {
200 if (not $q_operator eq 'eq') {
201 &main::daemon_log("$session_id WARNING: query option '$q_operator' is not allowed with query tag '$q_tag'".
202 ", return return complete mail queue as fallback", 3);
203 &main::daemon_log("$session_id DEBUG: \n$msg", 9);
204 $query_positiv++;
205 } else {
206 if ( &_exec_op($act_result->{'sender'}, $q_operator, $q_value)) {
207 $query_positiv++;
208 }
209 }
211 # Query for recipient
212 } elsif ($q_tag eq 'recipient') {
213 if (not $q_operator eq 'eq') {
214 &main::daemon_log("$session_id WARNING: query option '$q_operator' is not allowed with query tag '$q_tag'".
215 ", return return complete mail queue as fallback", 3);
216 &main::daemon_log("$session_id DEBUG: \n$msg", 9);
217 $query_positiv++;
218 } else {
219 if ( &_exec_op($act_result->{'recipient'}, $q_operator, $q_value)) {
220 $query_positiv++;
221 }
222 }
223 }
225 # If no query tag exists, return all mails in mailqueue
226 } elsif ((not defined $q_tag) && (not defined $q_operator) && (not defined $q_value)) {
227 $query_positiv++;
229 # If query tags are not complete return error message
230 } elsif ((not defined $q_tag) || (not defined $q_operator) || (not defined $q_value)) {
231 $error++;
232 $error_string = "'mailqueue_query'-msg is not complete, some query tags (q_tag, q_operator, q_value) are missing";
233 &main::daemon_log("$session_id WARNING: $error_string", 3);
234 }
236 # If query was successful, add resutls to answer
237 if ($query_positiv) {
238 $j++;
239 $result_collection->{$j} = $act_result;
240 }
241 }
242 }
244 #create outgoing msg
245 $out_hash = &main::create_xml_hash("answer_$session_id", $target, $source);
246 &add_content2xml_hash($out_hash, "session_id", $session_id);
247 &add_content2xml_hash($out_hash, "error", $error);
248 if (defined @{$msg_hash->{'forward_to_gosa'}}[0]){
249 &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]);
250 }
252 # add error infos to outgoing msg
253 if ($error) {
254 &add_content2xml_hash($out_hash, "error_string", $error_string);
255 $out_msg = &main::create_xml_string($out_hash);
257 # add mail infos to outgoing msg
258 } else {
259 my $collection_string = &db_res2xml($result_collection);
260 $out_msg = &main::create_xml_string($out_hash);
261 $out_msg =~ s/<\/xml>/$collection_string<\/xml>/
262 }
264 return $out_msg;
266 }
269 ###############################################################################
270 =over
272 =item B<mailqueue_hold ($$)>
274 =over
276 =item description
278 Executes '/usr/sbin/postsuper -h' and set mail to hold.
280 =item parameter
282 $msg - STRING - complete GOsa-si message
283 $msg_hash - HASHREF - content of GOsa-si message in a hash
285 =item GOsa-si message xml content
287 <msg_id> - STRING - postfix mail id
289 =item return
291 Nothing.
293 =back
295 =back
297 =cut
298 ###############################################################################
299 sub mailqueue_hold {
300 my ($msg, $msg_hash) = @_;
301 my $header = @{$msg_hash->{'header'}}[0];
302 my $source = @{$msg_hash->{'source'}}[0];
303 my $target = @{$msg_hash->{'target'}}[0];
304 my $session_id = @{$msg_hash->{'session_id'}}[0];
305 my $error = 0;
306 my $error_string;
308 # sanity check of input
309 if (not exists $msg_hash->{'msg_id'}) {
310 $error_string = "Message doesn't contain a XML tag 'msg_id";
311 &main::daemon_log("ERROR: $error_string : $msg", 1);
312 $error = 1;
313 } elsif (ref @{$msg_hash->{'msg_id'}}[0] eq "HASH") {
314 $error_string = "XML tag 'msg_id' is empty";
315 &main::daemon_log("ERROR: $error_string : $msg", 1);
316 $error = 1;
317 }
319 if (not $error) {
320 my @msg_ids = @{$msg_hash->{'msg_id'}};
321 foreach my $msg_id (@msg_ids) {
322 my $error = 0; # clear error status
324 # sanity check of each msg_id
325 if (not $msg_id =~ /^[0-9A-Z]{10,12}$/) {
326 $error = 1;
327 $error_string = "message ID is not valid ([0-9A-Z]{10,12}) : $msg_id";
328 &main::daemon_log("ERROR: $error_string : $msg", 1);
329 }
331 if (not $error) {
332 my $cmd = "/usr/sbin/postsuper -h $msg_id 2>&1";
333 &main::daemon_log("DEBUG: run $cmd", 7);
334 my $result = qx($cmd);
335 if ($result =~ /^postsuper: ([0-9A-Z]{10}): placed on hold/ ) {
336 &main::daemon_log("INFO: Mail $msg_id placed on hold", 5);
337 } elsif ($result eq "") {
338 &main::daemon_log("INFO: Mail $msg_id is alread placed on hold", 5);
340 } else {
341 &main::daemon_log("ERROR: '$cmd' failed : $result", 1);
342 }
343 }
344 }
345 }
347 return;
348 }
350 ###############################################################################
351 =over
353 =item B<mailqueue_unhold ($$)>
355 =over
357 =item description
359 Executes '/usr/sbin/postsuper -H' and set mail to unhold.
361 =item parameter
363 $msg - STRING - complete GOsa-si message
364 $msg_hash - HASHREF - content of GOsa-si message in a hash
366 =item GOsa-si message xml content
368 <msg_id> - STRING - postfix mail id
370 =item return
372 Nothing.
374 =back
376 =back
378 =cut
379 ###############################################################################
380 sub mailqueue_unhold {
381 my ($msg, $msg_hash) = @_;
382 my $header = @{$msg_hash->{'header'}}[0];
383 my $source = @{$msg_hash->{'source'}}[0];
384 my $target = @{$msg_hash->{'target'}}[0];
385 my $session_id = @{$msg_hash->{'session_id'}}[0];
386 my $error = 0;
387 my $error_string;
389 # sanity check of input
390 if (not exists $msg_hash->{'msg_id'}) {
391 $error_string = "Message doesn't contain a XML tag 'msg_id'";
392 &main::daemon_log("ERROR: $error_string : $msg", 1);
393 $error = 1;
394 } elsif (ref @{$msg_hash->{'msg_id'}}[0] eq "HASH") {
395 $error_string = "XML tag 'msg_id' is empty";
396 &main::daemon_log("ERROR: $error_string : $msg", 1);
397 $error = 1;
398 }
400 if (not $error) {
401 my @msg_ids = @{$msg_hash->{'msg_id'}};
402 foreach my $msg_id (@msg_ids) {
403 my $error = 0; # clear error status
405 # sanity check of each msg_id
406 if (not $msg_id =~ /^[0-9A-Z]{10,12}$/) {
407 $error = 1;
408 $error_string = "message ID is not valid ([0-9A-Z]{10,12}) : $msg_id";
409 &main::daemon_log("ERROR: $error_string : $msg", 1);
410 }
412 if (not $error) {
413 my $cmd = "/usr/sbin/postsuper -H $msg_id 2>&1";
414 &main::daemon_log("DEBUG: run $cmd\n", 7);
415 my $result = qx($cmd);
416 if ($result =~ /^postsuper: ([0-9A-Z]{10}): released from hold/ ) {
417 &main::daemon_log("INFO: Mail $msg_id released from on hold", 5);
418 } elsif ($result eq "") {
419 &main::daemon_log("INFO: Mail $msg_id is alread released from hold", 5);
421 } else {
422 &main::daemon_log("ERROR: '$cmd' failed : $result", 1);
423 }
425 }
426 }
427 }
429 return;
430 }
432 ###############################################################################
433 =over
435 =item B<mailqueue_requeue ($$)>
437 =over
439 =item description
441 Executes '/usr/sbin/postsuper -r' and requeue the mail.
443 =item parameter
445 $msg - STRING - complete GOsa-si message
446 $msg_hash - HASHREF - content of GOsa-si message in a hash
448 =item GOsa-si message xml content
450 <msg_id> - STRING - postfix mail id
452 =item return
454 Nothing.
456 =back
458 =back
460 =cut
461 ###############################################################################
462 sub mailqueue_requeue {
463 my ($msg, $msg_hash) = @_;
464 my $header = @{$msg_hash->{'header'}}[0];
465 my $source = @{$msg_hash->{'source'}}[0];
466 my $target = @{$msg_hash->{'target'}}[0];
467 my $session_id = @{$msg_hash->{'session_id'}}[0];
468 my @msg_ids = @{$msg_hash->{'msg_id'}};
469 my $error = 0;
470 my $error_string;
472 # sanity check of input
473 if (not exists $msg_hash->{'msg_id'}) {
474 $error_string = "Message doesn't contain a XML tag 'msg_id'";
475 &main::daemon_log("ERROR: $error_string : $msg", 1);
476 $error = 1;
477 } elsif (ref @{$msg_hash->{'msg_id'}}[0] eq "HASH") {
478 $error_string = "XML tag 'msg_id' is empty";
479 &main::daemon_log("ERROR: $error_string : $msg", 1);
480 $error = 1;
481 }
483 if (not $error) {
484 my @msg_ids = @{$msg_hash->{'msg_id'}};
485 foreach my $msg_id (@msg_ids) {
486 my $error = 0; # clear error status
488 # sanity check of each msg_id
489 if (not $msg_id =~ /^[0-9A-Z]{10,12}$/) {
490 $error = 1;
491 $error_string = "message ID is not valid ([0-9A-Z]{10,12}) : $msg_id";
492 &main::daemon_log("ERROR: $error_string : $msg", 1);
493 }
495 if (not $error) {
496 my $cmd = "/usr/sbin/postsuper -r $msg_id 2>&1";
497 &main::daemon_log("DEBUG: run '$cmd'", 7);
498 my $result = qx($cmd);
499 if ($result =~ /^postsuper: ([0-9A-Z]{10}): requeued/ ) {
500 &main::daemon_log("INFO: Mail $msg_id requeued", 5);
501 } elsif ($result eq "") {
502 &main::daemon_log("WARNING: Cannot requeue mail '$msg_id', mail not found!", 3);
504 } else {
505 &main::daemon_log("ERROR: '$cmd' failed : $result", 1);
506 }
508 }
509 }
510 }
512 return;
513 }
516 ###############################################################################
517 =over
519 =item B<mailqueue_del ($$)>
521 =over
523 =item description
525 Executes '/usr/sbin/postsuper -d' and deletes mail from queue.
527 =item parameter
529 $msg - STRING - complete GOsa-si message
530 $msg_hash - HASHREF - content of GOsa-si message in a hash
532 =item GOsa-si message xml content
534 <msg_id> - STRING - postfix mail id
536 =item return
538 Nothing.
540 =back
542 =back
544 =cut
545 ###############################################################################
546 sub mailqueue_del {
547 my ($msg, $msg_hash) = @_;
548 my $header = @{$msg_hash->{'header'}}[0];
549 my $source = @{$msg_hash->{'source'}}[0];
550 my $target = @{$msg_hash->{'target'}}[0];
551 my $session_id = @{$msg_hash->{'session_id'}}[0];
552 my @msg_ids = @{$msg_hash->{'msg_id'}};
553 my $error = 0;
554 my $error_string;
556 # sanity check of input
557 if (not exists $msg_hash->{'msg_id'}) {
558 $error_string = "Message doesn't contain a XML tag 'msg_id'";
559 &main::daemon_log("ERROR: $error_string : $msg", 1);
560 $error = 1;
561 } elsif (ref @{$msg_hash->{'msg_id'}}[0] eq "HASH") {
562 $error_string = "XML tag 'msg_id' is empty";
563 &main::daemon_log("ERROR: $error_string : $msg", 1);
564 $error = 1;
565 }
567 if (not $error) {
568 my @msg_ids = @{$msg_hash->{'msg_id'}};
569 foreach my $msg_id (@msg_ids) {
570 my $error = 0; # clear error status
572 # sanity check of each msg_id
573 if (not $msg_id =~ /^[0-9A-Z]{10,12}$/) {
574 $error = 1;
575 $error_string = "message ID is not valid ([0-9A-Z]{10,12}) : $msg_id";
576 &main::daemon_log("ERROR: $error_string : $msg", 1);
577 }
579 if (not $error) {
580 my $cmd = "/usr/sbin/postsuper -d $msg_id 2>&1";
581 &main::daemon_log("DEBUG: run '$cmd'", 7);
582 my $result = qx($cmd);
583 if ($result =~ /^postsuper: ([0-9A-Z]{10}): removed/ ) {
584 &main::daemon_log("INFO: Mail $msg_id deleted", 5);
585 } elsif ($result eq "") {
586 &main::daemon_log("WARNING: Cannot remove mail '$msg_id', mail not found!", 3);
588 } else {
589 &main::daemon_log("ERROR: '$cmd' failed : $result", 1);
590 }
592 }
593 }
594 }
596 return;
597 }
599 ###############################################################################
600 =over
602 =item B<mailqueue_header ($$)>
604 =over
606 =item description
608 Executes 'postcat -q', parse the informations and return them.
610 =item parameter
612 $msg - STRING - complete GOsa-si message
613 $msg_hash - HASHREF - content of GOsa-si message in a hash
615 =item GOsa-si message xml content
617 <msg_id> - STRING - postfix mail id
619 =item return
621 $out_msg - STRING - GOsa-si valid xml message containing recipient, sender and subject.
623 =back
625 =back
627 =cut
628 ###############################################################################
629 sub mailqueue_header {
630 my ($msg, $msg_hash) = @_;
631 my $header = @{$msg_hash->{'header'}}[0];
632 my $source = @{$msg_hash->{'source'}}[0];
633 my $target = @{$msg_hash->{'target'}}[0];
634 my $session_id = @{$msg_hash->{'session_id'}}[0];
635 my $error = 0;
636 my $error_string;
637 my $sender;
638 my $recipient;
639 my $subject;
640 my $out_hash;
641 my $out_msg;
643 # sanity check of input
644 if (not exists $msg_hash->{'msg_id'}) {
645 $error_string = "Message doesn't contain a XML tag 'msg_id'";
646 &main::daemon_log("ERROR: $error_string : $msg", 1);
647 $error = 1;
648 } elsif (ref @{$msg_hash->{'msg_id'}}[0] eq "HASH") {
649 $error_string = "XML tag 'msg_id' is empty";
650 &main::daemon_log("ERROR: $error_string : $msg", 1);
651 $error = 1;
652 }
654 # sanity check of each msg_id
655 my $msg_id;
656 if (not $error) {
657 $msg_id = @{$msg_hash->{'msg_id'}}[0];
658 if (not $msg_id =~ /^[0-9A-Z]{10,12}$/) {
659 $error = 1;
660 $error_string = "message ID is not valid ([0-9A-Z]{10,12}) : $msg_id";
661 &main::daemon_log("ERROR: $error_string : $msg", 1);
662 }
663 }
665 # parsing information
666 my $msg_header;
667 if (not $error) {
668 my $cmd = "postcat -q $msg_id";
669 &main::daemon_log("DEBUG: run '$cmd'", 7);
670 my $result = qx($cmd);
672 my @header_l = split(/\n\n/, $result);
673 $msg_header = $header_l[0];
674 }
676 # create outgoing msg
677 $out_hash = &main::create_xml_hash("answer_$session_id", $target, $source);
678 &add_content2xml_hash($out_hash, "session_id", $session_id);
679 &add_content2xml_hash($out_hash, "error", $error);
680 if (defined @{$msg_hash->{'forward_to_gosa'}}[0]){
681 &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]);
682 }
684 # add error infos to outgoing msg
685 if ($error) {
686 &add_content2xml_hash($out_hash, "error_string", $error_string);
687 $out_msg = &main::create_xml_string($out_hash);
689 # add mail infos to outgoing msg
690 } else {
691 #&add_content2xml_hash($out_hash, "msg_header", &decode_base64($msg_header));
692 &add_content2xml_hash($out_hash, "msg_header", $msg_header);
693 $out_msg = &main::create_xml_string($out_hash);
694 }
696 return $out_msg;
697 }
699 sub _exec_op {
700 my ($a, $op, $b) = @_ ;
701 my $res;
703 if ($op eq "eq") {
704 $res = $a =~ /$b/ ? 1 : 0 ;
705 } elsif ($op eq "gt") {
706 $res = $a > $b ? 1 : 0 ;
707 } elsif ($op eq "lt") {
708 $res = $a < $b ? 1 : 0 ;
709 }
711 return $res;
712 }
714 my $mo_hash = { "Jan"=>'01', "Feb"=>'02',"Mar"=>'03',"Apr"=>'04',"May"=>'05',"Jun"=>'06',
715 "Jul"=>'07',"Aug"=>'08',"Sep"=>'09',"Oct"=>'10',"Nov"=>'11',"Dec"=>'12'};
717 sub _parse_mailq_time {
718 my ($time) = @_ ;
720 my $local_time = &get_time();
721 my $local_year = substr($local_time,0,4);
723 my ($dow, $mo, $dd, $date) = split(/\s/, $time);
724 my ($hh, $mi, $ss) = split(/:/, $date);
725 my $mailq_time = $local_year.$mo_hash->{$mo}."$dd$hh$mi$ss";
727 # This is realy nasty
728 if (int($local_time) < int($mailq_time)) {
729 # Mailq_time is in the future, this cannot be possible, so mail must be from last year
730 $mailq_time = int($local_year) - 1 .$mo_hash->{$mo}."$dd$hh$mi$ss";
731 }
733 return $mailq_time;
734 }
736 # vim:ts=4:shiftwidth:expandtab
738 1;