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

Contents of /trunk/httpd.pl

Parent Directory Parent Directory | Revision Log Revision Log


Revision 47 - (show annotations)
Tue May 11 22:59:27 2004 UTC (19 years, 11 months ago) by dpavlin
File MIME type: text/plain
File size: 8390 byte(s)
Major code update: 0.9-rc3 if no serious problems are found, this will
become first public version (0.9).

- search.pl is working again
- fixed Mail::Box problem with unimplemented lock_type => 'none' on Maildir
- documented Mozilla 1.5 problem with sidebar float: right
- don't output anything from swish-e while indexing
- remove (e-mail) from addresses (it seems that Exchange like to add those)
- added progress report while indexing
- documented all command-line utilities

1 #!/usr/bin/perl
2
3 # based on post
4 # http://www.mail-archive.com/libwww@perl.org/msg04750.html
5
6 BEGIN {
7 my $basedir = readlink($0) || $0; $basedir =~ s#/[^/]+$##;
8 unshift(@INC, $basedir);
9 }
10
11 =head1 NAME
12
13 httpd.pl - http server for Mail::Box Web Search
14
15 =head1 SYNOPSYS
16
17 httpd.pl [local.conf]
18
19 =head1 DESCRIPTION
20
21 This is small http server, based on C<HTTP::Daemon> which is designed
22 for single-user use (on laptop for example) via loopback.
23
24 It doesn't provide any authentification or authorisation, and it can handle
25 just one request at the time, so it's not suted for public-facing sites,
26 even if you don't care about security of your mailboxes.
27
28 =head1 SEE ALSO
29
30 C<MWS> perl modules which are part of this package
31
32 =cut
33
34 use strict;
35 use warnings;
36 use MWS::SWISH;
37 #use MWS::Plucene;
38 use HTTP::Daemon;
39 use HTTP::Status;
40 use IO::String;
41 use CGI::Lite;
42 use Template;
43 use URI::Escape;
44
45 use Data::Dumper;
46
47 my $debug = 1;
48
49 my $config_file = shift @ARGV || 'global.conf';
50
51 if (! -f $config_file) {
52 print qq{Usage: $0 [/path/to/local.conf]
53
54 If local.conf is not specified, global.conf in current directory will
55 be used.
56 };
57 exit 1;
58 }
59
60 my $mws = MWS::SWISH->new(config_file => $config_file);
61 #my $mws = MWS::Plucene->new(config_file => $config_file, debug => $debug);
62
63 my ($local_addr,$local_port) = ('127.0.0.1',6969);
64
65 my $listen = $mws->{config}->val('global', 'listen');
66
67 print STDERR "using listen $listen\n" if ($listen);
68
69 if ($listen && $listen =~ m/:/) {
70 ($local_addr,$local_port) = split(/:/,$listen,2);
71 } elsif ($listen) {
72 $local_addr = $listen;
73 }
74
75 my $d = HTTP::Daemon->new(
76 Reuse => 1,
77 LocalAddr => $local_addr,
78 LocalPort => $local_port,
79 ) || die "can't create HTTP::Daemon on $local_addr:$local_port: $!";
80
81 my $cgi = new CGI::Lite;
82 my $tt = Template->new({
83 INCLUDE_PATH => $mws->{config}->val('global', 'templates'),
84 FILTERS => {
85 'body5' => \&body5_filter,
86 'body' => \&body_filter,
87 },
88 EVAL_PERL => 1,
89 });
90
91 my $static_html = $mws->{config}->val('global', 'static_html');
92
93 print "Web server ready at: ", $d->url, "\n";
94
95
96 while ( my $c = $d->accept ) {
97 while ( my $r = $c->get_request ) {
98
99 # environs that a webserver should set.
100 $ENV{'REQUEST_METHOD'} = $r->method;
101 $ENV{'GATEWAY_INTERFACE'} = "CGI/1.0";
102 $ENV{'SERVER_PROTOCOL'} = $r->protocol;
103 $ENV{'CONTENT_TYPE'} = $r->content_type;
104
105 # this part is based on CGI::Lite
106
107 $cgi->close_all_files();
108 $cgi->{web_data} = {};
109 $cgi->{ordered_keys} = [];
110 $cgi->{all_handles} = [];
111 $cgi->{error_status} = 0;
112 $cgi->{error_message} = undef;
113
114 if ( $r->method eq 'GET' || $r->uri =~ /\?/ ) {
115 my $query_string = $r->uri;
116 $query_string =~ s/[^\?]+\?(.*)/$1/;
117 $cgi->_decode_url_encoded_data (\$query_string, 'form');
118
119 } elsif ( $r->method eq 'POST' ) {
120
121 if ($r->content_type eq 'application/x-www-form-urlencoded') {
122 # local $^W = 0;
123 $cgi->_decode_url_encoded_data (\$r->content, 'form');
124 } elsif ($r->content_type =~ /multipart\/form-data/) {
125 my ($boundary) = $r->content_type =~ /boundary=(\S+)$/;
126 $cgi->_parse_multipart_data ($r->content_length, $boundary);
127 }
128 } else {
129 $c->send_error(RC_FORBIDDEN);
130 }
131
132 my $param = $cgi->{web_data};
133 my $url = $r->url->path;
134
135 # XXX LOG
136 print $r->method," ",$url,"\n";
137 print Dumper($param,$mws->{counter}),"\n" if ($debug);
138
139 # is this static page?
140 if ($static_html && -f "$static_html/$url") {
141 print "static file: $static_html/$url\n" if ($debug);
142 $c->send_file_response("$static_html/$url");
143 $c->close;
144 next;
145 }
146
147 # template file name (use ?format=html as default)
148 my $tpl_file = 'master.';
149 $tpl_file .= $param->{'format'} || 'html';
150
151 # parse date from url
152 my ($yyyy,$mm,$dd) = $mws->yyyymmdd;
153
154 my $yyyymm;
155
156 my $date_limit;
157
158 if ($url =~ m,^/(\d{4})[/-](\d+)[/-](\d+),) {
159 ($yyyy, $mm, $dd) = $mws->fmtdate($1,$2,$3);
160 $date_limit = "$yyyy-$mm-$dd";
161 } elsif ($url =~ m,^/(\d{4})[/-](\d+),) {
162 ($yyyy,$mm) = $mws->fmtdate($1,$2);
163 $date_limit = "$yyyy-$mm";
164 } elsif ($url =~ m,^/(\d{4}),) {
165 $date_limit = $mws->fmtdate($1);
166 }
167
168 #
169 # implement functionality and generate HTML
170 #
171 my $html;
172
173 if ($param->{'search_val'} && $param->{'search_fld'} && !$param->{'search'}) {
174 $param->{'search'} = $param->{'search_fld'}.":".$param->{'search_val'};
175 } elsif ($param->{'search'}) {
176 ($param->{'search_fld'}, $param->{'search_val'}) = split(/:/,$param->{'search'},2);
177 }
178
179 my $tpl_var = {
180 param => $param,
181 yyyy => $yyyy,
182 mm => $mm,
183 dd => $dd,
184 date_limit => $date_limit,
185 };
186
187 # is this access to root of web server?
188 if ($url eq "/" && !$param->{'search'}) {
189 # if first access, go to current year
190 $date_limit = $mws->fmtdate($yyyy);
191 $param->{sort_by} = "date desc";
192 }
193
194 # ?show_id=XXXXxxxx___message_id___xxxxXXXX
195 if ($param->{'show_id'}) {
196
197 $mws->reset_counters;
198 my $row = $mws->fetch_result_by_id($param->{'show_id'});
199 $tpl_var->{message} = $row;
200 } elsif ($param->{'search'} || $date_limit) {
201
202 # show search results
203 # ?search=foo:bar
204
205 my @search;
206 push @search, $param->{'search'} if ($param->{'search'});
207
208 if ($date_limit) {
209 push @search, "and" if (@search);
210 push @search, "date:\"$date_limit\"";
211 }
212
213 if ($param->{sort_by}) {
214 push @search, "sort:".$param->{sort_by};
215 }
216
217 print STDERR "search: ",join(" ",@search),"\n";
218
219 my $results = $mws->search(@search);
220 my @res = $mws->fetch_all_results();
221
222 $tpl_var->{results} = \@res if (@res);
223 $tpl_var->{total_hits} = $mws->{total_hits} || 0;
224
225 # no hits, offer suggestions
226 if (! $tpl_var->{results}) {
227 @{$tpl_var->{apropos}} = $mws->apropos_index($param->{'search_fld'}, $param->{'search_val'});
228 }
229
230 }
231
232
233 # push counters to template
234 foreach my $f (qw(from to cc bcc folder)) {
235 my $h = $mws->counter($f) || next;
236 my @a;
237 foreach my $k (sort { $h->{$b}->{usage} <=> $h->{$a}->{usage} } keys %$h) {
238 push @a, $h->{$k};
239 }
240 $tpl_var->{counters}->{$f} = [ @a ] if (@a);
241 }
242
243 # push calendar in template
244 $tpl_var->{calendar} = $mws->counter('calendar');
245
246 $tt->process($tpl_file, $tpl_var, \$html) || die $tt->error();
247
248 #
249 # send HTMLto client
250 #
251
252 my $res = HTTP::Response->new(RC_OK);
253 $res->header( 'Content-type' => 'text/html; charset=ISO-8859-2' );
254 $res->content($html);
255 $c->send_response($res);
256
257 $c->close;
258 }
259 undef($c);
260 }
261
262 # template toolkit filter
263
264 sub html_escape($) {
265 my $text = shift || return;
266
267 # don't re-escape html
268 #return $text if ($text =~ /&(?:lt|gt|amp|quot);/);
269
270 # Escape <, >, & and ", and to produce valid XML
271 my %escape = ('<'=>'&lt;', '>'=>'&gt;', '&'=>'&amp;', '"'=>'&quot;');
272 my $escape_re = join '|' => keys %escape;
273
274 $text =~ s/($escape_re)/$escape{$1}/gs;
275
276 while ($text =~ s/#-#(quote|signature)(\d*)##(.+?)##\1\2#-#/<span class="$1">$3<\/span>/gs) { } ;
277
278 return $text;
279 }
280
281 #use Text::Context::EitherSide;
282
283 sub body5_filter {
284 my $text = shift;
285
286 # remove quote
287 $text =~ s/^[\>:\|=]+[^\n\r]*[\n\r]*$/#-q-#/msg;
288 # remove quote author
289 $text =~ s/[\n\r]+[^\n\r]+:\s*(?:#-q-#[\n\r*])+//gs;
290 $text =~ s/^[^\n\r]+:\s*(?:#-q-#[\n\r]*)+//gs;
291 $text =~ s/#-q-#[\n\r]*//gs;
292 # outlook quoting
293 $text =~ s/(\s*--+\s*Original\s+Message\s*--+.*)$//si;
294 $text =~ s/(\s*--+\s*Forwarded\s+message.+\s*--+.*)$//si;
295
296 # remove signature
297 $text =~ s/(?:^|[\n\r]+)*--\s*[\n\r]+.*$//s;
298 $text =~ s/(?:^|[\n\r]+)*_____+[\n\r]+.*$//s;
299
300 # compress cr/lf
301 $text =~ s/[\n\r]+/\n/gs;
302
303 # remove whitespaces
304 $text =~ s/^\n+//gs;
305 $text =~ s/[\s\n]+$//gs;
306
307 if ($text eq "") {
308 $text="#-#quote##forwarded message##quote#-#";
309 }
310
311 # cut to 5 lines;
312 if ($text =~ s,^((?:.*?[\n\r]){5}).*$,$1,s) {
313 $text =~ s/[\n\r]*$/ .../;
314 }
315
316 # my $context = Text::Context::EitherSide->new($text, context => 5);
317 # return $context->as_string("perl");
318
319 return html_escape($text);
320 }
321
322 sub body_filter {
323 my $text = shift;
324
325 my $sig = '';
326
327 # remove signature
328 if ($text =~ s/([\n\r]+)(--\s*[\n\r]+.*)$//s) {
329 $sig = "$1#-#signature##$2##signature#-#";
330 } elsif ($text =~s/(^|[\n\r]+)*(_____+[\n\r]+.*)$//s) {
331 $sig = "$1#-#signature##$2##signature#-#";
332 }
333
334 # find quoted text
335 $text =~ s/^([\>:\|=]+[^\n\r]*[\n\r]*)$/#-#quote1##$1##quote1#-#/mg;
336 $text =~ s/(--+\s*Original\s+Message\s*--+.*)$/#-#quote2##$1##quote2#-#/si;
337 $text =~ s/(--+\s*Forwarded\s+message.+\s*--+.*)$/#-#quote3##$1##quote3#-#/si;
338
339 $text = html_escape($text . $sig);
340 return $text;
341 }
342

Properties

Name Value
svn:executable *

  ViewVC Help
Powered by ViewVC 1.1.26