package TDS::Mail2Tdf;
# $Id: Mail2Tdf.pm,v 1.36 2000/12/03 13:37:54 tom Exp $
################################################################

=head1 NAME

TDS::Mail2Tdf - tdf mail

=head1 SYNOPSIS

=head1 DESCRIPTION

mail2tdf.pl ¤Î¤ß»ÈÍÑ

=cut

################################################################

use strict;
use vars qw($UseFTP $SignatureStart $UpdateHtmlFiles $AdditionalAcceptFromString);

use ObjectTemplate;
use CGI::Tools;
use DateTime::Format;
use Crypt;
use SimpleDB::Append;
use JConv;

use TDS::System;
use TDS::Admin::Authorize;
use TDS::Admin::FTP;
use TDS::AccessLog::DirInfo;
use TDS::IdentInfo;
use TDS::DirInfo;
use TDS::Mode;
use TDS::Static;

use vars qw(@ISA);
@ISA = qw(ObjectTemplate);

=head1 MEMBER VARIABLES

 accept_from        array of acceptable sender

=cut

attributes qw(logfile log accept_from
	      mode from reply_to password date action body
	      reason
	      ftp_user ftp_password);


$UseFTP = 0 unless defined $UseFTP;
$SignatureStart = '^-- $' unless defined $SignatureStart;
$UpdateHtmlFiles = 1 unless defined $UpdateHtmlFiles;
$AdditionalAcceptFromString = "" unless defined $AdditionalAcceptFromString;

################################################################
sub initialize($)
{
    my $self = shift;

    $self->logfile(GetLogDir() . "/mail2tdf.log") unless $self->logfile;
    $self->accept_from([TDS::IdentInfo->Get('address')]);
    if ($AdditionalAcceptFromString){
	for (split(/, */, $AdditionalAcceptFromString)){
	    push(@{$self->accept_from}, $_);
	}
    }
    $self->body([]);
    $self->log($self->log(new SimpleDB::Append($self->logfile)));
    
    $self->SUPER::initialize;
}

################################################################

=head2 $m->Read;

read from STDIN.
return mode('tdf' or 'data') or
    undef if not for mail2tdf.

=cut

sub Read
{
    my $self = shift;
    my @lines;

    # mail body
    my $from;
    my $tdf_mode;                  # whether if it should be accept as mail2tdf
                                   # judge from Subject
                                   #   "tdf" : diary
                                   #   "todo|schedule|...": data(TODO, schedule..)
    my ($is_body, $is_tdf_body, $found_tdf_header);

    # should be iso-2022-jp
    *jconv_tmp = mkjconv('jis', $TDS::System::InternalJcode);

    while (<>){
	my $tmp = $_;
	jconv_tmp(\$tmp);
	unless ($is_body){            # mail header
	    if ($tmp =~ /^$/){              # end of header
		unless ($tdf_mode){         # return undef
		    return undef;           #  if not for mail2tdf
		}
		$is_body = 1;
		next;
	    }
	    chomp $tmp;
	    my ($tag, $content) = split(/: */, $tmp);
	    if ($tag =~ /^Subject$/i){
		if ($content =~ /^\[mail2([a-z]+)\] *(.*)$/){
		    $tdf_mode = $1;
		    $self->action($2);
#		} else {
#		    $self->log->Append("not mail2tdf");
		}
	    } elsif ($tag =~ /^From$/i){
		$self->from($content);
	    } elsif ($tag =~ /^Reply-To$/i){
		$self->reply_to($content);
	    }
	} else {			# mail body
	    unless ($is_tdf_body){      # tdf header(PASSWORD, DATE)
		if ($tmp =~ /^$/){              # end of tdf
		    if ($found_tdf_header){
			$is_tdf_body = 1;
		    }
		    next;
		}
		my ($tag, $content) = $tmp =~ /^(.*?)\s+(.*)$/;
		if ($tag eq 'PASSWORD'){
		    $self->password($content);
		} elsif ($tag eq 'DATE'){
		    $self->date($content);
		} elsif ($tag eq 'FTP_PASSWORD'){
		    $self->ftp_password($content);
		} elsif ($tag eq 'FTP_USER'){
		    $self->ftp_user($content);
		}
		$found_tdf_header = 1;
	    } else {
		last if $SignatureStart &&$tmp =~ /$SignatureStart/;
		push(@{$self->body}, $tmp);     # $self->body contains diary content
	    }
	}
    }
    return $self->mode($tdf_mode);
}

