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 MIME::Base64;
34 use GOsaSI::GosaSupportDaemon;
36 use Exporter;
38 our @ISA = qw(Exporter);
40 my @events = (
41 "get_events",
42 "mailqueue_query",
43 "mailqueue_hold",
44 "mailqueue_unhold",
45 "mailqueue_requeue",
46 "mailqueue_del",
47 "mailqueue_header",
48 );
50 our @EXPORT = @events;
52 BEGIN {}
54 END {}
57 ###############################################################################
58 =over
60 =item B<get_events ()>
62 =over
64 =item description
66 Reports all provided functions.
68 =item parameter
70 None.
72 =item return
74 \@events - ARRAYREF - array containing all functions
76 =back
78 =back
80 =cut
81 ###############################################################################
82 sub get_events { return \@events; }
85 ###############################################################################
86 =over
88 =item B<mailqueue_query ($$)>
90 =over
92 =item description
94 Executes /usr/sbin/mailq, parse the informations and return them
96 =item parameter
98 $msg - STRING - complete GOsa-si message
99 $msg_hash - HASHREF - content of GOsa-si message in a hash
101 =item GOsa-si message xml content
103 None.
105 =item return
107 $out_msg - STRING - GOsa-SI valid xml message containing msg_id, msg_hold, msg_size, arrival_time, sender and recipient.
109 =back
111 =back
113 =cut
114 ###############################################################################
115 sub mailqueue_query {
116 my ($msg, $msg_hash) = @_;
117 my $header = @{$msg_hash->{'header'}}[0];
118 my $source = @{$msg_hash->{'source'}}[0];
119 my $target = @{$msg_hash->{'target'}}[0];
120 my $session_id = @{$msg_hash->{'session_id'}}[0];
121 # q_tag can be: msg_id | msg_hold | msg_size | arrival_time | sender | recipient
122 my $q_tag = exists $msg_hash->{'q_tag'} ? @{$msg_hash->{'q_tag'}}[0] : undef ;
123 # q_operator can be: eq | gt | lt
124 my $q_operator = exists $msg_hash->{'q_operator'} ? @{$msg_hash->{'q_operator'}}[0] : undef ;
125 my $q_value = exists $msg_hash->{'q_value'} ? @{$msg_hash->{'q_value'}}[0] : undef ;
126 my $error = 0;
127 my $error_string;
128 my $msg_id;
129 #my $msg_hold;
130 #my $msg_size;
131 #my $arrival_time;
132 my $sender;
133 my $recipient;
134 #my $status_message;
135 my $out_hash;
136 my $out_msg;
138 &main::daemon_log("DEBUG: run /usr/bin/mailq\n", 7);
139 my $result = qx("/usr/bin/mailq");
140 my @result_l = split(/([0-9A-Z]{10,12})/, $result);
142 if (length($result) == 0) {
143 $error = 1;
144 $error_string = "/usr/bin/mailq has no result";
145 &main::daemon_log("ERROR: $error_string : $msg", 1);
146 }
148 my $result_collection = {};
149 if (not $error) {
150 # parse information
151 my $result_length = @result_l;
152 my $j = 0;
153 for (my $i = 1; $i < $result_length; $i+=2) {
155 # Fetch and prepare all information
156 my $act_result;
157 $act_result->{'msg_id'} = $result_l[$i];
158 $result_l[$i+1] =~ /^([\!| ])\s+(\d+)\s+(\w{3}\s+\w{3}\s+\d+\s+\d+:\d+:\d+)\s+([\w.-]+@[\w.-]+)\s+/ ;
159 $act_result->{'msg_hold'} = $1 eq "!" ? 1 : 0 ;
160 $act_result->{'msg_size'} = $2;
161 $act_result->{'arrival_time'} = $3;
162 $act_result->{'sender'} = $4;
163 my @info_l = split(/\n/, $result_l[$i+1]);
164 $act_result->{'recipient'} = $info_l[2] =~ /([\w.-]+@[\w.-]+)/ ? $1 : 'unknown' ;
165 $act_result->{'msg_status'} = $info_l[1] =~ /^([\s\S]*)$/ ? $1 : 'unknown' ;
167 # If all query tags exists, perform the selection
168 my $query_positiv = 0;
169 if (defined $q_tag && defined $q_operator && defined $q_value) {
171 # Query for message id
172 if ( $q_tag eq 'msg_id') {
173 if (not $q_operator eq 'eq') {
174 &main::daemon_log("$session_id WARNING: query option '$q_operator' is not allowed with query tag '$q_tag'".
175 ", return return complete mail queue as fallback", 3);
176 &main::daemon_log("$session_id DEBUG: \n$msg", 9);
177 $query_positiv++;
178 } else {
179 if ( &_exec_op($act_result->{'msg_id'}, $q_operator, $q_value) ) {
180 $query_positiv++;
181 }
182 }
184 # Query for message size
185 } elsif ($q_tag eq 'msg_size') {
186 my $result_size = int($act_result->{'msg_size'});
187 my $query_size = int($q_value);
188 if ( &_exec_op($result_size, $q_operator, $query_size) ) {
189 $query_positiv++;
190 }
192 # Query for arrival time
193 } elsif ($q_tag eq 'arrival_time') {
194 my $result_time = int(&_parse_mailq_time($act_result->{'arrival_time'}));
195 my $query_time = int($q_value);
197 if ( &_exec_op($result_time, $q_operator, $query_time) ) {
198 $query_positiv++;
199 }
201 # Query for sender
202 }elsif ($q_tag eq 'sender') {
203 if (not $q_operator eq 'eq') {
204 &main::daemon_log("$session_id WARNING: query option '$q_operator' is not allowed with query tag '$q_tag'".
205 ", return return complete mail queue as fallback", 3);
206 &main::daemon_log("$session_id DEBUG: \n$msg", 9);
207 $query_positiv++;
208 } else {
209 if ( &_exec_op($act_result->{'sender'}, $q_operator, $q_value)) {
210 $query_positiv++;
211 }
212 }
214 # Query for recipient
215 } elsif ($q_tag eq 'recipient') {
216 if (not $q_operator eq 'eq') {
217 &main::daemon_log("$session_id WARNING: query option '$q_operator' is not allowed with query tag '$q_tag'".
218 ", return return complete mail queue as fallback", 3);
219 &main::daemon_log("$session_id DEBUG: \n$msg", 9);
220 $query_positiv++;
221 } else {
222 if ( &_exec_op($act_result->{'recipient'}, $q_operator, $q_value)) {
223 $query_positiv++;
224 }
225 }
226 }
228 # If no query tag exists, return all mails in mailqueue
229 } elsif ((not defined $q_tag) && (not defined $q_operator) && (not defined $q_value)) {
230 $query_positiv++;
232 # If query tags are not complete return error message
233 } elsif ((not defined $q_tag) || (not defined $q_operator) || (not defined $q_value)) {
234 $error++;
235 $error_string = "'mailqueue_query'-msg is not complete, some query tags (q_tag, q_operator, q_value) are missing";
236 &main::daemon_log("$session_id WARNING: $error_string", 3);
237 }
239 # If query was successful, add results to answer
240 if ($query_positiv) {
241 $j++;
242 foreach my $key (keys %{ $act_result }) {
243 $act_result->{$key} =~ s/\</\<\;/g;
244 $act_result->{$key} =~ s/\>/\>\;/g;
245 }
246 $result_collection->{$j} = $act_result;
247 }
248 }
249 }
251 #create outgoing msg
252 $out_hash = &main::create_xml_hash("answer_$session_id", $target, $source);
253 &add_content2xml_hash($out_hash, "session_id", $session_id);
254 &add_content2xml_hash($out_hash, "error", $error);
255 if (defined @{$msg_hash->{'forward_to_gosa'}}[0]){
256 &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]);
257 }
259 # add error infos to outgoing msg
260 if ($error) {
261 &add_content2xml_hash($out_hash, "error_string", $error_string);
262 $out_msg = &main::create_xml_string($out_hash);
264 # add mail infos to outgoing msg
265 } else {
266 my $collection_string = &db_res2xml($result_collection);
267 $out_msg = &main::create_xml_string($out_hash);
268 $out_msg =~ s/<\/xml>/$collection_string<\/xml>/
269 }
271 return $out_msg;
273 }
276 ###############################################################################
277 =over
279 =item B<mailqueue_hold ($$)>
281 =over
283 =item description
285 Executes '/usr/sbin/postsuper -h' and set mail to hold.
287 =item parameter
289 $msg - STRING - complete GOsa-si message
290 $msg_hash - HASHREF - content of GOsa-si message in a hash
292 =item GOsa-si message xml content
294 <msg_id> - STRING - postfix mail id
296 =item return
298 Nothing.
300 =back
302 =back
304 =cut
305 ###############################################################################
306 sub mailqueue_hold {
307 my ($msg, $msg_hash) = @_;
308 my $header = @{$msg_hash->{'header'}}[0];
309 my $source = @{$msg_hash->{'source'}}[0];
310 my $target = @{$msg_hash->{'target'}}[0];
311 my $session_id = @{$msg_hash->{'session_id'}}[0];
312 my $error = 0;
313 my $error_string;
315 # sanity check of input
316 if (not exists $msg_hash->{'msg_id'}) {
317 $error_string = "Message doesn't contain a XML tag 'msg_id";
318 &main::daemon_log("ERROR: $error_string : $msg", 1);
319 $error = 1;
320 } elsif (ref @{$msg_hash->{'msg_id'}}[0] eq "HASH") {
321 $error_string = "XML tag 'msg_id' is empty";
322 &main::daemon_log("ERROR: $error_string : $msg", 1);
323 $error = 1;
324 }
326 if (not $error) {
327 my @msg_ids = @{$msg_hash->{'msg_id'}};
328 foreach my $msg_id (@msg_ids) {
329 my $error = 0; # clear error status
331 # sanity check of each msg_id
332 if (not $msg_id =~ /^[0-9A-Z]{10,12}$/) {
333 $error = 1;
334 $error_string = "message ID is not valid ([0-9A-Z]{10,12}) : $msg_id";
335 &main::daemon_log("ERROR: $error_string : $msg", 1);
336 }
338 if (not $error) {
339 my $cmd = "/usr/sbin/postsuper -h $msg_id 2>&1";
340 &main::daemon_log("DEBUG: run $cmd", 7);
341 my $result = qx($cmd);
342 if ($result =~ /^postsuper: ([0-9A-Z]{10}): placed on hold/ ) {
343 &main::daemon_log("INFO: Mail $msg_id placed on hold", 5);
344 } elsif ($result eq "") {
345 &main::daemon_log("INFO: Mail $msg_id is alread placed on hold", 5);
347 } else {
348 &main::daemon_log("ERROR: '$cmd' failed : $result", 1);
349 }
350 }
351 }
352 }
354 return;
355 }
357 ###############################################################################
358 =over
360 =item B<mailqueue_unhold ($$)>
362 =over
364 =item description
366 Executes '/usr/sbin/postsuper -H' and set mail to unhold.
368 =item parameter
370 $msg - STRING - complete GOsa-si message
371 $msg_hash - HASHREF - content of GOsa-si message in a hash
373 =item GOsa-si message xml content
375 <msg_id> - STRING - postfix mail id
377 =item return
379 Nothing.
381 =back
383 =back
385 =cut
386 ###############################################################################
387 sub mailqueue_unhold {
388 my ($msg, $msg_hash) = @_;
389 my $header = @{$msg_hash->{'header'}}[0];
390 my $source = @{$msg_hash->{'source'}}[0];
391 my $target = @{$msg_hash->{'target'}}[0];
392 my $session_id = @{$msg_hash->{'session_id'}}[0];
393 my $error = 0;
394 my $error_string;
396 # sanity check of input
397 if (not exists $msg_hash->{'msg_id'}) {
398 $error_string = "Message doesn't contain a XML tag 'msg_id'";
399 &main::daemon_log("ERROR: $error_string : $msg", 1);
400 $error = 1;
401 } elsif (ref @{$msg_hash->{'msg_id'}}[0] eq "HASH") {
402 $error_string = "XML tag 'msg_id' is empty";
403 &main::daemon_log("ERROR: $error_string : $msg", 1);
404 $error = 1;
405 }
407 if (not $error) {
408 my @msg_ids = @{$msg_hash->{'msg_id'}};
409 foreach my $msg_id (@msg_ids) {
410 my $error = 0; # clear error status
412 # sanity check of each msg_id
413 if (not $msg_id =~ /^[0-9A-Z]{10,12}$/) {
414 $error = 1;
415 $error_string = "message ID is not valid ([0-9A-Z]{10,12}) : $msg_id";
416 &main::daemon_log("ERROR: $error_string : $msg", 1);
417 }
419 if (not $error) {
420 my $cmd = "/usr/sbin/postsuper -H $msg_id 2>&1";
421 &main::daemon_log("DEBUG: run $cmd\n", 7);
422 my $result = qx($cmd);
423 if ($result =~ /^postsuper: ([0-9A-Z]{10}): released from hold/ ) {
424 &main::daemon_log("INFO: Mail $msg_id released from on hold", 5);
425 } elsif ($result eq "") {
426 &main::daemon_log("INFO: Mail $msg_id is alread released from hold", 5);
428 } else {
429 &main::daemon_log("ERROR: '$cmd' failed : $result", 1);
430 }
432 }
433 }
434 }
436 return;
437 }
439 ###############################################################################
440 =over
442 =item B<mailqueue_requeue ($$)>
444 =over
446 =item description
448 Executes '/usr/sbin/postsuper -r' and requeue the mail.
450 =item parameter
452 $msg - STRING - complete GOsa-si message
453 $msg_hash - HASHREF - content of GOsa-si message in a hash
455 =item GOsa-si message xml content
457 <msg_id> - STRING - postfix mail id
459 =item return
461 Nothing.
463 =back
465 =back
467 =cut
468 ###############################################################################
469 sub mailqueue_requeue {
470 my ($msg, $msg_hash) = @_;
471 my $header = @{$msg_hash->{'header'}}[0];
472 my $source = @{$msg_hash->{'source'}}[0];
473 my $target = @{$msg_hash->{'target'}}[0];
474 my $session_id = @{$msg_hash->{'session_id'}}[0];
475 my @msg_ids = @{$msg_hash->{'msg_id'}};
476 my $error = 0;
477 my $error_string;
479 # sanity check of input
480 if (not exists $msg_hash->{'msg_id'}) {
481 $error_string = "Message doesn't contain a XML tag 'msg_id'";
482 &main::daemon_log("ERROR: $error_string : $msg", 1);
483 $error = 1;
484 } elsif (ref @{$msg_hash->{'msg_id'}}[0] eq "HASH") {
485 $error_string = "XML tag 'msg_id' is empty";
486 &main::daemon_log("ERROR: $error_string : $msg", 1);
487 $error = 1;
488 }
490 if (not $error) {
491 my @msg_ids = @{$msg_hash->{'msg_id'}};
492 foreach my $msg_id (@msg_ids) {
493 my $error = 0; # clear error status
495 # sanity check of each msg_id
496 if (not $msg_id =~ /^[0-9A-Z]{10,12}$/) {
497 $error = 1;
498 $error_string = "message ID is not valid ([0-9A-Z]{10,12}) : $msg_id";
499 &main::daemon_log("ERROR: $error_string : $msg", 1);
500 }
502 if (not $error) {
503 my $cmd = "/usr/sbin/postsuper -r $msg_id 2>&1";
504 &main::daemon_log("DEBUG: run '$cmd'", 7);
505 my $result = qx($cmd);
506 if ($result =~ /^postsuper: ([0-9A-Z]{10}): requeued/ ) {
507 &main::daemon_log("INFO: Mail $msg_id requeued", 5);
508 } elsif ($result eq "") {
509 &main::daemon_log("WARNING: Cannot requeue mail '$msg_id', mail not found!", 3);
511 } else {
512 &main::daemon_log("ERROR: '$cmd' failed : $result", 1);
513 }
515 }
516 }
517 }
519 return;
520 }
523 ###############################################################################
524 =over
526 =item B<mailqueue_del ($$)>
528 =over
530 =item description
532 Executes '/usr/sbin/postsuper -d' and deletes mail from queue.
534 =item parameter
536 $msg - STRING - complete GOsa-si message
537 $msg_hash - HASHREF - content of GOsa-si message in a hash
539 =item GOsa-si message xml content
541 <msg_id> - STRING - postfix mail id
543 =item return
545 Nothing.
547 =back
549 =back
551 =cut
552 ###############################################################################
553 sub mailqueue_del {
554 my ($msg, $msg_hash) = @_;
555 my $header = @{$msg_hash->{'header'}}[0];
556 my $source = @{$msg_hash->{'source'}}[0];
557 my $target = @{$msg_hash->{'target'}}[0];
558 my $session_id = @{$msg_hash->{'session_id'}}[0];
559 my @msg_ids = @{$msg_hash->{'msg_id'}};
560 my $error = 0;
561 my $error_string;
563 # sanity check of input
564 if (not exists $msg_hash->{'msg_id'}) {
565 $error_string = "Message doesn't contain a XML tag 'msg_id'";
566 &main::daemon_log("ERROR: $error_string : $msg", 1);
567 $error = 1;
568 } elsif (ref @{$msg_hash->{'msg_id'}}[0] eq "HASH") {
569 $error_string = "XML tag 'msg_id' is empty";
570 &main::daemon_log("ERROR: $error_string : $msg", 1);
571 $error = 1;
572 }
574 if (not $error) {
575 my @msg_ids = @{$msg_hash->{'msg_id'}};
576 foreach my $msg_id (@msg_ids) {
577 my $error = 0; # clear error status
579 # sanity check of each msg_id
580 if (not $msg_id =~ /^[0-9A-Z]{10,12}$/) {
581 $error = 1;
582 $error_string = "message ID is not valid ([0-9A-Z]{10,12}) : $msg_id";
583 &main::daemon_log("ERROR: $error_string : $msg", 1);
584 }
586 if (not $error) {
587 my $cmd = "/usr/sbin/postsuper -d $msg_id 2>&1";
588 &main::daemon_log("DEBUG: run '$cmd'", 7);
589 my $result = qx($cmd);
590 if ($result =~ /^postsuper: ([0-9A-Z]{10}): removed/ ) {
591 &main::daemon_log("INFO: Mail $msg_id deleted", 5);
592 } elsif ($result eq "") {
593 &main::daemon_log("WARNING: Cannot remove mail '$msg_id', mail not found!", 3);
595 } else {
596 &main::daemon_log("ERROR: '$cmd' failed : $result", 1);
597 }
599 }
600 }
601 }
603 return;
604 }
606 ###############################################################################
607 =over
609 =item B<mailqueue_header ($$)>
611 =over
613 =item description
615 Executes 'postcat -q', parse the informations and return them.
617 =item parameter
619 $msg - STRING - complete GOsa-si message
620 $msg_hash - HASHREF - content of GOsa-si message in a hash
622 =item GOsa-si message xml content
624 <msg_id> - STRING - postfix mail id
626 =item return
628 $out_msg - STRING - GOsa-si valid xml message containing recipient, sender and subject.
630 =back
632 =back
634 =cut
635 ###############################################################################
636 sub mailqueue_header {
637 my ($msg, $msg_hash) = @_;
638 my $header = @{$msg_hash->{'header'}}[0];
639 my $source = @{$msg_hash->{'source'}}[0];
640 my $target = @{$msg_hash->{'target'}}[0];
641 my $session_id = @{$msg_hash->{'session_id'}}[0];
642 my $error = 0;
643 my $error_string;
644 my $sender;
645 my $recipient;
646 my $subject;
647 my $out_hash;
648 my $out_msg;
650 # sanity check of input
651 if (not exists $msg_hash->{'msg_id'}) {
652 $error_string = "Message doesn't contain a XML tag 'msg_id'";
653 &main::daemon_log("ERROR: $error_string : $msg", 1);
654 $error = 1;
655 } elsif (ref @{$msg_hash->{'msg_id'}}[0] eq "HASH") {
656 $error_string = "XML tag 'msg_id' is empty";
657 &main::daemon_log("ERROR: $error_string : $msg", 1);
658 $error = 1;
659 }
661 # sanity check of each msg_id
662 my $msg_id;
663 if (not $error) {
664 $msg_id = @{$msg_hash->{'msg_id'}}[0];
665 if (not $msg_id =~ /^[0-9A-Z]{10,12}$/) {
666 $error = 1;
667 $error_string = "message ID is not valid ([0-9A-Z]{10,12}) : $msg_id";
668 &main::daemon_log("ERROR: $error_string : $msg", 1);
669 }
670 }
672 # parsing information
673 my $msg_header;
674 if (not $error) {
675 my $cmd = "postcat -q $msg_id";
676 &main::daemon_log("DEBUG: run '$cmd'", 7);
677 my $result = qx($cmd);
679 my @header_l = split(/\n\n/, $result);
680 $msg_header = $header_l[0];
681 }
683 # create outgoing msg
684 $out_hash = &main::create_xml_hash("answer_$session_id", $target, $source);
685 &add_content2xml_hash($out_hash, "session_id", $session_id);
686 &add_content2xml_hash($out_hash, "error", $error);
687 if (defined @{$msg_hash->{'forward_to_gosa'}}[0]){
688 &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]);
689 }
691 # add error infos to outgoing msg
692 if ($error) {
693 &add_content2xml_hash($out_hash, "error_string", $error_string);
694 $out_msg = &main::create_xml_string($out_hash);
696 # add mail infos to outgoing msg
697 } else {
698 #&add_content2xml_hash($out_hash, "msg_header", &decode_base64($msg_header));
699 &add_content2xml_hash($out_hash, "msg_header", $msg_header);
700 $out_msg = &main::create_xml_string($out_hash);
701 }
703 return $out_msg;
704 }
706 sub _exec_op {
707 my ($a, $op, $b) = @_ ;
708 my $res;
710 if ($op eq "eq") {
711 $res = $a =~ /$b/ ? 1 : 0 ;
712 } elsif ($op eq "gt") {
713 $res = $a > $b ? 1 : 0 ;
714 } elsif ($op eq "lt") {
715 $res = $a < $b ? 1 : 0 ;
716 }
718 return $res;
719 }
721 my $mo_hash = { "Jan"=>'01', "Feb"=>'02',"Mar"=>'03',"Apr"=>'04',"May"=>'05',"Jun"=>'06',
722 "Jul"=>'07',"Aug"=>'08',"Sep"=>'09',"Oct"=>'10',"Nov"=>'11',"Dec"=>'12'};
724 sub _parse_mailq_time {
725 my ($time) = @_ ;
727 my $local_time = &get_time();
728 my $local_year = substr($local_time,0,4);
730 my ($dow, $mo, $dd, $date) = split(/\s/, $time);
731 my ($hh, $mi, $ss) = split(/:/, $date);
732 my $mailq_time = $local_year.$mo_hash->{$mo}."$dd$hh$mi$ss";
734 # This is realy nasty
735 if (int($local_time) < int($mailq_time)) {
736 # Mailq_time is in the future, this cannot be possible, so mail must be from last year
737 $mailq_time = int($local_year) - 1 .$mo_hash->{$mo}."$dd$hh$mi$ss";
738 }
740 return $mailq_time;
741 }
743 # vim:ts=4:shiftwidth:expandtab
745 1;