File Coverage

blib/lib/DiaColloDB/WWW/CGI.pm
Criterion Covered Total %
statement 49 288 17.0
branch 0 150 0.0
condition 0 59 0.0
subroutine 17 74 22.9
pod 52 52 100.0
total 118 623 18.9


line stmt bran cond sub pod time code
1             ##-*- Mode: CPerl; coding: utf-8; -*-
2             ##
3             ## File: DiaColloDB/WWW/CGI.pm
4             ## Author: Bryan Jurish
5             ## Description: collocation db, www wrappers: (f)cgi handler
6             ## + adapted from DbCgi.pm ( svn+ssh://odo.dwds.de/home/svn/dev/dbcgi/trunk/DbCgi.pm )
7              
8             package DiaColloDB::WWW::CGI;
9 1     1   60678 use DiaColloDB;
  1         291434  
  1         36  
10 1     1   14 use DiaColloDB::Logger;
  1         2  
  1         19  
11 1     1   708 use CGI qw(:standard :cgi-lib);
  1         22492  
  1         4  
12 1     1   3341 use URI;
  1         2  
  1         22  
13 1     1   5 use URI::Escape qw(uri_escape_utf8);
  1         1  
  1         53  
14 1     1   5 use HTTP::Status;
  1         2  
  1         203  
15 1     1   5 use Encode qw(); #qw(encode decode encode_utf8 decode_utf8);
  1         1  
  1         15  
16 1     1   7 use File::Basename qw(basename dirname);
  1         4  
  1         37  
17 1     1   345 use File::ShareDir qw(); ##-- for shared template data
  1         4473  
  1         20  
18 1     1   5 use Cwd qw(getcwd abs_path);
  1         2  
  1         43  
19             #use LWP::UserAgent;
20 1     1   341 use Template;
  1         13540  
  1         38  
21 1     1   10 use JSON qw();
  1         3  
  1         31  
22 1     1   7 use Time::HiRes qw();
  1         3  
  1         24  
23 1     1   12 use utf8;
  1         3  
  1         8  
24 1     1   37 use Carp;
  1         3  
  1         72  
25 1     1   7 use strict;
  1         3  
  1         41  
