# vim: set cindent expandtab ts=4 sw=4: # # Copyright (c) 1998-2005 Chi-Keung Ho. All rights reserved. # # This programe is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # Extmail - a high-performance webmail to maildir # $Id$ package Ext::Abook; use Exporter; use Ext::CSV; use Fcntl qw(:flock); use vars qw(@ISA @ABOOK $REF); @ISA = qw(Exporter); undef @ABOOK; $REF = undef; sub new { my $class = shift; my %opt = @_; my $self = { file => $opt{file} ? $opt{file} : 'abook.cf', lock => $opt{lock} ? 1:0, sort => $opt{sort} ? $opt{sort} : 'by_mailaddr', }; bless $self, $class; $REF = new Ext::CSV unless($REF); $self->parse; # XXX auto $self; } sub parse { my $self = shift; my $file = $self->{file}; my $count = 0; open(FD, "< $file") or warn "$file not exists, abort\n" and return ""; while() { chomp; if ($REF->parse($_)) { my @field = $REF->fields; $ABOOK[$count] = \@field; $count ++; }else { warn $REF->error_input; } } close FD; } # CSV format: # # 'ID', 'Name', 'Email', 'Company', 'Mobile' # 0 1 2 3 4 sub by_name { $a->[1] cmp $b->[1]; } sub by_mailaddr { $a->[2] cmp $b->[2]; } sub by_mobile { $a->[4] <=> $b->[4]; } sub by_company { $a->[3] cmp $b->[4]; } sub dump { shift; return \@ABOOK; } sub sort { my $self = shift; my @mybuf = @ABOOK; # deep copy for (0...scalar @mybuf-1) { $mybuf[$_] = [$_, @{$mybuf[$_]}]; } shift @mybuf; # ignore the first line # sort it now my $method = $self->{sort}; eval { @mybuf = sort $method @mybuf; }; if ($@) { # default sort method @mybuf = sort by_mailaddr @mybuf; } unshift @mybuf, [0, 'Name', 'Email', 'Company', 'Mobile']; \@mybuf; # sorted array } sub search { my $self = shift; my $key = $_[0]; my @id; my $ref = $self->sort; foreach(my $k=1; $k < scalar @$ref; $k++) { # join to a big string my $lid = $ref->[$k]->[0]; # line ID my $s = join('', splice(@{$ref->[$k]}, 1)); # ignore the first id if($s=~/$key/i) { push @id, $lid;# return $k only? } } @id; } # Schema: field0,1,2,3,4 => id, name, mailaddr, company, mobile sub lookup { my $self = shift; return $ABOOK[$_[0]] if($ABOOK[$_[0]]); ""; } sub delete { my $self = shift; my @id = @_; my @tarray; my $newid = 0; foreach(my $k=0; $k < scalar @ABOOK; $k++) { my $del = 0; for(@id) { if($k eq $_) { $del = 1; last; } } unless($del) { $tarray[$newid] = $ABOOK[$k]; $newid++; } } @ABOOK = @tarray; undef @tarray; } sub append { my $self = shift; my $ref = $_[0]; # ARRAY ref my $id = scalar @ABOOK; $ABOOK[$id] = $ref; # add into it } sub update { my $self = shift; my ($id,$ref) = @_; # ref => ARRAY ref $ABOOK[$id] = $ref;# if not present, will auto # append it } sub save { my $self = shift; my $file = $self->{file} || 'abook.cf'; my $addhdr = 0; $addhdr = 1 if(!-r $file); open(FD, "> $file") or die "Write to $file error: $!\n"; flock(FD, LOCK_EX); if($addhdr) { $REF->combine(('Name', 'Email', 'Company', 'Mobile')); print FD $REF->string,"\n"; } foreach(my $k=0; $k< scalar @ABOOK; $k++) { my $val = $ABOOK[$k]; $val = $REF->combine(@$val); $val = $REF->string($val); print FD $val,"\n"; } flock(FD, LOCK_UN); close FD; 1; } sub DESTROY { undef $REF; undef @ABOOK; } 1;