################################################################

=head2 $m->DoAction;

action by mode.

=cut

sub DoAction ($)
{
    my $self = shift;

    if ($self->mode eq 'tdf'){
	# diary file
	$self->DoTdf;
    } else {
	# data file
	$self->DoData;
    }
}
################################################################

=head2 $m->DoTdf;

action as tdf.
return non-zero if success.

=cut

sub DoTdf
{
    my $self = shift;

    # default setting
    my $action = $self->action || "ADD";       # default: add
    my $dt = new DateTime::Date;
    my $date = $self->date;
    if ($date){ 
	$dt->Set($date =~ /^(\d{4}) *(\d{2}) *(\d{2})/);
	unless ($dt->IsValid){
	    $self->Append("invalid date: $date\n");
	    exit;
	}
    } else {                         # default date: accept date
	$dt->SetTime(time(), $TDS::System::TZ);
	$date = sprintf("%04d %02d %02d", $dt->year, $dt->month, $dt->day);
    }
    
    # logging
    my $now_date_str = time2str("%a, %d %b %Y %H:%M:%S %Z", $^T, $TDS::System::TZ);
    my $from = $self->from;
    $self->log->Append("==== start ====
Action: $action
From: $from
Date: $now_date_str
Tdf-Date: $date

");

    return 0 unless $self->CheckAuthorized;

    # writing filename
    my $md_format = "%02d/%02d";
    my $diarydir = GetDiaryDir();
    my $file = sprintf("$diarydir/%04d/$md_format.tdf",
		       $dt->year, $dt->month, $dt->day);
    
    # directory check
    my $yeardir = sprintf("$diarydir/%04d", $dt->year);
    unless (-d $yeardir){    # create it if not exist
	 mkdir($yeardir, 0755);
	 print "$yeardir created\n";
     } 
    my $monthdir = sprintf("$diarydir/%04d/%02d", 
			    $dt->year, $dt->month);
    unless (-d $monthdir){
	 mkdir($monthdir, 0755);
 	print "$monthdir created\n";
    } 
 
    # do action
    if ($action eq 'ADD'){
	unless (open(TDF, ">>$file")){
	    $self->log->Append("can't append tdf: $file\n");
	    return 0;
	}
	print TDF @{$self->body};
	close TDF;
    } elsif ($action eq 'REPLACE'){
	if (-f $file){                # make backup
	    rename($file, "$file.bak");
	}
	unless (open(TDF, ">$file")){
	    $self->log->Append("can't replace tdf: $file\n");
	    return 0;
	}
	print TDF @{$self->body};
	close TDF;
    } elsif ($action eq 'DELETE'){
	rename($file, "$file.bak");
    } elsif ($action eq 'GET'){
	my $address = TDS::IdentInfo->Get('address');
	my $from = $self->reply_to || $self->from;
	my $body;
	unless (open(F, $file)){
	    $body = "no file: $file";
	} else {
	    $body = join('', <F>);
	    close F;
	}
	*jconv_tmp = mkjconv($TDS::System::InternalJcode, 'jis');
	jconv_tmp(\$body);
	
	open(MAIL, "|$TDS::System::SendmailPath -t") || die "can't mail: $TDS::System::SendmailPath";
	my $mail =  "To: $from
Subject: GET from mail2tdf
From: $address

$body";
	print MAIL $mail;
	close MAIL;
	$self->log->Append("GET\n");
	return;
    }
    close TDF;

    if (-f $file){
	# procmail set permision 0600 caused by umask,
	# so set it 0644 for readable.
 	chmod(0644, $file);
    }
    
    # FTP put
    if ($UseFTP && $action ne 'GET'){
	require Net::FTP;
	my $ftp = new Net::FTP($TDS::Admin::FTP::Server);
	$ftp->login($self->ftp_user, $self->ftp_password) || die "login failed";
	my $server_dir = sprintf("%s/%04d/%02d",
				 $TDS::Admin::FTP::ServerDiaryDir,
				 $dt->year,
				 $dt->month);
	$ftp->cwd($server_dir) || die "cwd $server_dir failed";

	# write new part to tmp file
	my $tmp_file = sprintf("%02d.tdf", $dt->day);
	open(F, ">$tmp_file") || die $tmp_file;
	print F @{$self->body};
	close F;
	
	if ($action eq 'ADD'){
	    $ftp->append($tmp_file) || die "append $tmp_file failed";
	} elsif ($action eq 'REPLACE'){
	    $ftp->put($tmp_file) || die "put $tmp_file failed";
	} elsif ($action eq 'DELETE'){
	    $ftp->rename($tmp_file, "$tmp_file-bak") || die "rename $tmp_file to $tmp_file-bak failed";
	}
	$ftp->quit;
	unlink($tmp_file);
    }

    # HTML creation for static mode
    if ($UpdateHtmlFiles && &TDS::Mode::IsStatic()){
	my $static = new TDS::Static(quiet=>1);
	$static->AddAllAction;
	
	$static->Do;
    }

    # logging
    $self->log->Append(join("", @{$self->body}));
    return 1;
}
################################################################

=head2 $m->DoData;

action for datafile
return non-zero if success

=cut

sub DoData
{
    my $self = shift;

    my $action = $self->action || 'ADD';

    my $datadir = GetDataDir();
    my $confdir = GetConfDir();
    my %files = ('todo'=>"$datadir/todo.dat",
		 'schedule'=>"$datadir/schedule.dat",
		 'url'=>"$datadir/url.dat",
		 'term'=>"$datadir/term.dat",
		 'category'=>"$datadir/category.dat",
		 'infomation'=>"$datadir/infomation.dat",
		 'dictionary'=>"$datadir/dictionary.dat",
		 
#		 'robotlist'=>"$confdir/robotlist.txt",
		 'authorid'=>"$confdir/author_id.txt");

    # check mode
    my $mode = $self->mode;
    my $filename = $files{$mode};
    unless ($filename){
	$self->log->Append("'$mode' is not defined.\n");
	return 0;
    }
    # authorize
    return 0 unless $self->CheckAuthorized;

    # writing data
    unless (open(F, ">>$filename")){
	$self->log->Append("can't write $filename($mode).\n");
	return 0;

    }
    print F @{$self->body};
    close F;

    # logging
    my $from = $self->from;
    my $now_date_str = time2str("%a, %d %b %Y %H:%M:%S %Z", $^T, $TDS::System::TZ);
    $self->log->Append("==== start ====
Action: $action
From: $from
Date: $now_date_str

");
    $self->log->Append(join("", @{$self->body}));
    return 1;
}
################################################################
sub CheckAuthorized
{
    my $self = shift;

    # From authorization
    my $flg = 0;                  # @accecpt_from
    for (@{$self->accept_from}){
	if ($self->from =~ /$_/){
	    $flg = 1;
	    last;
	}
    }
    unless ($flg){                # not acceptable
	$self->log->Append("invalid from address: " . $self->from . "\n");
	return 0;
    }

    # password authorization
    my $passwd_file = GetConfFilename(".passwd");  # crypted
    unless(open(F, $passwd_file)){
	$self->log->Append("can't open password file: $passwd_file.\n");
	return 0;
    }
    my $origin_password = <F>;
    chomp $origin_password;
    unless (IsLegalPassword($self->password, $origin_password)){
	$self->log->Append("illegal password: ", $self->password, ".\n");
	return 0;
    }
    return 1;
}
1;
