Seit einigen Tagen schlage ich mich ja nun schon immer zwischendurch mal eine halbe Stunde mit dem Autoreply-Messenger des ISPCP Systems rum. Wir benutzen das ISPCP als eine Komponente aus einem Konstrukt von OpenSource Systemen welche ineinander greifen. Vom ISPCP benutzen wir in diesem Spezialfall hauptsächlich den Daemon welcher prima im Hintergrund EMailfächer etc. anlegen kann.
Eher suboptimal ist da die Tatsache das das Perl-Skript, welches den Autoreply Messenger steuert, ein arges Problem kriegt sobald man in seinen Mailserver den Amavis einbindet. Das ganze produziert zwar noch den gewünschten Autoresponder, leider aber auch unschönerweise eine Mail Delivery Failure Mail mit einer Meldung über ein abgesoffenes Perlskript. Da der offizielle Fix erst im RC3 vorgesehen ist (und RC4 oder Release ist da auch noch möglich) habe ich nun, trotz eher durschnittlicher Perlkenntnisse, mich dranbegeben und habe, so meine ich zumindest zu glauben, den Fehler gefunden und zumindest soweit korrigiert das diese Fehlermail entfällt. Hier meine Lösung:
Generell vorweg möchte ich anmerken das ich an dem ispcp-arpl-msgr Skript einige Änderungen zum Testen ein- und wieder ausgebaut habe, sowohl funktionell als auch z.B. Debugausgaben. Diese habe ich zwar immer zwischendurch wieder entfernt bzw. auf das Original geändert, aber trotzdem will ich nicht ausschließen das dadurch ggf. bei mir die Zeilennummern sich ein wenig verschoben haben. Aus diesem Grund nehme ich dann immer ganze Ausschnitte aus dem Bereich den ich geändert habe und verzichte auf ausschließliche Zeilenangaben wie “Füge in Zeile 783 folgendes ein:” – also wer es nachbauen will bitte vergleichen mit den Auszügen die ich mitgebe!
Das Problem:
Sobald man in seine ISPCP Standardkonfiguration (also Postfix als Mailserver) den Amavis einkonfiguriert, funktioniert das Autoreply-Skript von ISPCP nicht mehr richtig. Zwar kommen die Autoreply Mails an, aber der Absender erhält zusätzlich zu dem Autoreply auch noch immer eine “Mail Delivery Failure” mit dem Hinweis eines ungültig beendeten Skriptes:
: Command died with status 255:
“/var/www/ispcp/engine/messager/ispcp-arpl-msgr”
Der Hintergrund:
Das generelle des Autoreply-Skriptes in Zusammenspiel mit Amavis ist, so mutmasse ich zumindest, der Rückgabeweg den eine Mail vom Amavis nimmt in Verbindung mit der Art wie ISPCP den Aufruf des Autoreplyskriptes realisiert hat. Der typische Ansatz von Postfix und Amavis verfolgt den Grundgedanken das Amavis als Content-Filter implementiert wird, welcher auf einem bestimmten Port (üblicherweise 10024) lauscht und an den Postfix eingegangene Mails durchreicht. Nach dem Testen von Amavis reicht dieser die Mail wieder an eine SMTP Instanz des Postfix die Mail wieder zurück auf welcher der Content-Filter per speziellen Konfigurationsparameter für diese Instanz abgeschaltet wurde um keinen Loop zu erzeugen. Diese Instanz läuft meistens lokal auf Port 10025, das sind so die typischen Ports die man in allen Howto’s findet.
Dies sieht dann im Regefall wie folgt aus:
in der main.cf von Postfix findet sich etwas das wie folgt aussieht:
content_filter = amavis:[127.0.0.1]:10024
Und in der master.cf findet man meist so etwas:
# ====================================================================
# ISPCP Ï~I OMEGA configuration
# ====================================================================
# AMaViS => Antivir / Antispam
amavis unix – – n – 2 smtp
-o smtp_data_done_timeout=1200
-o smtp_send_xforward_command=yes
-o disable_dns_lookups=yeslocalhost:10025 inet n – n – – smtpd
-o content_filter=
-o local_recipient_maps=
-o relay_recipient_maps=
-o smtpd_restriction_classes=
-o smtpd_client_restrictions=
-o smtpd_helo_restrictions=
-o smtpd_sender_restrictions=
-o smtpd_recipient_restrictions=permit_mynetworks,reject
-o smtpd_override_options=no_address_mappings
-o mynetworks=127.0.0.0/8
-o strict_rfc821_envelopes=yes# ISPCP autoresponder
ispcp-arpl unix – n n – – pipe
flags=O user=vmail argv=/var/www/ispcp/engine/messager/ispcp-arpl-msgr
-o content_filter=
-o local_recipient_maps=
-o relay_recipient_maps=
-o smtpd_restriction_classes=
-o smtpd_client_restrictions=
-o smtpd_helo_restrictions=
-o smtpd_sender_restrictions=
-o smtpd_recipient_restrictions=permit_mynetworks,reject
-o smtpd_override_options=no_address_mappings
-o mynetworks=127.0.0.0/8
-o strict_rfc821_envelopes=yes
Als zweiter Faktor der hier beiträgt ist die Art und Weise wie ISPCP den Autoresponder implementiert. Hier wird bei Aktivierung des Autoreplys vom ISPCP-Daemon eine Änderung in der Alias-Table des Postfix vorgenommen und die typische Zuordnung von EMailadresse -> interne Zuordnung (in Fall von ISPCP natürlich lustigerweise meist identisch wenn man keine Weiterleitungspostfächer hat) um einen Eintrag erweitert, welcher die EMail zusätzlich immer an user@ispcp-arpl.domain.tld ausliefert (auch wenn diese Subdomain nicht konfiguriert ist). Ein typischer Eintrag sähe also wie folgt aus:
“hans.mueller@foo.bar hans.mueller@foo.bar,hans.mueller@ispcp-arpl.foo.bar”
Das sorgt dafür, das der Empfänger die Mail zwar bekommt, aber eine Kopie davon auch an den Sonderfall ispcp-arpl geschickt wird.
“Sonderfall ispcp-arpl” da in der transport Konfiguration eingetragen ist:
#
# MTA Managment Transport List;
#
# Please do NOT edit it manually;
#
ispcp-arpl.foo.bar ispcp-arpl:
Das führt also dazu das Mails an die domain ispcp-arpl.foo.bar (was übrigens ebenfalls für jede angelegte Domain von ISPCP Daemon dann dort ergänzt wird, die ispcp-arpl.domain.tld existiert also immer für den Mailserver intern) an den Eintrag ispcp-arpl aus der master.cf weitergegeben werden, und dieser war, zur Erinnerung:
# ISPCP autoresponder
ispcp-arpl unix – n n – – pipe
flags=O user=vmail argv=/var/www/ispcp/engine/messager/ispcp-arpl-msgr
-o content_filter=
-o local_recipient_maps=
-o relay_recipient_maps=
-o smtpd_restriction_classes=
-o smtpd_client_restrictions=
-o smtpd_helo_restrictions=
-o smtpd_sender_restrictions=
-o smtpd_recipient_restrictions=permit_mynetworks,reject
-o smtpd_override_options=no_address_mappings
-o mynetworks=127.0.0.0/8
-o strict_rfc821_envelopes=yes
Gut – die Mail geht also an das Skript /var/www/ispcp/engine/messager/ispcp-arpl-msgr. Und eben dieses ist ja laut Postfix Meldung auch das, welches absäuft.
Soweit die Vorgeschichte, die man kennen muss. Jetzt passiert nämlich folgendes eher suboptimales. Beim ersten “rausgeben” der Mail bekommt der Reply-Manager diese mit, und zwar mit der Original-Empfängeradresse. Allerdings sobald man den Amavis reinkonfiguriert erhält der Reply-Manager diese scheinbar ein zweites Mal (wir erinnerung uns: Amavis gibt die Mail nach dem Testen an eine Instanz des Postfix auf Port 10025 zurück). Diesmal aber ist schon der Empfänger, quasi der Header der Mail verändert und wird nicht als verändert erkannt (klar, kommt ja so aus nem anderem Programm als neue Mail an Postfix zurück) und dort ist der Empfänger dann auf benutzer@ispcp-arpl.domain.tld verändert. Und eben dieser Umstand löst scheinbar die Katastrophe dann aus. Denn eine SQL Abfrage im Autoreply-Skript versucht den Autorespondertext zu der Domain des Empfängers zu ermitteln, allerdings wird hierbei das ispcp-arpl nicht aus der Domain entfernt. So erzeugt die SQL Abfrage aus dem Autoreplyskript aus Zeile 781:
$sql = "select mail_auto_respond from mail_users LEFT JOIN subdomain ON mail_users.sub_id=subdomain.subdomain_id LEFT JOIN domain ON subdomain.domain_id=domain.domain_id where mail_acc = '$user' and (CONCAT(subdomain_name,'.',domain_name) = '$edmn');";
ein Resultset welches leer ist. Denn: $edmn enthält nicht die eigentliche Domain sondern die Domain mit dem Zusatz ispcp-arpl(.domain.tld) auf einmal davor. Dazu gibt es in den ISPCP Datenbanken im Regefall keinen Eintrag und somit erzeugt das SQL-Statement an dieser Stelle nur noch ein leeres Resultset.
Die anschließenden Folgen sind klar. Der Autoreply-Text kann mangels Results aus der Datenbank nicht abgefragt werden, ein paar Perl Variablen sind nicht initialisiert, die Fehlerbehandlung im Skript ist nicht ausreichend ausgereift für diesen Fall, das Skript geht mit einem Fehler den Bach runter. Der Postfix merkt das das Skript nicht mit Resultcode 0 beendet ist, und generiert die Meldung über das verstorbe Perlskript.
Die Lösung
…ist relativ simpel vom Ansatz: Wir brauchen einfach nur eine bessere Behandlung für den Fall das das Perl-Skript einmal an dieser Stelle versagt.
Jetzt muss ich zugeben trotz das ich gelernter Softwareentwickler bin, bin ich nicht wirklich tief in der Thematik Perl drin. Grundlagen halt, aber kein sonderlicher Überblick über alle Feinheiten und Module die es da so geben mag. Zudem ist das ein erster Fix der das Problem für mich beseitigen soll, ich hoffe auf eine saubere Lösung im RC3 des ISPCP von Leuten die deutlich “Perl-fester” sind als ich. Zudem bin ich ja selbsständig, Zeit ist Geld, und da eine sauberere Lösung zu erwarten ist reichte mir erst einmal eine die geht.
Aus diesem Grunde habe ich nicht lange an dem Grundsatz dieses Zusammenspiels geschraubt sondern einfach nur eine Behandlung eingebaut die prüft ob der Autoreplytext länger als 0 Zeichen ist ( den ist er die Variable nicht initialisiert wie in diesem Fall ist er nicht größer 0). In meiner rosaroten Wattebäuschenwelt gehe ich jetzt mal einfach davon aus das jeder der eine Autoreply-Funktion benutzt einen Autoantworttext einträgt der mindestens aus einem Zeichen besteht, alles andere wäre vielleicht möglich, aber sinnlos (dann gibts halt keine Autoreply *schulterzuck*).
Daher habe ich im ispcp-arpl-msgr Skript in Zeile 794 ergänzt: “if (length($auto_message) > 0) {” so das es dann in dem Bereich wie folgt aussieht:
($rs, $ref) = doSQL($sql); return $rs if ($rs != 0); $ref = @$ref[0]; my $auto_message = @$ref[0]; if (length($auto_message) > 0) { # if($auto_message ne '_no_') { if($name) { $mail_to = "\"".$name."\" "."<".$to_ma->address.">"; } else{ $mail_to = $to_ma->address; } my $out = new MIME::Entity;
und, da wir hier eine neue If-Bedingung eingefügt haben, natürlich in Zeile 835 eine schließende Klammer für das If, so das es dann dort wie folgt aussieht:
$out -> attach( Type => "message/rfc822", Description => "Original Message", Data => $msg ); open MAIL, "| /usr/sbin/sendmail -t -oi"; $out -> print(\*MAIL); close MAIL; # } } push_el(\@main::el, 'arpl_msgr_engine()', 'Ending...'); } else { $buffer = $to_ma->address; $buffer =~ s/"//g; }
Mit dieser Modifikation verschwindet die Fehlermeldungsmail vom Postfix da geprüft wird ob die Länge des Autoreply-Textes größer 0 ist (was es nicht ist sollte die Variable nicht initialisert sein) – zumindest bei mir gehts. Sollte dies nicht der Fall sein wird nicht mehr, wie vorher, versucht trotzdem eine Mail zu generieren was dann fehlschlägt wenn er versucht den Body aus dem Autoreply-Text zu erzeugen da er dort auf eine Nicht-Initialiserte Variable stößt.
Nebenschauplätze
Zwei verschmerzbare Nachteile hat dieser Ansatz im ersten Moment:
- Es wird kein Autoreply mehr geschickt wenn der Benutzer die Autoreply-Funktion aktiviert, aber keinen Text für den Autoreply einträgt (geht das überhaupt in der ISPCP Oberfläche das Textfeld leerzulassen ? Wir modifzieren die Datenbank von Hand und stossen dann den Daemon an, daher habe ich das gar nicht geprüft)
- Das Skript erzeugt immernoch sowas wie eine “Warning” beim Durchführen das er dann bei der Abfrage nach der Länge des Autoreplytextes auf eine leere Variable gestossen ist. Da diese aber am StdErr ausgeben wird, und dieser in die /var/log/ispcp/ispcp-arpl-msgr/ispcp-arpl-msgr.stderr geschrieben wird, also nur bei uns lokal auf dem Server landet und nicht mehr als Mail zum Absender geschickt wird, kann man diese erstmal getrost ignorieren. Da die StdErr bei jedem Durchführen des Autoreplyskriptes überschrieben wird, quillt die Datei auch nicht bombastisch an sondern hat halt nur immer ein paar Byte. Wenn man den Debugmodus abschaltet taucht diese, glaube ich, auch gar nicht mehr auf.
Und abschließend generell noch als Bemerkung: Wer meine Auszüge gründlich gelesen hat bestimmt ein auskommentiertes ” # if($auto_message ne ‘_no_’) {” und die dazugehörige auskommentierte Klammer etwas weiter unten im Quellcode gefunden. Das stammt noch aus meiner Rumtesterei, habe ich aber so stehen lassen, weil:
- Nur wenn der Autoreply deaktiviert ist, ist der Autoreplytext “_no_”. Dann aber geht die Mail eh nicht mehr an das Skript. Wenn die Mail an das Skript geht, ist der Autoreplytext sowie immer alles andere außer “_no_”, also immer n(ot)e(qual).
- Auch eine nicht initialiserte Variable ist “not equal” zu “_no_”. Er geht also auch in dem Fall in die Bedinung…
Zusammengefasst: Diese Abfrage bringt quasi: Gar nix!
Über Kommentare, Anregungen, Kritik bzw. Bedenken bei dem schnellen Lösungsansatz wie ich Ihn gewählt habe würde ich mich natürlich freuen, einfach in die Kommentare zu diesem Post
Abschließend: Mein komplettes modifiziertes ispcp-arpl-msgr Skript
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 | #!/usr/bin/perl # ispCP ? (OMEGA) a Virtual Hosting Control Panel # Copyright (c) 2001-2006 by moleSoftware GmbH # http://www.molesoftware.com # Copyright (c) 2006-2007 by isp Control Panel # http://isp-control.net # # # License: # This program is free software; you can redistribute it and/or # modify it under the terms of the MPL Mozilla Public License # as published by the Free Software Foundation; either version 1.1 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MPL Mozilla Public License for more details. # # You may have received a copy of the MPL Mozilla Public License # along with this program. # # An on-line copy of the MPL Mozilla Public License can be found # http://www.mozilla.org/MPL/MPL-1.1.html # # # The ispCP ? Home Page is at: # # http://isp-control.net # # # common code: BEGIN # BEGIN { my @needed = (strict, warnings, IO::Socket, DBI, DBD::mysql, MIME::Entity, MIME::Parser, Crypt::CBC, Crypt::Blowfish, MIME::Base64, Mail::Address, Term::ReadPassword); my ($mod, $mod_err, $mod_missing) = ('', '_off_', ''); for $mod (@needed) { if (eval "require $mod") { $mod -> import(); } else { print STDERR "\nCRITICAL ERROR: Module [$mod] WAS NOT FOUND !\n" ; $mod_err = '_on_'; if ($mod_missing eq '') { $mod_missing .= $mod; } else { $mod_missing .= ", $mod"; } } } if ($mod_err eq '_on_') { print STDERR "\nModules [$mod_missing] WAS NOT FOUND in your system...\n"; exit 1; } else { $| = 1; } } $main::el_sep = "\t#\t"; @main::el = (); sub push_el { my ($el, $sub_name, $msgDebug) = @_; push @$el, "$sub_name".$main::el_sep."$msgDebug"; if (defined($main::engine_debug)) { print STDOUT "push_el() sub_name: $sub_name, msg: $msgDebug\n"; } } sub pop_el { my ($el) = @_; my $data = pop @$el; if (!defined($data)) { if (defined($main::engine_debug)) { print STDOUT "DEBUG: pop_el() Empty 'EL' Stack !\n"; } return undef; } my ($sub_name, $msgDebug) = split(/$main::el_sep/, $data); if (defined($main::engine_debug)) { print STDOUT "DEBUG: pop_el() sub_name: $sub_name, msg: $msgDebug\n"; } return $data; } sub dump_el { my ($el, $fname) = @_; my $res = undef; if ($fname ne 'stdout') { $res = open(FP, ">>", $fname); if (!defined($res)) { return 0; } } my $el_data = undef; # #if ($fname eq 'stdout') { # # print STDOUT "%-20s | %s\n", ' function', 'message'; # # print STDOUT "---------------------|---------------------------------------------------------------\n"; # #} else { # # print FP "%-20s | %s\n", ' function', 'message'; # # print FP "---------------------|---------------------------------------------------------------\n"; # #} # while (defined($el_data = pop_el(\@main::el))) { my ($sub_name, $msgDebug) = split(/$main::el_sep/, $el_data); if ($fname eq 'stdout') { printf STDOUT "%-30s | %s\n", $sub_name, $msgDebug; } else { printf FP "%-30s | %s\n", $sub_name, $msgDebug; } } close(FP); } # Global variables; $main::db_host = undef; $main::db_user = undef; $main::db_pwd = undef; $main::db_name = undef; @main::db_connect = (); $main::db = undef; # sub doSQL { my ($sql) = @_; my $qr = undef; push_el(\@main::el, 'doSQL()', 'Starting...'); if (!defined($sql) || ($sql eq '')) { push_el(\@main::el, 'doSQL()', 'ERROR: Undefined SQL query !'); return (-1, ''); } if (!defined($main::db) || !ref($main::db)) { $main::db = DBI -> connect(@main::db_connect, {PrintError => 0}); if ( !defined($main::db) ) { push_el( \@main::el, 'doSQL()', 'ERROR: Unable to connect SQL server !' ); return (-1, ''); } } if ($sql =~ /select/i) { $qr = $main::db -> selectall_arrayref($sql); } else { $qr = $main::db -> do($sql); } if (defined($qr)) { push_el(\@main::el, 'doSQL()', 'Ending...'); return (0, $qr); } else { push_el(\@main::el, 'doSQL()', 'ERROR: Incrrect SQL Query -> '.$main::db -> errstr); return (-1, ''); } } sub get_file { my ($fname) = @_; push_el(\@main::el, 'get_file()', 'Starting...'); if (!defined($fname) || ($fname eq '')) { push_el( \@main::el, 'get_file()', "ERROR: Undefined input data, fname: |$fname| !" ); return (-1, ''); } if (! -e $fname) { push_el( \@main::el, 'get_file()', "ERROR: File '$fname' does not exist !" ); return (-1, ''); } my $res = open(F, '< ', $fname); if (!defined($res)) { push_el( \@main::el, 'get_file()', "ERROR: Can't open '$fname' for reading !" ); return (-1, ''); } my @fdata = <f>;</f> close(F); my $line = join('', @fdata); push_el(\@main::el, 'get_file()', 'Ending...'); return (0, $line); } sub del_file { my ($fname) = @_; push_el(\@main::el, 'del_file()', 'Starting...'); if (!defined($fname) || ($fname eq '')) { push_el( \@main::el, 'del_file()', "ERROR: Undefined input data, fname: |$fname| !" ); return -1; } if (! -e $fname) { push_el( \@main::el, 'del_file()', "ERROR: File '$fname' does not exist !" ); return -1; } my $res = unlink ($fname); if ($res != 1) { push_el( \@main::el, 'del_file()', "ERROR: Can't unlink '$fname' !" ); return -1; } push_el(\@main::el, 'del_file()', 'Ending...'); return 0; } # Global variables %main::cfg = (); $main::cfg_file = '/etc/ispcp/ispcp.conf'; $main::cfg_re = '^([\_A-Za-z0-9]+) *= *([^\n\r]*)[\n\r]'; use FindBin; use lib "$FindBin::Bin/"; require 'ispcp-db-keys.pl'; sub encrypt_db_password { my ($pass) = @_; push_el(\@main::el, 'encrypt_db_password()', 'Starting...'); if (!defined($pass) || $pass eq '') { push_el(\@main::el, 'encrypt_db_password()', 'ERROR: Undefined input data ($pass)...'); return (1, ''); } my $cipher = Crypt::CBC -> new( { 'key' => $main::db_pass_key, 'keysize' => 32, 'cipher' => 'Blowfish', 'iv' => $main::db_pass_iv, 'regenerate_key' => 0, 'padding' => 'space', 'prepend_iv' => 0 } ); my $ciphertext = $cipher->encrypt($pass); my $encoded = encode_base64($ciphertext); chop($encoded); push_el(\@main::el, 'encrypt_db_password()', 'Ending...'); return (0, $encoded); } sub decrypt_db_password { my ($pass) = @_; push_el(\@main::el, 'decrypt_db_password()', 'Starting...'); if (!defined($pass) || $pass eq '') { push_el(\@main::el, 'decrypt_db_password()', 'ERROR: Undefined input data ($pass)...'); return (1, ''); } my $cipher = Crypt::CBC -> new( { 'key' => $main::db_pass_key, 'keysize' => 32, 'cipher' => 'Blowfish', 'iv' => $main::db_pass_iv, 'regenerate_key' => 0, 'padding' => 'space', 'prepend_iv' => 0 } ); my $decoded = decode_base64("$pass\n"); my $plaintext = $cipher -> decrypt($decoded); push_el(\@main::el, 'decrypt_db_password()', 'Ending...'); return (0, $plaintext); } sub setup_main_vars { push_el(\@main::el, 'setup_main_vars()', 'Starting...'); # # Database backend vars; # $main::db_host = $main::cfg{'DATABASE_HOST'}; $main::db_user = $main::cfg{'DATABASE_USER'}; $main::db_pwd = $main::cfg{'DATABASE_PASSWORD'}; if ($main::db_pwd ne '') { my $rs = undef; ($rs, $main::db_pwd) = decrypt_db_password($main::db_pwd); } $main::db_name = $main::cfg{'DATABASE_NAME'}; @main::db_connect = ( "DBI:mysql:$main::db_name:$main::db_host", $main::db_user, $main::db_pwd ); push_el(\@main::el, 'setup_main_vars()', 'Ending...'); return 0; } sub get_conf { push_el(\@main::el, 'get_conf()', 'Starting...'); my ($rs, $fline) = get_file($main::cfg_file); return -1 if ($rs != 0); my @frows = split(/\n/, $fline); my $i = ''; for ($i = 0; $i < scalar(@frows); $i++) { $frows[$i] = "$frows[$i]\n"; if ($frows[$i] =~ /$main::cfg_re/) { $main::cfg{$1} = $2; } } return -1 if (setup_main_vars() != 0); push_el(\@main::el, 'get_conf()', 'Ending...'); return 0; } my $rs = get_conf(); if ($rs != 0) { if( -d "/var/log/ispcp/ispcp-arpl-msgr/") { $main::ispcp_arpl_msgr_el = "/var/log/ispcp/ispcp-arpl-msgr/ispcp-arpl-msgr.el"; $main::ispcp_arpl_msgr_stdout = "/var/log/ispcp/ispcp-arpl-msgr/ispcp-arpl-msgr.stdout"; $main::ispcp_arpl_msgr_stderr = "/var/log/ispcp/ispcp-arpl-msgr/ispcp-arpl-msgr.stderr"; open(STDOUT, ">", $main::ispcp_arpl_msgr_stdout) || die "Can't redirect stdout"; open(STDERR, ">", $main::ispcp_arpl_msgr_stderr) || die "Can't redirect stderr"; } print STDERR "\nCRITICAL ERROR: Failed open /etc/ispcp/ispcp.conf! Maybe wrong permissions?\n" ; exit 0; } # $main::engine_debug = '_on_'; # debug dump files; $main::log_dir = $main::cfg{'LOG_DIR'}; $main::ispcp_arpl_msgr_el = "$main::log_dir/ispcp-arpl-msgr/ispcp-arpl-msgr.el"; $main::ispcp_arpl_msgr_stdout = "$main::log_dir/ispcp-arpl-msgr/ispcp-arpl-msgr.stdout"; $main::ispcp_arpl_msgr_stderr = "$main::log_dir/ispcp-arpl-msgr/ispcp-arpl-msgr.stderr"; open(STDOUT, ">", $main::ispcp_arpl_msgr_stdout) || die "Can't redirect stdout"; open(STDERR, ">", $main::ispcp_arpl_msgr_stderr) || die "Can't redirect stderr"; # # common code: END # use strict; use warnings; my @msg_rows = <stdin>;</stdin> my $msg = join('', @msg_rows); sub arpl_msgr_start_up { my ($rs, $rdata) = (undef, undef); push_el(\@main::el, 'arpl_msgr_start_up()', 'Starting...'); # Let's clear Execution Logs, if any. if (-e $main::ispcp_arpl_msgr_el) { $rs = del_file($main::ispcp_arpl_msgr_el); return $rs if ($rs != 0); } # config check; $rs = get_conf(); return $rs if ($rs != 0); # sql check; # # getting initial data also must be done here; # my $sql = " SELECT domain_id, domain_name, domain_gid, domain_uid, domain_admin_id, domain_created_id, domain_created, domain_last_modified, domain_mailacc_limit, domain_ftpacc_limit, domain_traffic_limit, domain_sqld_limit, domain_sqlu_limit, domain_status, domain_alias_limit, domain_ip_id, domain_disk_limit, domain_disk_usage, domain_php, domain_cgi FROM domain "; ($rs, $rdata) = doSQL($sql); return $rs if ($rs != 0); push_el(\@main::el, 'arpl_msgr_start_up()', 'Ending...'); return 0; } sub arpl_msgr_shut_down { my $rs = undef; push_el(\@main::el, 'arpl_msgr_shut_down()', 'Starting...'); $main::db -> disconnect(); if (!defined($main::engine_debug)) { $rs = del_file($main::ispcp_arpl_msgr_stdout); return $rs if ($rs != 0); $rs = del_file($main::ispcp_arpl_msgr_stderr); return $rs if ($rs != 0); push_el(\@main::el, 'arpl_msgr_shut_down()', "delete debugfiles."); } push_el(\@main::el, 'arpl_msgr_shut_down()', 'Ending...'); return 0; } sub arpl_msgr_engine { push_el(\@main::el, 'arpl_msgr_engine()', 'Starting...'); my ($sql, $rs) = (undef, undef); my $msg_parser = new MIME::Parser; $msg_parser -> output_to_core(1); my $msg_entity = $msg_parser -> parse_data($msg); my $head = $msg_entity -> head(); my @from_ma = Mail::Address->parse($head -> get('From')); my @to_addrs = Mail::Address->parse($head -> get('X-Original-To')); my $buffer = "0"; my $edmn = undef; my @edmn = @_; my $name = undef; my $mail_to = undef; my $to_ma = undef; my ($mailHeaderCheck1, $mailHeaderCheck2, $mailHeaderCheck3) = (1, 1, 1); if($head -> get('X-Mailer')) { if($head -> get('X-Mailer') =~ m/Autoreply Manager/i) { $mailHeaderCheck1 = 0; } } if($head -> get('Auto-Submitted')) { if($head -> get('Auto-Submitted') =~ m/auto-replied/i) { $mailHeaderCheck2 = 0; } } if($head -> get('Sender')) { if($head -> get('Sender')=~ m/autoresponder/i) { $mailHeaderCheck3 = 0; } } if( $mailHeaderCheck1 && $mailHeaderCheck2 && $mailHeaderCheck3 && !$head -> get('X-Autoresponse-From')) { foreach $to_ma (@to_addrs) { if($to_ma->address =~ m/@/i && $to_ma->address !~ m/"/g) { if($buffer) { $name = $buffer; $buffer = "0"; } else { if($to_ma->phrase) { $name = $to_ma->phrase; } else { $name = ""; } } push_el(\@main::el, 'arpl_msgr_engine()', ">>> From: |".$from_ma[0]->address."|, To: |".$to_ma->address."|"); push_el(\@main::el, 'arpl_msgr_engine()', "loop!"); my $ma = $to_ma->address."\n"; return 0 if (!($ma =~ /^([^\@]+)\@([^\n]+)\n$/)); my ($user, $dmn) = ($1, $2); my ($ref, $dmn_id, $sub_id, $pt) = (undef, undef, undef, undef, undef, undef); my ($domainformsubdomain, $subformsubdomain) = (undef, undef); #@edmn = split(/\./,$2,2); #$edmn = $edmn[1]; $edmn = $2; $sql = "select count(domain_id) as cnt from domain where domain_name = '$edmn'"; ($rs, $ref) = doSQL($sql); return $rs if ($rs != 0); $ref = @$ref[0]; if (@$ref[0] == 1) { $pt = 1; } else { $sql = "select count(alias_id) as cnt from domain_aliasses where alias_name = '$edmn'"; ($rs, $ref) = doSQL($sql); return $rs if ($rs != 0); $ref = @$ref[0]; if (@$ref[0] == 1) { $pt = 2; } else { $pt = 3; } } if ($pt == 1) { $sql = "select mail_auto_respond from mail_users LEFT JOIN domain ON mail_users.domain_id=domain.domain_id where mail_acc = '$user' and domain_name = '$edmn' and sub_id=0;"; } elsif ($pt == 2) { $sql = "select mail_auto_respond from mail_users LEFT JOIN domain_aliasses ON mail_users.sub_id=domain_aliasses.alias_id where mail_acc = '$user' and alias_name = '$edmn';"; } elsif ($pt == 3) { $sql = "select mail_auto_respond from mail_users LEFT JOIN subdomain ON mail_users.sub_id=subdomain.subdomain_id LEFT JOIN domain ON subdomain.domain_id=domain.domain_id where mail_acc = '$user' and (CONCAT(subdomain_name,'.',domain_name) = '$edmn');"; } push_el(\@main::el, 'arpl_msgr_engine()', "sql: |".$sql."|"); ($rs, $ref) = doSQL($sql); return $rs if ($rs != 0); $ref = @$ref[0]; my $auto_message = @$ref[0]; if (length($auto_message) > 0) { # if($auto_message ne '_no_') { if($name) { $mail_to = "\"".$name."\" "."< ".$to_ma->address.">"; } else{ $mail_to = $to_ma->address; } my $out = new MIME::Entity; $mail_to=~s/\@ispcp-arpl./\@/; $out -> build( From => $mail_to, To => $head -> get('From'), Subject => "[Autoreply] ".$head -> get('Subject'), Type => "multipart/mixed", 'X-Autoresponse-From' => $mail_to, 'X-Mailer' => "ISPCP $main::cfg{'VersionH'} Autoreply Manager" ); $out -> attach( Type => "text/plain", Encoding => "7bit", Description => "Mail User Autoreply Message", Data => $auto_message ); $out -> attach( Type => "message/rfc822", Description => "Original Message", Data => $msg ); open MAIL, "| /usr/sbin/sendmail -t -oi"; $out -> print(\*MAIL); close MAIL; # } } push_el(\@main::el, 'arpl_msgr_engine()', 'Ending...'); } else { $buffer = $to_ma->address; $buffer =~ s/"//g; } } } return 0; } $rs = undef; $rs = arpl_msgr_start_up(); if ($rs != 0) { dump_el(\@main::el, $main::ispcp_arpl_msgr_el); arpl_msgr_shut_down(); exit 1; } $rs = arpl_msgr_engine(); if ($rs != 0) { dump_el(\@main::el, $main::ispcp_arpl_msgr_el); arpl_msgr_shut_down(); exit 1; } $rs = arpl_msgr_shut_down(); if ($rs != 0) { dump_el(\@main::el, $main::ispcp_arpl_msgr_el); exit 1; } exit 0; |
Das ISPCP Autoreply Messenger Skript (ispcp-arpl-msgr) ist Teil von ISPCP Omage – opensource virtual hosting system. ISPCP steht, je nach Komponente, unter der Mozilla Public License bzw. unter der Gnu Public License (V2).
