# $File: //depot/libOurNet/BBS/lib/OurNet/BBS/MAPLE3/Article.pm $ $Author: autrijus $
# $Revision: #7 $ $Change: 4012 $ $DateTime: 2003/01/29 11:06:24 $
package OurNet::BBS::MAPLE3::Article;
use if ($^O eq 'MSWin32'), 'open' => (IN => ':bytes', OUT => ':bytes');
use if $OurNet::BBS::Encoding, 'open' => ":encoding($OurNet::BBS::Encoding)";
use if $OurNet::BBS::Encoding, 'encoding' => 'big5', STDIN => undef, STDOUT => undef;
use strict;
use warnings;
no warnings 'deprecated';
use fields qw/basepath board name dir hdrfile recno mtime btime _ego _hash/;
use subs qw/readok writeok remove/;
use OurNet::BBS::Base (
'ArticleGroup' => [qw/$packsize $packstring @packlist &new_id/],
'Board' => [qw/&remove_entry/],
);
my %chronos;
sub readok { 1 };
sub writeok {
my ($self, $user, $op) = @_;
return if $op eq 'DELETE';
# STORE
return (
$self->{author} eq $user->id
or $user->has_perm('PERM_SYSOP')
);
}
sub basedir {
my $self = shift;
return join('/', $self->{basepath}, $self->{board});
}
sub stamp {
my $chrono = shift;
my $str = '';
for (1 .. 7) {
$str = ((0 .. 9, 'A' .. 'V')[$chrono & 31]) . $str;
$chrono >>= 5;
}
return "A$str";
}
sub _refresh_body {
my $self = shift;
$self->refresh_meta unless ($self->{name});
my $file = "$self->{basepath}/$self->{board}/".
($self->{name} =~ /^@/ ? '@' : substr($self->{name}, -1)).
'/'.$self->{name};
die "no such file: $file" unless -e $file;
return if $self->filestamp($file, 'btime')
and defined $self->{_hash}{body};
$self->{_hash}{date} ||= sprintf(
"%02d/%2d/%02d",
substr((localtime)[5], -2),
(localtime($self->{btime}))[4] + 1,
(localtime($self->{btime}))[3],
);
$self->_parse_body($file);
OurNet::BBS::Utils::set_msgid(
$self->{_hash}{header}
) unless $self->{_hash}{header}{'Message-ID'};
return 1;
}
sub _parse_body {
my ($self, $file) = @_;
local $/;
open(my $DIR, "<$file") or die "can't open DIR file for $self->{board}";
$self->{_hash}{body} = <$DIR>;
my ($from, $title, $date);
if ($self->{_hash}{body} =~
s/^§@ªÌ: ([^ \(]+)\s?(?:\((.+?)\) )?[^\n]*\n¼ÐÃD: (.*)\n®É¶¡: (.+)\n\n//
) {
($from, $self->{_hash}{nick}, $title, $date) = ($1, $2, $3, $4);
}
else {
$self->refresh_meta;
}
$self->{_hash}{header} = {
From => ($from || $self->{_hash}{author}) .
($self->{_hash}{nick} ? " ($self->{_hash}{nick})" : ''),
Subject => $title ||= $self->{_hash}{title},
Date => $date ||= scalar localtime($self->{btime}),
Board => $self->{board},
};
}
sub refresh_body {
shift->_refresh_body;
}
sub refresh_header {
shift->_refresh_body;
}
sub refresh_meta {
my $self = shift;
my $cachetime;
$self->{name} = stamp($cachetime = $self->new_id)
unless (defined $self->{name});
my $file = "$self->{basepath}/$self->{board}/$self->{hdrfile}";
return if $self->filestamp($file);
local $/ = \$packsize;
open(my $DIR, "<$file") or die "can't read DIR file $file: $!";
binmode($DIR);
if (defined $self->{name} and defined $self->{recno}) {
seek $DIR, $packsize * $self->{recno}, 0;
@{$self->{_hash}}{@packlist} = unpack($packstring, <$DIR>);
if ($self->{_hash}{id} ne $self->{name}) {
undef $self->{recno};
seek $DIR, 0, 0;
}
}
unless (defined $self->{name} and defined $self->{recno}) {
no warnings 'uninitialized';
if (not defined $cachetime) { # seek for name
$self->{recno} = 0;
while (my $data = <$DIR>) {
@{$self->{_hash}}{@packlist} = unpack($packstring, $data);
last if ($self->{_hash}{id} eq $self->{name});
$self->{recno}++;
}
}
else { # append
seek $DIR, 0, 2;
$self->{_hash}{time} = $cachetime;
$self->{recno} = (stat($DIR))[7] / $packsize; # filesize/packsize
}
if ($self->{_hash}{id} ne $self->{name}) {
my @localtime = localtime($cachetime || time);
$self->{_hash}{id} = $self->{name};
$self->{_hash}{filemode} = 0;
$self->{_hash}{date} ||= sprintf(
"%02d/%02d/%02d", substr($localtime[5], -2),
$localtime[4] + 1, $localtime[3]
);
no warnings qw/uninitialized numeric/;
open($DIR, "+>>$file")
or die "can't write DIR file for $self->{board}: $!";
binmode($DIR);
print $DIR pack($packstring, @{$self->{_hash}}{@packlist});
close $DIR;
}
}
return 1;
}
sub STORE {
my ($self, $key, $value) = @_;
$self = $self->ego;
$self->refresh_meta($key);
if ($key eq 'body') {
my $file = "$self->{basepath}/$self->{board}/".
substr($self->{name}, -1).'/'.$self->{name};
unless (-s $file) {
$value =
"§@ªÌ: $self->{_hash}{author} ".
(defined $self->{_hash}{nick}
? "($self->{_hash}{nick}) " : '').
"¬ÝªO: $self->{board} \n".
"¼ÐÃD: ".substr($self->{_hash}{title}, 0, 60)."\n".
"®É¶¡: ".localtime($self->{_hash}{time} || time).
"\n\n".
$value;
}
open(my $BODY, ">$file") or die "cannot open $file";
print $BODY $value;
close $BODY;
$self->{_hash}{$key} = $value;
$self->filestamp($file, 'btime');
}
else {
no warnings 'uninitialized';
$self->{_hash}{$key} = $value;
my $file = "$self->{basepath}/$self->{board}/$self->{hdrfile}";
open(my $DIR, "+<$file") or die "cannot open $file for writing";
binmode($DIR);
seek $DIR, $packsize * $self->{recno}, 0;
print $DIR pack($packstring, @{$self->{_hash}}{@packlist});
close $DIR;
$self->filestamp($file);
}
}
sub remove {
my $self = shift->ego;
$self->remove_entry("$self->{basepath}/$self->{board}/$self->{hdrfile}");
return unlink "$self->{basepath}/$self->{board}/$self->{name}";
}
1;