# Perl module for note # general database backend. see docu: perldoc NOTEDB::general # using Config::General as backend. package NOTEDB::general; $NOTEDB::general::VERSION = "1.03"; use strict; #use Data::Dumper; use File::Spec; use Config::General qw(ParseConfig SaveConfig SaveConfigString); use MIME::Base64; use FileHandle; use NOTEDB; use Fcntl qw(LOCK_EX LOCK_UN); use Exporter (); use vars qw(@ISA @EXPORT); @ISA = qw(NOTEDB Exporter); sub new { my($this, %param) = @_; my $class = ref($this) || $this; my $self = {}; bless($self,$class); $self->{dbname} = $param{dbname} || File::Spec->catfile($ENV{HOME}, ".notedb"); if(! -e $param{dbname}) { open(TT,">$param{dbname}") or die "Could not create $param{dbname}: $!\n"; close (TT); } elsif(! -w $param{dbname}) { print "$param{dbname} is not writable!\n"; exit(1); } $self->{mtime} = $self->get_stat(); $self->{unread} = 1; $self->{data} = {}; $self->{LOCKFILE} = $param{dbname} . "~LOCK"; return $self; } sub DESTROY { # clean the desk! } sub version { my $this = shift; return $NOTEDB::general::VERSION; } sub get_stat { my ($this) = @_; my $mtime = (stat($this->{dbname}))[9]; return $mtime; } sub changed { my ($this) = @_; my $current = $this->get_stat(); if ($current > $this->{mtime}) { $this->{mtime} = $current; return $current; } else { return 0; } } sub set_del_all { my $this = shift; unlink $this->{dbname}; open(TT,">$this->{dbname}") or die "Could not create $this->{dbname}: $!\n"; close (TT); } sub get_single { my($this, $num) = @_; my($address, $note, $date, $buffer, $n, $t, $buffer, ); my %data = $this->get_all(); return ($data{$num}->{note}, $data{$num}->{date}); } sub get_all { my $this = shift; my($num, $note, $date, %res); if ($this->unchanged) { return %{$this->{cache}}; } my %data = $this->_retrieve(); foreach my $num (keys %data) { $res{$num}->{note} = $this->ude($data{$num}->{note}); $res{$num}->{date} = $this->ude($data{$num}->{date}); } $this->cache(%res); return %res; } sub import_data { my ($this, $data) = @_; my %res = $this->_retrieve(); my $pos = (scalar keys %res) + 1; foreach my $num (keys %{$data}) { $res{$pos}->{note} = $this->uen($data->{$num}->{note}); $res{$pos}->{date} = $this->uen($data->{$num}->{date}); $pos++; } $this->_store(\%res); } sub get_nextnum { my $this = shift; my($num, $te, $me, $buffer); if ($this->unchanged) { $num = 1; foreach (keys %{$this->{cache}}) { $num++; } return $num; } my %data = $this->get_all(); my @numbers = sort { $a <=> $b } keys %data; $num = pop @numbers; $num++; return $num; return $num; } sub get_search { my($this, $searchstring) = @_; my($buffer, $num, $note, $date, %res, $t, $n, $match); my $regex = $this->generate_search($searchstring); eval $regex; if ($@) { print "invalid expression: \"$searchstring\"!\n"; return; } $match = 0; if ($this->unchanged) { foreach my $num (keys %{$this->{cache}}) { $_ = $this->{cache}{$num}->{note}; eval $regex; if ($match) { $res{$num}->{note} = $this->{cache}{$num}->{note}; $res{$num}->{date} = $this->{cache}{$num}->{date} } $match = 0; } return %res; } my %data = $this->get_all(); foreach my $num(sort keys %data) { $_ = $data{$num}->{note}; eval $regex; if($match) { $res{$num}->{note} = $data{$num}->{note}; $res{$num}->{date} = $data{$num}->{data}; } $match = 0; } return %res; } sub set_edit { my($this, $num, $note, $date) = @_; my %data = $this->_retrieve(); $data{$num} = { note => $this->uen($note), date => $this->uen($date) }; $this->_store(\%data); $this->changed; } sub set_new { my($this, $num, $note, $date) = @_; $this->set_edit($num, $note, $date); } sub set_del { my($this, $num) = @_; my(%data, $note, $date, $T, $setnum, $buffer, $n, $N, $t); $setnum = 1; %data = $this->_retrieve(); return "ERROR" if (! exists $data{$num}); delete $data{$num}; $this->_store(\%data); $this->changed; return; } sub set_recountnums { my($this) = @_; my(%orig, %data, $note, $date, $T, $setnum, $buffer, $n, $N, $t); $setnum = 1; %orig = $this->_retrieve(); foreach $N (sort {$a <=> $b} keys %orig) { $data{$setnum} = { note => $orig{$N}->{note}, date => $orig{$N}->{date} }; $setnum++; } $this->_store(\%data); $this->changed; return; } sub uen { my ($this, $raw) = @_; my($crypted); if($NOTEDB::crypt_supported == 1) { eval { $crypted = $this->{cipher}->encrypt($raw); }; print $@; } else { $crypted = $raw; } my $coded = encode_base64($crypted); return $coded; } sub ude { my ($this, $crypted) = @_; my($raw); if($NOTEDB::crypt_supported == 1) { eval { $raw = $this->{cipher}->decrypt(decode_base64($crypted)); }; } else { $raw = decode_base64($crypted) } return $raw; } sub _store { my ($this, $data) = @_; open NOTE, ">$this->{dbname}" or die "could not open $this->{dbname}: $!\n"; flock NOTE, LOCK_EX; if (%{$data}) { my $content = SaveConfigString($data) or die "could not serialize data: $!\n"; print NOTE $content; } else { print NOTE ""; } flock NOTE, LOCK_UN; close NOTE; # finally re-read the db, so that we always have the latest data $this->_retrieve(); } sub _retrieve { my ($this) = @_; my $file = $this->{dbname}; if (-s $file) { if ($this->changed() || $this->{unread}) { my $fh = new FileHandle "<$this->{dbname}" or die "could not open $this->{dbname}\n"; flock $fh, LOCK_EX; my %data = ParseConfig(-ConfigFile => $fh) or die "could not read to database: $!\n"; flock $fh, LOCK_UN; $fh->close(); $this->{unread} = 0; $this->{data} = \%data; return %data; } else { return %{$this->{data}}; } } else { return (); } } 1; # keep this! __END__ =head1 NAME NOTEDB::general - module lib for accessing a notedb from perl =head1 SYNOPSIS # include the module use NOTEDB; # create a new NOTEDB object $db = new NOTEDB("text", "/home/tom/.notedb", 4096, 24); # decide to use encryption # $key is the cipher to use for encryption # $method must be either Crypt::IDEA or Crypt::DES # you need Crypt::CBC, Crypt::IDEA and Crypt::DES to have installed. $db->use_crypt($key,$method); # do not use encryption # this is the default $db->no_crypt; # get a single note ($note, $date) = $db->get_single(1); # search for a certain note %matching_notes = $db->get_search("somewhat"); # format of returned hash: #$matching_notes{$numberofnote}->{'note' => 'something', 'date' => '23.12.2000 10:33:02'} # get all existing notes %all_notes = $db->get_all(); # format of returnes hash like the one from get_search above # get the next noteid available $next_num = $db->get_nextnum(); # modify a certain note $db->set_edit(1, "any text", "23.12.2000 10:33:02"); # create a new note $db->set_new(5, "any new text", "23.12.2000 10:33:02"); # delete a certain note $db->set_del(5); # turn on encryption. CryptMethod must be IDEA, DES or BLOWFISH $db->use_crypt("passphrase", "CryptMethod"); # turn off encryption. This is the default. $db->no_crypt(); =head1 DESCRIPTION You can use this module for accessing a note database. This backend uses a text file for storage and Config::General for accessing the file. Currently, NOTEDB module is only used by note itself. But feel free to use it within your own project! Perhaps someone want to implement a webinterface to note... =head1 USAGE please see the section SYNOPSIS, it says it all. =head1 AUTHOR Thomas Linden . =cut