ispcp-arpl-msgr und amavis

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=yes

localhost: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:

  1. 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)
  2. 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:

  1. 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).
  2. 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 -&gt; new(
{
'key'             =&gt; $main::db_pass_key,
'keysize' 		  =&gt; 32,
'cipher'          =&gt; 'Blowfish',
'iv'              =&gt; $main::db_pass_iv,
'regenerate_key'  =&gt; 0,
'padding'         =&gt; 'space',
'prepend_iv'      =&gt; 0
}
);
 
my $ciphertext = $cipher-&gt;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 -&gt; new(
{
'key'             =&gt; $main::db_pass_key,
'keysize' 		  =&gt; 32,
'cipher'          =&gt; 'Blowfish',
'iv'              =&gt; $main::db_pass_iv,
'regenerate_key'  =&gt; 0,
'padding'         =&gt; 'space',
'prepend_iv'      =&gt; 0
}
);
 
my $decoded = decode_base64("$pass\n");
 
my $plaintext = $cipher -&gt; 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 &lt; 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, "&gt;", $main::ispcp_arpl_msgr_stdout) || die "Can't redirect stdout";
open(STDERR, "&gt;", $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, "&gt;", $main::ispcp_arpl_msgr_stdout) || die "Can't redirect stdout";
open(STDERR, "&gt;", $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 -&gt; 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 -&gt; output_to_core(1);
 
my $msg_entity = $msg_parser -&gt; parse_data($msg);
 
my $head = $msg_entity -&gt; head();
 
my @from_ma = Mail::Address-&gt;parse($head -&gt; get('From'));
 
my @to_addrs = Mail::Address-&gt;parse($head -&gt; 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 -&gt; get('X-Mailer')) { if($head -&gt; get('X-Mailer') =~ m/Autoreply Manager/i) { $mailHeaderCheck1 = 0; } }
if($head -&gt; get('Auto-Submitted')) { if($head -&gt; get('Auto-Submitted') =~ m/auto-replied/i) { $mailHeaderCheck2 = 0; } }
if($head -&gt; get('Sender')) { if($head -&gt; get('Sender')=~ m/autoresponder/i) { $mailHeaderCheck3 = 0; } }
 
if(	$mailHeaderCheck1 &amp;&amp; $mailHeaderCheck2 &amp;&amp; $mailHeaderCheck3 &amp;&amp; !$head -&gt; get('X-Autoresponse-From')) {
 
foreach $to_ma (@to_addrs) {
 
if($to_ma-&gt;address =~ m/@/i &amp;&amp; $to_ma-&gt;address !~ m/"/g) {
 
if($buffer) {
$name = $buffer;
$buffer = "0";
}
else {
if($to_ma-&gt;phrase) { $name = $to_ma-&gt;phrase; }
else { $name = ""; }
}
 
push_el(\@main::el, 'arpl_msgr_engine()', "&gt;&gt;&gt; From: |".$from_ma[0]-&gt;address."|, To: |".$to_ma-&gt;address."|");
 
push_el(\@main::el, 'arpl_msgr_engine()', "loop!");
 
my $ma = $to_ma-&gt;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) &gt; 0) {
#				if($auto_message ne '_no_') {
 
if($name) { $mail_to = "\"".$name."\" "."&lt; ".$to_ma-&gt;address."&gt;"; }
 
else{ $mail_to = $to_ma-&gt;address; }
 
my $out = new MIME::Entity;
 
$mail_to=~s/\@ispcp-arpl./\@/;
 
$out -&gt; build(
From =&gt; $mail_to,
To =&gt; $head -&gt; get('From'),
Subject =&gt; "[Autoreply] ".$head -&gt; get('Subject'),
Type =&gt; "multipart/mixed",
'X-Autoresponse-From' =&gt; $mail_to,
'X-Mailer' =&gt; "ISPCP $main::cfg{'VersionH'} Autoreply Manager"
);
 
$out -&gt; attach(
Type =&gt; "text/plain",
Encoding =&gt; "7bit",
Description =&gt; "Mail User Autoreply Message",
Data =&gt; $auto_message
);
 
$out -&gt; attach(
Type =&gt; "message/rfc822",
Description =&gt; "Original Message",
Data =&gt; $msg
);
 
open MAIL, "| /usr/sbin/sendmail -t -oi";
 
$out -&gt; print(\*MAIL);
 
close MAIL;
#				}
 
}
 
push_el(\@main::el, 'arpl_msgr_engine()', 'Ending...');
 
}
else
{
$buffer = $to_ma-&gt;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).

Kommentare sind geschlossen.