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;
28 use Exporter;
29 @ISA = qw(Exporter);
30 my @events = (
31 "get_events",
32 "mailqueue_query",
33 "mailqueue_hold",
34 "mailqueue_unhold",
35 "mailqueue_requeue",
36 "mailqueue_del",
37 "mailqueue_header",
38 );
39 @EXPORT = @events;
41 use strict;
42 use warnings;
43 use GOSA::GosaSupportDaemon;
44 use Data::Dumper;
45 use MIME::Base64;
47 BEGIN {}
49 END {}
52 ###############################################################################
53 =over
55 =item B<get_events ()>
57 =over
59 =item description
61 Reports all provided functions.
63 =item parameter
65 None.
67 =item return
69 \@events - ARRAYREF - array containing all functions
71 =back
73 =back
75 =cut
76 ###############################################################################
77 sub get_events { return \@events; }
80 ###############################################################################
81 =over
83 =item B<mailqueue_query ($$)>
85 =over
87 =item description
89 Executes /usr/sbin/mailq, parse the informations and return them
91 =item parameter
93 $msg - STRING - complete GOsa-si message
94 $msg_hash - HASHREF - content of GOsa-si message in a hash
96 =item GOsa-si message xml content
98 None.
100 =item return
102 $out_msg - STRING - GOsa-SI valid xml message containing msg_id, msg_hold, msg_size, arrival_time, sender and recipient.
104 =back
106 =back
108 =cut
109 ###############################################################################
110 sub mailqueue_query {
111 my ($msg, $msg_hash) = @_;
112 my $header = @{$msg_hash->{'header'}}[0];
113 my $source = @{$msg_hash->{'source'}}[0];
114 my $target = @{$msg_hash->{'target'}}[0];
115 my $session_id = @{$msg_hash->{'session_id'}}[0];
116 # q_tag can be: msg_id | msg_hold | msg_size | arrival_time | sender | recipient
117 my $q_tag = exists $msg_hash->{'q_tag'} ? @{$msg_hash->{'q_tag'}}[0] : undef ;
118 # q_operator can be: eq | gt | lt
119 my $q_operator = exists $msg_hash->{'q_operator'} ? @{$msg_hash->{'q_operator'}}[0] : undef ;
120 my $q_value = exists $msg_hash->{'q_value'} ? @{$msg_hash->{'q_value'}}[0] : undef ;
121 my $error = 0;
122 my $error_string;
123 my $msg_id;
124 my $msg_hold;
125 my $msg_size;
126 my $arrival_time;
127 my $sender;
128 my $recipient;
129 my $status_message;
130 my $out_hash;
131 my $out_msg;
133 &main::daemon_log("DEBUG: run /usr/bin/mailq\n", 7);
134 my $result = qx("/usr/bin/mailq");
135 my @result_l = split(/([0-9A-Z]{10,12})/, $result);
137 if (length($result) == 0) {
138 $error = 1;
139 $error_string = "/usr/bin/mailq has no result";
140 &main::daemon_log("ERROR: $error_string : $msg", 1);
141 }
143 my $result_collection = {};
144 if (not $error) {
145 # parse information
146 my $result_length = @result_l;
147 my $j = 0;
148 for (my $i = 1; $i < $result_length; $i+=2) {
150 # Fetch and prepare all information
151 my $act_result;
152 $act_result->{'msg_id'} = $result_l[$i];
153 $result_l[$i+1] =~ /^([\!| ])\s+(\d+)\s+(\w{3}\s+\w{3}\s+\d+\s+\d+:\d+:\d+)\s+([\w.-]+@[\w.-]+)\s+/ ;
154 $act_result->{'msg_hold'} = $1 eq "!" ? 1 : 0 ;
155 $act_result->{'msg_size'} = $2;
156 $act_result->{'arrival_time'} = $3;
157 $act_result->{'sender'} = $4;
158 my @info_l = split(/\n/, $result_l[$i+1]);
159 $act_result->{'recipient'} = $info_l[2] =~ /([\w.-]+@[\w.-]+)/ ? $1 : 'unknown' ;
160 $act_result->{'msg_status'} = $info_l[1] =~ /^([\s\S]*)$/ ? $1 : 'unknown' ;
162 # If all query tags exists, perform the selection
163 my $query_positiv = 0;
164 if (defined $q_tag && defined $q_operator && defined $q_value) {
166 # Query for message id
167 if ( $q_tag eq 'msg_id') {
168 if (not $q_operator eq 'eq') {
169 &main::daemon_log("$session_id WARNING: query option '$q_operator' is not allowed with query tag '$q_tag'".
170 ", return return complete mail queue as fallback", 3);
171 &main::daemon_log("$session_id DEBUG: \n$msg", 9);
172 $query_positiv++;
173 } else {
174 if ( &_exec_op($act_result->{'msg_id'}, $q_operator, $q_value) ) {
175 $query_positiv++;
176 }
177 }
179 # Query for message size
180 } elsif ($q_tag eq 'msg_size') {
181 my $result_size = int($act_result->{'msg_size'});
182 my $query_size = int($q_value);
183 if ( &_exec_op($result_size, $q_operator, $query_size) ) {
184 $query_positiv++;
185 }
187 # Query for arrival time
188 } elsif ($q_tag eq 'arrival_time') {
189 my $result_time = int(&_parse_mailq_time($act_result->{'arrival_time'}));
190 my $query_time = int($q_value);
192 if ( &_exec_op($result_time, $q_operator, $query_time) ) {
193 $query_positiv++;
194 }
196 # Query for sender
197 }elsif ($q_tag eq 'sender') {
198 if (not $q_operator eq 'eq') {
199 &main::daemon_log("$session_id WARNING: query option '$q_operator' is not allowed with query tag '$q_tag'".
200 ", return return complete mail queue as fallback", 3);
201 &main::daemon_log("$session_id DEBUG: \n$msg", 9);
202 $query_positiv++;
203 } else {
204 if ( &_exec_op($act_result->{'sender'}, $q_operator, $q_value)) {
205 $query_positiv++;
206 }
207 }
209 # Query for recipient
210 } elsif ($q_tag eq 'recipient') {
211 if (not $q_operator eq 'eq') {
212 &main::daemon_log("$session_id WARNING: query option '$q_operator' is not allowed with query tag '$q_tag'".
213 ", return return complete mail queue as fallback", 3);
214 &main::daemon_log("$session_id DEBUG: \n$msg", 9);
215 $query_positiv++;
216 } else {
217 if ( &_exec_op($act_result->{'recipient'}, $q_operator, $q_value)) {
218 $query_positiv++;
219 }
220 }
221 }
223 # If no query tag exists, return all mails in mailqueue
224 } elsif ((not defined $q_tag) && (not defined $q_operator) && (not defined $q_value)) {
225 $query_positiv++;
227 # If query tags are not complete return error message
228 } elsif ((not defined $q_tag) || (not defined $q_operator) || (not defined $q_value)) {
229 $error++;
230 $error_string = "'mailqueue_query'-msg is not complete, some query tags (q_tag, q_operator, q_value) are missing";
231 &main::daemon_log("$session_id WARNING: $error_string", 3);
232 }
234 # If query was successful, add resutls to answer
235 if ($query_positiv) {
236 $j++;
237 $result_collection->{$j} = $act_result;
238 }
239 }
240 }
242 #create outgoing msg
243 $out_hash = &main::create_xml_hash("answer_$session_id", $target, $source);
244 &add_content2xml_hash($out_hash, "session_id", $session_id);
245 &add_content2xml_hash($out_hash, "error", $error);
246 if (defined @{$msg_hash->{'forward_to_gosa'}}[0]){
247 &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]);
248 }
250 # add error infos to outgoing msg
251 if ($error) {
252 &add_content2xml_hash($out_hash, "error_string", $error_string);
253 $out_msg = &main::create_xml_string($out_hash);
255 # add mail infos to outgoing msg
256 } else {
257 my $collection_string = &db_res2xml($result_collection);
258 $out_msg = &main::create_xml_string($out_hash);
259 $out_msg =~ s/<\/xml>/$collection_string<\/xml>/
260 }
262 return $out_msg;
264 }
267 ###############################################################################
268 =over
270 =item B<mailqueue_hold ($$)>
272 =over
274 =item description
276 Executes '/usr/sbin/postsuper -h' and set mail to hold.
278 =item parameter
280 $msg - STRING - complete GOsa-si message
281 $msg_hash - HASHREF - content of GOsa-si message in a hash
283 =item GOsa-si message xml content
285 <msg_id> - STRING - postfix mail id
287 =item return
289 Nothing.
291 =back
293 =back
295 =cut
296 ###############################################################################
297 sub mailqueue_hold {
298 my ($msg, $msg_hash) = @_;
299 my $header = @{$msg_hash->{'header'}}[0];
300 my $source = @{$msg_hash->{'source'}}[0];
301 my $target = @{$msg_hash->{'target'}}[0];
302 my $session_id = @{$msg_hash->{'session_id'}}[0];
303 my $error = 0;
304 my $error_string;
306 # sanity check of input
307 if (not exists $msg_hash->{'msg_id'}) {
308 $error_string = "Message doesn't contain a XML tag 'msg_id";
309 &main::daemon_log("ERROR: $error_string : $msg", 1);
310 $error = 1;
311 } elsif (ref @{$msg_hash->{'msg_id'}}[0] eq "HASH") {
312 $error_string = "XML tag 'msg_id' is empty";
313 &main::daemon_log("ERROR: $error_string : $msg", 1);
314 $error = 1;
315 }
317 if (not $error) {
318 my @msg_ids = @{$msg_hash->{'msg_id'}};
319 foreach my $msg_id (@msg_ids) {
320 my $error = 0; # clear error status
322 # sanity check of each msg_id
323 if (not $msg_id =~ /^[0-9A-Z]{10,12}$/) {
324 $error = 1;
325 $error_string = "message ID is not valid ([0-9A-Z]{10,12}) : $msg_id";
326 &main::daemon_log("ERROR: $error_string : $msg", 1);
327 }
329 if (not $error) {
330 my $cmd = "/usr/sbin/postsuper -h $msg_id 2>&1";
331 &main::daemon_log("DEBUG: run $cmd", 7);
332 my $result = qx($cmd);
333 if ($result =~ /^postsuper: ([0-9A-Z]{10}): placed on hold/ ) {
334 &main::daemon_log("INFO: Mail $msg_id placed on hold", 5);
335 } elsif ($result eq "") {
336 &main::daemon_log("INFO: Mail $msg_id is alread placed on hold", 5);
338 } else {
339 &main::daemon_log("ERROR: '$cmd' failed : $result", 1);
340 }
341 }
342 }
343 }
345 return;
346 }
348 ###############################################################################
349 =over
351 =item B<mailqueue_unhold ($$)>
353 =over
355 =item description
357 Executes '/usr/sbin/postsuper -H' and set mail to unhold.
359 =item parameter
361 $msg - STRING - complete GOsa-si message
362 $msg_hash - HASHREF - content of GOsa-si message in a hash
364 =item GOsa-si message xml content
366 <msg_id> - STRING - postfix mail id
368 =item return
370 Nothing.
372 =back
374 =back
376 =cut
377 ###############################################################################
378 sub mailqueue_unhold {
379 my ($msg, $msg_hash) = @_;
380 my $header = @{$msg_hash->{'header'}}[0];
381 my $source = @{$msg_hash->{'source'}}[0];
382 my $target = @{$msg_hash->{'target'}}[0];
383 my $session_id = @{$msg_hash->{'session_id'}}[0];
384 my $error = 0;
385 my $error_string;
387 # sanity check of input
388 if (not exists $msg_hash->{'msg_id'}) {
389 $error_string = "Message doesn't contain a XML tag 'msg_id'";
390 &main::daemon_log("ERROR: $error_string : $msg", 1);
391 $error = 1;
392 } elsif (ref @{$msg_hash->{'msg_id'}}[0] eq "HASH") {
393 $error_string = "XML tag 'msg_id' is empty";
394 &main::daemon_log("ERROR: $error_string : $msg", 1);
395 $error = 1;
396 }
398 if (not $error) {
399 my @msg_ids = @{$msg_hash->{'msg_id'}};
400 foreach my $msg_id (@msg_ids) {
401 my $error = 0; # clear error status
403 # sanity check of each msg_id
404 if (not $msg_id =~ /^[0-9A-Z]{10,12}$/) {
405 $error = 1;
406 $error_string = "message ID is not valid ([0-9A-Z]{10,12}) : $msg_id";
407 &main::daemon_log("ERROR: $error_string : $msg", 1);
408 }
410 if (not $error) {
411 my $cmd = "/usr/sbin/postsuper -H $msg_id 2>&1";
412 &main::daemon_log("DEBUG: run $cmd\n", 7);
413 my $result = qx($cmd);
414 if ($result =~ /^postsuper: ([0-9A-Z]{10}): released from hold/ ) {
415 &main::daemon_log("INFO: Mail $msg_id released from on hold", 5);
416 } elsif ($result eq "") {
417 &main::daemon_log("INFO: Mail $msg_id is alread released from hold", 5);
419 } else {
420 &main::daemon_log("ERROR: '$cmd' failed : $result", 1);
421 }
423 }
424 }
425 }
427 return;
428 }
430 ###############################################################################
431 =over
433 =item B<mailqueue_requeue ($$)>
435 =over
437 =item description
439 Executes '/usr/sbin/postsuper -r' and requeue the mail.
441 =item parameter
443 $msg - STRING - complete GOsa-si message
444 $msg_hash - HASHREF - content of GOsa-si message in a hash
446 =item GOsa-si message xml content
448 <msg_id> - STRING - postfix mail id
450 =item return
452 Nothing.
454 =back
456 =back
458 =cut
459 ###############################################################################
460 sub mailqueue_requeue {
461 my ($msg, $msg_hash) = @_;
462 my $header = @{$msg_hash->{'header'}}[0];
463 my $source = @{$msg_hash->{'source'}}[0];
464 my $target = @{$msg_hash->{'target'}}[0];
465 my $session_id = @{$msg_hash->{'session_id'}}[0];
466 my @msg_ids = @{$msg_hash->{'msg_id'}};
467 my $error = 0;
468 my $error_string;
470 # sanity check of input
471 if (not exists $msg_hash->{'msg_id'}) {
472 $error_string = "Message doesn't contain a XML tag 'msg_id'";
473 &main::daemon_log("ERROR: $error_string : $msg", 1);
474 $error = 1;
475 } elsif (ref @{$msg_hash->{'msg_id'}}[0] eq "HASH") {
476 $error_string = "XML tag 'msg_id' is empty";
477 &main::daemon_log("ERROR: $error_string : $msg", 1);
478 $error = 1;
479 }
481 if (not $error) {
482 my @msg_ids = @{$msg_hash->{'msg_id'}};
483 foreach my $msg_id (@msg_ids) {
484 my $error = 0; # clear error status
486 # sanity check of each msg_id
487 if (not $msg_id =~ /^[0-9A-Z]{10,12}$/) {
488 $error = 1;
489 $error_string = "message ID is not valid ([0-9A-Z]{10,12}) : $msg_id";
490 &main::daemon_log("ERROR: $error_string : $msg", 1);
491 }
493 if (not $error) {
494 my $cmd = "/usr/sbin/postsuper -r $msg_id 2>&1";
495 &main::daemon_log("DEBUG: run '$cmd'", 7);
496 my $result = qx($cmd);
497 if ($result =~ /^postsuper: ([0-9A-Z]{10}): requeued/ ) {
498 &main::daemon_log("INFO: Mail $msg_id requeued", 5);
499 } elsif ($result eq "") {
500 &main::daemon_log("WARNING: Cannot requeue mail '$msg_id', mail not found!", 3);
502 } else {
503 &main::daemon_log("ERROR: '$cmd' failed : $result", 1);
504 }
506 }
507 }
508 }
510 return;
511 }
514 ###############################################################################
515 =over
517 =item B<mailqueue_del ($$)>
519 =over
521 =item description
523 Executes '/usr/sbin/postsuper -d' and deletes mail from queue.
525 =item parameter
527 $msg - STRING - complete GOsa-si message
528 $msg_hash - HASHREF - content of GOsa-si message in a hash
530 =item GOsa-si message xml content
532 <msg_id> - STRING - postfix mail id
534 =item return
536 Nothing.
538 =back
540 =back
542 =cut
543 ###############################################################################
544 sub mailqueue_del {
545 my ($msg, $msg_hash) = @_;
546 my $header = @{$msg_hash->{'header'}}[0];
547 my $source = @{$msg_hash->{'source'}}[0];
548 my $target = @{$msg_hash->{'target'}}[0];
549 my $session_id = @{$msg_hash->{'session_id'}}[0];
550 my @msg_ids = @{$msg_hash->{'msg_id'}};
551 my $error = 0;
552 my $error_string;
554 # sanity check of input
555 if (not exists $msg_hash->{'msg_id'}) {
556 $error_string = "Message doesn't contain a XML tag 'msg_id'";
557 &main::daemon_log("ERROR: $error_string : $msg", 1);
558 $error = 1;
559 } elsif (ref @{$msg_hash->{'msg_id'}}[0] eq "HASH") {
560 $error_string = "XML tag 'msg_id' is empty";
561 &main::daemon_log("ERROR: $error_string : $msg", 1);
562 $error = 1;
563 }
565 if (not $error) {
566 my @msg_ids = @{$msg_hash->{'msg_id'}};
567 foreach my $msg_id (@msg_ids) {
568 my $error = 0; # clear error status
570 # sanity check of each msg_id
571 if (not $msg_id =~ /^[0-9A-Z]{10,12}$/) {
572 $error = 1;
573 $error_string = "message ID is not valid ([0-9A-Z]{10,12}) : $msg_id";
574 &main::daemon_log("ERROR: $error_string : $msg", 1);
575 }
577 if (not $error) {
578 my $cmd = "/usr/sbin/postsuper -d $msg_id 2>&1";
579 &main::daemon_log("DEBUG: run '$cmd'", 7);
580 my $result = qx($cmd);
581 if ($result =~ /^postsuper: ([0-9A-Z]{10}): removed/ ) {
582 &main::daemon_log("INFO: Mail $msg_id deleted", 5);
583 } elsif ($result eq "") {
584 &main::daemon_log("WARNING: Cannot remove mail '$msg_id', mail not found!", 3);
586 } else {
587 &main::daemon_log("ERROR: '$cmd' failed : $result", 1);
588 }
590 }
591 }
592 }
594 return;
595 }
597 ###############################################################################
598 =over
600 =item B<mailqueue_header ($$)>
602 =over
604 =item description
606 Executes 'postcat -q', parse the informations and return them.
608 =item parameter
610 $msg - STRING - complete GOsa-si message
611 $msg_hash - HASHREF - content of GOsa-si message in a hash
613 =item GOsa-si message xml content
615 <msg_id> - STRING - postfix mail id
617 =item return
619 $out_msg - STRING - GOsa-si valid xml message containing recipient, sender and subject.
621 =back
623 =back
625 =cut
626 ###############################################################################
627 sub mailqueue_header {
628 my ($msg, $msg_hash) = @_;
629 my $header = @{$msg_hash->{'header'}}[0];
630 my $source = @{$msg_hash->{'source'}}[0];
631 my $target = @{$msg_hash->{'target'}}[0];
632 my $session_id = @{$msg_hash->{'session_id'}}[0];
633 my $error = 0;
634 my $error_string;
635 my $sender;
636 my $recipient;
637 my $subject;
638 my $out_hash;
639 my $out_msg;
641 # sanity check of input
642 if (not exists $msg_hash->{'msg_id'}) {
643 $error_string = "Message doesn't contain a XML tag 'msg_id'";
644 &main::daemon_log("ERROR: $error_string : $msg", 1);
645 $error = 1;
646 } elsif (ref @{$msg_hash->{'msg_id'}}[0] eq "HASH") {
647 $error_string = "XML tag 'msg_id' is empty";
648 &main::daemon_log("ERROR: $error_string : $msg", 1);
649 $error = 1;
650 }
652 # sanity check of each msg_id
653 my $msg_id;
654 if (not $error) {
655 $msg_id = @{$msg_hash->{'msg_id'}}[0];
656 if (not $msg_id =~ /^[0-9A-Z]{10,12}$/) {
657 $error = 1;
658 $error_string = "message ID is not valid ([0-9A-Z]{10,12}) : $msg_id";
659 &main::daemon_log("ERROR: $error_string : $msg", 1);
660 }
661 }
663 # parsing information
664 my $msg_header;
665 if (not $error) {
666 my $cmd = "postcat -q $msg_id";
667 &main::daemon_log("DEBUG: run '$cmd'", 7);
668 my $result = qx($cmd);
670 my @header_l = split(/\n\n/, $result);
671 $msg_header = $header_l[0];
672 }
674 # create outgoing msg
675 $out_hash = &main::create_xml_hash("answer_$session_id", $target, $source);
676 &add_content2xml_hash($out_hash, "session_id", $session_id);
677 &add_content2xml_hash($out_hash, "error", $error);
678 if (defined @{$msg_hash->{'forward_to_gosa'}}[0]){
679 &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]);
680 }
682 # add error infos to outgoing msg
683 if ($error) {
684 &add_content2xml_hash($out_hash, "error_string", $error_string);
685 $out_msg = &main::create_xml_string($out_hash);
687 # add mail infos to outgoing msg
688 } else {
689 #&add_content2xml_hash($out_hash, "msg_header", &decode_base64($msg_header));
690 &add_content2xml_hash($out_hash, "msg_header", $msg_header);
691 $out_msg = &main::create_xml_string($out_hash);
692 }
694 return $out_msg;
695 }
697 sub _exec_op {
698 my ($a, $op, $b) = @_ ;
699 my $res;
701 if ($op eq "eq") {
702 $res = $a =~ /$b/ ? 1 : 0 ;
703 } elsif ($op eq "gt") {
704 $res = $a > $b ? 1 : 0 ;
705 } elsif ($op eq "lt") {
706 $res = $a < $b ? 1 : 0 ;
707 }
709 return $res;
710 }
712 my $mo_hash = { "Jan"=>'01', "Feb"=>'02',"Mar"=>'03',"Apr"=>'04',"May"=>'05',"Jun"=>'06',
713 "Jul"=>'07',"Aug"=>'08',"Sep"=>'09',"Oct"=>'10',"Nov"=>'11',"Dec"=>'12'};
715 sub _parse_mailq_time {
716 my ($time) = @_ ;
718 my $local_time = &get_time();
719 my $local_year = substr($local_time,0,4);
721 my ($dow, $mo, $dd, $date) = split(/\s/, $time);
722 my ($hh, $mi, $ss) = split(/:/, $date);
723 my $mailq_time = $local_year.$mo_hash->{$mo}."$dd$hh$mi$ss";
725 # This is realy nasty
726 if (int($local_time) < int($mailq_time)) {
727 # Mailq_time is in the future, this cannot be possible, so mail must be from last year
728 $mailq_time = int($local_year) - 1 .$mo_hash->{$mo}."$dd$hh$mi$ss";
729 }
731 return $mailq_time;
732 }
734 # vim:ts=4:shiftwidth:expandtab
736 1;