#!/usr/bin/perl #============================================================================= # ubiqun.pl: main program of ubiqun.pl/ubiqun.exe (ver.20030913) # # Copyright (C) 2002-2003 OHKUBO Masahiko # The latest version is available here: http://mah.pobox.ne.jp/ubiqun/ #============================================================================= use strict; use vars '%setting'; use lib('./Net'); BEGIN { my($path, $tmp); # プログラムがあるディレクトリを調べる $path = $0; if (index($path, '\\') > -1) { # 区切りに \ を使う環境? $path =~ s/\\/\//g; } if (index($path, '/') > -1) { # 区切りに / を使う環境? $path =~ /(.*)\/(.*)$/; $setting{'file_program'} = $2; if ($1 ne '') { $setting{'dir_program'} = $1 . '/'; } else { $setting{'dir_program'} = './'; } } else { # 判別ができない環境 $setting{'file_program'} = $path; $setting{'dir_program'} = './'; } require $setting{'dir_program'} . 'ubiqun_lib.pl'; # require $setting{'dir_program'} . 'jcode.pl'; # require $setting{'dir_program'} . 'mimer.pl'; # require $setting{'dir_program'} . 'mimew.pl'; # # require './ubiqun_lib_win32.pl'; # # require './jcode.pl'; # # require './mimer.pl'; # # require './mimew.pl'; # unshift(@INC, substr($setting{'dir_program'}, 0, -1)); # # 必要不可欠なものを設定する require 5.004; $setting{'program_name'} = 'ubiqun.pl'; # # $setting{'program_name'} = 'ubiqun.exe'; # $setting{'program_version'} = '20030913'; $setting{'program_author'} = 'OHKUBO Masahiko '; $setting{'program_url'} = 'http://mah.pobox.ne.jp/ubiqun.shtml'; $setting{'file_log'} = 'ubiqun.log'; $setting{'seconds_filelock'} = 30; # crypt 関数の実装を調べる (for PerlCE) $tmp = 'crypt("test", "test");'; # eval $tmp; # if ($@) { # $setting{'function_crypt'} = 'no'; # } # } use Net::SMTP; use Net::POP3; use Jcode; use MIME::Base64; #use Mail::IMAPClient; &MAIN(); exit; # メインルーチン sub MAIN { my($ref_setting_default, $ref_setting_cmdline); my($ref_header, $ref_text); my($ref_symbol); my($byte_mail_before, $byte_mail_after, $split, $flag_status, $flag_skip); ($ref_setting_default, $ref_setting_cmdline) = &SECTION_SET_SETTING(); if ($$ref_setting_cmdline{'mode'} eq 'client') { # POP クライアントモード ($ref_header, $ref_text) = &SECTION_READ_MAIL($ref_setting_default, $ref_setting_cmdline, 'dummy'); &SECTION_RECEIVE_MAIL($setting{'dir_data'} . $setting{'file_data_server'}, $setting{'dir_data'} . $setting{'dir_data_pop'}); } else { # メール標準入力モード ($ref_header, $ref_text) = &SECTION_READ_MAIL($ref_setting_default, $ref_setting_cmdline); if ($setting{'mail_statuscheck'} eq 'yes') { # 状態・時間帯チェック処理 ($flag_status, $flag_skip) = &SECTION_STATUSCHECK($ref_header, $ref_text); if ($flag_status eq 's') { &VERBOSE('normal', 'normal', 'EXIT: it is my sleeping time now. mails are spooled.'); &SECTION_EXIT(); exit; } elsif ($flag_status eq 'x') { &VERBOSE('normal', 'normal', 'EXIT: it is my sleeping time now. mails are ignored.'); &SECTION_EXIT(); exit; } } if ($flag_skip ne 'yes') { # コマンドメールでなければ ($ref_header, $ref_text, $ref_symbol, $byte_mail_before, $byte_mail_after) = &SECTION_PREPROCESS_MAIL($ref_header, $ref_text); ($ref_header, $ref_text, $split) = &SECTION_SPLITSEND_MAIL($ref_header, $ref_text, $ref_symbol, $byte_mail_before, $byte_mail_after); &VERBOSE('normal', 'normal', 'EXIT(' . sprintf('%d', $setting{'mail_ratio_compression'}) . '%/' . ($split) . '): subject=『' . $$ref_header{'SUBJECT'} . '』; from=' . $$ref_header{'FROM'} . '; to=' . $$ref_header{'TO'} . ';'); } } &SECTION_EXIT(); return; } # セクション: 各種設定を決定する sub SECTION_SET_SETTING { my(%setting_default, %setting_cmdline); my($key); # デフォルト値とコマンドラインオプションの読み込み %setting_default = %{&SET_SETTING_DEFAULT()}; %setting_cmdline = %{&READ_CMDLINE(\@ARGV)}; # 特別扱い: $setting{'verbose_level_*'} 関係 if ($setting_cmdline{'debug'} eq 'yes') { $setting{'verbose_level_stdout'} = 2; $setting{'verbose_level_log'} = 2; } elsif ($setting_cmdline{'quiet'} eq 'yes') { $setting{'verbose_level_stdout'} = 0; } elsif ($setting_cmdline{'debug_log'} eq 'yes') { $setting{'verbose_level_stdout'} = 0; $setting{'verbose_level_log'} = 2; } # プログラム開始メッセージ &VERBOSE('normal', 'nothing', $setting{'program_name'} . ' (ver.' . $setting{'program_version'} . ') by ' . $setting{'program_author'}); # 特別扱い: $setting{'dir_data'} 関係 if (exists($setting_cmdline{'dir_data'})) { # コマンドラインによる指定 if (substr($setting_cmdline{'dir_data'}, -1, 1) ne '/') { # おしりに / が付いていなかったら付ける $setting_cmdline{'dir_data'} = $setting_cmdline{'dir_data'} . '/'; } $setting{'dir_data'} = $setting_cmdline{'dir_data'}; } else { $setting{'dir_data'} = $setting{'dir_program'}; } # 特別扱い: $setting{'file_log'} 関係 if (exists($setting_cmdline{'file_log'})) { # コマンドラインによる指定 if (index($setting_cmdline{'file_log'}, '/') == -1) { # / が含まれていない場合,ルートを $setting{'dir_data'} に $setting{'file_log'} = $setting_cmdline{'dir_data'} . $setting_cmdline{'file_log'}; } } else { $setting{'file_log'} = $setting{'dir_data'} . $setting{'file_log'}; } # %setting にコピー (優先順位: %setting_cmdline,%setting_default) while (($key, undef) = each(%setting_default)) { if (exists($setting_cmdline{$key})) { $setting{$key} = $setting_cmdline{$key}; } else { if (exists($setting{$key})) { # プログラムによる指定 } else { # デフォルト値による指定 $setting{$key} = $setting_default{$key}; } } } &CHECK_DEFINITION('commandline', \%setting_default, \%setting_cmdline); return(\%setting_default, \%setting_cmdline); } # セクション: メール読み込み sub SECTION_READ_MAIL { my($ref_setting_default, $ref_setting_cmdline, $mode) = @_; my(%setting_command, %setting_filtering, %setting_phone_default, %setting_phone); my(%setting_phone_use_value, %setting_phone_use_from); my($ref_header, $ref_text); my($key, $tmp1, $tmp2, @text, $filename); my($ref_symbol); my(%setting_default) = %$ref_setting_default; my(%setting_cmdline) = %$ref_setting_cmdline; if ($mode ne 'dummy') { if ($setting{'file_input'} ne '') { # メールをファイルから取り込み $/ = undef; open(FILE, $setting{'file_input'}); $tmp1 = ; close(FILE); $/ = "\n"; } else { # メールを標準入力から取り込み read(STDIN, $tmp1, $setting{'input_byte_max'}); } $tmp1 =~ s/\x00//g; &VERBOSE('normal', 'normal', 'INPUT(header+body): byte = '. length($tmp1)); @text = split("\n", ${(&CONVERT_EUC(\$tmp1))[0]}); # メールヘッダの抜き出し ($ref_header, $ref_text) = &EXTRACT_HEADER(\@text, $setting{'mail_address_regex'}); # メールのテキスト部分のみを抜き出し (同時に $setting{'mail_charset'} 設定) ($ref_text, $filename) = &EXTRACT_PLAINTEXT($ref_header, $ref_text, $setting{'mail_address_regex'}); } # 特別扱い: $setting{'mail_charset'} 関係 if ($setting{'mail_charset'} eq '') { # どのヘッダでも決まらなかったら $setting{'mail_charset'} = $setting{'mail_charset_default'}; } if ($setting{'mail_charset'} =~ /$setting{'mail_charset_japanese'}/o) { # 日本語なら特別に japanese とする $setting{'mail_charset'} = 'japanese'; } if ($filename ne '') { chop($filename); $$ref_header{'X-UBIQUN-FILENAME'} = '<' . $filename . '>' . "\n"; } # 記号文字ファイルを仮読み込み $ref_symbol = &LOAD_FILE_SYMBOL($setting{'dir_program'} . $setting{'file_data_symbol'}); # コマンドメール設定ファイルを読み込む if (-r ($setting{'dir_data'} . $setting{'file_data_command'})) { %setting_command = %{&LOAD_CONFIGFILE_CONDITION($setting{'dir_data'} . $setting{'file_data_command'}, $ref_header, $ref_text, $ref_symbol, 'sensitive')}; &CHECK_DEFINITION($setting{'dir_data'} . $setting{'file_data_command'}, \%setting_default, \%setting_command); } else { &VERBOSE('detail', 'detail', 'NOT EXIST: file = ' . $setting{'dir_data'} . $setting{'file_data_command'}); } # フィルタリング設定ファイルを読み込む if (-r ($setting{'dir_data'} . $setting{'file_data_filtering'})) { %setting_filtering = %{&LOAD_CONFIGFILE_CONDITION($setting{'dir_data'} . $setting{'file_data_filtering'}, $ref_header, $ref_text, $ref_symbol, 'insensitive')}; &CHECK_DEFINITION($setting{'dir_data'} . $setting{'file_data_filtering'}, \%setting_default, \%setting_filtering); } else { &VERBOSE('detail', 'detail', 'NOT EXIST: file = ' . $setting{'dir_data'} . $setting{'file_data_filtering'}); } # 携帯電話の設定ファイルを読み込む %setting_phone_default = %{&LOAD_CONFIGFILE($setting{'dir_program'} . $setting{'file_data_phone_default'})}; %setting_phone = %{&LOAD_CONFIGFILE($setting{'dir_data'} . $setting{'file_data_phone'})}; # 特別扱い: $setting_phone{'phone_name:default'} 関係 if (exists($setting_cmdline{'phone_name'})) { $setting_phone{'phone_name:default'} = $setting_cmdline{'phone_name'}; } elsif (exists($setting_filtering{'phone_name'})) { $setting_phone{'phone_name:default'} = $setting_filtering{'phone_name'}; } elsif (exists($setting_phone{'phone_name:phone_1'})) { $setting_phone{'phone_name:default'} = $setting_phone{'phone_name:phone_1'}; } else { &VERBOSE('error', 'error', 'ERROR: not defined - phone_name:default'); } # $setting{'file_data_phone'} に登録された携帯電話の何番 ($tmp1) を使うのか決めて for ($tmp1 = 1; $tmp1 <= (10 ** $setting{'phone_max_order'}); $tmp1 ++) { if ($setting_phone{'phone_name:phone_' . $tmp1} eq $setting_phone{'phone_name:default'}) { # その電話の元々のデフォルト値 (%setting_phone_default) を %setting_phone_use_* にコピー $setting_phone_use_value{'phone_type'} = $setting_phone{'phone_type:phone_' . $tmp1}; $setting_phone_use_from{'phone_type'} = $setting{'dir_data'} . $setting{'file_data_phone'}; while (($key, undef) = each(%setting_phone_default)) { if ($key =~ /(\S+):$setting_phone_use_value{'phone_type'}$/) { $tmp2 = $1; if (exists($setting_phone{$tmp2 . ':phone_' . $tmp1})) { # しかしコピーの際,%setting_phone (:phone_N) に定義があればそっち優先 $setting_phone_use_value{$tmp2} = $setting_phone{$tmp2 . ':phone_' . $tmp1}; $setting_phone_use_from{$tmp2} = $setting{'dir_data'} . $setting{'file_data_phone'} . ' [phone_' . $tmp1 . ']'; } elsif (exists($setting_phone{$tmp2 . ':default'})) { # 次に,%setting_phone (:default) に定義があればそっち優先 $setting_phone_use_value{$tmp2} = $setting_phone{$tmp2 . ':default'}; $setting_phone_use_from{$tmp2} = $setting{'dir_data'} . $setting{'file_data_phone'} . ' [default]'; } else { # と,ここまでくれば,%setting_phone_default をコピー $setting_phone_use_value{$tmp2} = $setting_phone_default{$key}; $setting_phone_use_from{$tmp2} = $setting{'dir_program'} . $setting{'file_data_phone_default'} . ' [' . $setting_phone{'phone_type:phone_' . $tmp1} . ']'; } } } # 特別扱い: $setting_phone{'program_address:default'} 関係 if (exists($setting_cmdline{'program_address'})) { $setting_phone_use_value{'program_address'} = $setting_cmdline{'program_address'}; $setting_phone_use_from{'program_address'} = 'commandline'; } elsif (exists($setting_filtering{'program_address'})) { $setting_phone_use_value{'program_address'} = $setting_filtering{'program_address'}; $setting_phone_use_from{'program_address'} = $setting{'dir_data'} . $setting{'file_data_filtering'}; } elsif (exists($setting_phone{'program_address:default'})) { $setting_phone_use_value{'program_address'} = $setting_phone{'program_address:default'}; $setting_phone_use_from{'program_address'} = $setting{'dir_program'} . $setting{'file_data_phone_default'} . ' [default]'; } else { &VERBOSE('error', 'error', 'ERROR: not defined - program_address:default'); } last; } } &CHECK_DEFINITION(\%setting_phone_use_from, \%setting_default, \%setting_phone_use_value); # %setting にコピー (優先順位: %setting_cmdline,%setting_command,%setting_filtering,%setting_phone_use_*,%setting) while (($key, undef) = each(%setting_default)) { if (exists($setting_cmdline{$key})) { # コマンドラインによる指定 $setting{$key} = $setting_cmdline{$key}; &VERBOSE('detail', 'detail', 'SET: ' . $key . ' = ' . $setting{$key} . ' (by commandline)'); } elsif (exists($setting_command{$key})) { # コマンドメール設定ファイルによる指定 $setting{$key} = $setting_command{$key}; &VERBOSE('detail', 'detail', 'SET: ' . $key . ' = ' . $setting{$key} . ' (by ' . $setting{'dir_data'} . $setting{'file_data_command'} . ')'); } elsif (exists($setting_filtering{$key})) { # フィルタリング設定ファイルによる指定 $setting{$key} = $setting_filtering{$key}; &VERBOSE('detail', 'detail', 'SET: ' . $key . ' = ' . $setting{$key} . ' (by ' . $setting{'dir_data'} . $setting{'file_data_filtering'} . ')'); } elsif (exists($setting_phone_use_value{$key})) { # 携帯電話設定ファイルによる指定 $setting{$key} = $setting_phone_use_value{$key}; &VERBOSE('detail', 'detail', 'SET: ' . $key . ' = ' . $setting{$key} . ' (by ' . $setting_phone_use_from{$key} . ')'); } elsif ($setting{$key} eq $setting_default{$key}) { # デフォルト値による指定 &VERBOSE('detail', 'detail', 'SET: ' . $key . ' = ' . $setting{$key} . ' (by default)'); } else { # プログラム決定による指定 &VERBOSE('detail', 'detail', 'SET: ' . $key . ' = ' . $setting{$key} . ' (by program)'); } } return($ref_header, $ref_text); } # セクション: 状態ファイル・時間帯による処理 sub SECTION_STATUSCHECK { my($ref_header, $ref_text, $mode) = @_; my($weekday, $time, $hour, $min, $flag_status, $flag_skip); if ($setting{'command_forwarding'} =~ /^(o|s|x)$/) { # コマンドメール: 転送制御 (o|s|x) $flag_status = $1; &VERBOSE('normal', 'normal', 'COMMAND(forwarding): status = ' . $flag_status . ';'); $flag_skip = 'yes'; &SAVE_FILE_STATUS($setting{'dir_data'} . $setting{'file_data_status'}, $flag_status); if ($flag_status eq 'o') { # まずはスプールをさばく &LOAD_MAIL_SPOOL($setting{'dir_data'} . $setting{'dir_data_spool'}, \@ARGV); } } else { if ($setting{'command_forwarding'} eq 'usual') { # コマンドメール: 転送制御 (usual) &VERBOSE('normal', 'normal', 'COMMAND(forwarding): status = usual;'); unlink($setting{'dir_data'} . $setting{'file_data_status'}); $flag_skip = 'yes'; $flag_status = ''; } else { # 通常のメール $flag_status = &LOAD_FILE_STATUS($setting{'dir_data'} . $setting{'file_data_status'}); } if ($flag_status eq '') { # 時間帯による処理 ($weekday, $time) = (split(' ', (&GET_DATE())[2]))[0,4]; ($hour, $min) = (split(':', $time))[0,1]; chop($weekday); $weekday =~ tr/A-Z/a-z/; if (length($setting{'mail_timecheck_' . $weekday}) != 48) { &VERBOSE('error', 'error', 'ERROR: length(mail_timecheck_' . $weekday . ') is not 48.'); } $flag_status = substr($setting{'mail_timecheck_' . $weekday}, $hour * 2 + int($min / 30), 1); $flag_status =~ tr/A-Z/a-z/; } if ($flag_status eq 'o') { # まずはスプールをさばく &LOAD_MAIL_SPOOL($setting{'dir_data'} . $setting{'dir_data_spool'}, \@ARGV); } elsif ($flag_status eq 's') { # メールをスプールする &SAVE_MAIL_SPOOL($ref_header, $ref_text, $setting{'dir_data'} . $setting{'dir_data_spool'}); } } return($flag_status, $flag_skip); } # セクション: メール転送前の処理 sub SECTION_PREPROCESS_MAIL { my($ref_header, $ref_text) = @_; my($counter, $ref_symbol, $byte_mail_before, $byte_mail_after); my(@subject); # 記号文字ファイルを本読み込み $ref_symbol = &LOAD_FILE_SYMBOL($setting{'dir_program'} . $setting{'file_data_symbol'}); # 原文 (といってもテキスト部分のみ) を保存 if ($setting{'mail_save'} eq 'yes') { $counter = &LOAD_FILE_MAILCOUNTER($setting{'dir_data'} . $setting{'dir_data_mbox'}, $setting{'file_data_mbox_counter'}); $setting{'mail_counter'} = $counter; &SAVE_MAIL($counter, $ref_header, $ref_text, $setting{'dir_data'} . $setting{'dir_data_mbox'}); } # 本文に関して $byte_mail_before = &COUNT_LENGTH($ref_text); &VERBOSE('normal', 'normal', 'INPUT(body): byte = '. ($byte_mail_before + 0) . ' (euc,CRLF)'); if ($setting{'mail_cut_body_regex'} ne '') { # 正規表現による行のカット $ref_text = &CUT_LINE_REGEX($ref_text, $setting{'mail_cut_body_regex'}); } if (($setting{'mail_charset'} eq 'japanese') && ($setting{'mail_convert_zenhan'} eq 'yes')) { # 文字種の正規化 (1) $ref_text = &CONVERT_ZENHAN_1($ref_text); } if ($setting{'mail_compress_quotation'} eq 'yes') { # 引用行の圧縮 $ref_text = &COMPRESS_QUOTATION($ref_text, $setting{'mail_quotation_regex'}, $setting{'mail_compress_quotation_line'}, $setting{'mail_compress_quotation_counter'}, $setting{'mail_remain_body_regex'}); } if ($setting{'mail_convert_space'} eq 'yes') { # スペースの削除 $ref_text = &CONVERT_SPACE($ref_text, $ref_symbol); } if (($setting{'mail_charset'} eq 'japanese') && ($setting{'mail_convert_space'} eq 'yes') && ($setting{'mail_convert_sentence'} eq 'yes') && ($setting{'mail_compress_line'} > 0)) { # 行→文への変換 $ref_text = &CONVERT_SENTENCE($ref_text, $setting{'mail_quotation_regex'}); } if ($setting{'mail_cut_pattern'} eq 'yes') { # パターン部分をカット for ($counter = 0; $counter < scalar(@$ref_text); $counter ++) { $$ref_text[$counter] = ${&CUT_PART_PATTERN(\$$ref_text[$counter], $setting{'mail_cut_pattern_frequency'}, $ref_symbol)}; } } if ($setting{'mail_use_database'} eq 'yes') { # データベース処理 my($ref_flag_alive_common, $ref_flag_alive_personal); $ref_flag_alive_common = &CHECK_DATABASE('common', $setting{'dir_data'} . $setting{'dir_data_database'}, $setting{'mail_charset'}, $ref_header, $ref_text, $setting{'mail_database_common_rate_activate'}, $setting{'mail_database_common_rate_deactivate'}, $setting{'mail_database_common_rate_forget'}, $setting{'mail_quotation_regex'}, $setting{'mail_database_common_fuzzy'}, $setting{'mail_database_common_length_activate'}); $ref_flag_alive_personal = &CHECK_DATABASE('personal', $setting{'dir_data'} . $setting{'dir_data_database'}, $setting{'mail_charset'}, $ref_header, $ref_text, $setting{'mail_database_personal_rate_activate'}, $setting{'mail_database_personal_rate_deactivate'}, $setting{'mail_database_personal_rate_forget'}, $setting{'mail_quotation_regex'}); $ref_text = &CUT_LINE_DATABASE($ref_text, $ref_flag_alive_common, $ref_flag_alive_personal, $setting{'mail_remain_body_regex'}); # データベースへの最終アクセス日を比較・記録 my($day, $mon, $year, $date_last, $date_now); my($file_rename) = &LOCK_FILE($setting{'dir_data'} . $setting{'dir_data_database'} . $setting{'file_data_database_lastaccess'}); (undef, undef, undef, $day, $mon, $year, undef, undef, undef) = localtime((stat($file_rename))[9]); $date_last = sprintf('%04d%02d%02d', $year + 1900, $mon + 1, $day); $date_now = (&GET_DATE())[0]; $date_now = substr($date_now, 0, 4) . substr($date_now, 5, 2) . substr($date_now, 8, 2); if ($date_last ne $date_now) { $setting{'cleanup_database_flag'} = 'now'; open(FILE, ">$file_rename"); close(FILE); } else { $setting{'cleanup_database_flag'} = 'pass'; } &UNLOCK_FILE($file_rename); } if (($setting{'mail_charset'} eq 'japanese') && ($setting{'mail_convert_zenhan'} eq 'yes')) { # 文字種の正規化 (2) $ref_text = &CONVERT_ZENHAN_2($ref_text); } if ($setting{'mail_replace_url'} eq 'yes') { # URL 文字列を置換 $ref_text = &REPLACE_URL($ref_text, $setting{'mail_replace_url_string'}, $setting{'mail_url_http_regex'}, $setting{'mail_url_ftp_regex'}); } $byte_mail_after = &COUNT_LENGTH($ref_text); # Subject に関して if ($setting{'mail_cut_subject_regex'} ne '') { # 正規表現による部分のカット $$ref_header{'SUBJECT'} = &CUT_PART_REGEX($$ref_header{'SUBJECT'}, $setting{'mail_cut_subject_regex'}) ."\n"; } if (($setting{'mail_charset'} eq 'japanese') && ($setting{'mail_convert_zenhan'} eq 'yes')) { # 文字種の正規化 (1) (2) @subject = ($$ref_header{'SUBJECT'}); $$ref_header{'SUBJECT'} = @{&CONVERT_ZENHAN_2(&CONVERT_ZENHAN_1(\@subject))}[0]; } if ($setting{'mail_convert_space'} eq 'yes') { # スペースの削除 @subject = ($$ref_header{'SUBJECT'}); $$ref_header{'SUBJECT'} = ${&CONVERT_SPACE(\@subject, $ref_symbol)}[0]; } if ($setting{'mail_cut_pattern'} eq 'yes') { # パターン部分をカット $$ref_header{'SUBJECT'} = ${&CUT_PART_PATTERN(\$$ref_header{'SUBJECT'}, $setting{'mail_cut_pattern_frequency'}, $ref_symbol)}; } return($ref_header, $ref_text, $ref_symbol, $byte_mail_before, $byte_mail_after); } # セクション: メールの分割と送信処理 sub SECTION_SPLITSEND_MAIL { my($ref_header, $ref_text, $ref_symbol, $byte_mail_before, $byte_mail_after) = @_; my($ref_mail_from, $ref_mail_to, $ref_mail_data); my($counter, $counter_mail); my(@text, $line, $flag_header); # 改行文字をまとめる etc. if ($setting{'mail_compress_line'} > 0) { ($ref_text) = &COMPRESS_LINE($ref_text, $setting{'mail_compress_line'}, $setting{'mail_convert_space'}); if ($setting{'mail_compress_line'} > 1) { if ($setting{'mail_convert_space'} eq 'yes') { ($ref_text) = &CONVERT_SPACE($ref_text, $ref_symbol); } if ($setting{'mail_cut_pattern'} eq 'yes') { for ($counter = 0; $counter < scalar(@$ref_text); $counter ++) { $$ref_text[$counter] = ${&CUT_PART_PATTERN(\$$ref_text[$counter], $setting{'mail_cut_pattern_frequency'}, $ref_symbol)}; } } } } # メールを分割 ($ref_header, $ref_text, $ref_mail_from, $ref_mail_to, $ref_mail_data) = &SPLIT_MAIL($ref_header, $ref_text, $setting{'mail_split'}, $setting{'mail_byte'}, $setting{'mail_charset'}, $setting{'mail_jcode'}, $setting{'mail_jcode_subject'}, $setting{'mail_hankakukana'}, $setting{'mail_cr'}, $setting{'mail_convert_zenhan'}, $setting{'mail_optimize_zenhan'}, $setting{'mail_add_contenttype'}, $byte_mail_before, $byte_mail_after); if (($setting{'mail'} eq 'yes') || ($setting{'demo'} eq 'yes')) { if (($setting{'mail'} eq 'yes') && (scalar(@$ref_mail_data) > 0)) { # &CHECK_MODULE('Net::SMTP'); # } # # メールを送信・表示 for ($counter = 0; $counter < scalar(@$ref_mail_data); $counter ++) { if ($setting{'mail_sendreverse'} eq 'yes') { $counter_mail = scalar(@$ref_mail_data) - $counter - 1; } else { $counter_mail = $counter; } if ($setting{'mail'} eq 'yes') { # 送信 &VERBOSE('normal', 'detail', 'SEND: mail = ' . ($counter + 1) . '/' . scalar(@$ref_mail_data)); my $bcc_address; my $bcc_pointer; my $send_to; $bcc_pointer = index( $$ref_mail_to[$counter_mail] , ","); if ( $bcc_pointer != -1) { $send_to = substr($$ref_mail_to[$counter_mail],0,$bcc_pointer); $bcc_pointer++; $bcc_address = substr($$ref_mail_to[$counter_mail],$bcc_pointer); } else { $send_to = $$ref_mail_to[$counter_mail]; } &SEND_MAIL($setting{'server_smtp'}, $$ref_mail_from[$counter_mail], $send_to, $$ref_mail_data[$counter_mail], $setting{'seconds_nets'}, $setting{'verbose_level_stdout'},$bcc_address); if (scalar(@$ref_mail_data) - $counter - 1 > 0) { &VERBOSE('detail', 'detail', 'SLEEP: seconds = ' . $setting{'seconds_remail'}); sleep($setting{'seconds_remail'}); } } elsif ($setting{'demo'} eq 'yes') { # 表示 @text = split("\n", $$ref_mail_data[$counter_mail]); &VERBOSE('normal', 'nothing', "\t"); $flag_header = 'yes'; while (@text) { $line = shift(@text); if (($flag_header eq 'yes') && ($line eq '')) { $flag_header = 'no'; } if (($flag_header eq 'yes') && ($line =~ /^Subject: /)) { # Subject jcode::convert(\$line, 'euc', $setting{'mail_jcode_subject'}); &VERBOSE('normal', 'nothing', $line); } else { # それ以外 jcode::convert(\$line, 'euc', $setting{'mail_jcode'}); &VERBOSE('normal', 'nothing', $line); } } if ($counter == scalar(@$ref_mail_data) - 1) { &VERBOSE('normal', 'nothing', "\t"); } } } } &VERBOSE('normal', 'normal', 'OUTPUT(body): byte = '. ($byte_mail_after + 0) . ' (euc,CRLF)'); return($ref_header, $ref_text, scalar(@$ref_mail_data)); } # 新しいメッセージを受信して自分に流し込む sub SECTION_RECEIVE_MAIL { my($file_server, $dir_pop) = @_; my($tmp1, $protocol, $port, $server, $username, $password, $dir, $file, $file_mail, $argv); my($debug, $pop, $flag_auth, $ref_uidl, $ref_uidl_new); my($counter, $key, $ref_text, $line, $counter_send); my(%setting_server); my($flag_convert, %exist_password, $file_rename); my($mail_data,$mail_charcode); # サーバ設定ファイルを読み込む (こいつはロックが必要) if (-r ($file_server)) { $file_rename = &LOCK_FILE($file_server); %setting_server = %{&LOAD_CONFIGFILE($file_rename, 'direct')}; &UNLOCK_FILE($file_rename); } else { &VERBOSE('error', 'error', 'ERROR: file not found. / file = ' . $file_server); } for ($tmp1 = 1; $tmp1 <= (10 ** $setting{'server_max_order'}); $tmp1 ++) { $protocol = $setting_server{'protocol:server_' . $tmp1}; $port = $setting_server{'port:server_' . $tmp1}; $server = $setting_server{'server:server_' . $tmp1}; $username = $setting_server{'username:server_' . $tmp1}; if ($setting_server{'password:server_' . $tmp1} ne '') { # password の行が存在した → 後で password_crypted に書き換え $password = $setting_server{'password:server_' . $tmp1}; $flag_convert = 'yes'; $exist_password{$tmp1} = 'yes'; } elsif ($setting_server{'password_crypted:server_' . $tmp1} ne '') { $password = &DECRYPT_PASSWORD($setting_server{'password_crypted:server_' . $tmp1}); } if (($protocol eq 'pop') || ($protocol eq 'apop')) { # POP, APOP $dir = $dir_pop; if(($server eq '') || ($username eq '') || ($password eq '')) { &VERBOSE('normal', 'normal', 'ERROR: POP parameters are not filled at ' . $file_server . ' [server_' . $tmp1 . ']'); next; } else { &VERBOSE('detail', 'detail', 'SET: protocol = ' . $protocol . ' (by ' . $file_server . ' [server_' . $tmp1 . ']'); &VERBOSE('detail', 'detail', 'SET: port = ' . $port . ' (by ' . $file_server . ' [server_' . $tmp1 . ']'); &VERBOSE('detail', 'detail', 'SET: server = ' . $server . ' (by ' . $file_server . ' [server_' . $tmp1 . ']'); &VERBOSE('detail', 'detail', 'SET: username = ' . $username . ' (by ' . $file_server . ' [server_' . $tmp1 . ']'); &VERBOSE('detail', 'detail', 'SET: password = ******** (by ' . $file_server . ' [server_' . $tmp1 . ']'); # UIDL ファイル名の決定 &CHECK_DIR($dir, oct('700')); $file = $username . $server; $file =~ s/\W//g; $file =~ tr/A-Z/a-z/; $file = substr($file, 0, 12); } &CHECK_MODULE('Net::POP3'); # $file = $dir_pop . $file . '.dat'; $file_mail = $dir . 'received.' . $$; $argv = &REMAKE_ARGV(\@ARGV); $debug = int($setting{'verbose_level_stdout'} / 2); &VERBOSE('normal', 'normal', 'CONNECT: protocol = ' . $protocol . '; server = ' . $server . '; username = ' . $username. ';'); $pop = Net::POP3->new($server, Timeout => $setting{'seconds_net'}, Port => $port, Debug => $debug); if (!defined($pop)) { &VERBOSE('normal', 'normal', 'ERROR: cannot connect. / server = ' . $server); next; } if ($protocol eq 'apop') { $flag_auth = $pop->apop($username, $password); if (!defined($flag_auth)) { &VERBOSE('normal', 'normal', 'ERROR: authentication failed or APOP is not supported. / server = ' . $server . '; user = ' . $username . ';'); next; } } else { $pop->user($username); $flag_auth = $pop->pass($password); if (!defined($flag_auth)) { &VERBOSE('normal', 'normal', 'ERROR: authentication failed. / server = ' . $server . '; user = ' . $username . ';'); next; } } $ref_uidl = $pop->uidl() || &VERBOSE('error', 'error', 'ERROR: UIDL is not supported? / server = ' . $server . '; user = ' . $username . ';'); ($ref_uidl_new, $counter) = &CHECK_UIDL($file, $ref_uidl); while (($key, undef) = each(%$ref_uidl_new)) { &VERBOSE('detail', 'detail', 'GET(pop,apop): No. = ' . $key . '; UIDL = ' . $$ref_uidl_new{$key} . ';'); # 作業ファイル生成 $ref_text = $pop->get($key); if (ref($ref_text)) { open(FILE, ">$file_mail"); chmod(oct('600'), $file_mail); my($utf8) = "utf-8"; my(@utf_mime,$j); my($mime_origin,$mime_dec,$mime_to); while(@$ref_text) { $line = shift(@$ref_text); if ($line =~ /(=\?$utf8\?.*?=\?=)/i ) { #&VERBOSE('normal', 'normal', "utf8 $1"); push(@utf_mime,$1); } $line =~ s/^\.\././; $mail_data .= $line; } $mail_charcode = Jcode::getcode(\$mail_data); if ($mail_charcode eq "utf8") { # Convert To Mime foreach $mime_origin (@utf_mime) { #&VERBOSE('normal', 'normal', "changeutf8 $mime_origin"); $mime_dec = Jcode->new(\$mime_origin,'MIME-Header'); Jcode::convert(\$mime_dec,'jis'); $mime_to = MIME::Base64::encode($mime_dec); $mime_to =~ tr/\n//d; #&VERBOSE('normal', 'normal', "encmime $mime_to"); $mail_data =~ s/(=\?$utf8\?B\?)(.*?)(=\?=)/=\?ISO-2022-JP\?B\?$mime_to$3/i; } Jcode::convert(\$mail_data,'jis','utf8'); } # Original #while (@$ref_text) { # $line = shift(@$ref_text); # $line =~ s/^\.\././; # print FILE $line; #} print FILE $mail_data; close(FILE); # 再帰呼び出し &EXECUTE_CHILDPROCESS($argv . ' -file_input=' . &ESCAPE_SPACE($file_mail), 'pop_tmp'); unlink($file_mail); $counter_send ++; } } $pop->quit(); if (($setting{'cleanup_uidl'} >= 0) && ($counter > $setting{'cleanup_uidl'})) { &CLEANUP_UIDL($file, $setting{'cleanup_uidl'} / 2); } &SAVE_UIDL($file, $ref_uidl_new); if ($counter_send < 1) { &VERBOSE('normal', 'normal', 'EXIT(pop,apop): new mail is not found.'); } } else { next; } } if ($flag_convert eq 'yes') { # password (生書き) を少しばかり変換しておく &SAVE_CONFIGFILE_SERVER($setting{'dir_data'} . $setting{'file_data_server'}, \%exist_password); } return; } # 終了処理 sub SECTION_EXIT { if (($setting{'cleanup_database'} eq 'force') || (($setting{'cleanup_database'} eq 'yes') && ($setting{'cleanup_database_flag'} eq 'now'))) { # データベースファイルの掃除 &CLEANUP_DATABASE($setting{'dir_data'} . $setting{'dir_data_database'}, $setting{'mail_database_expire'}, $setting{'mail_database_quota'}); } return; }