Guest User

Untitled

a guest
Aug 22nd, 2018
313
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 84.44 KB | None | 0 0
  1. #!/usr/bin/perl
  2. #
  3. # Copyright (C) 1993-2001 Ken'ichi Fukamachi
  4. # All rights reserved.
  5. # 1993-1996 fukachan@phys.titech.ac.jp
  6. # 1996-2001 fukachan@sapporo.iij.ad.jp
  7. #
  8. # FML is free software; you can redistribute it and/or modify
  9. # it under the terms of GNU General Public License.
  10. # See the file COPYING for more details.
  11. #
  12. # $FML: fml.pl,v 2.124.2.6 2001/10/15 12:53:04 fukachan Exp $
  13.  
  14.  
  15. ##### SubRoutines #####
  16.  
  17. ####### Section: Main Mode Bifurcation
  18.  
  19. # NOT $Envelope{'mode:ctladdr'} IS IMPORTANT;
  20. # mode:ctladdr >> mode:distribute, mode:distribute*
  21. # Changed a lot at 2.1 DELTA
  22. sub ModeBifurcate
  23. {
  24. local($command_mode, $member_p, $compat_hml);
  25.  
  26. # Do nothing. Tricky. Please ignore
  27. if ($DO_NOTHING) { return 0;}
  28.  
  29. # DataBase Access Probe()
  30. # return ASAP if database server is inaccessible.
  31. if ($USE_DATABASE) {
  32. &use('databases');
  33.  
  34. # try to probe server
  35. my (%mib, %result, %misc, $error);
  36. &DataBaseMIBPrepare(\%mib, 'member_p', {'address' => $From_address});
  37. &DataBaseCtl(\%Envelope, \%mib, \%result, \%misc);
  38. if ($mib{'error'}) {
  39. &Mesg(*Envelope, 'database error occurs', 'configuration_error');
  40. return 0; # return ASAP
  41. }
  42. }
  43.  
  44. # member ot not?
  45. &AdjustActiveAndMemberLists;
  46. $member_p = &MailListMemberP($From_address);
  47. $member_p = 1 if $Envelope{"trap:+"};
  48. $Envelope{'mode:stranger'} = 1 unless $member_p;
  49.  
  50. # chaddr is available from new address if old to "chaddr" is a member;
  51. # hml compatible case (not default)
  52. if (!$member_p && $Envelope{'mode:uip:chaddr'}) {
  53. &use('utils');
  54. if (&ChAddrModeOK($Envelope{'mode:uip:chaddr'})) {
  55. $Envelope{'mode:uip'} = $member_p = 1;
  56. }
  57. }
  58. # hml compatible case (not default)
  59. elsif ($CHADDR_AUTH_TYPE eq 'confirmation' &&
  60. $Envelope{'mode:uip:chaddr-confirm'}) {
  61. $Envelope{'mode:uip'} = 1;
  62. }
  63.  
  64. # fml 0.x - fml 2.1gamma compat (not default)
  65. $compat_hml = &CompatFMLv1P;
  66.  
  67. # default
  68. $REJECT_POST_HANDLER = $REJECT_POST_HANDLER || 'Reject';
  69. $REJECT_COMMAND_HANDLER = $REJECT_COMMAND_HANDLER || 'Reject';
  70.  
  71. %RejectHandler = ("reject", "RejectHandler",
  72. "auto_regist", "AutoRegistHandler",
  73. "autoregist", "AutoRegistHandler",
  74. "ignore", "IgnoreHandler",
  75.  
  76. "auto_subscribe", "AutoRegistHandler",
  77. "auto_asymmetric_regist", "AutoRegistHandler",
  78. );
  79. if ($debug) {
  80. &Log("ModeBifurcate: \$PERMIT_POST_FROM $PERMIT_POST_FROM");
  81. &Log("ModeBifurcate: \$PERMIT_COMMAND_FROM $PERMIT_COMMAND_FROM");
  82. &Log("ModeBifurcate: artype $AUTO_REGISTRATION_TYPE");
  83. &Log("ModeBifurcate: key $AUTO_REGISTRATION_KEYWORD");
  84. &Log("ModeBifurcate: member_p $member_p");
  85. }
  86.  
  87. ### 00.01 Run Hooks
  88. if ($MODE_BIFURCATE_HOOK) {
  89. &eval($MODE_BIFURCATE_HOOK, "MODE_BIFURCATE_HOOK");
  90. }
  91.  
  92. ### 01: compat_hml mode
  93. if ($compat_hml) {
  94. &Log("compat_hml mode") if $debug;
  95.  
  96. if (!$Envelope{"compat:cf2:post_directive"} &&
  97. ($Envelope{'mode:req:guide'} || $Envelope{'req:guide'})) {
  98. &GuideRequest(*Envelope); # Guide Request from anyone
  99. return; # end;
  100. }
  101.  
  102. local($ca) = &CutFQDN($CONTROL_ADDRESS);
  103. # Default LOAD_LIBRARY SHOULD NOT BE OVERWRITTEN!
  104. if ($Envelope{'mode:uip'} &&
  105. ($Envelope{'trap:rcpt_fields'} =~ /$ca/i)) {
  106. $command_mode = 1;
  107. }
  108. }
  109.  
  110.  
  111. ### 02: determine command mode or not
  112. if ($Envelope{'mode:ctladdr'} || $COMMAND_ONLY_SERVER) {
  113. &Log("\$command_mode = 1;") if $debug;
  114. $command_mode = 1;
  115. }
  116. # BACKWARD COMPATIBLE
  117. # when trap the mail body's "# command" syntax but without --ctladdr
  118. # at this switch already "!$Envelope{'mode:ctladdr'}" is true
  119. # but post=* is exception
  120. elsif ($compat_hml && $Envelope{'mode:uip'}) {
  121. &Log("backward && uip => command_mode on") if $debug;
  122. $command_mode = 1;
  123. }
  124.  
  125. # post=* mode and !"ctladdr mode" disables commands
  126. if (!$Envelope{"mode:ctladdr"} &&
  127. $Envelope{"compat:cf2:post_directive"}) {
  128. &Log("02 undef \$command_mode = $command_mode;") if $debug;
  129. undef $command_mode;
  130. }
  131.  
  132. &Log("03 \$command_mode = $command_mode;") if $debug;
  133.  
  134. # initialize Information
  135. &GenInfo;
  136.  
  137. ### 03: Bifurcate by Condition
  138. # Do nothing. Tricky. Please ignore
  139. if ($DO_NOTHING) {
  140. return 0;
  141. }
  142. # command mode?
  143. elsif ($command_mode) {
  144. $Envelope{'pcb:mode'} = 'command'; # process control block
  145.  
  146. # NOT PERMIT COMMAND WHEN MAIL SIZE IS OVER LIMIT.
  147. if ($Envelope{'trap:mail_size_overflow'}) {
  148. &Log("ModeBifurcate: ignore too bit mail in command mode");
  149. return $NULL;
  150. }
  151.  
  152.  
  153. if ($PERMIT_COMMAND_FROM eq "anyone") {
  154. require($LOAD_LIBRARY = $LOAD_LIBRARY || 'libfml.pl');
  155. &Command() if $ForceKickOffCommand;
  156. }
  157. elsif ($PERMIT_COMMAND_FROM eq "members_only" ||
  158. $PERMIT_COMMAND_FROM eq "members"
  159. ) {
  160. if ($member_p) {
  161. if ($Envelope{'mode:req:unsubscribe-confirm'}) {
  162. undef $LOAD_LIBRARY;
  163. require 'libfml.pl';
  164. &Command($Envelope{'buf:req:unsubscribe-confirm'});
  165. }
  166. else {
  167. require($LOAD_LIBRARY = $LOAD_LIBRARY || 'libfml.pl');
  168. &Command() if $ForceKickOffCommand;
  169. }
  170. }
  171. # not member and ignore the mail
  172. elsif ((! $member_p) && ($REJECT_COMMAND_HANDLER eq "ignore")) {
  173. &Log("ignore request from not member");
  174. }
  175. # chaddr-confirm
  176. elsif ((! $member_p) && $Envelope{'mode:req:chaddr-confirm'}) {
  177. &use('trap');
  178. &Trap__ChaddrConfirm(*Envelope);
  179. }
  180. # chaddr
  181. elsif ((! $member_p) && $Envelope{'mode:req:chaddr'}) {
  182. &use('trap');
  183. &Trap__ChaddrRequest(*Envelope);
  184. }
  185. # we should return reply for "guide" request from even "stranger";
  186. elsif ((! $member_p) &&
  187. ($Envelope{'mode:req:guide'} || $Envelope{'req:guide'})) {
  188. &GuideRequest(*Envelope);
  189. }
  190. # MANUAL REGISTRATION REQUEST WITH CONFIRMATION (subscribe)
  191. elsif ((! $member_p) && $Envelope{'mode:req:subscribe'} &&
  192. &NonAutoRegistrableP) {
  193. &Log("manual subscribe request");
  194. &use('confirm');
  195.  
  196. &ManualRegistConfirm(*Envelope, 'subscribe',
  197. $Envelope{'buf:req:subscribe'});
  198. }
  199. # MANUAL REGISTRATION REQUEST WITH CONFIRMATION (confirm)
  200. elsif ((! $member_p) && $Envelope{'mode:req:confirm'} &&
  201. &NonAutoRegistrableP) {
  202. &Log("manual subscribe confirmed");
  203. &use('confirm');
  204. &ManualRegistConfirm(*Envelope, 'confirm',
  205. $Envelope{'buf:req:confirm'});
  206. }
  207. else {
  208. $fp = $RejectHandler{$REJECT_COMMAND_HANDLER}||"RejectHandler";
  209. &$fp(*Envelope);
  210. }
  211. }
  212. elsif ($PERMIT_COMMAND_FROM eq "moderator") { # dummay ?
  213. &use('moderated');
  214. $Envelope{'mode:moderator:command'} = 1;
  215. &ModeratedDelivery(*Envelope); # Moderated: check Approval;
  216. }
  217. else {
  218. &Log("ERROR: \$PERMIT_COMMAND_FROM is unknown type.");
  219. }
  220. }
  221. # distribute
  222. else {
  223. $Envelope{'pcb:mode'} = 'distribute'; # process control block
  224.  
  225. if ($PERMIT_POST_FROM eq "anyone") {
  226. &Distribute(*Envelope, 'permit from anyone');
  227. }
  228. elsif ($PERMIT_POST_FROM eq "members_only" ||
  229. $PERMIT_POST_FROM eq "members"
  230. ) {
  231. if ($member_p) {
  232. &Distribute(*Envelope, 'permit from members_only');
  233. }
  234. else {
  235. $fp = $RejectHandler{$REJECT_POST_HANDLER}||"RejectHandler";
  236. &$fp(*Envelope);
  237. }
  238. }
  239. elsif ($PERMIT_POST_FROM eq "moderator") {
  240. &use('moderated');
  241. &ModeratedDelivery(*Envelope); # Moderated: check Approval;
  242. }
  243. else {
  244. &Log("ERROR: \$PERMIT_POST_FROM is unknown type.");
  245. }
  246.  
  247. # to ensure the unique Date: (since the smallest unit is second).
  248. if ($DATE_TYPE =~ /distribute-date/) { sleep 1;}
  249. }
  250.  
  251. &LogFileNewSyslog if $LOGFILE_NEWSYSLOG_LIMIT; # log file turn over
  252. }
  253.  
  254. ####### Section: Main Functions
  255. #
  256. # Configuration Files: evalucation order
  257. #
  258. # 1 site_init site default
  259. # 2 <ML>/config.ph each ML configurations
  260. # 3 sitedef force site-own-rules to overwrite ML configrations
  261. #
  262. sub LoadConfig
  263. {
  264. # configuration file for each ML
  265. if (-e "$DIR/config.ph" && ((stat("$DIR/config.ph"))[4] != $<)) {
  266. print STDERR "\nFYI: include's owner != config.ph's owner, O.K.?\n\n";
  267. }
  268.  
  269. # 3.0B new loading configuration
  270. # XXX &__LoadConfiguration('__KERN__'); for main routine: fml.pl, libkern
  271. # XXX &__LoadConfiguration; for other tools: bin/* ...
  272. require 'libloadconfig.pl'; &__LoadConfiguration('__KERN__');
  273.  
  274. require 'libsmtp.pl'; # a library using smtp
  275.  
  276. # load MIME handling functions for convenience
  277. if ($LANGUAGE eq 'Japanese') { require 'libMIME.pl';}
  278.  
  279. # if mode:some is set, load the default configuration of the mode
  280. for (keys %Envelope) {
  281. /^mode:(\S+)/ && $Envelope{$_} && do { &DEFINE_MODE($1);};
  282. }
  283. }
  284.  
  285. sub SetDefaults
  286. {
  287. $NULL = ''; # useful constant :D
  288. $Envelope{'mci:mailer'} = 'ipc'; # use IPC(default)
  289. $Envelope{'mode:uip'} = ''; # default UserInterfaceProgram is nil.;
  290. $Envelope{'mode:req:guide'} = 0; # not member && guide request only
  291.  
  292. $LOCKFILE = "$$ $DIR"; # (variable name is historical, not meaning)
  293.  
  294. { # DNS AutoConfigure to set FQDN and DOMAINNAME;
  295. local(@n, $hostname, $list);
  296. chop($hostname = `hostname`); # beth or beth.domain may be possible
  297. $FQDN = $hostname;
  298. @n = (gethostbyname($hostname))[0,1]; $list .= " @n ";
  299. @n = split(/\./, $hostname); $hostname = $n[0]; # beth.dom -> beth
  300. @n = (gethostbyname($hostname))[0,1]; $list .= " @n ";
  301.  
  302. for (split(/\s+/, $list)) { /^$hostname\.\w+/ && ($FQDN = $_);}
  303. $FQDN =~ s/\.$//; # for e.g. NWS3865
  304. $DOMAINNAME = $FQDN;
  305. $DOMAINNAME =~ s/^$hostname\.//;
  306. }
  307.  
  308. # Architecture Dependence;
  309. $UNISTD = $HAS_ALARM = $HAS_GETPWUID = $HAS_GETPWGID = $HAS_GETGRENT = 1;
  310.  
  311. # REQUIRED AS DEFAULTS
  312. %SEVERE_ADDR_CHECK_DOMAINS = ('or.jp', +1, 'ne.jp', +1);
  313. $REJECT_ADDR = 'root|postmaster|MAILER-DAEMON|msgs|nobody';
  314. $REJECT_ADDR .= '|majordomo|listserv|listproc';
  315. $REJECT_ADDR .= '|\S+\-subscribe|\S+\-unsubscribe|\S+\-help';
  316. $SKIP_FIELDS = 'Received|Return-Receipt-To';
  317. $ADD_URL_INFO = $ML_MEMBER_CHECK = $CHECK_MESSAGE_ID = $USE_FLOCK = 1;
  318. $NOTIFY_MAIL_SIZE_OVERFLOW = 1;
  319. $CHADDR_CONFIRMATION_KEYWORD = 'chaddr-confirm';
  320. $UNSUBSCRIBE_CONFIRMATION_KEYWORD = 'unsubscribe-confirm';
  321.  
  322. # Envelope Filter
  323. $FILTER_ATTR_REJECT_NULL_BODY = $FILTER_ATTR_REJECT_ONE_LINE_BODY = 1;
  324. $FILTER_ATTR_REJECT_INVALID_COMMAND = 1;
  325.  
  326. ### default distribution and command mode
  327. $PERMIT_POST_FROM = $PERMIT_COMMAND_FROM = "members_only";
  328. $REJECT_POST_HANDLER = $REJECT_COMMAND_HANDLER = "reject";
  329.  
  330. ### fmlserv compat code; (e.g. a bit for umask and permissions ctrl)
  331. if (-d "$DIR/../fmlserv") { # tricky ;-)
  332. $USE_FML_WITH_FMLSERV = 1;
  333. $GID = (stat("$DIR/../fmlserv"))[5];
  334. }
  335.  
  336. ### Security; default security level (mainly backward compat)
  337. undef %Permit;
  338.  
  339. @DenyProcedure = ('library');
  340. @HdrFieldsOrder = # rfc822; fields = ...; Resent-* are ignored;
  341. ('Return-Path', 'Received',
  342. 'Delivered-To', # for postfix, qmail
  343. 'Date', 'Posted', 'X-Posted', 'X-Original-Date',
  344. 'From', 'Reply-To', 'Subject', 'Sender',
  345. 'To', 'Cc', 'Errors-To', 'Message-Id', 'In-Reply-To',
  346. 'References', 'Keywords', 'Comments', 'Encrypted',
  347. ':XMLNAME:', ':XMLCOUNT:', 'X-MLServer',
  348. 'XRef', 'X-Stardate', 'X-ML-Info',
  349. 'X-Mailer', 'X-Dispatcher', 'X-Newsreader', 'User-Agent',
  350. 'Mail-Followup-To', # I-D now?
  351. ':body:', ':any:',
  352. 'X-Authentication-Warning',
  353. 'Mime-Version', 'Content-Type', 'Content-Transfer-Encoding',
  354. 'Content-ID', 'Content-Description', # RFC2045
  355. 'Precedence', 'Lines');
  356.  
  357. # Content Filtering Handler for MIME
  358. @MailContentHandler = (); # Default: No filter
  359. }
  360.  
  361. sub GetTime
  362. {
  363. @WDay = ('Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat');
  364. @Month = ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
  365. 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec');
  366. ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
  367. $Now = sprintf("%02d/%02d/%02d %02d:%02d:%02d",
  368. ($year % 100), $mon + 1, $mday, $hour, $min, $sec);
  369. $MailDate = sprintf("%s, %d %s %d %02d:%02d:%02d %s",
  370. $WDay[$wday], $mday, $Month[$mon],
  371. 1900 + $year, $hour, $min, $sec,
  372. $isdst ? $TZONE_DST : $TZone);
  373.  
  374. # /usr/src/sendmail/src/envelop.c
  375. # (void) sprintf(tbuf, "%04d%02d%02d%02d%02d", tm->tm_year + 1900,
  376. # tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min);
  377. #
  378. $CurrentTime = sprintf("%04d%02d%02d%02d%02d",
  379. 1900 + $year, $mon + 1, $mday, $hour, $min);
  380. $PCurrentTime = sprintf("%04d%02d%02d%02d%02d%02d",
  381. 1900 + $year, $mon + 1, $mday, $hour, $min, $sec);
  382. }
  383.  
  384. sub InitConfig
  385. {
  386. &SetDefaults;
  387. &LoadConfig;
  388.  
  389. # XXX SetDefaults but works after &__LoadConfiguration, so defined here.
  390. # 3.0.1 compatible with 3.0's "From: $MAIL_LIST" rejection
  391. # disabled by fukachan (2000/06/19), PR8220 pointed out by MURASHITA Takuya
  392. # &DEFINE_FIELD_LOOP_CHECKED('from');
  393.  
  394. # $FML for process table readability
  395. if ($0 =~ m%^(.*)/(.*)%) { $ExecDir = $1; $FML = $2;}
  396.  
  397. # fml 4.0 modules
  398. push(@INC, "$ExecDir/module");
  399. push(@INC, "$ExecDir/module/CPAN");
  400. push(@INC, "$ExecDir/module/Japanese");
  401. push(@INC, "$ExecDir/module/fml-devel");
  402.  
  403. # a little configuration before the action
  404. if (defined $FML_UMASK) {
  405. $UMASK = $FML_UMASK;
  406. }
  407. elsif (defined $UMASK) {
  408. ;
  409. }
  410. elsif ($USE_FML_WITH_FMLSERV) {
  411. $UMASK = 007; # rw-rw----
  412. }
  413. else {
  414. $UMASK = 077; # rw-------
  415. }
  416.  
  417. umask($UMASK);
  418. defined $DEFAULT_DIR_MODE || ($DEFAULT_DIR_MODE = ($UMASK & 0777) ^ 0777);
  419.  
  420. ### Against the future loop possibility
  421. if (&AddressMatch($MAIL_LIST, $MAINTAINER)) {
  422. &Log("DANGER! \$MAIL_LIST == \$MAINTAINER, STOP!");
  423. exit 0;
  424. }
  425.  
  426. # set architechture if not defined
  427. if (! $COMPA_ARCH) {
  428. if ($CPU_TYPE_MANUFACTURER_OS =~ /(sysv4|solaris2)/i) {
  429. $COMPAT_ARCH = "SOLARIS2";
  430. }
  431. elsif ($CPU_TYPE_MANUFACTURER_OS =~ /windowsnt4$/i) {
  432. $COMPAT_ARCH = "WINDOWS_NT4";
  433. }
  434. }
  435.  
  436. ### Options
  437. &SetOpts;
  438.  
  439. # load architecture dependent default
  440. # here for command line options --COMPAT_ARCH
  441. if ($COMPAT_ARCH) { require "sys/$COMPAT_ARCH/depend.pl";}
  442.  
  443. if ($DUMPVAR) { require 'dumpvar.pl'; &dumpvar('main');}
  444. if ($debug) { require 'libdebug.pl';}
  445. if ($Opt{"opt:b"} eq 'd') { &use('utils'); &daemon;} # become daemon;
  446.  
  447. # COMPATIBILITY
  448. if ($COMPAT_CF1 || ($CFVersion < 2)) { &use('compat_cf1');}
  449. if ($CFVersion < 3) { &use('compat_cf2');}
  450. if ($COMPAT_FML15) { &use('compat_cf1'); &use('compat_fml15');}
  451. if (!$TZone) { $TZone = '+0900';} # TIME ZONE
  452.  
  453. &GetTime; # Time, (may be for compatible codes)
  454.  
  455. push(@MAIL_LIST_ALIASES, @PLAY_TO);
  456. unshift(@ARCHIVE_DIR, $ARCHIVE_DIR);
  457.  
  458. ### Initialize DIR's and FILE's of the ML server
  459. # FullPath-ed (FP)
  460. local($s);
  461. for (SPOOL_DIR,TMP_DIR,VAR_DIR,VARLOG_DIR,VARRUN_DIR,VARDB_DIR) {
  462. &eval("\$s = \$$_; \$s =~ s#\$DIR/##g; \$s =~ s#$DIR/##g;");
  463. &eval("\$FP_$_ = \"$DIR/\$s\";");
  464. &eval("\$$_ =~ s#\$DIR/##g; \$$_ =~ s#\$DIR/##g;");
  465. &eval("-d \$$_||&Mkdir(\$$_);");
  466. }
  467.  
  468. if ($USE_DATABASE) {
  469. for ($LOGFILE, $MGET_LOGFILE,
  470. $SEQUENCE_FILE, $SUMMARY_FILE, $LOG_MESSAGE_ID) {
  471. -f $_ || &Touch($_);
  472. }
  473. }
  474. else {
  475. for ($LOGFILE, $MEMBER_LIST, $MGET_LOGFILE,
  476. $SEQUENCE_FILE, $SUMMARY_FILE, $LOG_MESSAGE_ID) {
  477. -f $_ || &Touch($_);
  478. }
  479. }
  480.  
  481. ### CFVersion 3
  482. ### DEFINE INTERNAL FLAG FOR THE USE $DIR/members or $DIR/actives ?
  483. ### $ML_MEMBER_CHECK is internal variable to indicate file relation
  484. local($touch) = "${ACTIVE_LIST}_is_dummy_when_auto_regist";
  485. if (&AutoRegistrableP) {
  486. $ML_MEMBER_CHECK = 0; # backward
  487. if (&NotUseSeparateListP) {
  488. &Touch($touch) if ! -f $touch;
  489. }
  490. else {
  491. unlink $touch if -f $touch;
  492. }
  493. }
  494. else {
  495. -f $ACTIVE_LIST || &Touch($ACTIVE_LIST);
  496. unlink $touch if -f $touch;
  497. }
  498.  
  499. if ($SUBJECT_TAG_TYPE) {
  500. &use("tagdef");
  501. &SubjectTagDef($SUBJECT_TAG_TYPE);
  502. }
  503. ### END CFVersion 3
  504.  
  505. ### misc
  506. $LOG_MESSAGE_ID = $LOG_MESSAGE_ID || "$VARRUN_DIR/msgidcache";#important;
  507. $LOG_MAILBODY_CKSUM = $LOG_MAILBODY_CKSUM || "$VARRUN_DIR/bodycksumcache";
  508. $REJECT_ADDR_LIST = $REJECT_ADDR_LIST || "$DIR/spamlist";
  509. $FML .= "[".(split(/\@/, $MAIL_LIST))[0]."]"; # For tracing Process Table
  510.  
  511. # initialize some arrays; if auto-regist is clear here, we reset;
  512. &AdjustActiveAndMemberLists;
  513.  
  514. # since variables are defined in config.ph;
  515. @NEWSYSLOG_FILES = @NEWSYSLOG_FILES ||
  516. ("$MSEND_RC.bak", "$MEMBER_LIST.bak", "$ACTIVE_LIST.bak");
  517.  
  518. # struct sockaddr { ;}
  519. $STRUCT_SOCKADDR = $STRUCT_SOCKADDR || "S n a4 x8";
  520.  
  521. # &BackwardCompat;
  522. &DEFINE_MODE('expire') if $USE_EXPIRE; # 2.1 release built-in expire
  523. &DEFINE_MODE('archive') if $USE_ARCHIVE; # 2.1 release built-in archive;
  524. &DEFINE_MODE('html') if $AUTO_HTML_GEN;
  525.  
  526. # command trap keywrod : '# ' ; in default, we not use it.
  527. # XXX: "# command" is internal represention
  528. # XXX: it is historical, so remove '# command' part if exist and possible.
  529. $Envelope{'trap:ctk'} = &CompatFMLv1P ? '# ' : '';
  530.  
  531. # signal handling
  532. $SIG{'ALRM'} = 'Tick';
  533. $SIG{'INT'} = $SIG{'QUIT'} = $SIG{'TERM'} = 'SignalLog';
  534.  
  535. # MIME Content Handler(include backword compatible)
  536. if ($AGAINST_HTML_MAIL ||
  537. $HTML_MAIL_DEFAULT_HANDLER eq 'strip') {
  538. &ADD_CONTENT_HANDLER('multipart/.*', 'text/plain', 'allow');
  539. &ADD_CONTENT_HANDLER('multipart/.*', '.*/.*', 'strip');
  540. &ADD_CONTENT_HANDLER('text/plain', '.*/.*', 'allow');
  541. &ADD_CONTENT_HANDLER('!MIME', '.*/.*', 'allow');
  542. } elsif ($HTML_MAIL_DEFAULT_HANDLER eq 'reject') {
  543. &ADD_CONTENT_HANDLER('multipart/.*', 'text/plain', 'allow');
  544. &ADD_CONTENT_HANDLER('multipart/.*', '.*/.*', 'reject');
  545. &ADD_CONTENT_HANDLER('text/plain', '.*/.*', 'allow');
  546. &ADD_CONTENT_HANDLER('!MIME', '.*/.*', 'allow');
  547. }
  548.  
  549. # For example, $LOGFILE_SUFFIX = ".%C%y";
  550. if ($LOGFILE_SUFFIX) {
  551. use POSIX;
  552. $LOGFILE .= strftime($LOGFILE_SUFFIX, localtime(time));
  553. }
  554. }
  555.  
  556. # one pass to cut out the header and the body
  557. sub Parsing { &Parse;}
  558. sub Parse
  559. {
  560. $0 = "${FML}: Parsing header and body <$LOCKFILE>";
  561. local($bufsiz, $buf, $p, $maxbufsiz, $in_header);
  562.  
  563. $maxbufsiz = &ATOI($INCOMING_MAIL_SIZE_LIMIT) if $INCOMING_MAIL_SIZE_LIMIT;
  564.  
  565. undef $Envelope{'Body'};
  566. $in_header = 1; # firstly header comes.
  567. while ($p = sysread(STDIN, $_, 1024)) {
  568. $bufsiz += $p;
  569.  
  570. if ($INCOMING_MAIL_SIZE_LIMIT && ($bufsiz > $maxbufsiz)) {
  571. $Envelope{'trap:mail_size_overflow'} = 1;
  572. last;
  573. }
  574.  
  575. $Envelope{'Body'} .= $_;
  576.  
  577. if ($in_header) {
  578. # separator between header and body is found!
  579. if (($p = index($Envelope{'Body'}, "\n\n", 0)) > 0) {
  580. $Envelope{'Header'} = substr($Envelope{'Body'}, 0, $p + 1);
  581. $Envelope{'Body'} = substr($Envelope{'Body'}, $p + 2);
  582. $in_header = 0;
  583. }
  584. }
  585. }
  586.  
  587. # oops, header only mail ?! (2000/05/31 by fukachan)
  588. if ($in_header) {
  589. $Envelope{'Header'} = $Envelope{'Body'};
  590. undef $Envelope{'Body'};
  591. }
  592.  
  593. # Really? but check "what happen if no input is given?".
  594. if ($bufsiz == 0) {
  595. &Log("read 0 bytes, stop");
  596. exit(0);
  597. }
  598. $Envelope{'tmp:bufsiz'} = $bufsiz;
  599. }
  600.  
  601. # Phase 2 extract several fields
  602. sub GetFieldsFromHeader
  603. {
  604. local($field, $value, @hdr, %hf);
  605. local($s);
  606.  
  607. $0 = "${FML}: GetFieldsFromHeader <$LOCKFILE>";
  608.  
  609. # To ensure non exsistence
  610. for (split(/\|/, $SKIP_FIELDS)) { &DELETE_FIELD($_);}
  611.  
  612. # pass all fields through
  613. if ($SUPERFLUOUS_HEADERS || $PASS_ALL_FIELDS_IN_HEADER) {
  614. $hdr_entry = join("|", @HdrFieldsOrder);
  615. }
  616.  
  617. ### Header Fields Extraction
  618. $s = $Envelope{'Header'}."\n";
  619. # $* = 0; # match one line
  620. if ($s =~ /^From\s+(\S+)/is) {
  621. # XXX NO REACH HERE (1999/10/27 by fukachan)
  622. # $Envelope{'UnixFrom'} = $UnixFrom = $1;
  623. $s =~ s/^From\s+.*//i;
  624. }
  625.  
  626. $s = "\n".$s; # tricky
  627. $s =~ s/\n(\S+):/\n\n$1:\n\n/g; # trick for folding and unfolding.
  628. $s =~ s/^\n*//; # remove the first null lines;
  629.  
  630. @hdr = split(/\n\n/, $s);
  631. while (@hdr) {
  632. $_ = $field = shift @hdr;
  633. last if /^[\s\n]*$/; # null field must be end
  634. $value = shift @hdr; # not cut the first spaces of $value
  635.  
  636. print STDERR "FIELD>$field<\n >$value<\n" if $debug;
  637.  
  638. # Save Entry anyway. '.=' for multiple 'Received:'
  639. $field =~ tr/A-Z/a-z/;
  640. $hf{$field} = 1;
  641. $Envelope{$field} .= $Envelope{$field} ? "\n${_}$value" : $value;
  642.  
  643. # e.g. ONLY multiple Received: are possible.
  644. # "Cc: ..\n Cc: ..\n" also may exist
  645. $Envelope{"h:$field"} .=
  646. $Envelope{"h:$field"} ? "\n${_}$value" : $value;
  647.  
  648. next if /^($SKIP_FIELDS):/i;
  649.  
  650. # hold fields without in use_fields if $SUPERFLUOUS_HEADERS is 1.
  651. if ($SUPERFLUOUS_HEADERS || $PASS_ALL_FIELDS_IN_HEADER) {
  652. next if /^($hdr_entry)/i; # :\w+: not match
  653. $Envelope{'Hdr2add'} .= "${_}$value\n";
  654. }
  655. }
  656.  
  657. ### Anyway set all the fields (96/09/24) ###
  658. local($f, $fc, $k, $v, $x);
  659. while (($k, $v) = each %hf) {
  660. $_ = &FieldCapitalize($k);
  661. $Envelope{"h:$_"} = $Envelope{"h:$k"};
  662. }
  663.  
  664. ### fix Unix From
  665. if (! $Envelope{'UnixFrom'}) { # == !$UnixFrom
  666. $UnixFrom = $Envelope{'h:return-path:'} || $Envelope{'h:From:'} ||
  667. "postmaster\@$FQDN";
  668. $UnixFrom = $Unix_From = $Envelope{'UnixFrom'} =
  669. &Conv2mailbox($UnixFrom, *Envelope);
  670. }
  671. }
  672.  
  673. # LATTER PART is to fix extracts
  674. # Set variables to need special effects
  675. sub FixHeaderFields
  676. {
  677. local(*e) = @_;
  678. local($addr);
  679.  
  680. ### MIME: IF USE_LIBMIME && MIME-detected;
  681. if ($USE_MIME && $e{'Header'} =~ /=\?ISO\-2022\-JP\?/io) {
  682. $e{'MIME'}= 1;
  683. }
  684.  
  685. ### $MAIL_LIST Aliases
  686. $addr = "$e{'h:recent-to:'}, $e{'h:to:'}. $e{'h:cc:'}";
  687. for (@MAIL_LIST_ALIASES) {
  688. next unless $_;
  689. if ($addr =~ /$_/i) { $MAIL_LIST = $_;}
  690. }
  691.  
  692. $e{'h:Return-Path:'} = "<$MAINTAINER>"; # needed?
  693. $e{'h:Precedence:'} = $PRECEDENCE || 'list';
  694. # $e{'h:Lines:'} = $e{'nlines'}; now in CheckCurrentProc (97/12/07)
  695.  
  696. # Date: field type definition
  697. if ($DATE_TYPE eq 'original-date') {
  698. $e{'h:Date:'} = $e{'h:date:'};
  699. }
  700. elsif ($DATE_TYPE eq 'received-date+x-posted') {
  701. $e{'h:Date:'} = $MailDate;
  702. $e{'h:X-Posted:'} = $e{'h:date:'} || $e{'h:Date:'};
  703. }
  704. elsif ($DATE_TYPE eq 'received-date+x-original-date') {
  705. $e{'h:Date:'} = $MailDate;
  706. $e{'h:X-Original-Date:'} = $e{'h:date:'} || $e{'h:Date:'};
  707. }
  708. elsif (($DATE_TYPE eq 'received-date') ||
  709. ($DATE_TYPE eq 'received-date+posted') ||
  710. (!$DATE_TYPE)) { # default (backward)
  711. $e{'h:Date:'} = $MailDate;
  712. $e{'h:Posted:'} = $e{'h:date:'} || $e{'h:Date:'};
  713. }
  714.  
  715. # Some Fields need to "Extract the user@domain part"
  716. # Addr2Reply: is used to pass to sendmail as the recipient
  717. $From_address = &Conv2mailbox($e{'h:from:'}, *e);
  718. $e{'macro:x'} = $e{'tmp:x'};
  719. &Log("Gecos [$e{'macro:x'}]") if $debug;
  720.  
  721. # XXX should we nuke $COMMAND_RETURN_ADDR_POLICY ?
  722. if ($COMMAND_RETURN_ADDR_POLICY eq 'from' ||
  723. $MESSAGE_RETURN_ADDR_POLICY eq 'from') {
  724. $e{'Addr2Reply:'} = $From_address;
  725. }
  726. else { # $MESSAGE_RETURN_ADDR_POLICY eq 'reply-to'
  727. $e{'Addr2Reply:'} = &Conv2mailbox($e{'h:reply-to:'},*e)||$From_address;
  728. }
  729.  
  730. # KAKUSHI(SECRET) OPTION :) (UNDER DEVELOPMENT)
  731. # use Return-Path: as the sender for authentication
  732. if ($SENDER_AUTH_TYPE eq 'strict-envelope-from') {
  733. $_ = &Conv2mailbox($Envelope{'h:return-path:'}, *e);
  734. if ($_) {
  735. $From_address = $_;
  736. }
  737. else {
  738. &Log("\$SENDER_AUTH_TYPE eq 'strict-envelope-from'");
  739. &Log("INVALID Return-Path:<$_>");
  740. &Mesg(*e, "YOU ARE NOT A MEMBER!");
  741. &Mesg(*e, $NULL, 'not_member');
  742. $DO_NOTHING = 1;
  743. }
  744.  
  745. }
  746. elsif ($SENDER_AUTH_TYPE eq 'envelope-from-or-from') {
  747. for ($UnixFrom, $From_address) {
  748. &MailListMemberP($_) && ($From_address = $_, last);
  749. }
  750. }
  751.  
  752. # To: $MAIL_LIST for readability;
  753. # &RewriteField(*e, 'Cc') unless $NOT_REWRITE_CC;
  754. if ($REWRITE_TO < 0) {
  755. ; # pass through for the use of this flag when $CFVersion < 3.;
  756. }
  757. elsif ($CFVersion < 3.1) { # 3.1 is 2.1A#8 (1997/10/14)
  758. $REWRITE_TO = $NOT_REWRITE_TO ? 0 : 1; # 2.1 release default;
  759. }
  760.  
  761. &Log("REWRITE_TO $REWRITE_TO") if $debug;
  762. if ($REWRITE_TO == 2) {
  763. $e{'h:To:'} = "$MAIL_LIST $ML_FN"; # force the original To: to pass
  764. }
  765. elsif ($REWRITE_TO == 1) {
  766. # To: $MALI_LIST, plus original To: ...
  767. $e{'h:To:'} = "$MAIL_LIST $ML_FN";
  768. &RewriteField(*e, 'To');
  769. }
  770. elsif ((!$REWRITE_TO) || $REWRITE_TO < 0) {
  771. ; # do nothing, pass through
  772. }
  773.  
  774. # Subject:
  775. # 1. remove [Elena:id]
  776. # 2. while ( Re: Re: -> Re: ) (THIS IS REQUIED ANY TIME, ISN'T IT? but...)
  777. # Default: not remove multiple Re:'s),
  778. # which actions may be out of my business
  779. if ($_ = $e{'h:Subject:'}) {
  780. if ($STRIP_BRACKETS ||
  781. $SUBJECT_FREE_FORM_REGEXP || $SUBJECT_HML_FORM) {
  782. if ($e{'MIME'}) { # against cc:mail ;_;
  783. &use('MIME');
  784. &StripMIMESubject(*e);
  785. }
  786. else { # e.g. Subject: [Elena:003] E.. U so ...;
  787. print STDERR "IN: $_\n" if $debug;
  788. $e{'h:Subject:'} = &StripBracket($_);
  789. print STDERR "OUT: $e{'h:Subject:'}\n" if $debug;
  790. }
  791. }
  792. # Even if pass through, always strip of Re:*
  793. else {
  794. $e{'h:Subject:'} = &CutOffRe($_);
  795. }
  796. }
  797.  
  798. # Obsolete Errors-to:, against e.g. BBS like a nifty
  799. if ($USE_ERRORS_TO) {
  800. $e{'h:Errors-To:'} = $ERRORS_TO || $MAINTAINER;
  801. }
  802. else { # delete obsolete fields;
  803. &DELETE_FIELD('Errors-To');
  804. }
  805.  
  806. # Set Control-Address for reply, notify and message
  807. $e{'CtlAddr:'} = &CtlAddr;
  808.  
  809. ### USER MACROS: &COPY_FIELD(old, new);
  810. local($old, $new);
  811. while (($old,$new) = each %HdrFieldCopy) {
  812. &Debug("COPY_FIELD: \$e{\"h: $old => $new\"}") if $debug;
  813. $e{"h:$new:"} = $e{"h:$old:"};
  814. $e{"h:$new:"} =~ s/\n$old:/\n$new:/gi;
  815. }
  816. }
  817.  
  818. sub FieldCapitalize
  819. {
  820. local($_) = @_;
  821. s/^(\w)/ $x = $1, $x =~ tr%a-z%A-Z%, $x/e;
  822. s/(\-\w)/$x = $1, $x =~ tr%a-z%A-Z%, $x/eg;
  823. $_ =~ s/^X-ML-/X-ML-/i; # X-ML- is an exception. to avoid dup of X-M{L,;}
  824. $_;
  825. }
  826.  
  827. sub StripBracket
  828. {
  829. local($_) = @_;
  830. local($pat);
  831.  
  832. if ($SUBJECT_FREE_FORM_REGEXP) {
  833. $pat = $SUBJECT_FREE_FORM_REGEXP;
  834. }
  835. else { # default;
  836. # pessimistic ?
  837. if (! $BRACKET) { ($BRACKET) = split(/\@/, $MAIL_LIST);}
  838.  
  839. $pat = "\\[$BRACKET:\\d+\\]";
  840. }
  841.  
  842. # cut out all the e.g. [BRACKET:\d] form;
  843. s/$pat//g;
  844.  
  845. $_ = &CutOffRe($_);
  846. }
  847.  
  848. sub CutOffRe
  849. {
  850. local($_) = @_;
  851. local($pattern);
  852.  
  853. # Re: Re2: Re[2]: Re(2): Re^2: Re*2:
  854. $pattern = 'Re:|Re\d+:|Re\[\d+\]:|Re\(\d+\):|Re\^\d+:|Re\*\d+:';
  855. s/^(\s*($pattern)\s*)+/Re: /oi;
  856.  
  857. if ($LANGUAGE eq 'Japanese') {
  858. require("module/$LANGUAGE/liblangdep.pl");
  859. $_ = &Japanese'CutOffReReRe($_);
  860. }
  861.  
  862. $_;
  863. }
  864.  
  865. sub CheckCurrentProc
  866. {
  867. local(*e, $ccp_mode) = @_;
  868.  
  869. # log
  870. &Log("read $e{'tmp:bufsiz'} bytes") if $debug_fml_org;
  871. undef $e{'tmp:bufsiz'};
  872.  
  873. ### SubSection: Log socket connection info
  874. &eval("&use('kernsubr2'); &GetPeerInfo;") if $LOG_CONNECTION;
  875.  
  876. ### SubSection: Check Body Contents (For Command Mode)
  877. local($limit, $p, $buf, $boundary, $nclines, $cc);
  878.  
  879. ### SubSection: MIME info
  880. # MIME skip mode; against automatic-MIME-encapsulated fool MUA
  881. if ($e{'h:content-type:'} =~ /boundary=\"(.*)\"/i ||
  882. $e{'h:content-type:'} =~ /boundary=\s*(\S+)/i) {
  883. $boundary = $1;
  884. $boundary = "--$boundary";
  885. $e{'MIME:boundary'} = $boundary;
  886. }
  887. elsif ($e{'h:content-type:'} =~ /multipart/i) {
  888. &Log("cannot get boundary string of Content-Type");
  889. &Log("Content-Type: $e{'h:content-type:'}");
  890. }
  891.  
  892. ### SubSection: Check command in mail body
  893. # the range to scan
  894. $limit = $GUIDE_CHECK_LIMIT > $COMMAND_CHECK_LIMIT ?
  895. $GUIDE_CHECK_LIMIT : $COMMAND_CHECK_LIMIT;
  896.  
  897. ## QMAIL command hook
  898. ## dot-qmail(5) ~alias/.uja-default emulates uja-help@domain ("-> #help")
  899. if ($USE_DOT_QMAIL_EXT &&
  900. (!&AddressMatch($MAIL_LIST, $ENV{'RECIPIENT'}))) {
  901. &Log("sets in dot-qmail-ext") if $debug_qmail;
  902. &use('qmail');
  903. &DotQmailExt(*Envelope);
  904. }
  905.  
  906. # search the location of $limit's "\n";
  907. $limit += 10; # against MIME
  908. $p = 0;
  909. while ($limit-- > 0) {
  910. if (index($e{'Body'}, "\n", $p + 1) > 0) {
  911. $p = index($e{'Body'}, "\n", $p + 1);
  912. }
  913. else {
  914. last;
  915. }
  916. }
  917. # +1 for the last "\n";
  918. $buf = substr($e{'Body'}, 0, $p > 0 ? $p+1 : 1024);
  919.  
  920. # check only the first $limit lines.
  921. local($found, $mime_skip);
  922. for (split(/\n/, $buf)) {
  923. print STDERR "INPUT BUF> $_\n" if $debug;
  924.  
  925. # subscribe trap
  926. # XXX: "# command" is internal represention
  927. # XXX: remove '# command' part if exist
  928. if (/^(\s*|\#\s*)$CONFIRMATION_SUBSCRIBE\s+/i) {
  929. $e{'mode:req:subscribe'} = 1;
  930. $e{'buf:req:subscribe'} .= $_."\n";
  931. }
  932.  
  933. if ($CHADDR_AUTH_TYPE eq 'confirmation' &&
  934. (/^(\s*|\#\s*)$CHADDR_KEYWORD\s+/i)) {
  935. $e{'mode:req:chaddr'} = 1;
  936. $e{'buf:req:chaddr'} .= $_."\n";
  937. }
  938.  
  939. # chaddr-confirm trap (may be with citatin e.g. ">")
  940. if ($CHADDR_AUTH_TYPE eq 'confirmation' &&
  941. /$CHADDR_CONFIRMATION_KEYWORD\s+\S+/i) {
  942. $e{'mode:req:chaddr-confirm'} = 1;
  943. $e{'buf:req:chaddr-confirm'} .= $_."\n";
  944. }
  945. # confirm trap (may be with citatin e.g. ">")
  946. elsif (/$CONFIRMATION_KEYWORD\s+\S+/i) {
  947. $e{'mode:req:confirm'} = 1;
  948. $e{'buf:req:confirm'} .= $_."\n";
  949. }
  950.  
  951. # unsubscribe-confirm ID
  952. if (/($UNSUBSCRIBE_CONFIRMATION_KEYWORD\s+\S+.*)/i) {
  953. $e{'buf:req:unsubscribe-confirm'} .= $1."\n";
  954. $e{'mode:req:unsubscribe-confirm'} = 1;
  955. }
  956.  
  957. if ($boundary) { # if MIME skip mode;
  958. if ($_ eq $boundary) { $found++; $mime_skip++; next;}
  959. if (/^Content-Type:/i && $mime_skip) { next;}
  960. # skip the null line after the first MIME separator
  961. if ($mime_skip) { $mime_skip = 0; next;}
  962. }
  963.  
  964. # skip before the first MIME boundary
  965. next if $boundary && !$found;
  966.  
  967. $cc++;
  968. print STDERR " SCAN BUF> $_ ($cc line)\n\n" if $debug;
  969.  
  970. # DO NOT "skip useless checks (2.23?)"
  971. # which we uses in the "guide" request check from a stranger.
  972. # if (! $e{'trap:ctk'}) {
  973. # print STDERR " -skip fml rel. 1 compatible scan\n" if $debug;
  974. # next;
  975. # }
  976.  
  977. # Guide Request from the unknown
  978. if ($GUIDE_CHECK_LIMIT-- > 0) {
  979. $e{'mode:req:guide'} = 1 if /^\#\s*$GUIDE_KEYWORD\s*$/i;
  980.  
  981. # accept 'guide' under --ctladdr;
  982. $e{'mode:req:guide'} = 1
  983. if $e{'mode:ctladdr'} && /^\s*$GUIDE_KEYWORD\s*$/i;
  984. }
  985.  
  986. # Command or not is checked within the first 3 lines.
  987. # '# help\s*' is OK. '# guide"JAPANESE"' & '# JAPANESE' is NOT!
  988. # BUT CANNOT JUDGE '# guide "JAPANESE CHARS"' SYNTAX;-);
  989. if ($COMMAND_CHECK_LIMIT-- > 0) {
  990. $e{'mode:uip'} = 'on' if /^\#\s*[\w\-]+\s|^\#\s*[\w\-]+$/;
  991. $e{'mode:uip:chaddr'} = $_
  992. if /^\#\s*($CHADDR_KEYWORD)\s+/i;
  993. $e{'mode:uip:chaddr-confirm'} = $_
  994. if /^\#\s*($CHADDR_CONFIRMATION_KEYWORD)\s+/i;
  995. }
  996.  
  997. $nclines++ if /^\#/o; # the number of command lines
  998. }
  999.  
  1000. ### close(STDIN); # close(STDIN) prevents open2, really?
  1001.  
  1002. $e{'nlines'} = ($e{'Body'} =~ tr/\n/\n/);
  1003. $e{'nclines'} = $nclines;
  1004. $e{'size'} = $bufsiz;
  1005. $e{'h:Lines:'} = $e{'nlines'};
  1006.  
  1007. ### SubSection: special trap
  1008. return 0 if $CheckCurrentProcUpperPartOnly;
  1009. return 0 if $ccp_mode eq 'upper_part_only';
  1010.  
  1011. ### SubSection: MailBody Size
  1012. if ($e{'trap:mail_size_overflow'}) {
  1013. &use('error');
  1014. &NotifyMailSizeOverFlow(*e);
  1015.  
  1016. if ($ANNOUNCE_MAIL_SIZE_OVERFLOW) {
  1017. &AnnounceMailSizeOver(*e); # call &Distribute;
  1018. }
  1019. else {
  1020. $DO_NOTHING = 1;
  1021. return $NULL;
  1022. }
  1023. }
  1024.  
  1025. # Against a lot of mails for MIME partial, e.g. Outlook
  1026. # Content-Type: message/partial; number=1; total=6; ...
  1027. if ($e{'h:content-type:'} =~ /\/partial\s*;/ &&
  1028. $INCOMING_MAIL_SIZE_LIMIT) {
  1029. local($n, $total, $bufsiz);
  1030.  
  1031. $e{'h:content-type:'} =~ s/number=(\d+)/$n = $1/e;
  1032. $e{'h:content-type:'} =~ s/total=(\d+)/$total = $1/e;
  1033. $bufsiz = length($Envelope{'Body'}) * $total;
  1034.  
  1035. if ($bufsiz > &ATOI($INCOMING_MAIL_SIZE_LIMIT)) {
  1036. &Log("reject for too large size mail");
  1037. &Log("partial message's <$n/$total> total mail size seems too large");
  1038. &Log("evaluated whole size $bufsiz > \$INCOMING_MAIL_SIZE_LIMIT[$INCOMING_MAIL_SIZE_LIMIT]");
  1039.  
  1040. # WARNING in n ==1 partial case.
  1041. if ($n == 1 && $NOTIFY_MAIL_SIZE_OVERFLOW) {
  1042. &use('error');
  1043. &NotifyMailSizeOver;
  1044. }
  1045.  
  1046. if ($n == 1 && $ANNOUNCE_MAIL_SIZE_OVERFLOW) {
  1047. &use('error');
  1048. &AnnounceMailSizeOver(*e); # call &Distribute;
  1049. }
  1050. else {
  1051. $DO_NOTHING = 1;
  1052. }
  1053. }
  1054. else {
  1055. &Log("partial message but the whole size seems enough small")
  1056. if $debug;
  1057. }
  1058. }
  1059.  
  1060. ### SubSection: Access Control based on address
  1061. ## WE SHOULD REJCECT "CANNOT IDENTIFIED AS PERSONAL" ADDRESSES;
  1062. ## In addition, we check another atack possibility;
  1063. ## e.g. majorodmo,listproc,list-subscribe <-> fml-ctl
  1064. if ($REJECT_ADDR && $From_address =~ /^($REJECT_ADDR)\@(\S+)/i) {
  1065. local($addr, $domain) = ($1, $2);
  1066. &Log("reject mail from $addr\@$domain");
  1067. &WarnE("reject mail from $addr\@$domain",
  1068. "reject mail from $addr\@$domain\n");
  1069. $DO_NOTHING = 1;
  1070. return 0;
  1071. }
  1072.  
  1073. # XXX reject all "From: MAIL_LIST" mails (3.0)
  1074. # XXX fix for 3.0.1
  1075. # XXX controllable by %LOOP_CHECKED_HDR_FIELD.
  1076. if (%LOOP_CHECKED_HDR_FIELD) {
  1077. my($f, $v);
  1078.  
  1079. for $f (keys %LOOP_CHECKED_HDR_FIELD) {
  1080. next unless $LOOP_CHECKED_HDR_FIELD{$f};
  1081. if ($v = $e{"h:${f}:"}) {
  1082. $v = &Conv2mailbox($v);
  1083. if (&LoopBackWarn($v)) {
  1084. &Log("$f: <$v> may cause loop. rejected");
  1085. &WarnE("reject mail $f:<$v>",
  1086. "rejected since '$f' header\n".
  1087. "may cause mail loop.\n".
  1088. "${f}: ".
  1089. $e{"h:${f}:"}.
  1090. "\n");
  1091. $DO_NOTHING = 1;
  1092. return 0;
  1093. }
  1094. }
  1095. }
  1096. }
  1097.  
  1098. ## security level (?)
  1099. while (($k, $v) = each %SEVERE_ADDR_CHECK_DOMAINS) {
  1100. print STDERR "/$k/ && ADDR_CHECK_MAX += $v\n" if $debug;
  1101. ($From_address =~ /$k/) && ($ADDR_CHECK_MAX += $v);
  1102. }
  1103.  
  1104. # AGAINST SPAM MAILS
  1105. if (-f $REJECT_ADDR_LIST) {
  1106. if (&RejectAddrP($From_address) ||
  1107. &RejectAddrP($UnixFrom)) {
  1108. $s="Reject spammers: UnixFrom=[$UnixFrom], From=[$From_address]";
  1109. &WarnE("Spam mail from a spammer is rejected $ML_FN",
  1110. "Reject Spammers:\n".
  1111. " UnixFrom\t$UnixFrom\n From\t\t$From_address\n");
  1112. &Log($s);
  1113. $DO_NOTHING = 1;
  1114. return 0;
  1115. }
  1116. }
  1117.  
  1118. ### SubSection: misc
  1119. ### For CommandMode Check(see the main routine in this flie)
  1120. $e{'trap:rcpt_fields'} = $e{'h:to:'} || $e{'h:apparently-to:'};
  1121. $e{'trap:rcpt_fields'} .= ", $e{'h:Cc:'}, ";
  1122. $e{'trap:rcpt_fields'} =~ s/\n(\s+)/$1/g;
  1123.  
  1124. # SUBJECT: GUIDE SYNTAX
  1125. if ($USE_SUBJECT_AS_COMMANDS && $e{'h:Subject:'}) {
  1126. local($_) = $e{'h:Subject:'};
  1127. s/^\s*//;
  1128.  
  1129. $e{'mode:req:guide'}++ if /^\#\s*$GUIDE_KEYWORD\s*$/i;
  1130. $e{'mode:uip'} = 'on' if /^\#\s*[\w\-]+\s|^\#\s*[\w\-]+$/;
  1131. $e{'mode:req:guide'}++
  1132. if $COMMAND_ONLY_SERVER && /^\s*$GUIDE_KEYWORD\s*$/i;
  1133. $e{'mode:uip'} = 'on'
  1134. if $COMMAND_ONLY_SERVER && /^\s*[\w\-]+\s|^\s*[\w\-]+$/;
  1135. }
  1136.  
  1137. # ? for --distribute, here and again in &MLMemberCheck;
  1138. &AdjustActiveAndMemberLists;
  1139.  
  1140. ### SubSection: debug info dump
  1141. if ($debug) { &eval(&FieldsDebug, 'FieldsDebug');}
  1142.  
  1143. ### SubSection: Mail Loop Check
  1144. ## LOOP CHECK PHASE 1: Message-Id
  1145. if ($CHECK_MESSAGE_ID && &DupMessageIdP) { exit 0;}
  1146.  
  1147. ## LOOP CHECK PHASE 2
  1148. # now before flock();
  1149. if ((! $NOT_USE_UNIX_FROM_LOOP_CHECK) &&
  1150. &AddressMatch($UnixFrom, $MAINTAINER)) {
  1151. &Log("WARNING: UNIX FROM Loop[$UnixFrom == $MAINTAINER]");
  1152. &WarnE("WARNING: UNIX FROM Loop",
  1153. "UNIX FROM[$UnixFrom] == MAINTAINER[$MAINTAINER]\n\n");
  1154. exit 0;
  1155. }
  1156.  
  1157. ### SubSection: Address Test Mode; (Become Test Mode)
  1158. if ($Opt{"opt:b"} eq 't') {
  1159. $DO_NOTHING = 1; &Log("Address Test Mode:Do nothing");
  1160. }
  1161.  
  1162. ### SubSection: Crosspost emulation
  1163. # Check crosspost in To: and Cc:
  1164. if ($USE_CROSSPOST) { &use('crosspost');}
  1165. }
  1166.  
  1167. # We REWRITED To: to "To: MAIL_LIST" FOR MORE READABILITY;
  1168. # Check the To: and overwrite it;
  1169. # if To: has $MAIL_LIST, ok++; IF NOT, add $MAIL_LIST to To:
  1170. sub RewriteField
  1171. {
  1172. local(*e, $ruleset) = @_;
  1173. local($f) = 'RuleSetTo' if $ruleset eq 'To';
  1174. $f ? &$f(*e) : &Log("RewriteField: unknown ruleset $ruleset");
  1175. }
  1176.  
  1177. # 2000/11/17 version is modified to use Mail::Address.pm
  1178. # based on the patch by OGAWA Kunihiko <kuni@edit.ne.jp>
  1179. sub RuleSetTo
  1180. {
  1181. local(*e) = @_;
  1182. local($ok, $match, $addr, $x_addr, $ml, $buf, $to);
  1183. local(@ml) = ($MAIL_LIST, @MAIL_LIST_ALIASES); # PLAY_TO Trick;
  1184.  
  1185. eval "require Mail::Address; Mail::Address->import();";
  1186.  
  1187. # if fails, call fml 3.0 compatible routine.
  1188. if ($@ ne '') {
  1189. &Log("WARN: Mail::Address.pm not works well");
  1190. &Log("disable RuleSetTo function");
  1191. return $NULL;
  1192. }
  1193.  
  1194. # search $MALI_LIST (+ aliases) contained in To: ?
  1195. $to = $e{'h:to:'};
  1196. $to =~ s/\s*To: /, /gi;
  1197. for $addr (Mail::Address->parse($to)) {
  1198. $x_addr = $addr->format;
  1199. $addr = $addr->address;
  1200. $match = 0;
  1201.  
  1202. # match one of $MALI_LIST ?
  1203. for $ml (@ml) {
  1204. # ignore $MALI_LIST (or aliases) addresses to avoid noisy To;)
  1205. if (&AddressMatch($addr, $ml)) { $ok++; $match = 1;}
  1206. }
  1207.  
  1208. # IF NOT match one of $MAIL_LIST,
  1209. # gobble not $MALI_LIST address to $buf
  1210. if (! $match) {
  1211. $buf .= $buf ? ", ". $addr : $addr;
  1212. &Log("RuleSetTo: buf = {$buf}") if $debug;
  1213. }
  1214. }
  1215.  
  1216. if ($buf) {
  1217. $e{'h:To:'} .= $e{'h:To:'} ? "\n\t,".$buf : $buf;
  1218. }
  1219. }
  1220.  
  1221. # Expand mailbox in RFC822
  1222. # From_address is user@domain syntax for e.g. member check, logging, commands
  1223. # return "1#mailbox" form ?(anyway return "1#1mailbox" 95/6/14)
  1224. #
  1225. # macro:x is moved to FixHeaderFields (97/05/07 fukui@sonic.nm.fujitsu.co.jp)
  1226. #
  1227. # 2000/11/17 version is modified to use Mail::Address.pm
  1228. # based on the patch by OGAWA Kunihiko <kuni@edit.ne.jp>
  1229. sub Conv2mailbox
  1230. {
  1231. local($mb, *e) = @_; # original string
  1232. local($addr);
  1233.  
  1234. eval "require Mail::Address; Mail::Address->import();";
  1235.  
  1236. # if fails, call fml 3.0 compatible routine.
  1237. if ($@ ne '') {
  1238. &Log($@);
  1239. &Log("WARN: Mail::Address.pm not works well");
  1240. return &__Conv2mailbox30($mb, *e);
  1241. }
  1242.  
  1243. # NULL is given, return NULL
  1244. ($mb =~ /^\s*$/) && (return $NULL);
  1245.  
  1246. ($addr) = (Mail::Address->parse($mb))[0];
  1247. return $NULL unless $addr;
  1248.  
  1249. $e{'tmp:x'} = $addr->phrase . $addr->comment;
  1250. return $addr->address;
  1251. }
  1252.  
  1253. sub __Conv2mailbox30
  1254. {
  1255. local($mb, *e) = @_; # original string
  1256.  
  1257. # return NULL if addr does not contain @. ?
  1258. # return $NULL unless $mb =~ /\@/;
  1259.  
  1260. # $mb = &Strip822Comments($mb);
  1261.  
  1262. # NULL is given, return NULL
  1263. ($mb =~ /^\s*$/) && (return $NULL);
  1264.  
  1265. # RFC822 unfolding and cut the first SPACE|HTAB;
  1266. $mb =~ s/\n(\s+)/$1/g;
  1267. $mb =~ s/^\s*//;
  1268.  
  1269. # Hayakawa Aoi <Aoi@aoi.chan.panic>
  1270. if ($mb =~ /^\s*(.*)\s*<(\S+)>.*$/io) { $e{'tmp:x'} = $1; return $2;}
  1271.  
  1272. # Aoi@aoi.chan.panic (Chacha Mocha no cha nu-to no 1)
  1273. if ($mb =~ /^\s*(\S+)\s*\((.*)\)$/io || $mb =~ /^\s*(\S+)\s*(.*)$/io) {
  1274. $e{'tmp:x'} = $2, return $1;
  1275. }
  1276.  
  1277. # Aoi@aoi.chan.panic
  1278. return $mb;
  1279. }
  1280.  
  1281. # When just guide request from unknown person, return the guide only
  1282. # change reply-to: for convenience
  1283. sub GuideRequest
  1284. {
  1285. local(*e) = @_;
  1286. local($ap);
  1287.  
  1288. if ($debug) { @c=caller; &Log("GuideRequest called from $c[2]");}
  1289.  
  1290. &Log("Guide request");
  1291.  
  1292. $e{'GH:Reply-To:'} = $e{'CtlAddr:'};
  1293. &SendFile($e{'Addr2Reply:'}, "Guide $ML_FN", $GUIDE_FILE);
  1294. }
  1295.  
  1296. # MAIL_LIST == CONTROL_ADDRESS or !CONTROL_ADDRESS ?
  1297. # ATTENTION!
  1298. # if cf == 3, !$CONTROL_ADDRESS IS JUST "distribute only"
  1299. # if cf < 2, !$CONTROL_ADDRESS => Command($MAIL_LIST==$CONTROL_ADDRESS)
  1300. sub CompatFMLv1P
  1301. {
  1302. local($ml) = split(/\@/, $MAIL_LIST);
  1303. local($ca) = split(/\@/, $CONTROL_ADDRESS);
  1304.  
  1305. if ($CFVersion < 3) { return &CF2CompatFMLv1P($ca, $ml);}
  1306.  
  1307. # Version 3, criterion is only "MAIL_LIST == CONTROL_ADDRESS"
  1308. $ml eq $ca && return 1;
  1309. $MAIL_LIST eq $CONTROL_ADDRESS && return 1;
  1310.  
  1311. # Version 3, compat mode for before FML 2.1
  1312. $MAIL_LIST_ACCEPT_COMMAND && return 1;
  1313.  
  1314. 0;
  1315. }
  1316.  
  1317. # Distribute mail to members (fml.pl -> libdist.pl)
  1318. sub Distribute
  1319. {
  1320. local(*e, $mode, $compat_hml) = @_;
  1321. local($ml) = (split(/\@/, $MAIL_LIST))[0];
  1322.  
  1323. # Japanese specific: convert hankaku to zenkaku
  1324. if ($USE_HANKAKU_CONVERTER) {
  1325. if ($LANGUAGE eq 'Japanese') {
  1326. require("module/$LANGUAGE/libhankaku2zenkaku.pl");
  1327. eval &FixJapaneseMDChars(*e);
  1328. &Log($@) if $@;
  1329. }
  1330. }
  1331.  
  1332. # Filtering mail body from members but not check other cases
  1333. # e.g. null body subscribe request in "no-keyword" case
  1334. if ($USE_DISTRIBUTE_FILTER) {
  1335. &EnvelopeFilter(*e, 'distribute');
  1336. return $NULL if $DO_NOTHING;
  1337. }
  1338.  
  1339. # check duplication baesd on mailbody MD5 cksum cache
  1340. if ($CHECK_MAILBODY_CKSUM) {
  1341. &use('cksum');
  1342.  
  1343. if (&CheckMailBodyCKSUM(*e)) {
  1344. # looped !
  1345. $DO_NOTHING = 1;
  1346. return $NULL;
  1347. }
  1348. else {
  1349. # not looped ! O.K. now cache on for the future
  1350. &CacheMailBodyCksum(*e);
  1351. }
  1352. }
  1353.  
  1354. # Security: Mail Traffic Information
  1355. if ($USE_MTI) {
  1356. &use('mti');
  1357. &MTICache(*e, 'distribute');
  1358. return $NULL if &MTIError(*e);
  1359. }
  1360.  
  1361. if ($debug) { @c = caller; &Log("Distritute called from $c[2] ");}
  1362.  
  1363. if ($mode eq 'permit from members_only') {
  1364. $Rcsid .= "; post only (only members can post)";
  1365. }
  1366. elsif ($mode eq 'permit from anyone') {
  1367. $Rcsid .= "; post only (anyone can post)";
  1368. }
  1369. elsif ($mode eq 'permit from moderator') {
  1370. $Rcsid =~ s/^(.*)(\#\d+:\s+.*)/$1."(moderated mode)".$2/e;
  1371. undef $e{'h:approval:'}; # delete the passwd entry;
  1372. undef $e{'h:Approval:'}; # delete the passwd entry;
  1373. }
  1374. ### NOT REMOVE FOR BACKWARD?
  1375. else {
  1376. $Rcsid .= "; post + commands (members only)"; # default ;
  1377. }
  1378.  
  1379. if ($MAIL_LIST eq $CONTROL_ADDRESS) {
  1380. $Rcsid =~ s/post only (from.*)/post $1 + commands/;
  1381. }
  1382.  
  1383.  
  1384. # Dabasase access: dump recipients
  1385. if ($USE_DATABASE) {
  1386. my (%mib, %result, %misc, $error);
  1387. &DataBaseMIBPrepare(\%mib, 'dump_active_list');
  1388. &DataBaseCtl(\%Envelope, \%mib, \%result, \%misc);
  1389. &Log("fail to dump active list") if $mib{'error'};
  1390. return $NULL if $mib{'error'};
  1391.  
  1392. # tricky but effective
  1393. # we push cache file to recipient lists, so that
  1394. # @ACTIVE_LIST = ($ACTIVE_LIST, $mib{'_cache_file'});
  1395. push(@ACTIVE_LIST, $mib{'_cache_file'}) if -f $mib{'_cache_file'};
  1396. &Log("ACTIVE = ( @ACTIVE_LIST )") if $debug;
  1397. }
  1398.  
  1399. require 'libdist.pl';
  1400. &DoDistribute(*e);
  1401. }
  1402.  
  1403. sub RunStartHooks
  1404. {
  1405. $0 = "${FML}: RunStartHooks <$LOCKFILE>";
  1406.  
  1407. # additional before action
  1408. $START_HOOK && &eval($START_HOOK, 'Start hook');
  1409.  
  1410. for (keys %FmlStartHook) {
  1411. print STDERR "Run StartHook $_ -> $FmlStartHook{$_}\n" if $debug;
  1412. next unless $FmlStartHook{$_};
  1413. $0 = "${FML}: Run FmlStartHook [$_] <$LOCKFILE>";
  1414. &eval($FmlStartHook{$_}, "Run FmlStartHook [$_]");
  1415. }
  1416. }
  1417.  
  1418. sub RunEndHooks
  1419. {
  1420. $0 = "${FML}: RunEndHooks <$LOCKFILE>";
  1421.  
  1422. # additional before action
  1423. $END_HOOK && &eval($END_HOOK, 'END_HOOK');
  1424.  
  1425. for (keys %FmlEndHook) {
  1426. print STDERR "Run EndHook $_ -> $FmlEndHook{$_}\n" if $debug;
  1427. next unless $FmlEndHook{$_};
  1428. $0 = "${FML}: Run FmlEndHook [$_] <$LOCKFILE>";
  1429. &eval($FmlEndHook{$_}, "Run FmlEndHook [$_]");
  1430. }
  1431. }
  1432.  
  1433. # Lastly exec to be exceptional process
  1434. sub ExExec { &RunHooks(@_);}
  1435. sub RunExitHooks
  1436. {
  1437. local($s);
  1438. $0 = "${FML}: RunExitHooks <$LOCKFILE>";
  1439.  
  1440. # FIX COMPATIBILITY
  1441. $FML_EXIT_HOOK .= $_PCB{'hook', 'str'};
  1442.  
  1443. if ($s = $FML_EXIT_HOOK) {
  1444. print STDERR "\nmain::eval >$s<\n\n" if $debug;
  1445. $0 = "${FML}: Run Hooks(eval) <$LOCKFILE>";
  1446. &eval($s, 'Run Hooks:');
  1447. }
  1448.  
  1449. for (keys %FmlExitHook) {
  1450. print STDERR "Run hooks $_ -> $FmlExitHook{$_}\n" if $debug;
  1451. next unless $FmlExitHook{$_};
  1452. $0 = "${FML}: Run FmlExitHook [$_] <$LOCKFILE>";
  1453. &eval($FmlExitHook{$_}, "Run FmlExitHook [$_]");
  1454. }
  1455. }
  1456.  
  1457. sub ExecNewProcess
  1458. {
  1459. $0 = "${FML}: Run New Process <$LOCKFILE>";
  1460. $FML_EXIT_PROG .= $_PCB{'hook', 'prog'};
  1461. if ($FML_EXIT_PROG) { &use('kernsubr2'); &__ExecNewProcess;}
  1462. }
  1463.  
  1464. sub SpawnProcess
  1465. {
  1466. local(@xargv) = @_; &use('kernsubr2'); &__SpawnProcess(@xargv);
  1467. }
  1468.  
  1469. ####### Section: Member Check
  1470. # fix array list;
  1471. #
  1472. # files to check for the authentication 96/09/17
  1473. # @MEMBER_LIST = ($MEMBER_LIST) unless @MEMBER_LIST;
  1474. sub AdjustActiveAndMemberLists
  1475. {
  1476. local($f, $status);
  1477.  
  1478. if ($status = &AutoRegistrableP) {
  1479. # automatic asymmetric registration
  1480. # XXX: fml 2.x auto_asymmetric_regist fakes "only member list".
  1481. if ($status eq "auto_asymmetric_regist") {
  1482. $FILE_TO_REGIST = $FILE_TO_REGIST || $ACTIVE_LIST;
  1483. &Touch($FILE_TO_REGIST) unless -f $FILE_TO_REGIST;
  1484. }
  1485. # XXX: fml 2.x auto_regist uses only member file.
  1486. # XXX: fml 3.x auto_subscribe uses actives and members.
  1487. elsif (&NotUseSeparateListP) {
  1488. # XXX: this block is always true in 2.x but false in 3.x.
  1489. $ACTIVE_LIST = $MEMBER_LIST;
  1490. for (@MEMBER_LIST) {
  1491. grep(/$_/, @ACTIVE_LIST) || push(@ACTIVE_LIST, $_);
  1492. }
  1493. }
  1494. }
  1495.  
  1496. grep(/$MEMBER_LIST/, @MEMBER_LIST) || push(@MEMBER_LIST, $MEMBER_LIST);
  1497. grep(/$ACTIVE_LIST/, @ACTIVE_LIST) || push(@ACTIVE_LIST, $ACTIVE_LIST);
  1498.  
  1499. if (($f = $FILE_TO_REGIST) && -f $FILE_TO_REGIST) {
  1500. grep(/$f/, @MEMBER_LIST) || push(@MEMBER_LIST, $f);
  1501. grep(/$f/, @ACTIVE_LIST) || push(@ACTIVE_LIST, $f);
  1502. }
  1503. elsif (-f $FILE_TO_REGIST) {
  1504. &Log("ERROR: \$FILE_TO_REGIST NOT EXIST");
  1505. }
  1506.  
  1507. # ONLY IF EXIST ALREADY, add the admin list (if not, noisy errors...;-)
  1508. if (($f = $ADMIN_MEMBER_LIST) && -f $ADMIN_MEMBER_LIST) {
  1509. grep(/$f/, @MEMBER_LIST) || push(@MEMBER_LIST, $f);
  1510. }
  1511. }
  1512.  
  1513. # if found, return the non-null file name;
  1514. sub DoMailListMemberP
  1515. {
  1516. local($addr, $type) = @_;
  1517. local($file, @file, %file);
  1518.  
  1519. # DataBase Access
  1520. if ($USE_DATABASE) {
  1521. &use('databases');
  1522.  
  1523. my ($action) = $type eq 'm' ? 'member_p' : 'active_p';
  1524.  
  1525. # try to access database to verify the server is alive
  1526. # and try to verify the address is member or not.
  1527. my (%mib, %result, %misc, $error);
  1528. &DataBaseMIBPrepare(\%mib, $action, {'address' => $addr});
  1529. &DataBaseCtl(\%Envelope, \%mib, \%result, \%misc);
  1530.  
  1531. # if search fails for $MEMBER_LIST,
  1532. if ( !$mib{'_result'} && ($type eq 'm')) {
  1533. # retry search in $ADMIN_MEMBER_LIST
  1534. my (%xmib, %result, %misc, $error);
  1535. &DataBaseMIBPrepare(\%xmib,'admin_member_p',{'address' => $addr});
  1536. &DataBaseCtl(\%Envelope, \%xmib, \%result, \%misc);
  1537.  
  1538. # search fails for both $MEMBER_LIST and $ADMIN_MEMBER_LIST
  1539. if ($xmib{'error'}) {
  1540. return 0;
  1541. }
  1542. else {
  1543. $mib{ _result } = $xmib{ _result };
  1544. }
  1545. }
  1546. # if search fails for $ACTIVE_LIST, return here ASAP.
  1547. elsif ($mib{'error'}) {
  1548. return 0;
  1549. }
  1550.  
  1551. $Envelope{'database:cache:$action'} = 1 if $mib{'_result'};
  1552.  
  1553. return $mib{'_result'};
  1554. }
  1555. # XXX not reach here when you use $USE_DATABASE
  1556.  
  1557. $SubstiteForMemberListP = 1;
  1558.  
  1559. @file = $type eq 'm' ? @MEMBER_LIST : @ACTIVE_LIST;
  1560.  
  1561. for $file (@file) {
  1562. next unless -f $file;
  1563. next if $file{$file}; $file{$file} = 1; # uniq
  1564.  
  1565. # prohibit ordinary people operations (but should permit probing only)
  1566. # NOT CHECK OUTSIDE "amctl" procedures in &Command;
  1567. # WITHIN "amctl"
  1568. # check also $ADMIN_MEMBER_LIST if IN ADMIN MODE
  1569. # ignore $ADMIN_MEMBER_LIST if NOT IN ADMIN MODE
  1570. if ($e{'mode:in_amctl'} && # in "amctl" library
  1571. ($file eq $ADMIN_MEMBER_LIST) &&
  1572. (! $e{'mode:admin'})) { # called NOT in ADMIN MODE
  1573. next;
  1574. }
  1575.  
  1576. if ($debug && -f $file) {
  1577. &Debug(" DoMailListMemberP(\n\t$addr\n\tin $file);\n");
  1578. }
  1579.  
  1580. if (-f $file && &Lookup($addr, $file)) {
  1581. &Debug("+++Hit: $addr in $file") if $debug;
  1582. $SubstiteForMemberListP = 0;
  1583. return $file;
  1584. }
  1585. }
  1586. $SubstiteForMemberListP = 0;
  1587.  
  1588. if ($IDENTIFY_MIGRATING_DOMAIN) {
  1589. # avoid recursive call under libmgrdom.pl
  1590. return $NULL if $Envelope{'mode:in_mgrdom'};
  1591. &use('mgrdom');
  1592. &MgrdomConsider($addr, $type);
  1593. }
  1594. else {
  1595. $NULL;
  1596. }
  1597. }
  1598.  
  1599. sub MailListMemberP { return &DoMailListMemberP(@_, 'm');}
  1600. sub MailListActiveP { return &DoMailListMemberP(@_, 'a');}
  1601.  
  1602. sub MailListAdminMemberP
  1603. {
  1604. my ($addr) = @_;
  1605.  
  1606. # DataBase Access
  1607. if ($USE_DATABASE) {
  1608. &use('databases');
  1609.  
  1610. my (%mib, %result, %misc, $error);
  1611. &DataBaseMIBPrepare(\%mib, 'admin_member_p', {'address' => $addr});
  1612. &DataBaseCtl(\%Envelope, \%mib, \%result, \%misc);
  1613. if ($mib{'error'}) { return 0;}
  1614.  
  1615. $Envelope{'database:cache:admin_member_p'} = 1 if $mib{'_result'};
  1616.  
  1617. return $mib{'_result'};
  1618. }
  1619. else {
  1620. &Lookup($addr, $ADMIN_MEMBER_LIST);
  1621. }
  1622. }
  1623.  
  1624. sub NonAutoRegistrableP { ! &AutoRegistrableP;}
  1625. sub AutoRegistrableP
  1626. {
  1627. if ($REJECT_POST_HANDLER =~ /auto\S+regist/ &&
  1628. $REJECT_COMMAND_HANDLER eq 'auto_asymmetric_regist') {
  1629. &Log("These HANDLER configuration may not work well");
  1630. }
  1631.  
  1632. if ($Envelope{'mode:ctladdr'} &&
  1633. ($REJECT_POST_HANDLER eq 'auto_asymmetric_regist' ||
  1634. $REJECT_COMMAND_HANDLER eq 'auto_asymmetric_regist')) {
  1635. "auto_asymmetric_regist";
  1636. }
  1637. elsif ($Envelope{'mode:ctladdr'} &&
  1638. ($REJECT_POST_HANDLER eq 'auto_subscribe' ||
  1639. $REJECT_COMMAND_HANDLER eq 'auto_subscribe')) {
  1640. "auto_subscribe";
  1641. }
  1642. elsif ($REJECT_COMMAND_HANDLER =~ /auto_regist/i ||
  1643. $REJECT_COMMAND_HANDLER =~ /auto_subscribe/i ||
  1644. $REJECT_COMMAND_HANDLER =~ /autoregist/i) {
  1645. $REJECT_COMMAND_HANDLER;
  1646. }
  1647. elsif ($REJECT_POST_HANDLER =~ /auto_regist/i ||
  1648. $REJECT_POST_HANDLER =~ /auto_subscribe/i ||
  1649. $REJECT_POST_HANDLER =~ /autoregist/i) {
  1650. $REJECT_POST_HANDLER;
  1651. }
  1652. else {
  1653. 0;
  1654. }
  1655. }
  1656.  
  1657. sub NotUseSeparateListP { ! &UseSeparateListP;}
  1658. sub UseSeparateListP
  1659. {
  1660. local($x) = &AutoRegistrableP;
  1661.  
  1662. if ($debug_fml30 == 1) {
  1663. &Log("AutoRegistrableP = $x"); $debug_fml30++;
  1664. }
  1665.  
  1666. if ($x eq 'auto_subscribe' || (! $x)) {
  1667. 1;
  1668. }
  1669. else {
  1670. 0;
  1671. }
  1672. }
  1673.  
  1674. # canonicalize address for database storing
  1675. # since we search it in case sensitive mode.
  1676. # preserve case in localpart but not in domain part
  1677. sub TrivialRewrite
  1678. {
  1679. my ($addr) = @_;
  1680. my ($local, $domain) = split(/\@/, $addr);
  1681. $domain =~ tr/A-Z/a-z/;
  1682. $local. '@'. $domain;
  1683. }
  1684.  
  1685. sub AutoRegistHandler
  1686. {
  1687. if ($debug) { @c = caller; &Log("AutoRegistHandler called from $c[2]");}
  1688.  
  1689. &use('amctl');
  1690. &AutoRegist(*Envelope);
  1691. }
  1692.  
  1693. sub RejectHandler
  1694. {
  1695. if ($debug) { @c = caller; &Log("RejectHandler called from $c[2]");}
  1696.  
  1697. &Log("Rejected: \"From:\" field is not member");
  1698. &WarnE("NOT MEMBER article from $From_address $ML_FN",
  1699. "NOT MEMBER article from $From_address\n\n");
  1700. if (-f $DENY_FILE) {
  1701. &SendFile($From_address,
  1702. "You $From_address are not member $ML_FN", $DENY_FILE);
  1703. }
  1704. else {
  1705. &Mesg(*Envelope, 'you are not member.', 'info.reject');
  1706. }
  1707. }
  1708.  
  1709. sub IgnoreHandler
  1710. {
  1711. &Log("Ignored: \"From:\" field is not member");
  1712. &WarnE("Ignored NOT MEMBER article from $From_address $ML_FN",
  1713. "Ignored NOT MEMBER article from $From_address");
  1714. }
  1715.  
  1716. # Lookup(key, file); return 1 if the "key" is found in the "file".
  1717. # e.g. Lookup(addr, member-list-file)
  1718. # return 1 if a given address is authenticated as member's (found in the file).
  1719. #
  1720. # performance test example 1 (100 times for 158 entries == 15800)
  1721. # fastest case
  1722. # old 1.880u 0.160s 0:02.04 100.0% 74+34k 0+1io 0pf+0w
  1723. # new 1.160u 0.160s 0:01.39 94.9% 73+36k 0+1io 0pf+0w
  1724. # slowest case
  1725. # old 20.170u 1.520s 0:22.76 95.2% 74+34k 0+1io 0pf+0w
  1726. # new 9.050u 0.190s 0:09.90 93.3% 74+36k 0+1io 0pf+0w
  1727. #
  1728. # the actual performance is the average between values above
  1729. # but the new version provides stable performance.
  1730. #
  1731. sub CheckMember { &Lookup(@_);}
  1732. sub Lookup
  1733. {
  1734. local($address, $file) = @_;
  1735. local($addr, $has_special_char, $auto_registrable);
  1736.  
  1737. # mode
  1738. $auto_registrable = &AutoRegistrableP;
  1739.  
  1740. # more severe check;
  1741. $address =~ s/^\s*//;
  1742. if ($address =~ /\@/) { # RFC822 addrspec
  1743. ($addr) = split(/\@/, $address);
  1744. }
  1745. else { # not addrspec, arbitrary string
  1746. $addr = substr($address, 0, 8);
  1747. }
  1748.  
  1749. # MUST BE ONLY * ? () [] but we enhance the category -> shell sc
  1750. # add + (+ed user) 1998/11/08
  1751. if ($addr =~ /[\+\$\&\*\(\)\{\}\[\]\'\\\"\;\\\\\|\?\<\>\~\`]/) {
  1752. $has_special_char = 1;
  1753. }
  1754.  
  1755. open(LOOKUP_TABLE, $file) || do {
  1756. &Log("LookUp: cannot open $file");
  1757. return 0;
  1758. };
  1759. getline: while (<LOOKUP_TABLE>) {
  1760. chop;
  1761.  
  1762. if ($auto_registrable || $SubstiteForMemberListP) {
  1763. /^\#\s*(.*)/ && ($_ = $1);
  1764. }
  1765.  
  1766. next getline if /^\#/o; # strip comments
  1767. next getline if /^\s*$/o; # skip null line
  1768. /^\s*(\S+)\s*.*$/o && ($_ = $1); # including .*#.*
  1769.  
  1770. # member nocheck(for nocheck but not add mode)
  1771. # fixed by yasushi@pier.fuji-ric.co.jp 95/03/10
  1772. # $ENCOUNTER_PLUS by fukachan@phys 95/08
  1773. # $Envelope{'mode:anyone:ok'} by fukachan@phys 95/10/04
  1774. # $Envelope{'trap:+'} by fukachan@sapporo 97/06/28
  1775. if (/^\+\s*$/o) {
  1776. &Debug("encounter + [$_]") if $debug;
  1777. $Envelope{'trap:+'} = 1;
  1778. close(LOOKUP_TABLE);
  1779. return 1;
  1780. }
  1781.  
  1782. # for high performance(Firstly special character check)
  1783. if (! $has_special_char) { next getline unless /^$addr/i;}
  1784.  
  1785. # This searching algorithm must require about N/2, not tuned,
  1786. if (1 == &AddressMatch($_, $address)) {
  1787. close(LOOKUP_TABLE);
  1788. return 1;
  1789. }
  1790. }# end of while loop;
  1791.  
  1792. close(LOOKUP_TABLE);
  1793. return 0;
  1794. }
  1795.  
  1796. # for convenience
  1797. sub ExactAddressMatch
  1798. {
  1799. local($addr1, $addr2) = @_;
  1800. &SaveACL;
  1801. $ADDR_CHECK_MAX = 100;
  1802. local($r) = &AddressMatch($addr1, $addr2);
  1803. &RetACL;
  1804. $r;
  1805. }
  1806.  
  1807. # sub AddressMatching($addr1, $addr2)
  1808. # return 1 given addresses are matched at the accuracy of 4 fields
  1809. sub AddressMatching { &AddressMatch(@_);}
  1810. sub AddressMatch
  1811. {
  1812. local($addr1, $addr2) = @_;
  1813.  
  1814. &Debug(" AddressMatch($addr1, $addr2)".
  1815. " [\$ADDR_CHECK_MAX=$ADDR_CHECK_MAX]\n") if $debug_addrmatch;
  1816.  
  1817. # canonicalize to lower case
  1818. $addr1 =~ y/A-Z/a-z/;
  1819. $addr2 =~ y/A-Z/a-z/;
  1820.  
  1821. # try exact match. must return here in a lot of cases.
  1822. if ($addr1 eq $addr2) {
  1823. if ($debug) {
  1824. &Debug(" AddressMatch($addr1, $addr2) => exact match");
  1825. &Log("AddressMatch($addr1, $addr2) => exact match");
  1826. }
  1827. return 1;
  1828. }
  1829.  
  1830. # for further investigation, parse account and host
  1831. local($acct1, $addr1) = split(/@/, $addr1);
  1832. local($acct2, $addr2) = split(/@/, $addr2);
  1833.  
  1834. # At first, account is the same or not?;
  1835. if ($acct1 ne $acct2) { return 0;}
  1836.  
  1837. # Get an array "jp.ac.titech.phys" for "fukachan@phys.titech.ac.jp"
  1838. local(@d1) = reverse split(/\./, $addr1);
  1839. local(@d2) = reverse split(/\./, $addr2);
  1840.  
  1841. # Check only "jp.ac.titech" part( = 3)(default)
  1842. # If you like to strict the address check,
  1843. # change $ADDR_CHECK_MAX = e.g. 4, 5 ...
  1844. local($i, $m) = (0, 0);
  1845. while ($d1[$i] && $d2[$i] && ($d1[$i] eq $d2[$i])) { $i++;}
  1846.  
  1847. $m = ($ADDR_CHECK_MAX > 0) ? $ADDR_CHECK_MAX : 3;
  1848.  
  1849. if ($debug) {
  1850. &Debug(" AddressMatch($acct1\@$addr1, $acct2\@$addr2) => ".
  1851. (($i >= $m) ? "match" : "not match").
  1852. " [$i >= $m ? y : n]");
  1853. &Log("AddressMatch($acct1\@$addr1, $acct2\@$addr2) => ".
  1854. (($i >= $m) ? "match" : "not match"));
  1855. &Log("AddressMatch: $i >= $m ? match : not match");
  1856. }
  1857.  
  1858. ($i >= $m) ? 1 : 0;
  1859. }
  1860.  
  1861. sub LowerDomain
  1862. {
  1863. my ($addr) = @_;
  1864. my (@addr) = split(/\@/, $addr);
  1865.  
  1866. if ($DATABASE_DRIVER_ATTRIBUTES =~ /always_lower_domain/) {
  1867. $addr[1] =~ tr/A-Z/a-z/;
  1868. return $addr[0].'@'.$addr[1];
  1869. }
  1870. else {
  1871. return $addr;
  1872. }
  1873. }
  1874.  
  1875. ####### Section: Info
  1876. # Recreation of the whole mail for error infomation
  1877. sub WholeMail {
  1878. local(@xargv) = @_; &use('kernsubr2'); &__WholeMail(@xargv);
  1879. }
  1880.  
  1881. sub ForwMail {
  1882. local(@xargv) = @_; &use('kernsubr2'); &__ForwMail(@xargv);
  1883. }
  1884.  
  1885. sub Translate
  1886. {
  1887. local(*e, $s, $mesgle_key, @mesgle_argv) = @_;
  1888. if ($MESSAGE_LANGUAGE && $mesgle_key) {
  1889. &use('mesgle');
  1890. &MesgLE(*e, $mesgle_key, @mesgle_argv);
  1891. }
  1892. else { $NULL;}
  1893. }
  1894.  
  1895. # &Mesg(*e, );
  1896. # $mesgle == Message Languae Extension
  1897. sub Mesg
  1898. {
  1899. local(*e, $s, $mesgle_key, @mesgle_argv) = @_;
  1900.  
  1901. if ($MESSAGE_LANGUAGE && $mesgle_key) {
  1902. &Debug("MesgLE:try to translate key <$mesgle_key>") if $debug;
  1903. &use('mesgle');
  1904. $s = &MesgLE(*e, $mesgle_key, @mesgle_argv) || $s;
  1905. }
  1906.  
  1907. # if $s is null, return just now! (DUMMY_OPS may be useful)
  1908. return unless $s;
  1909.  
  1910. $e{'message'} .= "$s\n";
  1911. $MesgBuf .= "$s\n";
  1912.  
  1913. # dup to admins
  1914. $e{'message:to:admin'} .= "$s\n" if $e{'mode:notify_to_admin_also'};
  1915. }
  1916.  
  1917. # no real data copy but
  1918. # enable flag to mail body forwarding in Smtp() via Notify().
  1919. sub MesgMailBodyCopyOn
  1920. {
  1921. &Mesg(*e, "Original mail as follows:\n");
  1922. $Envelope{'message:ebuf2socket'} = 1;
  1923. }
  1924.  
  1925. sub MesgSetBreakPoint { undef $MesgBuf;}
  1926. sub MesgGetABP { $MesgBuf;} # After Break Point
  1927.  
  1928. # Forwarded and Warned to Maintainer;
  1929. sub Warn { &Forw(@_);}
  1930. sub Forw { &Sendmail($MAINTAINER, $_[0], $_[1]);}
  1931. sub WarnFile
  1932. {
  1933. local($subject, $file, $preamble, $trailor) = @_;
  1934. local($to, @file);
  1935.  
  1936. @file = $file; undef $file;
  1937. @to = ($MAINTAINER);
  1938. $Envelope{'preamble'} = $preamble;
  1939. &NeonSendFile(*to, *subject,*file);
  1940. undef $Envelope{'preamble'};
  1941. }
  1942.  
  1943. # Warn() with direct buffer copy from %Envelope to Socket
  1944. # and with "mh forwarding" separators (added in smtp library).
  1945. sub WarnF
  1946. {
  1947. $Envelope{'ctl:smtp:forw:ebuf2socket'} = 1;
  1948. &WarnE(@_);
  1949. $Envelope{'ctl:smtp:forw:ebuf2socket'} = 0;
  1950. }
  1951.  
  1952. # Extended Warn() with direct buffer copy from %Envelope to Socket
  1953. sub WarnE
  1954. {
  1955. local($subject, $body, $preamble, $trailor) = @_;
  1956. local($title);
  1957.  
  1958. $Envelope{'preamble'} = $preamble;
  1959.  
  1960. $title = $Envelope{"tmp:ws"} || "Original mail as follows";
  1961. $title = "\n$title:\n\n";
  1962.  
  1963. $Envelope{'ctl:smtp:ebuf2socket'} = 1;
  1964. &Sendmail($MAINTAINER, $subject, $body.$title);
  1965. $Envelope{'ctl:smtp:ebuf2socket'} = 0;
  1966.  
  1967. undef $Envelope{'preamble'};
  1968. }
  1969.  
  1970. sub Notify
  1971. {
  1972. local(@xargv) = @_; &use('kernsubr'); &__Notify(@xargv);
  1973. }
  1974.  
  1975. sub EnableReportForw2Admin
  1976. {
  1977. local(*e) = @_; $e{'mode:notify_to_admin_also'} = 1;
  1978. }
  1979.  
  1980. sub DisableReportForw2Admin
  1981. {
  1982. local(*e) = @_; $e{'mode:notify_to_admin_also'} = 0;
  1983. }
  1984.  
  1985. ##
  1986. ## Negative Cache Wrapper()'s
  1987. ##
  1988. # wrap &Mesg() with negative cache
  1989. sub CMesg
  1990. {
  1991. local($msgkey, $howold, *e, $s, $mesgle_key, @mesgle_argv) = @_;
  1992. if (&OutOfNegativeCacheP($msgkey, $howold)) {
  1993. &Mesg(*e, $s, $mesgle_key, @mesgle_argv);
  1994. }
  1995. }
  1996.  
  1997. # wrap &Warn() with negative cache
  1998. sub CWarn
  1999. {
  2000. local($msgkey, $howold, $subject, $body) = @_;
  2001. if (&OutOfNegativeCacheP($msgkey, $howold)) {
  2002. &Warn($subject, $body);
  2003. }
  2004. }
  2005.  
  2006. sub OutOfNegativeCacheP
  2007. {
  2008. local($msgkey, $howold) = @_;
  2009. local($dir) = "$VAR_DIR/mesgcache";
  2010. local($cf) = "$dir/$msgkey";
  2011. local($x);
  2012.  
  2013. -d $dir || &Mkdir($dir);
  2014.  
  2015. if (-f $cf) {
  2016. $x = time - (stat($cf))[9]; # how lod
  2017. if ($x < $howold) {
  2018. print STDERR "ignore now ($count, $x $howold)\n" if $debug;
  2019. return 0; # in negative cache
  2020. }
  2021. }
  2022.  
  2023. &Touch($cf);
  2024.  
  2025. 1;
  2026. }
  2027.  
  2028. # Generate additional information for command mail reply.
  2029. # return the STRING
  2030. sub GenInfo
  2031. {
  2032. local($s, $c, $d, $del);
  2033. local($message, $has_ctladdr_p, $addr, $trap);
  2034.  
  2035. # initialize variables
  2036. $del = ('*' x 60);
  2037.  
  2038. # if has control-address
  2039. if ($CONTROL_ADDRESS) {
  2040. $addr = $Envelope{'CtlAddr:'};
  2041. $has_ctladdr_p = 1;
  2042. }
  2043. # if !control-address but MAIL_LIST==CONTROL_ADDRESS
  2044. elsif ((! $CONTROL_ADDRESS) && &CompatFMLv1P) {
  2045. $addr = $MAIL_LIST;
  2046. $has_ctladdr_p = 1;
  2047. }
  2048. elsif ((! $CONTROL_ADDRESS) && $MAIL_LIST_ACCEPT_COMMAND) {
  2049. $addr = $MAIL_LIST;
  2050. $has_ctladdr_p = 1;
  2051. }
  2052.  
  2053. # help style;
  2054. $message = $Envelope{"mode:fmlserv"} ? "help": "$Envelope{'trap:ctk'}help";
  2055. if ($MAIL_LIST =~ /^(fmlserv|majordomo|listserv)/i) {
  2056. $trap = '';
  2057. }
  2058. else {
  2059. $trap = &CompatFMLv1P ? '#' : '';
  2060. }
  2061.  
  2062. $s .= "\n$del\n";
  2063.  
  2064. # URL Extentions
  2065. if ($ADD_URL_INFO) {
  2066. if ($Envelope{'mode:stranger'}) {
  2067. $URLInfo = ";\n\t<mailto:$MAINTAINER>";
  2068. $URLComInfo = &GenXMLInfo;
  2069. }
  2070. # not stranger and has ctladdr (From: is a member).
  2071. elsif ($has_ctladdr_p) {
  2072. $s .= "\n";
  2073. $s .= " Help: <mailto:$addr?body=${trap}help>\n";
  2074. $s .= "Unsubscribe: <mailto:$addr?body=${trap}unsubscribe>\n";
  2075. $s .= "\n";
  2076.  
  2077. $URLInfo = ";\n\thelp=<mailto:$addr?body=${trap}help>";
  2078. $URLComInfo = &GenXMLInfo;
  2079. }
  2080. # not stranger and has no ctladdr (From: is a member).
  2081. else {
  2082. $URLInfo = ";\n\t<mailto:$MAINTAINER>";
  2083. }
  2084. }
  2085.  
  2086. # RFC2369; Proposed Standard (so fml optional)
  2087. if ($USE_RFC2369) { &use('kernsubr2'); &EmulRFC2369;}
  2088.  
  2089. $s .= "If you have any questions or problems,\n";
  2090. $s .= " please contact $MAINTAINER\n";
  2091.  
  2092. if (! $Envelope{'mode:stranger'} && $has_ctladdr_p) { # a member
  2093. $s .= " or \n";
  2094. $s .= " send e-mail with the body \"$message\"(without quotes) to\n";
  2095. $s .= " $addr\n";
  2096. $s .= " (here is the automatic reply, so more preferable)\n\n";
  2097. $s .= "e.g. on a Unix Machine\n";
  2098. $s .= "(shell prompt)\% echo \"$message\" |Mail $addr";
  2099. }
  2100.  
  2101. $s .= "\n\n$del\n";
  2102.  
  2103. $s;
  2104. }
  2105.  
  2106.  
  2107. sub GenXMLInfo
  2108. {
  2109. if ($X_ML_INFO_MESSAGE) {
  2110. $X_ML_INFO_MESSAGE;
  2111. }
  2112. elsif ($Envelope{'mode:stranger'} ||
  2113. (!$CONTROL_ADDRESS &&
  2114. $PERMIT_POST_FROM =~ /^(anyone|members_only)$/)) {
  2115. "If you have a question,\n\tplease contact $MAINTAINER".
  2116. ";\n\t<mailto:$MAINTAINER>";
  2117. }
  2118. else {
  2119. "If you have a question, send e-mail with the body\n".
  2120. "\t\"". $Envelope{'trap:ctk'}.
  2121. "help\" (without quotes) to the address ". &CtlAddr .
  2122. $URLInfo;
  2123. }
  2124. }
  2125.  
  2126. ####### Section: IO
  2127. # Log: Logging function
  2128. # ALIAS:Logging(String as message) (OLD STYLE: Log is an alias)
  2129. # delete \015 and \012 for seedmail return values
  2130. # $s for ERROR which shows trace infomation
  2131. sub Logging { &Log(@_);} # BACKWARD COMPATIBILITY
  2132. sub LogWEnv { local($s, *e) = @_; &Log($s); $e{'message'} .= "$s\n";}
  2133.  
  2134. sub Log
  2135. {
  2136. local($str, $s) = @_;
  2137. local($package, $filename, $line) = caller; # called from where?
  2138. local($from) = $PeerAddr ? "$From_address[$PeerAddr]" : $From_address;
  2139. local($error);
  2140.  
  2141. &GetTime;
  2142.  
  2143. $str =~ s/\015\012$//; # FIX for SMTP (cut \015(^M));
  2144.  
  2145. if ($debug_smtp && ($str =~ /^5\d\d\s/)) {
  2146. $error .= "Sendmail ERROR:\n";
  2147. $error .= "\t$Now $str $_\n\t($package, $filename, $line)\n\n";
  2148. }
  2149.  
  2150. $str = "$filename:$line% $str" if $debug_caller;
  2151.  
  2152. # existence and append(open system call check)
  2153. if (-f $LOGFILE && open(APP, ">> $LOGFILE")) {
  2154. &Append2("$Now $str ($from)", $LOGFILE);
  2155. &Append2("$Now $filename:$line% $s", $LOGFILE) if $s;
  2156. }
  2157. else {
  2158. print STDERR "$Now ($package, $filename, $line) $LOGFILE\n";
  2159. print STDERR "$Now $str ($from)\n\t$s\n";
  2160. }
  2161.  
  2162. $Envelope{'error'} .= $error if $error;
  2163.  
  2164. print STDERR "*** $str; $s;\n" if $debug;
  2165. }
  2166.  
  2167. # $mode: see open(2)
  2168. sub __Write30
  2169. {
  2170. local(*e, *s, *f, $mode, $envelope_hash_key) = @_;
  2171. local($status);
  2172.  
  2173. if ($mode eq "O_APPEND") {
  2174. $status = open(WRITE2_OUT, ">> $f");
  2175. }
  2176. else {
  2177. $status = open(WRITE2_OUT, "> $f");
  2178. }
  2179.  
  2180. if ($status) {
  2181. select(WRITE2_OUT); $| = 1; select(STDOUT);
  2182.  
  2183. # XXX Caution: "\n" handling differs.
  2184. if ($envelope_hash_key) {
  2185. print WRITE2_OUT $e{$envelope_hash_key};
  2186. }
  2187. else {
  2188. print WRITE2_OUT $s, "\n";
  2189. }
  2190. close(WRITE2_OUT);
  2191.  
  2192. 1;
  2193. }
  2194. else {
  2195. 0;
  2196. }
  2197. }
  2198.  
  2199. sub HashValueAppend
  2200. {
  2201. local(*e, $key, $f) = @_;
  2202.  
  2203. &__Write30(*e, *NULL, *f, "O_APPEND", $key) || do {
  2204. local(@caller) = caller;
  2205. print STDERR "HashValueAppend(@_)::Error caller=<@caller>\n";
  2206. };
  2207. }
  2208.  
  2209. # append $s >> $file
  2210. # if called from &Log and fails, must be occur an infinite loop. set $nor
  2211. # return NONE
  2212. sub Append2
  2213. {
  2214. local($s, $f, $o_append) = @_;
  2215.  
  2216. ($s && &__Write30(*NULL, *s, *f, "O_APPEND")) || do {
  2217. local(@caller) = caller;
  2218. print STDERR "Append2(@_)::Error caller=<@caller>\n";
  2219. };
  2220. }
  2221.  
  2222. sub Write2
  2223. {
  2224. local($s, $f, $o_append) = @_;
  2225.  
  2226. if ($o_append) {
  2227. return &Append2(@_);
  2228. }
  2229. elsif ($s && &__Write30(*NULL, *s, *f, "O_RWONLY")) {
  2230. ;
  2231. }
  2232. else {
  2233. local(@caller) = caller;
  2234. print STDERR "Write2(@_)::Error caller=<@caller>\n";
  2235. return 0;
  2236. };
  2237.  
  2238. 1;
  2239. }
  2240.  
  2241. sub Touch { open(APP, ">>$_[0]"); close(APP); chown $<, $GID, $_[0] if $GID;}
  2242.  
  2243. sub Write3
  2244. {
  2245. local(@xargv) = @_; &use('kernsubr'); &__Write3(@xargv);
  2246. }
  2247.  
  2248. sub GetFirstLineFromFile
  2249. {
  2250. if (open(GFLFF, $_[0])) {
  2251. chop($_ = <GFLFF>);
  2252. close(GFLFF);
  2253. $_;
  2254. }
  2255. else {
  2256. return $NULL;
  2257. }
  2258. }
  2259.  
  2260. # $id = IncrementCounter(file, modulus)
  2261. sub IncrementCounter
  2262. {
  2263. my ($f, $modulus) = @_;
  2264. my ($id) = 0;
  2265.  
  2266. # return cached id (against duplicated calls within one thread).
  2267. $IncrementCounterCalled{$f}++;
  2268. return $IncrementCounter{$f} if $IncrementCounterCached{$f};
  2269.  
  2270. &Touch($f) unless -f $f;
  2271. if (-f $f) {
  2272. $id = &GetFirstLineFromFile($f);
  2273. $id++;
  2274. if ($modulus > 0) { $id = $id % $modulus;}
  2275. if (open($f, "> ${f}.$$.new")) {
  2276. select($f); $| = 1; select(STDOUT);
  2277. print $f $id, "\n";
  2278. close($f);
  2279. rename("${f}.$$.new", $f);
  2280. }
  2281. }
  2282. $IncrementCounter{$f} = $id; # (0 .. modulus-unit)
  2283. $IncrementCounterCached{$f} = 1;
  2284. $id;
  2285. }
  2286.  
  2287. # For Example,
  2288. # $pp = $p = 0;
  2289. # while (1) {
  2290. # $p = &GetLinePtrFromHash(*Envelope, "Body", $pp);
  2291. # print substr($Envelope{'Body'}, $pp, $p-$pp+1);
  2292. # last if $p < 0;
  2293. # $pp = $p + 1;
  2294. # }
  2295. sub GetLinePtrFromHash
  2296. {
  2297. local(*e, $key, $ptr) = @_;
  2298. index($e{$key}, "\n", $ptr);
  2299. }
  2300.  
  2301. # For example,
  2302. # ($p, $pb, $pe) = &GetBlockPtrFromHash(*Envelope, 'Body', $b, $pp);
  2303. # last if $p < 0;
  2304. # print substr($Envelope{'Body'}, $pb, $pe - $pb);
  2305. sub GetBlockPtrFromHash
  2306. {
  2307. local(*e, $key, $b, $ptr) = @_;
  2308. local($p, $pb, $pe);
  2309. $p = &GetPtrFromHash(*e, 'Body', $b, $ptr);
  2310. $pb = &GetPtrFromHash(*e, 'Body', "\n\n", $p + 1);
  2311. $pe = &GetPtrFromHash(*e, 'Body', $b, $pb + 1);
  2312. ($p, $pb + 2, $pe)
  2313. }
  2314.  
  2315. sub GetPtrFromHash
  2316. {
  2317. local(*e, $key, $pat, $ptr) = @_;
  2318. index($e{$key}, $pat, $ptr);
  2319. }
  2320.  
  2321. # useful for "Read Open"
  2322. sub Open
  2323. {
  2324. if ((!-f $_[1]) || $_[1] eq '') {
  2325. local(@c) = caller; local($c) = "$c[1],$c[2]"; $c =~ s#^\S+/##;
  2326. if (! -f $_[1]) { &Log("${c}::Open $_[1] NOT FOUND");}
  2327. if ($_[1] eq '') { &Log("${c}::Open $_[1] IS NULL; NOT DEFINED");}
  2328. return 0;
  2329. }
  2330. open($_[0], $_[1]) || do {
  2331. local(@c) = caller; local($c) = "$c[1],$c[2]"; $c =~ s#^\S+/##;
  2332. &Log("$c::Open failed $_[1]"); return 0;
  2333. };
  2334. }
  2335.  
  2336. sub Copy
  2337. {
  2338. local($in, $out) = @_;
  2339. local($mode) = (stat($in))[2];
  2340. open(COPYIN, $in) || (&Log("ERROR: Copy::In [$!]"), return 0);
  2341. open(COPYOUT, "> $out") || (&Log("ERROR: Copy::Out [$!]"), return 0);
  2342. select(COPYOUT); $| = 1; select(STDOUT);
  2343. chmod $mode, $out;
  2344. while (sysread(COPYIN, $_, 4096)) { print COPYOUT $_;}
  2345. close(COPYOUT);
  2346. close(COPYIN);
  2347. 1;
  2348. }
  2349.  
  2350. # checks the executable "prog" in "prog option".
  2351. sub ProgExecuteP
  2352. {
  2353. local($prog) = @_;
  2354.  
  2355. $prog || return 0; # no input
  2356.  
  2357. ($prog) = (split(/\s+/, $prog))[0];
  2358. -x $prog ? 1 : 0;
  2359. }
  2360.  
  2361. # check fundamental programs existence
  2362. sub DiagPrograms
  2363. {
  2364. my (@prog) = @_;
  2365. my ($x, $bad);
  2366.  
  2367. if ($UNISTD) {
  2368. for $x (@prog) {
  2369. &ProgExecuteP( ${ $x } ) || do {
  2370. $bad++;
  2371. &Log("ERROR: program \$${x} is not defined");
  2372. };
  2373. }
  2374. }
  2375.  
  2376. $bad ? 0 : 1; # if bad, return 0;
  2377. }
  2378.  
  2379. # mainly search e.g. "sendmail"
  2380. sub SearchPath
  2381. {
  2382. local($prog, @path) = @_;
  2383. for ("/usr/sbin", "/usr/lib", @path) {
  2384. if (-e "$_/$prog" && -x "$_/$prog") { return "$_/$prog";}
  2385. }
  2386. }
  2387.  
  2388. sub SearchFileInLIBDIR
  2389. {
  2390. for (@LIBDIR) {
  2391. &Debug("SearchFileInLIBDIR: <$_>/$_[0]") if $debug;
  2392. if (-f "$_/$_[0]") { return "$_/$_[0]";}
  2393. }
  2394. $NULL;
  2395. }
  2396.  
  2397. sub SearchFileInINC
  2398. {
  2399. for (@INC) { if (-f "$_/$_[0]") { return "$_/$_[0]";}}
  2400. $NULL;
  2401. }
  2402.  
  2403. sub GetFirstMultipartBlock
  2404. {
  2405. local(*e) = @_;
  2406.  
  2407. if ($e{'MIME:boundary'}) {
  2408. ($p, $pb, $pe) =
  2409. &GetBlockPtrFromHash(*e, 'Body', $e{'MIME:boundary'}, 0);
  2410. if ($pb > 0 && $pe > 0) {
  2411. substr($e{'Body'}, $pb, $pe - $pb);
  2412. }
  2413. else {
  2414. &Log("GetFirstMultipartBlock: invalid MIME/multipart message");
  2415. $NULL;
  2416. }
  2417. }
  2418. else {
  2419. &Log("GetFirstMultipartBlock: invalid MIME/multipart message");
  2420. $NULL;
  2421. }
  2422. }
  2423.  
  2424. ####### Section: Utilities
  2425. # we suppose &Uniq(*array)'s "array" is enough small.
  2426. sub Uniq
  2427. {
  2428. local(*q) = @_;
  2429. local(%p, @p);
  2430. for (@q) { next if $p{$_}; $p{$_} = $_; push(@p, $_);}
  2431. @q = @p;
  2432. }
  2433.  
  2434. # $pat is included in $list (A:B:C:... syntax)
  2435. sub ListIncludePatP
  2436. {
  2437. local($pat, $list) = @_;
  2438. for (split(/:/, $list)) { return 1 if $pat eq $_;}
  2439. 0;
  2440. }
  2441.  
  2442. sub DebugLog
  2443. {
  2444. local($s) = @_;
  2445. local($f) = $DEBUG_LOGFILE || $LOGFILE.".debug";
  2446. &GetTime;
  2447. &Append2("$Now $s", $f);
  2448. }
  2449.  
  2450. sub Debug
  2451. {
  2452. print STDERR "$_[0]\n";
  2453. &Mesg(*Envelope, "\nDEBUG $_[0]") if $debug_message;
  2454. &DebugLog($_[0]) if $debug > 1;
  2455. }
  2456.  
  2457. sub ABS { $_[0] < 0 ? - $_[0] : $_[0];}
  2458.  
  2459. sub ATOI
  2460. {
  2461. if ($_[0] eq '') {
  2462. return $NULL;
  2463. }
  2464. elsif ($_[0] =~ /^(\d+)$/i) {
  2465. $_[0];
  2466. }
  2467. elsif ($_[0] =~ /^(\d+)M$/i) {
  2468. $1 * 1024 * 1024;
  2469. }
  2470. elsif ($_[0] =~ /^(\d+)K$/i) {
  2471. $1 * 1024;
  2472. }
  2473. else {
  2474. &Log("ATOI: $_[0] is unknown type");
  2475. }
  2476. }
  2477.  
  2478. # eval and print error if error occurs.
  2479. # which is best? but SHOULD STOP when require fails.
  2480. sub use { require "lib$_[0].pl";}
  2481. sub Use { require "lib$_[0].pl";}
  2482.  
  2483. # &UseModule("Japanese", "langdep");
  2484. sub UseModule { require "module/$_[0]/lib$_[1].pl";}
  2485.  
  2486. sub MkDir { &Mkdir(@_);}
  2487. sub Mkdir
  2488. {
  2489. if ($_[1] ne '') { return &MkDirHier($_[0], $_[1]);}
  2490. &MkDirHier($_[0], $DEFAULT_DIR_MODE || 0700);
  2491. if ($USE_FML_WITH_FMLSERV && $SPOOL_DIR eq $_[0]) { chmod 0750, $_[0];}
  2492. if ($USE_FML_WITH_FMLSERV && $GID) { chown $<, $GID, $_[0];}
  2493. }
  2494.  
  2495. sub MkDirHier
  2496. {
  2497. local($pat) = $UNISTD ? '/|$' : '\\\\|/|$'; # on UNIX or NT4
  2498.  
  2499. while ($_[0] =~ m:$pat:go) {
  2500. next if (!$UNISTD) && $` =~ /^[A-Za-z]:$/; # ignore drive letter on NT4
  2501.  
  2502. if ($` ne "" && !-d $`) {
  2503. mkdir($`, $_[1] || 0777) || do {
  2504. &Log("cannot mkdir $`: $!");
  2505. return 0;
  2506. };
  2507. }
  2508. }
  2509.  
  2510. 1;
  2511. }
  2512.  
  2513. # eval and print error if error occurs.
  2514. sub eval
  2515. {
  2516. &CompatFML15_Pre if $COMPAT_FML15;
  2517. eval $_[0];
  2518. $@ ? (&Log("$_[1]:$@"), 0) : 1;
  2519. &CompatFML15_Post if $COMPAT_FML15;
  2520. }
  2521.  
  2522. sub PerlModuleExistP
  2523. {
  2524. local($pm) = @_;
  2525. if ($] !~ /^5\./) { &Log("ERROR: using $pm requires perl 5"); return 0;}
  2526. eval("use $pm");
  2527. if ($@) { &Log("${pm}.pm NOT FOUND; Please install ${pm}.pm"); return 0;}
  2528. 1;
  2529. }
  2530.  
  2531. # Getopt
  2532. sub Opt { push(@SetOpts, @_);}
  2533.  
  2534. # Setting CommandLineOptions after include config.ph
  2535. sub SetOpts
  2536. {
  2537. # should pararelly define ...
  2538. for (@SetOpts) {
  2539. /^\-\-MLADDR=(\S+)/i && (&use("mladdr"), &MLAddr($1));
  2540. if (/^\-\-([_a-z0-9]+)$/||/^\-\-([_a-z0-9]+=\S+)$/) {&DEFINE_MODE($1);}
  2541. }
  2542.  
  2543. for (@SetOpts) {
  2544. if (/^\-\-(force|fh):(\S+)=(\S+)/) { # "foreced header";
  2545. &DEFINE_FIELD_FORCED($2, $3); next;
  2546. }
  2547. elsif (/^\-\-(original|org|oh):(\S+)/) { # "foreced header";
  2548. &DEFINE_FIELD_ORIGINAL($2); next;
  2549. }
  2550. elsif (/^\-\-([_A-Z0-9]+)=(\S+)/) { # USER DEFINED VARIABLES
  2551. eval("\$$1 = '$2';"); next;
  2552. }
  2553. elsif (/^\-\-(\S+)/) { # backward mode definition is moved above
  2554. local($_) = $1;
  2555. /^[_a-z0-9]+$/ || eval("\$${_} = 1;");
  2556. /^permit:([a-z0-9:]+)$/ && ($Permit{$1} = 1); # set %Permit;
  2557. next;
  2558. }
  2559.  
  2560. /^\-(\S)/ && ($Opt{"opt:$1"} = 1);
  2561. /^\-(\S)(\S+)/ && ($Opt{"opt:$1"} = $2);
  2562.  
  2563. /^\-d(\d+)/ && ($debug = $1) && next;
  2564. /^\-d|^\-bt/ && ($debug = 1) && next;
  2565. /^\-s(\S+)/ && &eval("\$$1 = 1;") && next;
  2566. /^\-u(\S+)/ && &eval("undef \$$1;") && next;
  2567. /^\-l(\S+)/ && ($LOAD_LIBRARY = $1) && next;
  2568. }
  2569. }
  2570.  
  2571. sub GenMessageId
  2572. {
  2573. &GetTime;
  2574. $GenMessageId = $GenMessageId++ ? $GenMessageId : 'AAA';
  2575. "<${CurrentTime}.FML${GenMessageId}". $$ .".$MAIL_LIST>";
  2576. }
  2577.  
  2578. # which address to use a COMMAND control.
  2579. sub CtlAddr { &Addr2FQDN($CONTROL_ADDRESS);}
  2580.  
  2581. # Do FQDN of the given Address 1. $addr is set and has @, 2. MAIL_LIST
  2582. sub Addr2FQDN { $_[0]? ($_[0] =~ /\@/ ? $_[0]: $_[0]."\@$FQDN") : $MAIL_LIST;}
  2583. sub CutFQDN { $_[0] =~ /^(\S+)\@\S+/ ? $1 : $_[0];}
  2584.  
  2585. sub SRand
  2586. {
  2587. local($i) = time;
  2588. $i = (($i & 0xff) << 8) | (($i >> 8) & 0xff) | 1;
  2589. srand($i + $$);
  2590. }
  2591.  
  2592. # Reference: NetBSD:/usr/src/usr.bin/cksum/sum2.c
  2593. # *** cksum utility is expected to conform to IEEE Std 1003.2-1992 ***
  2594. sub TraditionalATTUnixCheckSum
  2595. {
  2596. my ($f) = @_;
  2597. my ($crc, $total, $nr);
  2598.  
  2599. $crc = $total = 0;
  2600. if (open($f, $f)) {
  2601. while (($nr = sysread($f, $buf, 1024)) > 0) {
  2602. my ($i) = 0;
  2603. $total += $nr;
  2604.  
  2605. for ($i = 0; $i < $nr; $i++) {
  2606. $r = substr($buf, $i, 1);
  2607. $crc += ord($r);
  2608. }
  2609. }
  2610. close($f);
  2611. $crc = ($crc & 0xffff) + ($crc >> 16);
  2612. $crc = ($crc & 0xffff) + ($crc >> 16);
  2613. }
  2614. else {
  2615. print STDERR "ERROR: no such file $f\n";
  2616. }
  2617.  
  2618. ($crc, $total);
  2619. }
  2620.  
  2621. sub LogFileNewSyslog
  2622. {
  2623. $LOGFILE_NEWSYSLOG_LIMIT = &ATOI($LOGFILE_NEWSYSLOG_LIMIT);
  2624. if ($LOGFILE_NEWSYSLOG_LIMIT) {
  2625. if ((stat($LOGFILE))[7] > $LOGFILE_NEWSYSLOG_LIMIT) {
  2626. require 'libnewsyslog.pl';
  2627. &NewSyslog($LOGFILE);
  2628. &Touch($LOGFILE);
  2629. }
  2630. }
  2631. }
  2632.  
  2633. sub CacheTurnOver
  2634. {
  2635. local($file, $size_limit) = @_;
  2636.  
  2637. if ((stat($file))[7] > $size_limit) {
  2638. &use('newsyslog');
  2639. &NewSyslog'TurnOverW0($file);#';
  2640. &Touch($file);
  2641. }
  2642. }
  2643.  
  2644. sub DBCtl
  2645. {
  2646. &use('db');
  2647. &FML_SYS_DBCtl(@_);
  2648. }
  2649.  
  2650. ####### Section: Security
  2651. # anyway alias now (1998/05/03)
  2652. # If sent back directly, X-ML-Info: exists and must contains e.g. $MAIL_LIST .
  2653. sub MailLoopP
  2654. {
  2655. if ($Envelope{'h:x-ml-info:'}) {
  2656. if ($Envelope{'h:x-ml-info:'} =~ /contact $MAINTAINER/i ||
  2657. $Envelope{'h:x-ml-info:'} =~ /(address\s+|mailto:)$MAIL_LIST/i ||
  2658. $Envelope{'h:x-ml-info:'} =~ /(address\s+|mailto:)$CONTROL_ADDRESS/i) {
  2659. &Log("Loop Alert: dup X-ML-Info:");
  2660. &WarnE("Loop Alert: dup X-ML-Info: $ML_FN",
  2661. "fml <$MAIL_LIST> has detected a loop condition so that\n"
  2662. ."input mail has already our ML X-ML-Info: field.\n\n");
  2663. return 1;
  2664. }
  2665. }
  2666.  
  2667. &DupMessageIdP;
  2668. }
  2669.  
  2670. sub SearchDupKey
  2671. {
  2672. local($key, $file) = @_;
  2673. local($status, $i);
  2674.  
  2675. # 1. scan current and
  2676. if (-f $file) {
  2677. $status = &Lookup($key, $file);
  2678. }
  2679. return $status if $status;
  2680.  
  2681. # 2. scan all available caches
  2682. for $i (0 .. $NEWSYSLOG_MAX) {
  2683. if ($status) {
  2684. last; # end if non null $status is returned.
  2685. }
  2686. elsif (-f "$file.$i") {
  2687. $status = &Lookup($key, "$file.$i");
  2688. }
  2689. }
  2690.  
  2691. $status;
  2692. }
  2693.  
  2694. # If O.K., record the Message-Id to the file $LOG_MESSAGE_ID);
  2695. # message-id cache should be done for mails in action
  2696. sub CacheMessageId
  2697. {
  2698. local(*e, $msgid) = @_;
  2699. local($id);
  2700.  
  2701. # canonicalize
  2702. $id = $msgid || $e{'h:Message-Id:'};
  2703. $id || (&Log("Invalid Message-Id:<$id>"), return $NULL);
  2704. $id =~ s/[\<\>]//g;
  2705. $id =~ s/^\s+//;
  2706.  
  2707. if ($CachedMessageID{$id}) {
  2708. &Log("CacheMessageId: warning: duplicated input") if $debug_loop;
  2709. return 0;
  2710. }
  2711.  
  2712. # Turn Over log file (against too big);
  2713. # The default value is evaluated as "once per about 100 mails".
  2714. &CacheTurnOver($LOG_MESSAGE_ID,
  2715. $MESSAGE_ID_CACHE_BUFSIZE || 60*100);
  2716.  
  2717. $CachedMessageID{$id} = 1;
  2718. &Append2($id." \# pid=$$", $LOG_MESSAGE_ID);
  2719. }
  2720.  
  2721. sub DupMessageIdP
  2722. {
  2723. local($status, $mid);
  2724.  
  2725. # no check -> "return not looped"
  2726. $CHECK_MESSAGE_ID || return 0;
  2727.  
  2728. local($mid) = $Envelope{'h:Message-Id:'};
  2729. $mid =~ s/[\<\>]//g;
  2730. $mid =~ s/^\s+//;
  2731.  
  2732. &Debug("DupMessageIdP::($mid, $LOG_MESSAGE_ID)") if $debug;
  2733.  
  2734. $status = &SearchDupKey($mid, $LOG_MESSAGE_ID);
  2735.  
  2736. if ($status) {
  2737. &Debug("\tDupMessageIdP::(DUPLICATED == LOOPED)") if $debug;
  2738. local($s) = "Duplicated Message-ID";
  2739. &Log("Loop Alert: $s");
  2740. &WarnE("Loop Alert: $s $ML_FN", "$s in <$MAIL_LIST>.\n\n");
  2741. 1;
  2742. }
  2743. else {
  2744. &Debug("\tDupMessageIdP::(OK NOT LOOPED)") if $debug;
  2745. 0;
  2746. }
  2747. }
  2748.  
  2749. # if the addr to reply is O.K., return value is 1;
  2750. sub CheckAddr2Reply
  2751. {
  2752. local(*e, @addr_list) = @_;
  2753. local($addr, $m);
  2754. my (@caller) = caller;
  2755.  
  2756. ### 01: check recipients == myself?
  2757. for $addr (@addr_list) {
  2758. if (&LoopBackWarn($addr)) {
  2759. &Log("Notify: ERROR: the mail is not sent to $addr",
  2760. "since the addr to reply == ML or ML-Ctl-Addr");
  2761. &Log(@caller);
  2762. $m .= "\nNotify: ERROR: the mail is not sent to [$addr]\n";
  2763. $m .= "since the addr to reply == ML or ML-Ctl-Addr.\n";
  2764. $m .= "-" x60; $m .= "\n";
  2765. }
  2766. else {
  2767. print STDERR "CheckAddr2Reply 01: OK\t$addr\n" if $debug;
  2768. }
  2769. }
  2770.  
  2771. ### 02: check the recipents
  2772. for $addr (@addr_list) {
  2773. if ($addr =~ /^($REJECT_ADDR)\@/i) {
  2774. $m .= "\nNotify: ERROR: the mail should not be sent to [$addr]\n";
  2775. $m .= "since the addr is not-personal or other agent softwares\n";
  2776. $m .= "-" x60; $m .= "\n";
  2777. }
  2778. else {
  2779. print STDERR "CheckAddr2Reply 02: OK\t$addr\n" if $debug;
  2780. }
  2781. }
  2782.  
  2783. # if anything happens, append the information;
  2784. if ($m) {
  2785. # append the original message and forwarding to the maintainer;
  2786. $m .= "=" x60; $m .= "\n";
  2787. $m .= "Original 'message' to send to the user:\n\n". $e{'message'};
  2788. $m .= "=" x60; $m .= "\n";
  2789.  
  2790. # message for the maintainer;
  2791. $e{'error'} .= $m;
  2792. }
  2793.  
  2794. $m ? 0 : 1; # if O.K., return 1;
  2795. }
  2796.  
  2797. # Check uid == euid && gid == egid
  2798. sub CheckUGID
  2799. {
  2800. print STDERR "\nsetuid is not set $< != $>\n\n" if $< != $>;
  2801. print STDERR "\nsetgid is not set $( != $)\n\n" if $( ne $);
  2802. # die("YOU SHOULD NOT RUN fml AS ROOT NOR DAEMON\n") if $< == 0 || $< == 1;
  2803. }
  2804.  
  2805. sub GetGID { (getgrnam($_[0]))[2];}
  2806.  
  2807. sub InSecureP { (! &SecureP(@_));}
  2808.  
  2809. sub SecureP {
  2810. local(@xargv) = @_; &use('kernsubr'); &__SecureP(@xargv);
  2811. }
  2812.  
  2813. sub ValidAddrSpecP
  2814. {
  2815. ($_[0] !~ /\s|\033\$[\@B]|\033\([BJ]/ &&
  2816. $_[0] =~ /^[\0-\177]+\@[\0-\177]+$/) ? 1 : 0;
  2817. }
  2818.  
  2819. # Check Looping
  2820. # return 1 if loopback
  2821. sub LoopBackWarning { &LoopBackWarn(@_);}
  2822. sub LoopBackWarn
  2823. {
  2824. local($to) = @_;
  2825. local($a);
  2826. local(@c) = caller;
  2827.  
  2828. for $a ($MAIL_LIST, $CONTROL_ADDRESS, @MAIL_LIST_ALIASES,
  2829. "fmlserv\@$DOMAINNAME", "majordomo\@$DOMAINNAME",
  2830. "listserv\@$DOMAINNAME", "mead\@$DOMAINNAME") {
  2831.  
  2832. next if $a =~ /^\s*$/oi; # for null control addresses
  2833. if (&AddressMatch($to, $a)) {
  2834. &Debug("AddressMatch($to, $a)") if $debug;
  2835. &Log("Loop Back Warning: ", "$to eq $a");
  2836. &Log("called from @c");
  2837. &WarnE("Loop Back Warning: [$to eq $a] $ML_FN",
  2838. "Loop Back Warning: [$to eq $a]");
  2839. return 1;
  2840. }
  2841. }
  2842.  
  2843. 0;
  2844. }
  2845.  
  2846. sub RejectAddrP {
  2847. local(@xargv) = @_; &use('kernsubr'); &__RejectAddrP(@xargv);
  2848. }
  2849.  
  2850. sub EnvelopeFilter {
  2851. local(@xargv) = @_; &use('envf'); &__EnvelopeFilter(@xargv);
  2852. }
  2853.  
  2854. # QUOTA
  2855. sub CheckResourceLimit
  2856. {
  2857. local(*e, $mode) = @_;
  2858.  
  2859. if ($mode eq 'member') {
  2860. &use('amctl'); return &MemberLimitP(*e);
  2861. }
  2862. elsif ($mode eq 'mti:distribute:max_traffic') {
  2863. &MTIProbe(*MTI, $From_address, 'distribute:max_traffic');
  2864. }
  2865. elsif ($mode eq 'mti:command:max_traffic') {
  2866. &MTIProbe(*MTI, $From_address, 'command:max_traffic');
  2867. }
  2868. }
  2869.  
  2870. ####### Section: Macros for the use of user-side-definition (config.ph)
  2871. ### moved to "libloadconfig.pl"
  2872.  
  2873. ####### Section: misc
  2874.  
  2875. # "get ID by auto-increment" for user
  2876. sub GET_ID_AUTOINC
  2877. {
  2878. local($f) = @_;
  2879.  
  2880. if (-f $f) { # Get the present ID
  2881. local($id) = &GetFirstLineFromFile($f);
  2882. $id++;
  2883. &Write2($id, $f);
  2884. $id;
  2885. }
  2886. else {
  2887. &Log("GET_ID_AUTOINC: cannot open $f");
  2888. 0;
  2889. }
  2890. }
  2891.  
  2892. # Get Next MIME Multipart Block
  2893. sub GetNextMPBPtr
  2894. {
  2895. local(*e, $ptr) = @_;
  2896. local($pTop, $pEndHeader, $pBottom, $xbuf);
  2897.  
  2898. if ($e{'MIME:boundary'}) {
  2899. $pTop = index($e{'Body'}, $e{'MIME:boundary'}, $ptr);
  2900. $pEndHeader = index($e{'Body'}, "\n\n", $pTop);
  2901. $pBottom = index($e{'Body'}, $e{'MIME:boundary'}, $pEndHeader);
  2902. ($pTop, $pEndHeader, $pBottom);
  2903. } else {
  2904. &Log("GetNextMPBPtr: no MIME boundary definition");
  2905. ();
  2906. }
  2907. }
  2908.  
  2909. # Get Next MIME Multipart Block
  2910. sub GetNextMultipartBlock
  2911. {
  2912. local(*e, $ptr) = @_;
  2913. local($pTop, $pEndHeader, $pBottom, $xbuf);
  2914.  
  2915. if ($e{'MIME:boundary'}) {
  2916. $pTop = index($e{'Body'}, $e{'MIME:boundary'}, $ptr);
  2917. $pEndHeader = index($e{'Body'}, "\n\n", $pTop);
  2918. $pBottom = index($e{'Body'}, $e{'MIME:boundary'}, $pEndHeader);
  2919.  
  2920. if ($pEndHeader > 0 && $pBottom > 0) {
  2921. $xhdr = substr($e{'Body'}, $pTop, $pEndHeader - $pTop);
  2922. $xbuf = substr($e{'Body'}, $pEndHeader, $pBottom - $pEndHeader);
  2923. ($xhdr, $xbuf, $pBottom)
  2924. } else {
  2925. $NULL;
  2926. }
  2927. } else {
  2928. &Log("GetNextMultipartBlock: no MIME boundary definition");
  2929. $NULL;
  2930. }
  2931. }
  2932.  
  2933. ####### Section: Switch
  2934. sub SaveACL { $ProcCtlBlock{"main:ADDR_CHECK_MAX"} = $ADDR_CHECK_MAX;}
  2935. sub RetACL { $ADDR_CHECK_MAX = $ProcCtlBlock{"main:ADDR_CHECK_MAX"};}
  2936.  
  2937. ####### Section: Event Handling Functions
  2938. sub SignalLog
  2939. {
  2940. local($sig) = @_;
  2941. &Log("Caught SIG$sig, shutting down");
  2942. sleep 1;
  2943. exit(1);
  2944. }
  2945.  
  2946. # Strange "Check flock() OK?" mechanism???
  2947. # fml.pl exits under all cases after 12 hours (IT IS TOO LONG)!
  2948. sub Lock
  2949. {
  2950. &SetEvent($TimeOut{'dead'} || 43200, 'TimeOut') if $HAS_ALARM;
  2951.  
  2952. # $LockQueueId is of mean under main locked phase
  2953. # "mget" runs after $LockQueueId is cleared.
  2954. $LockQueueId = &SetEvent($TimeOut{'lock'} || $TimeOut{'flock'} || 3600,
  2955. 'TimeOut') if $HAS_ALARM;
  2956. $USE_FLOCK ? &Flock : (&use('lock'), &V7Lock);
  2957. }
  2958.  
  2959. # for installer ?
  2960. sub ReloadMySelf
  2961. {
  2962. # If myself is changed after exec'ed, reload it again.
  2963. if ((-M __FILE__) < 0) {
  2964. &Log("FYI: reload myself against installation");
  2965. for ("libkern.pl", keys %INC) {
  2966. next unless /^lib\S+\.pl$|\/lib\S+\.pl$/;
  2967. delete $INC{$_};
  2968. &Log("reload $_ agasin against installation") if $debug;
  2969. require $_;
  2970. }
  2971. }
  2972. }
  2973.  
  2974. sub Unlock
  2975. {
  2976. $USE_FLOCK ? &Funlock : &V7Unlock;
  2977.  
  2978. # $LockQueueId is of mean under main locked phase
  2979. if ($LockQueueId) { &ClearEvent($LockQueueId);}
  2980. }
  2981.  
  2982. # lock algorithm using flock system call
  2983. # if lock does not succeed, fml process should exit.
  2984. sub Flock
  2985. {
  2986. local($min,$hour,$mday,$mon) =
  2987. (localtime(time + ($TimeOut{'flock'} || 3600)))[1..4];
  2988. local($ut) = sprintf("%02d/%02d %02d:%02d", $mon + 1, $mday, $hour, $min);
  2989.  
  2990. $FlockFile = $FlockFile ||
  2991. (open(LOCK,$FP_SPOOL_DIR) ? $FP_SPOOL_DIR : "$DIR/config.ph");
  2992.  
  2993. $0 = "${FML}: Locked(flock) until $ut <$LOCKFILE>";
  2994.  
  2995. # spool is also a file!
  2996. if (! open(LOCK, $FlockFile)) {
  2997. &Log("Flock:Cannot open FlockFile[$FlockFile]");
  2998. die("Flock:Cannot open FlockFile[$FlockFile]");
  2999. }
  3000. flock(LOCK, $LOCK_EX);
  3001. }
  3002.  
  3003. sub Funlock
  3004. {
  3005. $0 = "${FML}: Unlock <$LOCKFILE>";
  3006.  
  3007. flock(LOCK, $LOCK_UN);
  3008. close(LOCK); # unlock,close <kizu@ics.es.osaka-u.ac.jp>
  3009. }
  3010.  
  3011. # do not anything except for logging since now the fatal error case.
  3012. sub TimeOut
  3013. {
  3014. &GetTime;
  3015. $0 = "${FML}: TimeOut $Now <$LOCKFILE>";
  3016.  
  3017. # Now we may be not able to connect socket, isn't it?
  3018. # &WarnE("TimeOut: $MailDate ($From_address) $ML_FN", $NULL);
  3019. &Log("TimeOut[$$]: Caught SIGALRM, timeout");
  3020.  
  3021. if ($TimeOutCalled++) {
  3022. kill 9, $$;
  3023. }
  3024. else {
  3025. $TimeOutCalled++;
  3026. exit(0);
  3027. }
  3028. }
  3029.  
  3030. sub SetEvent
  3031. {
  3032. local($interval, $fp) = @_;
  3033. local($id, $now, $qp, $prev_qp);
  3034.  
  3035. $now = time; # the current time;
  3036.  
  3037. $id = $EventQueue++ + 1; # unique identifier
  3038.  
  3039. if ($interval < 60) {
  3040. &Log("SetEvent: input interval[$interval] is too short. reset to 60");
  3041. $interval = $interval < 60 ? 60 : $interval;
  3042. }
  3043.  
  3044. # the first reference is a dummy (without $fp);
  3045. if ($id == 1) {
  3046. $EventQueue{"time:${id}"} = $now;
  3047. $EventQueue{"next:${id}"} = $id + 1;
  3048. $id = $EventQueue++ + 1; # unique identifier
  3049. }
  3050.  
  3051. # search the event queue for correct position;
  3052. # here search all entries;
  3053. for ($qp = $EventQueue{"next:1"}, $prev_qp = 1;
  3054. $qp ne ""; $qp = $EventQueue{"next:${qp}"}) {
  3055. if ($EventQueue{"time:$qp"} >= $now + $interval) { last;}
  3056. $prev_qp = $qp;
  3057. }
  3058.  
  3059. $EventQueue{"time:${id}"} = $now + $interval;
  3060. $EventQueue{"debug:${id}"} = $interval if $debug;
  3061. $EventQueue{"fp:${id}"} = $fp;
  3062.  
  3063. # "next:id = null" if the next link does not exist.
  3064. $EventQueue{"next:${prev_qp}"} = $id; # pointed to the current id;
  3065. $EventQueue{"next:${id}"} = $qp != $id ? $qp : "";
  3066.  
  3067. &Tick; # tick(0);
  3068.  
  3069. $id; # return the identifier;
  3070. }
  3071.  
  3072. sub ClearEvent
  3073. {
  3074. local($id) = @_;
  3075. local($now, $qp, $prev_qp);
  3076.  
  3077. # search the event queue for correct position;
  3078. # here search all entries;
  3079. for ($qp = $EventQueue{"next:1"};
  3080. $qp ne "";
  3081. $qp = $EventQueue{"next:${qp}"}) {
  3082.  
  3083. if ($qp == $id) {
  3084. $EventQueue{"next:$prev_qp"} = $EventQueue{"next:$qp"};
  3085. &Debug("---ClearEvent: qp=$id fp=$EventQueue{\"fp:${id}\"}")
  3086. if $debug;
  3087. &Log("ClearEvent: qp=$EventQueue{\"fp:$id\"}") if $debug_tick;
  3088. undef $EventQueue{"fp:$id"};
  3089. last;
  3090. }
  3091. $prev_qp = $qp;
  3092. }
  3093. }
  3094.  
  3095. # ### ATTENTION! alarm(3) may conflict sleep(3); ###
  3096. # alarm(3) do actions as long as if needed;
  3097. # Plural functions may be done at the same time;
  3098. # but it is responsible Tick();
  3099. sub Tick
  3100. {
  3101. local($cur, $fp, $qp);
  3102.  
  3103. &GetTime; $0 = "${FML}: Tick $Now <$LOCKFILE>";
  3104.  
  3105. return unless $HAS_ALARM;
  3106.  
  3107. print STDERR "===Tick called\n" if $debug;
  3108.  
  3109. alarm(0); # before we sets in the routine, reset the current alarm;
  3110. $cur = time;
  3111.  
  3112. # scan all entries and do the function (if time < the current_time);
  3113. # so $qp (queue pointer) is set to the last action (< curret time)
  3114. for ($qp = $EventQueue{"next:1"};
  3115. $EventQueue{"time:$qp"} <= $cur;
  3116. $qp = $EventQueue{"next:${qp}"}) {
  3117.  
  3118. $fp = $EventQueue{"fp:$qp"};
  3119. next unless $fp;
  3120.  
  3121. # $EventQueue{time:$qp} and alarm(3) time may be at the same time!
  3122. undef $EventQueue{"fp:$qp"};
  3123. &Log("Tick[$$]: run fp=$fp");
  3124. eval("&$fp;");
  3125. &Log($@) if $@;
  3126.  
  3127. alarm(0);
  3128. $cur = time;
  3129. }
  3130.  
  3131. $SIG{'ALRM'} = 'Tick';
  3132.  
  3133. # info
  3134. &Debug("\tnow\tqp=$qp fp=$EventQueue{\"fp:${qp}\"}") if $debug;
  3135.  
  3136. # find the next $qp defined function pointer (time > cur_time)
  3137. # skip null functions since the functions has been expireed.
  3138. # *1 ignore $qp=0 case.
  3139. for (; $qp && !$EventQueue{"fp:${qp}"}; $qp = $EventQueue{"next:${qp}"}) {
  3140. ;
  3141. }
  3142. &Debug("\tfinally\tqp=$qp fp=$EventQueue{\"fp:${qp}\"}") if $debug;
  3143.  
  3144. $cur = $EventQueue{"time:${qp}"} - time;
  3145. $cur = $cur > 0 ? $cur : 3;
  3146. alarm($cur); # considering context switching;
  3147.  
  3148. &Log("Tick[$$]::alarm($cur)") if $debug_tick;
  3149. if ($debug) {
  3150. &OutputEventQueue;
  3151. &Debug("\tnow set alarm($cur) for the queue id $qp");
  3152. &Debug("\tfp = $qp->$EventQueue{\"fp:${qp}\"}") if $debug_tick;
  3153. }
  3154.  
  3155. if ($debug_tick) {
  3156. for ($qp = 1; $qp ne ""; $qp = $EventQueue{"next:${qp}"}) {
  3157. $cur = $EventQueue{"time:${qp}"} - time;
  3158. if ($cur >= 0) { # the future events list
  3159. &Log(sprintf(" when=%-5d qp=%-2d link->%-2d fp=%s",
  3160. $cur,
  3161. $qp,
  3162. $EventQueue{"next:$qp"},
  3163. $EventQueue{"fp:$qp"}));
  3164. }
  3165. }
  3166. }
  3167. }
  3168.  
  3169. 1;
Add Comment
Please, Sign In to add comment