/[mws]/trunk/MWS.pm
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Diff of /trunk/MWS.pm

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 4 by dpavlin, Tue May 4 15:47:14 2004 UTC revision 22 by dpavlin, Sat May 8 01:13:33 2004 UTC
# Line 6  use strict; Line 6  use strict;
6  use warnings;  use warnings;
7  use Carp;  use Carp;
8    
 use Plucene::Simple;  
9  use Mail::Box::Manager;  use Mail::Box::Manager;
10  use Config::IniFiles;  use Config::IniFiles;
11    use POSIX qw(strftime);
12    use Text::Autoformat;
13    use Text::Iconv;
14    use Text::Unaccent;
15    use Date::Parse;
16    use POSIX qw(strftime);
17    use MIME::Base64;
18    
19    #use MWS_plucene;
20    use MWS_swish;
21    
22  require Exporter;  require Exporter;
23    
# Line 20  our @EXPORT; Line 29  our @EXPORT;
29    
30  our $VERSION = '1.00';  our $VERSION = '1.00';
31    
   
32  my $folder;     # placeholder for folders  my $folder;     # placeholder for folders
33    
34  my $debug = 1;  my $debug = 2;
35    
36  sub new {  sub new {
37          my $class = shift;          my $class = shift;
# Line 36  sub new { Line 44  sub new {
44    
45          my $index_file = $self->{config}->val('global', 'index') || croak "can't find [index] section in config file with path of index";          my $index_file = $self->{config}->val('global', 'index') || croak "can't find [index] section in config file with path of index";
46    
47          $self->{index} = Plucene::Simple->open($index_file) || croak "can't open index '$index_file': $!";          $self->{mgr} = Mail::Box::Manager->new(access => 'r');
48            $self->{index_file} = $index_file;
         $self->{mgr} = Mail::Box::Manager->new;  
49    
50          # placeholder for opened folders          # placeholder for opened folders
51          $self->{folder} = {};          $self->{folder} = {};
52    
53            $self->{wrap_margin} = $self->{config}->val('global', 'wrap_margin');
54            $self->{max_results} = $self->{config}->val('global', 'max_results') || 100;
55            $self->reset_counters;
56    
57          return $self;          return $self;
58  }  }
59    
60    sub normalize_string {
61            my $self = shift;
62    
63  sub fetch_message {          my $v = shift || return;
64    
65            $v = unac_string('ISO-8859-2', $v);
66            $v = join('',sort split(/\s+/,$v));
67            $v =~ s/\W+//g;
68    
69            return $v;
70    }
71    
72    # reset tables for search results
73    sub reset_counters {
74          my $self = shift;          my $self = shift;
75    
76          my $mbox_id = shift || die "need mbox_id!";          $self->{counter} = {};
77          my ($mbox,$id) = split(/ /,$mbox_id);  
78    #       foreach my $c (qw(thread from to cc bcc lists links att)) {
79    #               $self->{counter}->{$c} = {};
80    #       }
81    
82    }
83    
84    sub add_counter($$) {
85            my $self = shift;
86    
87            my ($c,$v) = @_;
88            my $k = $self->normalize_string($v);
89    
90            $self->{counter}->{$c}->{$k}->{name} = $v;
91            return $self->{counter}->{$c}->{$k}->{usage}++;
92    }
93    
94    sub yyyymmdd {
95            my $self = shift;
96    
97            my $t = shift || time;
98    
99            my (undef,undef,undef,$dd,$mm,$yyyy) = localtime($t);
100            $mm++;
101            $yyyy+=1900;
102            return ($yyyy,$mm,$dd);
103    }
104    
105    sub fmtdate {
106            my $self = shift;
107    
108            my @out;
109            my @formats = qw(%04d %02d %02d);
110            while (my $v = shift) {
111                    my $f = shift @formats;
112                    push @out, sprintf($f, $v);
113            }
114    
115    print STDERR "fmtdate: ",join('|',@out),"\n";
116    
117            return (wantarray ? @out : join("-",@out));
118    }
119    
120    sub add_counter_calendar($) {
121            my $self = shift;
122    
123            my $t = shift || croak "add_counter_calendar without argument!";
124            
125            my ($yyyy,$mm,$dd) = $self->fmtdate($self->yyyymmdd($t));
126    
127            return $self->{counter}->{calendar}->{"$yyyy-$mm"}->{$dd}++;
128    }
129    
130    
131    sub counter {
132            my $self = shift;
133    
134            my $c = shift || return;
135    
136            return if (! $self->{counter}->{$c});
137    
138            return $self->{counter}->{$c};
139    }
140    
141    sub mbox_name2path {
142            my $self = shift;
143    
144            my $mbox = shift || croak "folder_name2path needs mbox name";
145    
146            return $self->{config}->val('folders', $mbox) || croak "comeone removed folder $mbox from config?";
147    }
148    
149    sub open_folder {
150            my $self = shift;
151    
152            my $mbox = shift || croak "open_folder needs mbox name";
153    
154          if (! $self->{folder}->{$mbox}) {          if (! $self->{folder}->{$mbox}) {
155                  my $mbox_path = $self->{config}->val('folders', $mbox) || croak "comeone removed folder $mbox from config?";                  my $mbox_path = $self->mbox_name2path($mbox);
156    
157                    print STDERR "about to open_folder($mbox)\n" if ($debug == 2);
158    
159                  $self->{folder}->{$mbox} = $self->{mgr}->open($mbox_path) || croak "can't open folder $mbox at '$mbox_path': $!";                  $self->{folder}->{$mbox} = $self->{mgr}->open($mbox_path) || croak "can't open folder $mbox at '$mbox_path': $!";
160                  print STDERR "## open($mbox)\n" if ($debug);  
161                    print STDERR "open_folder($mbox) ok\n" if ($debug);
162          }          }
163    
164          my $message = $self->{folder}->{$mbox}->find($id) ||          $self->{fetch_count} = 0;
165                  print STDERR "can't find message $id in $mbox. Time to re-index?\n";  
166            return $self->{folder}->{$mbox};
167    
168    }
169    
170    sub close_folder {
171            my $self = shift;
172    
173            my $mbox = shift || croak "open_folder needs mbox name";
174    
175            $self->{folder}->{$mbox}->close(write => 'NEVER') || croak "can't close folder $mbox";
176    
177            # XXX this is rather agressive!!!
178            $self->{folder} = {};
179            return
180    }
181    
182    sub fetch_message {
183            my $self = shift;
184    
185            my $mbox_id = shift || die "need mbox_id!";
186            my ($mbox,$id) = split(/ /,$mbox_id);
187    
188            # return message with ID
189            print STDERR "fetch $id from $mbox\n" if ($debug);
190    
191            if ($self->{fetch_count}++ > 100) {
192                    $self->close_folder($mbox);
193                    print STDERR "close_folder($mbox) forced on ",$self->{fetch_count},"iteration\n";
194            }
195                    
196          return $message;          my $msg = $self->open_folder($mbox)->find($id);
197            if ($msg) {
198                    return $msg;
199            } else {
200                    print STDERR "can't find message $id in $mbox. Time to re-index?\n";
201                    return;
202            }
203  }  }
204    
205    
206  sub search {  sub search {
207          my $self = shift;          my $self = shift;
208    
209          my $s = shift || carp "search called without argument!";          carp "search called without argument!" if (! @_);
210    
211            $self->reset_counters;
212    
213          my @index_ids = $self->{index}->search($s);          print STDERR "search(",join(" ",@_),")\n" if ($debug == 2);
214            my @index_ids = $self->search_index(@_);
215    
216          $self->{'index_ids'} = \@index_ids;          $self->{'index_ids'} = \@index_ids;
217    
218          my $results = $#index_ids + 1;          #my $results = $#index_ids + 1;
219          $self->{'results'} = $results;          #$self->{'results'} = $results;
220            
221            my $results = $self->{'total_hits'} || ($#index_ids + 1);
222    
223          $self->{'curr_result'} = 0;          $self->{'curr_result'} = 0;
224    
225            print STDERR "$results results\n" if ($debug == 2);
226    
227          return $results || 'error';          return $results || 'error';
228  }  }
229    
230    sub decode_qp($) {
231            my $self = shift;
232    
233            my $tmp = shift || return;
234    
235            sub decode($$$) {
236                    my ($cp,$enc,$qp) = @_;
237    
238                    print STDERR "decode($cp,$qp) -> " if ($debug == 2);
239    
240                    if (uc($enc) eq "Q") {
241                            $qp =~ s/=([a-f0-9][a-f0-9])/chr(hex($1))/ieg;
242                            $qp =~ s/_/ /g;
243                    } elsif (uc($enc) eq "B") {
244                            $qp = decode_base64($qp);
245                    } else {
246                            croak "unsupported encoding '$enc' in decode_qp\n";
247                            return $qp;
248                    }
249    
250                    print STDERR "$qp\n" if ($debug == 2);
251    
252                    my $iconv = Text::Iconv->new($cp,'ISO-8859-2');
253                    return $iconv->convert($qp) || '';
254            }
255    
256            $tmp =~ s/=\?([^\?]+)\?([QB])\?(.+?)\?=/decode($1,$2,$3)/ige;
257            $tmp =~ s/^\s*["']+(.*?)["']+\s*$/$1/g;
258            #print STDERR "$tmp\n" if ($debug == 2);
259            return $tmp;
260    }
261    
262  sub unroll($$$) {  sub unroll($$$) {
263            my $self = shift;
264    
265          my ($message,$part,$sub) = @_;          my ($message,$part,$sub) = @_;
266    
267          my @arr;          my @arr;
268    
269            return if (! $message->$part);
270    
271          foreach my $from ($message->$part) {          foreach my $from ($message->$part) {
272                  push @arr, $from->$sub;                  my $tmp = $from->$sub || next;
273    
274                    $tmp = $self->decode_qp($tmp);
275                    push @arr, $tmp;
276          }          }
277          return \@arr;  
278            return @arr;
279  }  }
280    
281  sub fetch_all_results {  sub fetch_all_results {
# Line 99  sub fetch_all_results { Line 283  sub fetch_all_results {
283    
284          croak "results called before search!" if (! $self->{'index_ids'});          croak "results called before search!" if (! $self->{'index_ids'});
285    
286            print STDERR "fetch_all_results [",scalar @{$self->{'index_ids'}},"]\n" if ($debug == 2);
287    
288          my @arr;          my @arr;
289    
290          foreach my $id (@{$self->{'index_ids'}}) {          foreach my $id (@{$self->{'index_ids'}}) {
291                  push @arr, $self->fetch_result_by_id($id);                  push @arr, $self->fetch_result_by_id($id);
292          }          }
293    
294    
295          return @arr;          return @arr;
296  }  }
297    
# Line 118  sub fetch_result { Line 305  sub fetch_result {
305          my $curr = $self->{'curr_result'}++;          my $curr = $self->{'curr_result'}++;
306    
307          my $id = $self->{'index_ids'}->[$curr];          my $id = $self->{'index_ids'}->[$curr];
308            
309            print STDERR "fetch_result: $curr = $id\n" if ($debug == 2);
310    
311          return $self->fetch_result_by_id($id);          return $self->fetch_result_by_id($id);
312  }  }
313    
314  sub fetch_result_by_id {  sub plain_text_body {
315          my $self = shift;          my $self = shift;
316            my $message = shift || croak "plain_text_body needs message!";
317    
318          my $id = shift || return;          my $body;
   
         my $message = $self->fetch_message($id);  
319    
         my $row;  
   
         $row->{'from'} = unroll($message,'from','phrase');  
         $row->{'subject'} = $message->get('Subject');  
320          if (! $message->isMultipart) {          if (! $message->isMultipart) {
321                  $row->{'body'} = $message->decoded->string;                  $body = $message->decoded->string;
322          } else {          } else {
323                  foreach my $part ($message->parts) {                  foreach my $part ($message->parts) {
324                          if ($part->body->mimeType eq 'text/plain') {                          if ($part->body->mimeType eq 'text/plain') {
325                                  $row->{'body'} = $part->decoded->string;                                  $body = $part->decoded->string;
326                                  last;                                  last;
327                          }                          }
328                  }                  }
329          }          }
330    
331            # reformat with Text::Autoformat
332            my $wrap = $self->{wrap_margin};
333            if ($wrap && $body && $body =~ m/^.{$wrap}..*$/m) {
334                    $body =~ s/[\r\n]/\n/gs;
335                    $body = autoformat($body, {right=>$wrap, all=>1});
336                    $body .="\n[reformated using autoformat, margin at $wrap]" if ($debug == 2);
337            }
338    
339            return $body;
340    }
341    
342    
343    sub fetch_result_by_id {
344            my $self = shift;
345    
346            my $id = shift || return;
347    
348            my $row = $self->{cache}->{$id};
349    
350            if (! $row) {
351    
352                    print STDERR "fetch_result_by_id($id) not in cache, hitting disk\n" if ($debug == 2);
353    
354                    my $message = $self->fetch_message($id) || return;
355    
356                    $row->{'id'} = $id;
357    
358                    foreach my $p (qw(from to cc bcc)) {
359                            foreach my $v ($self->unroll($message,$p,'phrase')) {
360                                    push @{$row->{$p}},$v;
361                                    $self->add_counter($p,$v);
362                            }
363                    }
364                    $row->{'subject'} = $self->decode_qp($message->subject);
365                    $row->{'body'} = $self->plain_text_body($message);
366                    my $utime = str2time($message->date);
367    
368                    $row->{'date_utime'} = $utime;
369    
370                    $row->{'date'} = strftime("%Y-%m-%d %H:%M:%S", localtime($utime));
371                    $self->add_counter_calendar($utime);
372    
373                    # XXX store in cache?
374                    $self->{cache}->{$id} = $row;
375                    print STDERR "$id stored in cache\n" if ($debug == 2);
376            } else {
377                    print STDERR "fetch_result_by_id($id) in cache\n" if ($debug == 2);
378            }
379    
380          return $row;          return $row;
381                    
382  }  }

Legend:
Removed from v.4  
changed lines
  Added in v.22

  ViewVC Help
Powered by ViewVC 1.1.26