26              
27             BEGIN {
28             #binmode(STDIN, ':utf8');
29             #binmode(STDOUT,':utf8');
30 1     1   5579 binmode(STDERR,':utf8');
31             }
32              
33             *isa = \&UNIVERSAL::isa;
34             *can = \&UNIVERSAL::can;
35              
36             ##======================================================================
37             ## globals
38              
39             our $VERSION = "0.02.003";
40             our @ISA = qw(DiaColloDB::Logger);
41              
42             ##======================================================================
43             ## constructors etc.
44              
45             ## $dbcgi = $that->new(%args)
46             ## + %args, object structure:
47             ## (
48             ## ##-- basic stuff
49             ## prog => basename($0),
50             ## ##
51             ## ##-- underlying CGI module
52             ## cgipkg => 'CGI',
53             ## ##
54             ## ##-- CGI params
55             ## defaults => {},
56             ## vars => undef,
57             ## charset => 'utf-8',
58             ## nodecode => {}, ##-- vars not to decode
59             ## ##
60             ## ##-- CGI environment stuff : see getenv() method
61             ## remote_addr => undef,
62             ## remote_user => undef,
63             ## request_method => undef,
64             ## request_uri => undef,
65             ## request_query => undef,
66             ## http_referer => undef,
67             ## http_host => undef,
68             ## server_addr => undef,
69             ## server_port => undef,
70             ## ##
71             ## ##-- template toolkit stuff
72             ## ttk_package => (ref($that)||$that),
73             ## ttk_vars => {}, ##-- template vars
74             ## ttk_config => {ENCODING=>'utf8'}, ##-- options for Template->new()
75             ## ttk_process => {binmode=>':utf8'}, ##-- options for Template->process()
76             ## ttk_dir => abs_path(dirname($0)),
77             ## ttk_key => undef, ##-- current template basename
78             ## ttk_rawkeys => { ##-- pseudo-set of raw keys
79             ## profile=>1,
80             ## },
81             ## ##
82             ## ##-- File::ShareDir stuff (fallbacks for ttk_dir)
83             ## ttk_sharedir => File::ShareDir::dist_dir("DiaColloDB-WWW")."/htdocs",
84             ## )
85             sub new {
86 0     0 1   my $that = shift;
87 0   0       my $dbcgi = bless({
      0        
88             ##-- basic stuff
89             prog => basename($0),
90             ##
91             ##-- underlying CGI module
92             cgipkg => 'CGI',
93             ##
94             ##-- CGI params
95             defaults => {},
96             vars => undef,
97             charset => 'utf-8',
98             nodecode => {}, ##-- vars not to decode
99             ##
100             ##-- CGI environment stuff : see getenv() method
101             remote_addr => undef,
102             remote_user => undef,
103             request_method => undef,
104             request_uri => undef,
105             request_query => undef,
106             http_referer => undef,
107             http_host => undef,
108             server_addr => undef,
109             server_port => undef,
110             ##
111             ##-- template toolkit stuff
112             ttk_package => (ref($that)||$that),
113             ttk_vars => {}, ##-- template vars
114             ttk_config => {ENCODING=>'utf8'}, ##-- options for Template->new()
115             ttk_process => {binmode=>':utf8'}, ##-- options for Template->process()
116             ttk_dir => abs_path(dirname($0)),
117             ttk_key => undef, ##-- current template basename
118             ttk_rawkeys => { ##-- pseudo-set of raw keys
119             profile=>1,
120             },
121             ##
122             ##-- File::ShareDir stuff (fallbacks for ttk_dir)
123             ttk_sharedir => File::ShareDir::dist_dir("DiaColloDB-WWW")."/htdocs",
124             ##
125             ##-- user args
126             @_,
127             }, ref($that)||$that);
128              
129             ##-- CGI package
130 0 0         if ($dbcgi->{cgipkg}) {
131 0           eval "use $dbcgi->{cgipkg} qw(:standard :cgi-lib);";
132 0 0         $dbcgi->logconfess("new(): could not use {cgipkg} $dbcgi->{cgipkg}: $@") if ($@);
133             }
134              
135             ##-- environment defaults
136 0           $dbcgi->_getenv();
137              
138 0           return $dbcgi;
139             }
140              
141             ## @keys = $dbcgi->_param()
142             ## $val = $dbcgi->_param($name)
143             sub _param {
144 0     0     my $dbcgi = shift;
145 0           return $dbcgi->cgi('param',@_);
146             }
147              
148             ## $dbcgi = $dbcgi->_reset()
149             ## + resets CGI environment
150             sub _reset {
151 0     0     my $dbcgi = shift;
152 0           delete @$dbcgi{(qw(vars),
153             qw(remote_addr remote_user),
154             qw(request_method request_uri request_query),
155             qw(http_referer http_host server_addr server_port),
156             )};
157 0           return $dbcgi;
158             }
159              
160             ## $dbcgi = $dbcgi->_getenv()
161             sub _getenv {
162 0     0     my $dbcgi = shift;
163 0   0       $dbcgi->{remote_addr} = ($ENV{REMOTE_ADDR}||'0.0.0.0');
164 0   0       $dbcgi->{remote_user} = ($ENV{REMOTE_USER} || getpwuid($>));
165 0   0       $dbcgi->{request_method} = ($ENV{REQUEST_METHOD}||'GET');
166 0   0       $dbcgi->{request_uri} = ($ENV{REQUEST_URI} || $0);
167 0           $dbcgi->{request_query} = $ENV{QUERY_STRING};
168 0           $dbcgi->{http_referer} = $ENV{HTTP_REFERER};
169 0           $dbcgi->{http_host} = $ENV{HTTP_HOST};
170 0           $dbcgi->{server_addr} = $ENV{SERVER_ADDR};
171 0           $dbcgi->{server_port} = $ENV{SERVER_PORT};
172 0           return $dbcgi;
173             }
174              
175             ## $dbcgi = $dbcgi->fromRequest($httpRequest,$csock)
176             ## + sets up $dbcgi from an HTTP::Request object
177             sub fromRequest {
178 0     0 1   my ($dbcgi,$hreq,$csock) = @_;
179              
180             ##-- setup pseudo-environment
181 0           my $uri = $hreq->uri;
182 0           my @path = grep {$_ ne ''} $uri->path_segments;
  0            
183 0   0       $dbcgi->{prog} = $path[$#path] || 'index';
184 0 0         $dbcgi->{remote_addr} = $ENV{REMOTE_ADDR} = $csock ? $csock->peerhost : '0.0.0.0';
185 0 0         $dbcgi->{remote_port} = $ENV{REMOTE_PORT} = $csock ? $csock->peerport : '0';
186 0           $dbcgi->{remote_user} = $ENV{REMOTE_USER} = '';
187 0           $dbcgi->{request_method} = $ENV{REQUEST_METHOD} = $hreq->method;
188 0           $dbcgi->{request_uri} = $ENV{REQUEST_URI} = $uri->as_string;
189 0           $dbcgi->{request_query} = $ENV{REQUEST_QUERY} = $uri->query;
190 0           $dbcgi->{http_referer} = $ENV{HTTP_REFERER} = $hreq->referer;
191 0   0       $dbcgi->{http_host} = $ENV{HTTP_HOST} = $uri->host || $csock->sockhost;
192 0 0         $dbcgi->{server_addr} = $ENV{SERVER_ADDR} = $csock ? $csock->sockaddr : '0.0.0.0';
193 0 0         $dbcgi->{server_port} = $ENV{SERVER_PORT} = $csock ? $csock->sockport : '0';
194              
195             ##-- setup variables
196 0           my %vars = $uri->query_form;
197             my $addVars = sub {
198 0     0     my $add = shift;
199 0           foreach (grep {defined $add->{$_}} keys %$add) {
  0            
200 0 0         if (!exists($vars{$_})) {
201 0           $vars{$_} = $add->{$_};
202             } else {
203 0 0         $vars{$_} = [ $vars{$_} ] if (!ref($vars{$_}));
204 0 0         push(@{$vars{$_}}, ref($add->{$_}) ? @{$add->{$_}} : $add->{$_});
  0            
  0            
205             }
206             }
207 0           };
208 0 0         if ($hreq->method eq 'POST') {
209 0 0         if ($hreq->content_type eq 'application/x-www-form-urlencoded') {
    0          
    0          
210             ##-- POST: x-www-form-urlencoded
211 0           $addVars->( {URI->new('?'.$hreq->content)->query_form} );
212             }
213             elsif ($hreq->content_type eq 'multipart/form-data') {
214             ##-- POST: multipart/form-data: parse by hand
215 0           foreach my $part ($hreq->parts) {
216 0           my $pdis = $part->header('Content-Disposition');
217 0 0         if ($pdis =~ /^form-data\b/) {
218             ##-- POST: multipart/form-data: part: form-data; name="PARAMNAME"
219 0 0         if ($pdis =~ /\bname=[\"\']?([\w\-\.\,\+]*)[\'\"]?/) {
220 0           $addVars->({ $1 => $part->content });
221 0           next;
222             }
223             }
224             ##-- POST: multipart/form-data: part: anything other than 'form-data; name="PARAMNAME"'
225 0           $addVars->({ POSTDATA => $part->content });
226             }
227             }
228             elsif ($hreq->content_length > 0) {
229             ##-- POST: anything else: use POSTDATA
230 0           $addVars->({ POSTDATA => $hreq->content });
231             }
232             }
233 0           $dbcgi->vars(\%vars);
234              
235 0           return $dbcgi;
236             }
237              
238              
239             ## \%vars = $dbcgi->vars()
240             ## \%vars = $dbcgi->vars(\%vars)
241             ## + get/set CGI variables, instantiating $dbcgi->{defaults} if present
242             sub vars {
243 0     0 1   my ($dbcgi,$vars) = @_;
244 0 0 0       return $dbcgi->{vars} if (defined($dbcgi->{vars}) && !defined($vars));
245 0 0 0       $vars ||= $dbcgi->cgi('param') ? { %{$dbcgi->cgi('Vars')} } : {};
  0            
246              
247 0 0 0       if (($dbcgi->{cgipkg}//'CGI') ne 'CGI' || defined($vars->{POSTDATA})) {
      0        
248             ##-- parse params from query string; required e.g. for CGI::Fast or non-form POST requests (which set POSTDATA)
249 0           my $uri = URI->new($dbcgi->{request_uri});
250 0           my %urif = $uri->query_form();
251 0           @$vars{keys %urif} = values %urif;
252             }
253              
254 0 0         foreach (grep {!exists($vars->{$_}) && defined($dbcgi->{defaults}{$_})} keys %{$dbcgi->{defaults}||{}}) {
  0 0          
  0            
255             ##-- defaults
256 0           $vars->{$_} = $dbcgi->{defaults}{$_}
257             }
258 0           my ($tmp);
259 0           foreach (keys %$vars) {
260             ##-- decode (annoying temporary variable hack hopefully ensures that utf8 flag is set!)
261 0           $tmp = $vars->{$_};
262 0           $tmp =~ s/\x{0}/ /g;
263 0 0 0       if ($dbcgi->{charset} && !utf8::is_utf8($tmp) && !exists($dbcgi->{nodecode}{$_})) {
      0        
264 0           $tmp = Encode::decode($dbcgi->{charset},$tmp);
265             #$dbcgi->trace("decode var '$_':\n+ OLD=$vars->{$_}\n+ NEW=$tmp\n");
266 0           $vars->{$_} = $tmp;
267             }
268             }
269 0           return $dbcgi->{vars} = $vars;
270             }
271              
272             ##======================================================================
273             ## config loading (optional)
274              
275             ## $dbcgi = $dbcgi->load_config($filename)
276             ## + clobers %$dbcgi keys from JSON filename
277             sub load_config {
278 0     0 1   my ($dbcgi,$file) = @_;
279 0 0         open(RC,"<:raw",$file)
280             or $dbcgi->logconfess("load_config(): failed for '$file': $!");
281 0           local $/ = undef;
282 0           my $buf = ;
283 0 0         close RC
284             or $dbcgi->logconfess("load_config(): close failed for '$file': $!");
285 0 0         my $data = JSON::from_json($buf,{utf8=>1,relaxed=>1})
286             or $dbcgi->logconfess("load_config(): from_json() failed for config data from '$file': $!");
287 0           @$dbcgi{keys %$data} = values %$data;
288 0           return $dbcgi;
289             }
290              
291             ##======================================================================
292             ## Template Toolkit stuff
293              
294             ## $key = $dbcgi->ttk_key($key)
295             ## $key = $dbcgi->ttk_key()
296             ## + returns current template key
297             ## + default is basename($dbcgi->{prog}) without final extension
298             sub ttk_key {
299 0     0 1   my ($dbcgi,$key) = @_;
300 0 0         ($key=basename($dbcgi->{prog})) =~ s/\.[^\.]*\z// if (!$key);
301 0           return $key;
302             }
303              
304             ## @paths = $dbcgi->ttk_include()
305             ## $paths = $dbcgi->ttk_include()
306             ## + returns ttk search path @$dbcgi->{qw(ttk_dir ttk_sharedir)}
307             ## + in scalar context returns ":"-separated list
308             sub ttk_include {
309 0     0 1   my $dbcgi = shift;
310 0 0         my @dirs = map {s/\/+\z//; abs_path($_)} grep {defined($_) && $_ ne ''} @$dbcgi{qw(ttk_dir ttk_sharedir)};
  0            
  0            
  0            
311 0 0         return wantarray ? @dirs : join(":",@dirs);
312             }
313              
314             ## $file = $dbcgi->ttk_file()
315             ## $file = $dbcgi->ttk_file($key)
316             ## + returns template filename for template key (basename) $key
317             ## + $key defaults to $dbcgi->{prog} without final extension
318             ## + searches in $dbcgi->{ttk_dir} or $dbcgi->{ttk_sharedir}
319             sub ttk_file {
320 0     0 1   my ($dbcgi,$key) = @_;
321 0   0       (my $dir = $dbcgi->{ttk_dir} || '.') =~ s/\/+\z//;
322 0           $key = $dbcgi->ttk_key($key);
323 0           my $file = "$key.ttk";
324 0           my @dirs = $dbcgi->ttk_include();
325 0           foreach (@dirs) {
326 0 0         return "$_/$file" if (-f "$_/$file");
327             }
328 0           $dbcgi->logconfess("ttk_file(): could not find template file '$file' in ttk search path ".$dbcgi->ttk_include);
329             }
330              
331             ## $t = $dbcgi->ttk_template(\%templateConfigArgs)
332             ## + returns a new Template object with default args set
333             sub ttk_template {
334 0     0 1   my ($dbcgi,$targs) = @_;
335             my $t = Template->new(
336             INTERPOLATE=>1,
337             PRE_CHOMP=>0,
338             POST_CHOMP=>1,
339             EVAL_PERL=>1,
340             ABSOLUTE=>1,
341             RELATIVE=>1,
342             INCLUDE_PATH =>scalar($dbcgi->ttk_include),
343 0 0         %{$dbcgi->{ttk_config}||{}},
344 0 0         %{$targs||{}},
  0            
345             );
346 0           return $t;
347             }
348              
349             ## $data = $dbcgi->ttk_process($srcFile, \%templateVars, \%templateConfigArgs, \%templateProcessArgs)
350             ## $dbcgi = $dbcgi->ttk_process($srcFile, \%templateVars, \%templateConfigArgs, \%templateProcessArgs, $outfh)
351             ## $dbcgi = $dbcgi->ttk_process($srcFile, \%templateVars, \%templateConfigArgs, \%templateProcessArgs, \$outbuf)
352             ## + process a template $srcFile, returns generated $data
353             sub ttk_process {
354 0     0 1   my ($dbcgi,$src,$tvars,$targs,$pargs,$output) = @_;
355 0           my $outbuf = '';
356 0           my $t = $dbcgi->ttk_template($targs);
357             $t->process($src,
358 0 0         {package=>$dbcgi->{ttk_package}, version=>$VERSION, ENV=>{%ENV}, %{$dbcgi->{ttk_vars}||{}}, cdb=>$dbcgi, %{$tvars||{}}},
  0 0          
359             (defined($output) ? $output : \$outbuf),
360 0 0         %{$dbcgi->{ttk_process}||{}},
361 0 0         %{$pargs||{}},
  0 0          
    0          
362             )
363             or $dbcgi->logconfess("ttk_process(): template error: ".$t->error);
364 0 0         return defined($output) ? $dbcgi : $outbuf;
365             }
366              
367             ##======================================================================
368             ## CGI stuff: generic
369              
370             ## @error = $dbcgi->htmlerror($status,@message)
371             ## + returns a print()-able HTML error
372             sub htmlerror {
373 0     0 1   my ($dbcgi,$status,@msg) = @_;
374 0 0         $status = 500 if (!defined($status)); ##-- RC_INTERNAL_SERVER_ERROR
375 0           my $title = 'Error: '.$status.' '.status_message($status);
376 0           charset($dbcgi->{charset});
377 0   0       my $msg = join(($,//''), @msg);
378 0           $msg =~ s/\beval\s*\'(?:\\.|[^\'])*\'/eval '...'/sg; ##-- suppress long eval '...' messsages
379             return
380 0           (header(-status=>$status),
381             start_html($title),
382             h1($title),
383             pre("\n",escapeHTML($msg),"\n"),
384             end_html,
385             );
386             }
387              
388             ## @whatever = $dbcgi->cgi($method, @args)
389             ## + call a method from the CGI package $dbcgi->{cgipkg}->can($method)
390             sub cgi {
391 0     0 1   my ($dbcgi,$method)=splice(@_,0,2);
392 0 0         CGI::charset($dbcgi->{charset}) if ($dbcgi->{charset});
393 0           my ($sub);
394 0 0         if (ref($method)) {
    0          
    0          
395 0           return $method->(@_);
396             }
397             elsif ($sub=$dbcgi->{cgipkg}->can($method)) {
398 0           return $sub->(@_);
399             }
400             elsif ($sub=CGI->can($method)) {
401 0           return $sub->(@_);
402             }
403 0           $dbcgi->logconfess("cgi(): unknown method '$method' for cgipkg='$dbcgi->{cgipkg}'");
404             }
405              
406             ## undef = $dbcgi->cgi_main()
407             ## undef = $dbcgi->cgi_main($ttk_key)
408             ## + wraps a template-instantiation for $ttk_key, by default basename($0)
409             sub cgi_main {
410 0     0 1   my ($dbcgi,$key) = @_;
411 0           my @content;
412 0           my $israw = $dbcgi->{ttk_rawkeys}{$dbcgi->ttk_key($key)};
413 0           eval {
414 0 0         @content = $dbcgi->ttk_process($dbcgi->ttk_file($key), $dbcgi->vars, ($israw ? {ENCODING=>undef} : undef), ($israw ? {binmode=>':raw'} : undef));
    0          
415             };
416 0 0 0       if ($@) {
    0          
417 0           $israw = 0;
418 0           @content = $dbcgi->htmlerror(undef, $@);
419             }
420             elsif (!@content || !defined($content[0])) {
421 0           $israw = 0;
422 0           @content = $dbcgi->htmlerror(undef, "template '$key' returned no content");
423             }
424              
425 0 0         if ($dbcgi->{charset}) {
426 0           charset($dbcgi->{charset});
427 0 0         binmode(\*STDOUT, ($israw ? ":raw" : ":encoding($dbcgi->{charset})"));
428             }
429 0           print @content;
430             }
431              
432             ## undef = $dbcgi->fcgi_main()
433             ## undef = $dbcgi->fcgi_main($ttk_key)
434             ## + wraps a template-instantiation for $ttk_key, by default basename($0)
435             sub fcgi_main {
436 0     0 1   my ($dbcgi,$key) = @_;
437 0           require CGI::Fast;
438 0           CGI::Fast->import(':standard');
439 0           $dbcgi->{cgipkg} = 'CGI::Fast';
440 0           while (CGI::Fast->new()) {
441 0           $dbcgi->_getenv();
442 0           $dbcgi->cgi_main($key);
443 0           $dbcgi->_reset();
444             }
445             }
446              
447             ##======================================================================
448             ## Template stuff: useful aliases
449              
450 0     0 1   sub remoteAddr { return $_[0]{remote_addr}; }
451 0     0 1   sub remoteUser { return $_[0]{remote_user}; }
452 0     0 1   sub requestMethod { return $_[0]{request_method}; }
453 0     0 1   sub requestUri { return $_[0]{request_uri}; }
454 0     0 1   sub requestQuery { return $_[0]{request_query}; }
455 0     0 1   sub httpReferer { return $_[0]{http_referer}; }
456 0     0 1   sub httpHost { return $_[0]{http_host}; }
457 0     0 1   sub serverAddr { return $_[0]{server_addr}; }
458             sub serverPort {
459 0 0   0 1   return $_[0]{server_port} if ($_[0]{server_port});
460 0           my $host = $_[0]->httpHost;
461 0 0 0       return $1 if ($host && $host =~ /:([0-9]+)$/);
462 0 0         return $ENV{HTTPS} ? 443 : 80; ##-- guess port from scheme
463             }
464              
465              
466             ## $uri = $dbcgi->uri()
467             ## $uri = $dbcgi->uri($uri)
468             sub uri {
469 0 0   0 1   return URI->new($_[1]) if (defined $_[1]);
470 0           my $dbcgi = shift;
471 0   0       my $host = $dbcgi->httpHost // '';
472 0           my $port = $dbcgi->serverPort;
473 0 0         my $scheme = ($ENV{HTTPS} ? 'https' : 'http');
474 0 0 0       return URI->new(
    0          
475             #($host ? "http://$host" : "file://")
476             ($host ? "${scheme}://$host" : "file://") ##-- guess scheme from HTTPS environment variable
477             .( ($host && $host =~ /:[0-9]+$/) || $port==($scheme eq 'https' ? 443 : 80) ? '' : ":$port" )
478             .$dbcgi->requestUri
479             );
480             }
481              
482             ## $scheme = $dbcgi->uriScheme($uri?)
483             ## $opaque = $dbcgi->uriOpaque($uri?)
484             ## $path = $dbcgi->uriPath($uri?)
485             ## $frag = $dbcgi->uriFragment($uri?)
486             ## $canon = $dbcgi->uriCanonical($uri?)
487             ## $abs = $dbcgi->uriAbs($uri?);
488 0     0 1   sub uriScheme { $_[0]->uri($_[1])->scheme; }
489 0     0 1   sub uriPath { $_[0]->uri($_[1])->path; }
490 0     0 1   sub uriFragment { $_[0]->uri($_[1])->fragment; }
491 0     0 1   sub uriCanonical { $_[0]->uri($_[1])->canonical->as_string; }
492 0     0 1   sub uriAbs { $_[0]->uri($_[1])->abs->as_string; }
493              
494             ## $dir = $dbcgi->uriDir($uri?)
495             ## + returns URI up to but not including query or fragment, truncating any trailing slashes
496             sub uriDir {
497 0     0 1   my $uri = $_[0]->uri($_[1])->as_string;
498 0           $uri =~ s{[?#].*$}{};
499 0           $uri =~ s{/+[^/]*$}{};
500 0           return $uri;
501             }
502              
503             ## $auth = $dbcgi->uriAuthority($uri?)
504             ## $pquery = $dbcgi->uriPathQuery($uri?)
505             ## \@segs = $dbcgi->uriPathSegments($uri?)
506             ## $query = $dbcgi->uriQuery($uri?)
507             ## \%form = $dbcgi->uriQueryForm($uri?)
508             ## \@kws = $dbcgi->uriQueryKeywords($uri?)
509 0     0 1   sub uriAuthority { $_[0]->uri($_[1])->authority; }
510 0     0 1   sub uriPathQuery { $_[0]->uri($_[1])->path_query; }
511 0     0 1   sub uriPathSegments { [$_[0]->uri($_[1])->path_segments]; }
512 0     0 1   sub uriQuery { $_[0]->uri($_[1])->query; }
513 0     0 1   sub uriQueryForm { {$_[0]->uri($_[1])->query_form}; }
  0            
514 0     0 1   sub uriQueryKeywords { [$_[0]->uri($_[1])->query_keywords]; }
515              
516             ## $userinfo = $dbcgi->uriUserInfo($uri?)
517             ## $host = $dbcgi->uriHost($uri?)
518             ## $port = $dbcgi->uriPort($uri?)
519 0     0 1   sub userinfo { $_[0]->uri($_[1])->userinfo; }
520 0     0 1   sub uriHost { $_[0]->uri($_[1])->host; }
521 0     0 1   sub uriPort { $_[0]->uri($_[1])->port; }
522              
523             ## $uristr = quri($base, \%form)
524             sub quri {
525 0 0   0 1   shift if (isa($_[0],__PACKAGE__));
526 0           my ($base,$form)=@_;
527 0           my $uri=URI->new($base);
528 0 0         $uri->query_form($uri->query_form, map {utf8::is_utf8($_) ? Encode::encode_utf8($_) : $_} %{$form||{}});
  0 0          
  0            
529 0           return $uri->as_string;
530             }
531              
532             ## $urisub = uuri($base, \%form)
533             ## $uristr = $urisub->(\%form)
534             sub uuri {
535 0 0   0 1   shift if (isa($_[0],__PACKAGE__));
536 0           my $qbase = quri(@_);
537 0     0     return sub { quri($qbase,@_); };
  0            
538             }
539              
540             ## $sqstring = sqstring($str)
541             sub sqstring {
542 0 0   0 1   shift if (isa($_[0],__PACKAGE__));
543 0           (my $s=shift) =~ s/([\\\'])/\\$1/g; "'$s'"
  0            
544             }
545              
546             ## $str = sprintf_(...)
547             sub sprintf_ {
548 0 0   0 1   shift if (isa($_[0],__PACKAGE__));
549 0           return CORE::sprintf($_[0],@_[1..$#_]);
550             }
551              
552             ## $mtime = $dbcgi->mtime($filename)
553             sub mtime {
554 0     0 1   my $dbcgi = shift;
555 0           my $file = shift;
556 0 0         $file =~ s/^.*?=(\w+).*$/$1/ if ($file =~ /^dbi:/); ##-- trim dsns
557 0           my @stat = stat($file);
558 0           return $stat[9];
559             }
560              
561             ## $str = $dbcgi->timestamp()
562             ## + gets localtime timestamp
563             sub timestamp {
564             #my $dbcgi = shift;
565 0     0 1   return POSIX::strftime('%Y-%m-%d %H:%M:%S', localtime());
566             }
567              
568             ## $json_str = PACKAGE->to_json($data)
569             ## $json_str = PACKAGE::to_json($data)
570             ## $json_str = PACKAGE->to_json($data,\%opts)
571             ## $json_str = PACKAGE::to_json($data,\%opts)
572             sub to_json {
573 0 0   0 1   shift if (isa($_[0],__PACKAGE__));
574 0 0         return JSON::to_json($_[0]) if (@_==1);
575 0           return JSON::to_json($_[0],$_[1]);
576             }
577              
578             ## $json_str = PACKAGE->from_json($data)
579             ## $json_str = PACKAGE::from_json($data)
580             sub from_json {
581 0 0   0 1   shift if (isa($_[0],__PACKAGE__));
582 0           return JSON::from_json(@_);
583             }
584              
585             ## \@timeofday = PACKAGE->gettimeofday()
586             ## \@timeofday = PACKAGE::gettimeofday()
587             sub gettimeofday {
588 0 0   0 1   shift if (isa($_[0],__PACKAGE__));
589 0           return [Time::HiRes::gettimeofday()];
590             }
591              
592             ## $secs = PACKAGE->tv_interval($t0,$t1)
593             ## $secs = PACKAGE::tv_interval($t0,$t1)
594             sub tv_interval {
595 0 0   0 1   shift if (isa($_[0],__PACKAGE__));
596 0           return Time::HiRes::tv_interval(@_);
597             }
598              
599             ## \@timeofday = PACKAGE->t_start()
600             ## \@timeofday = PACKAGE->t_start()
601             ## + sets package variable $t_started
602             our $t_started = [Time::HiRes::gettimeofday];
603             sub t_start {
604 0 0   0 1   shift if (isa($_[0],__PACKAGE__));
605 0           $t_started = [Time::HiRes::gettimeofday];
606             }
607              
608             ## $secs = PACKAGE->t_elapsed()
609             ## $secs = PACKAGE->t_elapsed($t1)
610             ## $secs = PACKAGE->t_elapsed($t0,$t1)
611             ## $secs = PACKAGE::t_elapsed()
612             ## $secs = PACKAGE::t_elapsed($t1)
613             ## $secs = PACKAGE::t_elapsed($t0,$t1)
614             sub t_elapsed {
615 0 0   0 1   shift if (isa($_[0],__PACKAGE__));
616 0           my ($t0,$t1) = @_;
617 0 0         return tv_interval($t_started,[Time::HiRes::gettimeofday]) if (!@_);
618 0 0         return tv_interval($t_started,$_[0]) if (@_==1);
619 0           return tv_interval($_[0],$_[1]);
620             }
621              
622             ## $enc = PACKAGE->encode_utf8($str, $force=0)
623             ## $enc = PACKAGE::encode_utf8($str, $force=0)
624             ## + encodes only if $force is true or if not already flagged as a byte-string
625             sub encode_utf8 {
626 0 0   0 1   shift if (isa($_[0],__PACKAGE__));
627 0 0 0       return $_[0] if (!$_[1] && !utf8::is_utf8($_[0]));
628 0           return Encode::encode_utf8($_[0]);
629             }
630              
631             ## $enc = PACKAGE->decode_utf8($str, $force=0)
632             ## $enc = PACKAGE::decode_utf8($str, $force=0)
633             ## + decodes only if $force is true or if not flagged as a byte-string
634             sub decode_utf8 {
635 0 0   0 1   shift if (isa($_[0],__PACKAGE__));
636 0 0 0       return $_[0] if (!$_[1] && utf8::is_utf8($_[0]));
637 0           return Encode::decode_utf8($_[0]);
638             }
639              
640             1; ##-- be happy
641              
642             __END__