line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Pepper::Utilities; |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
$Pepper::Utilities::VERSION = '1.5'; |
4
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
# for utf8 support with JSON |
6
|
1
|
|
|
1
|
|
634
|
use utf8; |
|
1
|
|
|
|
|
14
|
|
|
1
|
|
|
|
|
6
|
|
7
|
1
|
|
|
1
|
|
607
|
use Encode qw( encode_utf8 ); |
|
1
|
|
|
|
|
15477
|
|
|
1
|
|
|
|
|
72
|
|
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
# for encoding and decoding JSON |
10
|
1
|
|
|
1
|
|
7
|
use Cpanel::JSON::XS; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
72
|
|
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
# for logging via logger() |
13
|
1
|
|
|
1
|
|
2052
|
use Path::Tiny; |
|
1
|
|
|
|
|
11176
|
|
|
1
|
|
|
|
|
53
|
|
14
|
1
|
|
|
1
|
|
629
|
use Data::Dumper; |
|
1
|
|
|
|
|
6218
|
|
|
1
|
|
|
|
|
61
|
|
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
# need some date/time toys |
17
|
1
|
|
|
1
|
|
507
|
use Date::Format; |
|
1
|
|
|
|
|
7894
|
|
|
1
|
|
|
|
|
69
|
|
18
|
1
|
|
|
1
|
|
1675
|
use DateTime; |
|
1
|
|
|
|
|
574271
|
|
|
1
|
|
|
|
|
49
|
|
19
|
1
|
|
|
1
|
|
1752
|
use Date::Manip::Date; |
|
1
|
|
|
|
|
85958
|
|
|
1
|
|
|
|
|
48
|
|
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
# support template toolkit templates |
22
|
1
|
|
|
1
|
|
635
|
use Template; |
|
1
|
|
|
|
|
19176
|
|
|
1
|
|
|
|
|
36
|
|
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
# for being a good person |
25
|
1
|
|
|
1
|
|
7
|
use strict; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
20
|
|
26
|
1
|
|
|
1
|
|
5
|
use warnings; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
3786
|
|
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
sub new { |
29
|
1
|
|
|
1
|
0
|
922
|
my ($class, $args) = @_; |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
# make the object |
32
|
|
|
|
|
|
|
my $self = bless { |
33
|
|
|
|
|
|
|
'request' => $$args{request}, |
34
|
|
|
|
|
|
|
'response' => $$args{response}, |
35
|
|
|
|
|
|
|
'json_coder' => Cpanel::JSON::XS->new->utf8->allow_nonref->allow_blessed, |
36
|
|
|
|
|
|
|
'config_file' => $ENV{HOME}.'/pepper/config/pepper.cfg', |
37
|
1
|
|
|
|
|
29
|
'pepper_directory' => $ENV{HOME}.'/pepper', |
38
|
|
|
|
|
|
|
}, $class; |
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
# read in the system configuration |
41
|
1
|
50
|
|
|
|
6
|
$self->read_system_configuration() if !$$args{skip_config}; |
42
|
|
|
|
|
|
|
|
43
|
1
|
|
|
|
|
3
|
return $self; |
44
|
|
|
|
|
|
|
} |
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
### START METHODS FOR GENERATING RESPONSES AND LOGS |
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
# method to deliver html & json out to the client; |
49
|
|
|
|
|
|
|
# this must be in here to be available even if not in plack mode |
50
|
|
|
|
|
|
|
sub send_response { |
51
|
0
|
|
|
0
|
0
|
0
|
my ($self, $content, $stop_here, $content_type, $content_filename) = @_; |
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
# if not in Plack/PSGI land, we will skip working with $self->{response} |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
# $content needs to be one of a text/html string, an ARRAYREF or a HASHREF |
56
|
0
|
|
|
|
|
0
|
my $ref_type = ref($content); |
57
|
|
|
|
|
|
|
|
58
|
0
|
|
|
|
|
0
|
my ($access_message, $error_id, $access_error, $die_text, $display_error_message, $html_generator, $error_html); |
59
|
|
|
|
|
|
|
|
60
|
0
|
|
0
|
|
|
0
|
$stop_here ||= 0; # don't want an uninitiated value |
61
|
0
|
0
|
0
|
|
|
0
|
if ($stop_here == 1 || $stop_here == 3) { # if $stop_here is a 1 or 3, we are stopping due to an error condition |
62
|
|
|
|
|
|
|
# if it is plain text, we should most likely log the error message sent to us |
63
|
|
|
|
|
|
|
# and just present the error ID |
64
|
|
|
|
|
|
|
# exception is if you're a developer running a script; in that case, |
65
|
|
|
|
|
|
|
# set the 'development_server' in your system configuration |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
# note access errors for display below |
68
|
0
|
0
|
|
|
|
0
|
$access_error = 1 if $content =~ /^Access\:/; |
69
|
|
|
|
|
|
|
|
70
|
0
|
0
|
|
|
|
0
|
if (length($content)) { |
71
|
0
|
|
|
|
|
0
|
$error_id = $self->logger($content,'fatals'); # 'these errors go into the 'fatals' log |
72
|
|
|
|
|
|
|
# send an accurate response code |
73
|
0
|
|
|
|
|
0
|
$self->{response}->status(500); |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
# unless we are on the dev server or it's the no-app message, present the error ID instead |
76
|
0
|
0
|
0
|
|
|
0
|
if ($self->{config}{development_server} eq 'Y' || $content =~ /^No application exists/) { |
77
|
0
|
|
|
|
|
0
|
$display_error_message = $content; |
78
|
|
|
|
|
|
|
# need period at the end |
79
|
0
|
0
|
|
|
|
0
|
$display_error_message .= '.' if $display_error_message !~ /(\.|\?|\!)$/; |
80
|
|
|
|
|
|
|
} else { # hide the error |
81
|
0
|
|
|
|
|
0
|
$content = 'Execution failed; error ID: '.$error_id."\n"; |
82
|
0
|
|
|
|
|
0
|
$ref_type = ''; # make sure it gets treated as plain text; |
83
|
|
|
|
|
|
|
} |
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
# if we are in API mode, let's send back JSON |
86
|
0
|
0
|
|
|
|
0
|
if ($self->{auth_token}) { |
|
|
0
|
|
|
|
|
|
87
|
0
|
|
|
|
|
0
|
$ref_type = "HASH" ; |
88
|
0
|
|
|
|
|
0
|
$content = { |
89
|
|
|
|
|
|
|
'status' => 'Error', |
90
|
|
|
|
|
|
|
'error_id' => $error_id, |
91
|
|
|
|
|
|
|
'display_error_message' => $display_error_message, |
92
|
|
|
|
|
|
|
}; |
93
|
|
|
|
|
|
|
# developers see the actual message |
94
|
0
|
0
|
|
|
|
0
|
$$content{display_error_message} = $display_error_message if $display_error_message; |
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
# if we are in Web UI mode, pipe it out to the user as HTML; |
97
|
|
|
|
|
|
|
} elsif ($self->{request}) { |
98
|
|
|
|
|
|
|
|
99
|
0
|
|
|
|
|
0
|
$self->send_response($content); |
100
|
|
|
|
|
|
|
|
101
|
0
|
0
|
|
|
|
0
|
if ($self->{db}) { # if we connected to the DB, end our transaction |
102
|
0
|
|
|
|
|
0
|
$self->{db}->do_sql('rollback'); |
103
|
|
|
|
|
|
|
} |
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
# do not continue if in the inner eval{} loop |
106
|
0
|
0
|
|
|
|
0
|
if ($stop_here == 1) { |
107
|
0
|
|
|
|
|
0
|
die 'Execution stopped: '.$content; |
108
|
|
|
|
|
|
|
} else { # if $stop_here == 3, then we are in a 'superfatal' from pepper.psgi |
109
|
0
|
|
|
|
|
0
|
return; |
110
|
|
|
|
|
|
|
} |
111
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
} |
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
} |
115
|
|
|
|
|
|
|
} |
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
# if they sent a valid content type, no need to change it |
118
|
0
|
0
|
0
|
|
|
0
|
if ($content_type && $content_type =~ /\//) { |
|
|
0
|
0
|
|
|
|
|
|
|
0
|
0
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
119
|
|
|
|
|
|
|
# nothing to do here |
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
} elsif ($ref_type eq "HASH" || $ref_type eq "ARRAY") { # make it into json |
122
|
0
|
|
|
|
|
0
|
$content_type = 'application/json'; |
123
|
0
|
|
|
|
|
0
|
$content = $self->json_from_perl($content); |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
} elsif ($content =~ /^\/\/ This is Javascript./) { # it is 99% likely to be Javascript |
126
|
0
|
|
|
|
|
0
|
$content_type = 'text/javascript'; |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
} elsif ($content =~ /^\/\* This is CSS./) { # it is 99% likely to be CSS |
129
|
0
|
|
|
|
|
0
|
$content_type = 'text/css'; |
130
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
} elsif ($content =~ /<\S+>/) { # it is 99% likely to be HTML |
132
|
0
|
|
|
|
|
0
|
$content_type = 'text/html'; |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
} elsif (!$ref_type && length($content)) { # it is plain text |
135
|
0
|
|
|
|
|
0
|
$content_type = 'text/plain'; |
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
} else { # anything else? something of a mistake, panic a little |
138
|
0
|
|
|
|
|
0
|
$content_type = 'text/plain'; |
139
|
0
|
|
|
|
|
0
|
$content = 'ERROR: The resulting content was not deliverable.'; |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
} |
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
# if in Plack, pack the response for delivery |
144
|
0
|
0
|
|
|
|
0
|
if ($self->{response}) { |
145
|
0
|
|
|
|
|
0
|
$self->{response}->content_type($content_type); |
146
|
|
|
|
|
|
|
# is this an error? Change from 200 to 500, if not done so already |
147
|
0
|
0
|
0
|
|
|
0
|
if ($content =~ /^(ERROR|Execution failed)/ && $self->{response}->status() eq '200') { |
148
|
0
|
|
|
|
|
0
|
$self->{response}->status(500); |
149
|
|
|
|
|
|
|
} |
150
|
0
|
0
|
0
|
|
|
0
|
if ($content_filename && $content_type !~ /^image/) { |
151
|
0
|
|
|
|
|
0
|
$self->{response}->header('Content-Disposition' => 'attachment; filename="'.$content_filename.'"'); |
152
|
|
|
|
|
|
|
} |
153
|
0
|
|
|
|
|
0
|
$self->{response}->body($content); |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
} else { # print to stdout |
156
|
0
|
|
|
|
|
0
|
print $content; |
157
|
|
|
|
|
|
|
} |
158
|
|
|
|
|
|
|
|
159
|
0
|
0
|
|
|
|
0
|
if ($stop_here == 1) { # if they want us to stop here, do so; we should be in an eval{} loop to catch this |
160
|
0
|
|
|
|
|
0
|
$die_text = "Execution stopped."; |
161
|
0
|
0
|
|
|
|
0
|
$die_text .= '; Error ID: '.$error_id if $error_id; |
162
|
0
|
0
|
|
|
|
0
|
$self->{db}->do_sql('rollback') if $self->{db}; # end our transaction |
163
|
0
|
|
|
|
|
0
|
die $die_text; |
164
|
|
|
|
|
|
|
} |
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
} |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
# subroutine to process a template via template toolkit |
169
|
|
|
|
|
|
|
# this is for server-side processing of templates |
170
|
|
|
|
|
|
|
sub template_process { |
171
|
1
|
|
|
1
|
0
|
4
|
my ($self, $args) = @_; |
172
|
|
|
|
|
|
|
# $$args can contain: include_path, template_file, template_text, template_vars, send_out, save_file, stop_here |
173
|
|
|
|
|
|
|
# it *must* include either template_text or template_file |
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
# declare vars |
176
|
1
|
|
|
|
|
2
|
my ($output, $tt, $tt_error); |
177
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
# default include path |
179
|
1
|
50
|
|
|
|
4
|
if (!$$args{include_path}) { |
|
|
0
|
|
|
|
|
|
180
|
1
|
|
|
|
|
4
|
$$args{include_path} = $self->{pepper_directory}.'/template/'; |
181
|
|
|
|
|
|
|
} elsif ($$args{include_path} !~ /\/$/) { # make sure of trailing / |
182
|
0
|
|
|
|
|
0
|
$$args{include_path} .= '/'; |
183
|
|
|
|
|
|
|
} |
184
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
# $$args{tag_style} = 'star', 'template' or similiar |
186
|
|
|
|
|
|
|
# see https://metacpan.org/pod/Template#TAG_STYLE |
187
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
# default tag_style to regular, [% %] |
189
|
1
|
|
50
|
|
|
7
|
$$args{tag_style} ||= 'template'; |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
# crank up the template toolkit object, and set it up to save to the $output variable |
192
|
1
|
|
|
|
|
2
|
$output = ''; |
193
|
|
|
|
|
|
|
$tt = Template->new({ |
194
|
|
|
|
|
|
|
ENCODING => 'utf8', |
195
|
|
|
|
|
|
|
INCLUDE_PATH => $$args{include_path}, |
196
|
|
|
|
|
|
|
OUTPUT => \$output, |
197
|
|
|
|
|
|
|
TAG_STYLE => $$args{tag_style}, |
198
|
1
|
|
33
|
|
|
21
|
}) || $self->send_response("$Template::ERROR",1); |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
# process the template |
201
|
1
|
50
|
|
|
|
23451
|
if ($$args{template_file}) { |
|
|
50
|
|
|
|
|
|
202
|
0
|
|
|
|
|
0
|
$tt->process( $$args{template_file}, $$args{template_vars}, $output, {binmode => ':encoding(utf8)'} ); |
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
} elsif ($$args{template_text}) { |
205
|
1
|
|
|
|
|
8
|
$tt->process( \$$args{template_text}, $$args{template_vars}, $output, {binmode => ':encoding(utf8)'} ); |
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
} else { # one or the other |
208
|
0
|
|
|
|
|
0
|
$self->send_response("Error: you must provide either template_file or template_text",1); |
209
|
|
|
|
|
|
|
} |
210
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
# make sure to throw error if there is one |
212
|
1
|
|
|
|
|
29769
|
$tt_error = $tt->error(); |
213
|
1
|
50
|
|
|
|
18
|
$self->send_response("Template Error in $$args{template_file}: $tt_error",1) if $tt_error; |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
# send it out to the client, save to the filesystem, or return to the caller |
216
|
1
|
50
|
|
|
|
6
|
if ($$args{send_out}) { # output to the client |
|
|
50
|
|
|
|
|
|
217
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
# the '2' tells mr_zebra to avoid logging an error |
219
|
0
|
|
|
|
|
0
|
$self->send_response($output,2); |
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
} elsif ($$args{save_file}) { # save to the filesystem |
222
|
0
|
|
|
|
|
0
|
$self->filer( $$args{save_file}, 'write', $output); |
223
|
0
|
|
|
|
|
0
|
return $$args{save_file}; # just kick back the file name |
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
} else { # just return |
226
|
1
|
|
|
|
|
5
|
return $output; |
227
|
|
|
|
|
|
|
} |
228
|
|
|
|
|
|
|
} |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
# method to log messages under the 'log' directory |
231
|
|
|
|
|
|
|
sub logger { |
232
|
|
|
|
|
|
|
# takes three args: the message itself (required), the log_type (optional, one word), |
233
|
|
|
|
|
|
|
# and an optional log location/directory |
234
|
0
|
|
|
0
|
0
|
0
|
my ($self, $log_message, $log_type, $log_directory) = @_; |
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
# return if no message sent; no point |
237
|
0
|
0
|
|
|
|
0
|
return if !$log_message; |
238
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
# default is 'errors' log type |
240
|
0
|
|
0
|
|
|
0
|
$log_type ||= 'errors'; |
241
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
# no spaces or special chars in that $log_type |
243
|
0
|
|
|
|
|
0
|
$log_type =~ s/[^a-z0-9\_]//gi; |
244
|
|
|
|
|
|
|
|
245
|
0
|
|
|
|
|
0
|
my ($error_id, $todays_date, $current_time, $log_file, $now); |
246
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
# how about a nice error ID |
248
|
0
|
|
|
|
|
0
|
$error_id = $self->random_string(15); |
249
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
# what is today's date and current time |
251
|
0
|
|
|
|
|
0
|
$now = time(); # this is the unix epoch / also a quick-find id of the error |
252
|
0
|
|
|
|
|
0
|
$todays_date = $self->time_to_date($now,'to_date_db','utc'); |
253
|
0
|
|
|
|
|
0
|
$current_time = $self->time_to_date($now,'to_datetime_iso','utc'); |
254
|
0
|
|
|
|
|
0
|
$current_time =~ s/\s//g; # no spaces |
255
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
# target log file - did they provide a target log_directory? |
257
|
0
|
0
|
0
|
|
|
0
|
if ($log_directory && -d $log_directory) { # yes |
258
|
0
|
|
|
|
|
0
|
$log_file = $log_directory.'/'.$log_type.'-'.$todays_date.'.log'; |
259
|
|
|
|
|
|
|
} else { # nope, take default |
260
|
0
|
|
|
|
|
0
|
$log_file = $self->{pepper_directory}.'/log/'.$log_type.'-'.$todays_date.'.log'; |
261
|
|
|
|
|
|
|
} |
262
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
# sometimes time() adds a \n |
264
|
0
|
|
|
|
|
0
|
$log_message =~ s/\n//; |
265
|
|
|
|
|
|
|
|
266
|
|
|
|
|
|
|
# if they sent a hash or array, it's a developer doing testing. use Dumper() to output it |
267
|
0
|
0
|
0
|
|
|
0
|
if (ref($log_message) eq 'HASH' || ref($log_message) eq 'ARRAY') { |
268
|
0
|
|
|
|
|
0
|
$log_message = Dumper($log_message); |
269
|
|
|
|
|
|
|
} |
270
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
# if we have the plack object (created via pack_luggage()), append to the $log_message |
272
|
0
|
0
|
|
|
|
0
|
if ($self->{request}) { |
273
|
0
|
|
|
|
|
0
|
$log_message .= ' | https://'.$self->{request}->env->{HTTP_HOST}.$self->{request}->request_uri(); |
274
|
|
|
|
|
|
|
} |
275
|
|
|
|
|
|
|
|
276
|
|
|
|
|
|
|
# append to our log file via Path::Tiny |
277
|
0
|
|
|
|
|
0
|
path($log_file)->append_raw( 'ID: '.$error_id.' | '.$current_time.': '.$log_message."\n" ); |
278
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
# return the code/epoch for an innocent-looking display and for fast lookup |
280
|
0
|
|
|
|
|
0
|
return $error_id; |
281
|
|
|
|
|
|
|
} |
282
|
|
|
|
|
|
|
|
283
|
|
|
|
|
|
|
### START GENERAL UTILITIES |
284
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
# simple routine to get a DateTime object for a timestamp, e.g. 2016-09-04 16:30 |
286
|
|
|
|
|
|
|
sub get_datetime_object { |
287
|
1
|
|
|
1
|
0
|
4
|
my ($self, $time_string, $time_zone_name) = @_; |
288
|
|
|
|
|
|
|
|
289
|
|
|
|
|
|
|
# default timezone is New York |
290
|
1
|
|
|
|
|
2
|
$time_zone_name = $self->{time_zone_name}; |
291
|
1
|
|
50
|
|
|
3
|
$time_zone_name ||= 'America/New_York'; |
292
|
|
|
|
|
|
|
|
293
|
1
|
|
|
|
|
2
|
my ($dt, $year, $month, $day, $hour, $minute, $second); |
294
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
# be willing to just accept the date and presume midnight |
296
|
1
|
50
|
|
|
|
7
|
if ($time_string =~ /^\d{4}-\d{2}-\d{2}$/) { |
297
|
0
|
|
|
|
|
0
|
$time_string .= ' 00:00:00'; |
298
|
|
|
|
|
|
|
} |
299
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
# i will generally just send minutes; we want to support seconds too, and default to 00 seconds |
301
|
1
|
50
|
|
|
|
5
|
if ($time_string =~ /\s\d{2}:\d{2}$/) { |
302
|
1
|
|
|
|
|
3
|
$time_string .= ':00'; |
303
|
|
|
|
|
|
|
} |
304
|
|
|
|
|
|
|
|
305
|
|
|
|
|
|
|
# if that timestring is not right, just get one for 'now' |
306
|
1
|
50
|
|
|
|
6
|
if ($time_string !~ /^\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}$/) { |
307
|
|
|
|
|
|
|
|
308
|
0
|
|
|
|
|
0
|
$dt = DateTime->from_epoch( |
309
|
|
|
|
|
|
|
epoch => time(), |
310
|
|
|
|
|
|
|
time_zone => $time_zone_name, |
311
|
|
|
|
|
|
|
); |
312
|
|
|
|
|
|
|
|
313
|
|
|
|
|
|
|
# otherwise, get a custom datetime object |
314
|
|
|
|
|
|
|
} else { |
315
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
# have to slice-and-dice it a bit to make sure DateTime is happy |
317
|
1
|
|
|
|
|
3
|
$time_string =~ s/-0/-/g; |
318
|
1
|
|
|
|
|
10
|
($year,$month,$day,$hour,$minute,$second) = split /-|\s|:/, $time_string; |
319
|
1
|
|
|
|
|
4
|
$hour =~ s/^0//; |
320
|
1
|
|
|
|
|
3
|
$minute =~ s/^0//; |
321
|
|
|
|
|
|
|
|
322
|
|
|
|
|
|
|
# try to set up the DateTime object, wrapping in eval in case they send an invalid time |
323
|
|
|
|
|
|
|
# (which happens if you go for 2am on a 'spring-forward' day |
324
|
1
|
|
|
|
|
3
|
eval { |
325
|
1
|
|
|
|
|
11
|
$dt = DateTime->new( |
326
|
|
|
|
|
|
|
year => $year, |
327
|
|
|
|
|
|
|
month => $month, |
328
|
|
|
|
|
|
|
day => $day, |
329
|
|
|
|
|
|
|
hour => $hour, |
330
|
|
|
|
|
|
|
minute => $minute, |
331
|
|
|
|
|
|
|
second => $second, |
332
|
|
|
|
|
|
|
time_zone => $time_zone_name, |
333
|
|
|
|
|
|
|
); |
334
|
|
|
|
|
|
|
}; |
335
|
|
|
|
|
|
|
|
336
|
1
|
50
|
|
|
|
716
|
if ($@) { # if they called for an invalid time, just move ahead and hour and try again |
337
|
0
|
|
|
|
|
0
|
$hour++; |
338
|
0
|
|
|
|
|
0
|
$dt = DateTime->new( |
339
|
|
|
|
|
|
|
year => $year, |
340
|
|
|
|
|
|
|
month => $month, |
341
|
|
|
|
|
|
|
day => $day, |
342
|
|
|
|
|
|
|
hour => $hour, |
343
|
|
|
|
|
|
|
minute => $minute, |
344
|
|
|
|
|
|
|
second => $second, |
345
|
|
|
|
|
|
|
time_zone => $time_zone_name, |
346
|
|
|
|
|
|
|
); |
347
|
|
|
|
|
|
|
} |
348
|
|
|
|
|
|
|
|
349
|
|
|
|
|
|
|
} |
350
|
|
|
|
|
|
|
|
351
|
|
|
|
|
|
|
# send it out |
352
|
1
|
|
|
|
|
2
|
return $dt; |
353
|
|
|
|
|
|
|
} |
354
|
|
|
|
|
|
|
|
355
|
|
|
|
|
|
|
# method to read/write/append to a file via Path::Tiny |
356
|
|
|
|
|
|
|
sub filer { |
357
|
|
|
|
|
|
|
# required arg is the full path to the file |
358
|
|
|
|
|
|
|
# optional second arg is the operation: read, write, or append. default to 'read' |
359
|
|
|
|
|
|
|
# optional third arg is the content for write or append operations |
360
|
0
|
|
|
0
|
0
|
0
|
my ($self, $file_location, $operation, $content) = @_; |
361
|
|
|
|
|
|
|
|
362
|
|
|
|
|
|
|
# return if no good file path |
363
|
0
|
0
|
|
|
|
0
|
return if !$file_location; |
364
|
|
|
|
|
|
|
|
365
|
|
|
|
|
|
|
# default operation is 'read' |
366
|
0
|
0
|
0
|
|
|
0
|
$operation = 'read' if !$operation || $operation !~ /read|write|append|basename/; |
367
|
|
|
|
|
|
|
|
368
|
|
|
|
|
|
|
# return if write or append and no content |
369
|
0
|
0
|
0
|
|
|
0
|
return if $operation !~ /read|basename/ && !$content; |
370
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
# do the operations |
372
|
0
|
0
|
|
|
|
0
|
if ($operation eq 'read') { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
373
|
|
|
|
|
|
|
|
374
|
0
|
|
|
|
|
0
|
$content = path($file_location)->slurp_raw; |
375
|
0
|
|
|
|
|
0
|
return $content; |
376
|
|
|
|
|
|
|
|
377
|
|
|
|
|
|
|
} elsif ($operation eq 'write') { |
378
|
|
|
|
|
|
|
|
379
|
0
|
|
|
|
|
0
|
path($file_location)->spew_raw( $content ); |
380
|
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
} elsif ($operation eq 'append') { |
382
|
|
|
|
|
|
|
|
383
|
|
|
|
|
|
|
# make sure the new content ends with a \n |
384
|
0
|
0
|
|
|
|
0
|
$content .= "\n" if $content !~ /\n$/; |
385
|
|
|
|
|
|
|
|
386
|
0
|
|
|
|
|
0
|
path($file_location)->append_raw( $content ); |
387
|
|
|
|
|
|
|
|
388
|
|
|
|
|
|
|
} elsif ($operation eq 'basename') { |
389
|
|
|
|
|
|
|
|
390
|
0
|
|
|
|
|
0
|
return path($file_location)->basename; |
391
|
|
|
|
|
|
|
} |
392
|
|
|
|
|
|
|
|
393
|
|
|
|
|
|
|
} |
394
|
|
|
|
|
|
|
|
395
|
|
|
|
|
|
|
|
396
|
|
|
|
|
|
|
# two json translating methods using the great JSON module |
397
|
|
|
|
|
|
|
# First, make perl data structures into JSON objects |
398
|
|
|
|
|
|
|
sub json_from_perl { |
399
|
1
|
|
|
1
|
0
|
544
|
my ($self, $data_ref) = @_; |
400
|
|
|
|
|
|
|
|
401
|
|
|
|
|
|
|
# for this, we shall go UTF8 |
402
|
1
|
|
|
|
|
39
|
return $self->{json_coder}->encode( $data_ref ); |
403
|
|
|
|
|
|
|
} |
404
|
|
|
|
|
|
|
|
405
|
|
|
|
|
|
|
# Second, make JSON objects into Perl structures |
406
|
|
|
|
|
|
|
sub json_to_perl { |
407
|
1
|
|
|
1
|
0
|
7
|
my ($self, $json_text) = @_; |
408
|
|
|
|
|
|
|
|
409
|
|
|
|
|
|
|
# first, let's try via UTF-8 decoding |
410
|
1
|
|
|
|
|
9
|
my $json_text_ut8 = encode_utf8( $json_text ); |
411
|
1
|
|
|
|
|
3
|
my $perl_hashref = {}; |
412
|
1
|
|
|
|
|
2
|
eval { |
413
|
1
|
|
|
|
|
18
|
$perl_hashref = $self->{json_coder}->decode( $json_text_ut8 ); |
414
|
|
|
|
|
|
|
}; |
415
|
|
|
|
|
|
|
|
416
|
1
|
|
|
|
|
3
|
return $perl_hashref; |
417
|
|
|
|
|
|
|
} |
418
|
|
|
|
|
|
|
|
419
|
|
|
|
|
|
|
# utility to generate a random string |
420
|
|
|
|
|
|
|
sub random_string { |
421
|
0
|
|
|
0
|
0
|
0
|
my ($self, $length, $numbers_only) = @_; |
422
|
|
|
|
|
|
|
|
423
|
|
|
|
|
|
|
# default that to 10 |
424
|
0
|
|
0
|
|
|
0
|
$length ||= 10; |
425
|
|
|
|
|
|
|
|
426
|
0
|
|
|
|
|
0
|
my (@chars,$string); |
427
|
|
|
|
|
|
|
|
428
|
0
|
0
|
|
|
|
0
|
if ($numbers_only) { # what they want... |
429
|
0
|
|
|
|
|
0
|
@chars = ('0'..'9'); |
430
|
|
|
|
|
|
|
} else { # both |
431
|
0
|
|
|
|
|
0
|
@chars = ('0'..'9', 'A'..'F'); |
432
|
|
|
|
|
|
|
} |
433
|
|
|
|
|
|
|
|
434
|
0
|
|
|
|
|
0
|
while ($length--) { |
435
|
0
|
|
|
|
|
0
|
$string .= $chars[rand @chars] |
436
|
|
|
|
|
|
|
}; |
437
|
|
|
|
|
|
|
|
438
|
0
|
|
|
|
|
0
|
return $string; |
439
|
|
|
|
|
|
|
} |
440
|
|
|
|
|
|
|
|
441
|
|
|
|
|
|
|
|
442
|
|
|
|
|
|
|
# method to read a JSON file into a hashref |
443
|
|
|
|
|
|
|
sub read_json_file { |
444
|
0
|
|
|
0
|
0
|
0
|
my ($self, $json_file_path) = @_; |
445
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
# we shall give them an empty hashref if nothing else |
447
|
0
|
0
|
0
|
|
|
0
|
return {} if !$json_file_path || !(-e $json_file_path); |
448
|
|
|
|
|
|
|
|
449
|
0
|
|
|
|
|
0
|
my $json_content = $self->filer($json_file_path); |
450
|
|
|
|
|
|
|
|
451
|
0
|
0
|
|
|
|
0
|
return {} if !$json_content; |
452
|
|
|
|
|
|
|
|
453
|
0
|
|
|
|
|
0
|
return $self->json_to_perl($json_content); |
454
|
|
|
|
|
|
|
|
455
|
|
|
|
|
|
|
} |
456
|
|
|
|
|
|
|
|
457
|
|
|
|
|
|
|
# method to save JSON into a file |
458
|
|
|
|
|
|
|
sub write_json_file { |
459
|
0
|
|
|
0
|
0
|
0
|
my ($self, $json_file_path, $data_structure) = @_; |
460
|
|
|
|
|
|
|
|
461
|
0
|
0
|
0
|
|
|
0
|
return if !$json_file_path || ref($data_structure) !~ /ARRAY|HASH/; |
462
|
|
|
|
|
|
|
|
463
|
|
|
|
|
|
|
# writing one liners like this does not make me feel beautiful |
464
|
0
|
|
|
|
|
0
|
$self->filer($json_file_path, 'write', $self->json_from_perl($data_structure) ); |
465
|
|
|
|
|
|
|
|
466
|
|
|
|
|
|
|
} |
467
|
|
|
|
|
|
|
|
468
|
|
|
|
|
|
|
# start the timeToDate method, where we convert between UNIX timestamps and human-friendly dates |
469
|
|
|
|
|
|
|
sub time_to_date { |
470
|
|
|
|
|
|
|
# declare vars & grab args |
471
|
1
|
|
|
1
|
0
|
7
|
my ($self, $timestamp, $task, $time_zone_name) = @_; |
472
|
1
|
|
|
|
|
3
|
my ($day, $dt, $diff, $month, $templ, $year); |
473
|
|
|
|
|
|
|
|
474
|
|
|
|
|
|
|
# default timezone to UTC if no timezone sent or set |
475
|
|
|
|
|
|
|
# if they sent a 'utc', force it to be Etc/GMT -- this is for the logger |
476
|
1
|
50
|
33
|
|
|
5
|
$time_zone_name = 'Etc/GMT' if !$time_zone_name || $time_zone_name eq 'utc'; |
477
|
|
|
|
|
|
|
|
478
|
|
|
|
|
|
|
# allow them to set a default time zone by setting $pepper->{utilities}{time_zone_name} |
479
|
|
|
|
|
|
|
# or $ENV{PERL_DATETIME_DEFAULT_TZ} |
480
|
1
|
|
0
|
|
|
3
|
$time_zone_name ||= $self->{time_zone_name} || $ENV{PERL_DATETIME_DEFAULT_TZ}; |
|
|
|
33
|
|
|
|
|
481
|
|
|
|
|
|
|
|
482
|
|
|
|
|
|
|
# set the time zone if not set |
483
|
1
|
|
33
|
|
|
13
|
$self->{time_zone_name} ||= $time_zone_name; |
484
|
|
|
|
|
|
|
|
485
|
|
|
|
|
|
|
# fix up timestamp as necessary |
486
|
1
|
50
|
|
|
|
6
|
if (!$timestamp) { # empty timestamp --> default to current timestamp |
|
|
50
|
|
|
|
|
|
487
|
0
|
|
|
|
|
0
|
$timestamp = time(); |
488
|
|
|
|
|
|
|
} elsif ($timestamp =~ /\,/) { # human date...make it YYYY-MM-DD |
489
|
0
|
|
|
|
|
0
|
($month,$day,$year) = split /\s/, $timestamp; # get its pieces |
490
|
|
|
|
|
|
|
# turn the month into a proper number |
491
|
0
|
0
|
|
|
|
0
|
if ($month =~ /Jan/) { $month = "01"; |
|
0
|
0
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
492
|
0
|
|
|
|
|
0
|
} elsif ($month =~ /Feb/) { $month = "02"; |
493
|
0
|
|
|
|
|
0
|
} elsif ($month =~ /Mar/) { $month = "03"; |
494
|
0
|
|
|
|
|
0
|
} elsif ($month =~ /Apr/) { $month = "04"; |
495
|
0
|
|
|
|
|
0
|
} elsif ($month =~ /May/) { $month = "05"; |
496
|
0
|
|
|
|
|
0
|
} elsif ($month =~ /Jun/) { $month = "06"; |
497
|
0
|
|
|
|
|
0
|
} elsif ($month =~ /Jul/) { $month = "07"; |
498
|
0
|
|
|
|
|
0
|
} elsif ($month =~ /Aug/) { $month = "08"; |
499
|
0
|
|
|
|
|
0
|
} elsif ($month =~ /Sep/) { $month = "09"; |
500
|
0
|
|
|
|
|
0
|
} elsif ($month =~ /Oct/) { $month = "10"; |
501
|
0
|
|
|
|
|
0
|
} elsif ($month =~ /Nov/) { $month = "11"; |
502
|
0
|
|
|
|
|
0
|
} elsif ($month =~ /Dec/) { $month = "12"; } |
503
|
|
|
|
|
|
|
|
504
|
|
|
|
|
|
|
# remove the comma from the date and make sure it has two digits |
505
|
0
|
|
|
|
|
0
|
$day =~ s/\,//; |
506
|
0
|
0
|
|
|
|
0
|
$day = '0'.$day if $day < 10; |
507
|
|
|
|
|
|
|
|
508
|
0
|
|
|
|
|
0
|
$timestamp = $year.'-'.$month.'-'.$day; |
509
|
|
|
|
|
|
|
|
510
|
|
|
|
|
|
|
} |
511
|
|
|
|
|
|
|
# if they passed a YYYY-MM-DD date, also we will get a DateTime object |
512
|
|
|
|
|
|
|
|
513
|
|
|
|
|
|
|
# need that epoch if a date string was set / parsed |
514
|
1
|
50
|
33
|
|
|
9
|
if ($month || $timestamp =~ /-/) { |
515
|
1
|
|
|
|
|
6
|
$dt = $self->get_datetime_object($timestamp.' 00:00',$time_zone_name); |
516
|
1
|
|
|
|
|
6
|
$timestamp = $dt->epoch; |
517
|
1
|
|
|
|
|
31
|
$time_zone_name = 'Etc/GMT'; # don't offset dates, only timestamps |
518
|
|
|
|
|
|
|
} |
519
|
|
|
|
|
|
|
|
520
|
|
|
|
|
|
|
# default task is the epoch for the first second of the day |
521
|
1
|
|
50
|
|
|
4
|
$task ||= 'to_unix_start'; |
522
|
|
|
|
|
|
|
|
523
|
|
|
|
|
|
|
# proceed based on $task |
524
|
1
|
50
|
33
|
|
|
47
|
if ($task eq "to_unix_start") { # date to unix timestamp -- start of the day |
|
|
50
|
33
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
525
|
0
|
|
|
|
|
0
|
return $timestamp; # already done above |
526
|
|
|
|
|
|
|
} elsif ($task eq "to_unix_end") { # date to unix timestamp -- end of the day |
527
|
0
|
|
|
|
|
0
|
return ($timestamp + 86399); # most done above |
528
|
|
|
|
|
|
|
} elsif ($task eq "to_date_db") { # unix timestamp to db-date (YYYY-MM-DD) |
529
|
0
|
|
|
|
|
0
|
$templ = '%Y-%m-%d'; |
530
|
|
|
|
|
|
|
} elsif (!$task || $task eq "to_date_human") { # unix timestamp to human date (Mon DD, YYYY) |
531
|
0
|
|
|
|
|
0
|
($diff) = ($timestamp - time())/15552000; # drop the year if within the last six months |
532
|
0
|
0
|
0
|
|
|
0
|
if ($diff > -1 && $diff < 1) { |
533
|
0
|
|
|
|
|
0
|
$templ = '%B %e'; |
534
|
|
|
|
|
|
|
} else { |
535
|
0
|
|
|
|
|
0
|
$templ = '%B %e, %Y'; |
536
|
|
|
|
|
|
|
} |
537
|
|
|
|
|
|
|
} elsif ($task eq "to_date_human_full") { # force YYYY in above |
538
|
0
|
|
|
|
|
0
|
$templ = '%B %e, %Y'; |
539
|
|
|
|
|
|
|
} elsif ($task eq "to_date_human_abbrev") { # shorter month name in above |
540
|
0
|
|
|
|
|
0
|
$templ = '%b %e, %Y'; |
541
|
|
|
|
|
|
|
} elsif ($task eq "to_date_human_dayname") { # unix timestamp to human date (DayOfWeekName, Mon DD, YYYY) |
542
|
0
|
|
|
|
|
0
|
($diff) = ($timestamp - time())/15552000; # drop the year if within the last six months |
543
|
0
|
0
|
0
|
|
|
0
|
if ($diff > -1 && $diff < 1) { |
544
|
0
|
|
|
|
|
0
|
$templ = '%A, %b %e'; |
545
|
|
|
|
|
|
|
} else { |
546
|
0
|
|
|
|
|
0
|
$templ = '%A, %b %e, %Y'; |
547
|
|
|
|
|
|
|
} |
548
|
|
|
|
|
|
|
} elsif ($task eq "to_year") { # just want year |
549
|
0
|
|
|
|
|
0
|
$templ = '%Y'; |
550
|
|
|
|
|
|
|
} elsif ($task eq "to_month" || $task eq "to_month_name") { # unix timestamp to month name (Month YYYY) |
551
|
0
|
|
|
|
|
0
|
$templ = '%B %Y'; |
552
|
|
|
|
|
|
|
} elsif ($task eq "to_month_abbrev") { # unix timestamp to month abreviation (MonYY, i.e. Sep15) |
553
|
0
|
|
|
|
|
0
|
$templ = '%b%y'; |
554
|
|
|
|
|
|
|
} elsif ($task eq "to_date_human_time") { # unix timestamp to human date with time (Mon DD, YYYY at HH:MM:SS XM) |
555
|
0
|
|
|
|
|
0
|
($diff) = ($timestamp - time())/31536000; |
556
|
0
|
0
|
0
|
|
|
0
|
if ($diff >= -1 && $diff <= 1) { |
557
|
0
|
|
|
|
|
0
|
$templ = '%b %e at %l:%M%P'; |
558
|
|
|
|
|
|
|
} else { |
559
|
0
|
|
|
|
|
0
|
$templ = '%b %e, %Y at %l:%M%P'; |
560
|
|
|
|
|
|
|
} |
561
|
|
|
|
|
|
|
} elsif ($task eq "to_just_human_time") { # unix timestamp to humantime (HH:MM:SS XM) |
562
|
0
|
|
|
|
|
0
|
$templ = '%l:%M%P'; |
563
|
|
|
|
|
|
|
} elsif ($task eq "to_just_military_time") { # unix timestamp to military time |
564
|
0
|
|
|
|
|
0
|
$templ = '%R'; |
565
|
|
|
|
|
|
|
} elsif ($task eq "to_datetime_iso") { # ISO-formatted timestamp, i.e. 2016-09-04T16:12:00+00:00 |
566
|
0
|
|
|
|
|
0
|
$templ = '%Y-%m-%dT%X%z'; |
567
|
|
|
|
|
|
|
} elsif ($task eq "to_day_of_week") { # epoch to day of the week, like 'Saturday' |
568
|
1
|
|
|
|
|
3
|
$templ = '%A'; |
569
|
|
|
|
|
|
|
} elsif ($task eq "to_day_of_week_numeric") { # 0..6 day of the week |
570
|
0
|
|
|
|
|
0
|
$templ = '%w'; |
571
|
|
|
|
|
|
|
} |
572
|
|
|
|
|
|
|
|
573
|
|
|
|
|
|
|
# if they sent a time zone, offset the timestamp epoch appropriately |
574
|
1
|
50
|
|
|
|
3
|
if ($time_zone_name ne 'Etc/GMT') { |
575
|
|
|
|
|
|
|
# have we cached this? |
576
|
0
|
0
|
|
|
|
0
|
if (!$self->{tz_offsets}{$time_zone_name}) { |
577
|
0
|
|
|
|
|
0
|
$dt = DateTime->from_epoch( |
578
|
|
|
|
|
|
|
epoch => $timestamp, |
579
|
|
|
|
|
|
|
time_zone => $time_zone_name, |
580
|
|
|
|
|
|
|
); |
581
|
0
|
|
|
|
|
0
|
$self->{tz_offsets}{$time_zone_name} = $dt->offset; |
582
|
|
|
|
|
|
|
} |
583
|
|
|
|
|
|
|
|
584
|
|
|
|
|
|
|
# apply the offset |
585
|
0
|
|
|
|
|
0
|
$timestamp += $self->{tz_offsets}{$time_zone_name}; |
586
|
|
|
|
|
|
|
} |
587
|
|
|
|
|
|
|
|
588
|
|
|
|
|
|
|
# now run the conversion |
589
|
1
|
|
|
|
|
6
|
$timestamp = time2str($templ, $timestamp,'GMT'); |
590
|
1
|
|
|
|
|
166
|
$timestamp =~ s/ / /g; # remove double spaces; |
591
|
1
|
|
|
|
|
5
|
$timestamp =~ s/GMT //; |
592
|
1
|
|
|
|
|
16
|
return $timestamp; |
593
|
|
|
|
|
|
|
} |
594
|
|
|
|
|
|
|
|
595
|
|
|
|
|
|
|
### START METHODS FOR pepper setup |
596
|
|
|
|
|
|
|
|
597
|
|
|
|
|
|
|
# loads up $self->{config}; auto-called via new() above |
598
|
|
|
|
|
|
|
sub read_system_configuration { |
599
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
600
|
|
|
|
|
|
|
|
601
|
0
|
|
|
|
|
|
my ($the_file, $obfuscated_json, $config_json); |
602
|
|
|
|
|
|
|
|
603
|
|
|
|
|
|
|
# kick out if that file does not exist yet |
604
|
0
|
0
|
|
|
|
|
if (!(-e $self->{config_file})) { |
605
|
0
|
|
|
|
|
|
$self->send_response('ERROR: Can not find system configuration file.',1); |
606
|
|
|
|
|
|
|
} |
607
|
|
|
|
|
|
|
|
608
|
|
|
|
|
|
|
# try to read it in |
609
|
0
|
|
|
|
|
|
eval { |
610
|
0
|
|
|
|
|
|
$obfuscated_json = $self->filer( $self->{config_file} ); |
611
|
0
|
|
|
|
|
|
$config_json = pack "h*", $obfuscated_json; |
612
|
0
|
|
|
|
|
|
$self->{config} = $self->json_to_perl($config_json); |
613
|
|
|
|
|
|
|
}; |
614
|
|
|
|
|
|
|
|
615
|
|
|
|
|
|
|
# error out if there was any failure |
616
|
0
|
0
|
0
|
|
|
|
if ($@ || ref($self->{config}) ne 'HASH') { |
617
|
0
|
|
|
|
|
|
$self->send_response('ERROR: Could not read in system configuration file: '.$@,1); |
618
|
|
|
|
|
|
|
} |
619
|
|
|
|
|
|
|
|
620
|
|
|
|
|
|
|
} |
621
|
|
|
|
|
|
|
|
622
|
|
|
|
|
|
|
# save a system config file |
623
|
|
|
|
|
|
|
sub write_system_configuration { |
624
|
0
|
|
|
0
|
0
|
|
my ($self,$new_config) = @_; |
625
|
|
|
|
|
|
|
|
626
|
|
|
|
|
|
|
# convert config to JSON |
627
|
0
|
|
|
|
|
|
my $config_json = $self->json_from_perl($new_config); |
628
|
|
|
|
|
|
|
# slight obfuscation |
629
|
0
|
|
|
|
|
|
my $obfuscated_json = unpack "h*", $config_json; |
630
|
|
|
|
|
|
|
|
631
|
|
|
|
|
|
|
# stash out the file |
632
|
0
|
|
|
|
|
|
path( $self->{config_file} )->spew_raw( $obfuscated_json ); |
633
|
|
|
|
|
|
|
|
634
|
|
|
|
|
|
|
# set the permissions |
635
|
0
|
|
|
|
|
|
chmod 0600, $self->{config_file} ; |
636
|
|
|
|
|
|
|
} |
637
|
|
|
|
|
|
|
|
638
|
|
|
|
|
|
|
# method to update the endpoint mapping configs via 'pepper set-endpoint' |
639
|
|
|
|
|
|
|
sub set_endpoint_mapping { |
640
|
0
|
|
|
0
|
0
|
|
my ($self, $endpoint_uri, $endpoint_handler) = @_; |
641
|
|
|
|
|
|
|
|
642
|
0
|
0
|
0
|
|
|
|
if (!$endpoint_uri || !$endpoint_handler) { |
643
|
0
|
|
|
|
|
|
$self->send_response('Error: Both arguments are required for set_endpoint_mapping()',1); |
644
|
|
|
|
|
|
|
} |
645
|
|
|
|
|
|
|
|
646
|
|
|
|
|
|
|
# did they choose to store in a database table? |
647
|
0
|
0
|
|
|
|
|
if ($self->{config}{url_mappings_table}) { |
648
|
|
|
|
|
|
|
|
649
|
|
|
|
|
|
|
# make sure that table exists |
650
|
0
|
|
|
|
|
|
my ($database_name, $table_name) = split /\./, $self->{config}{url_mappings_table}; |
651
|
0
|
|
|
|
|
|
my ($table_exists) = $self->{db}->quick_select(qq{ |
652
|
|
|
|
|
|
|
select count(*) from information_schema.tables |
653
|
|
|
|
|
|
|
where table_schema=? and table_name=? |
654
|
|
|
|
|
|
|
},[ $database_name, $table_name ]); |
655
|
|
|
|
|
|
|
|
656
|
|
|
|
|
|
|
# if the table does not exist, try to make it |
657
|
0
|
0
|
|
|
|
|
if (!$table_exists) { |
658
|
|
|
|
|
|
|
|
659
|
|
|
|
|
|
|
# we won't create databases/schema in this library |
660
|
0
|
|
|
|
|
|
my ($database_exists) = $self->{db}->quick_select(qq{ |
661
|
|
|
|
|
|
|
select count(*) from information_schema.schemata |
662
|
|
|
|
|
|
|
where schema_name=? |
663
|
|
|
|
|
|
|
},[ $database_name ]); |
664
|
|
|
|
|
|
|
|
665
|
0
|
0
|
|
|
|
|
if (!$database_exists) { |
666
|
0
|
|
|
|
|
|
$self->send_response("Error: Database schema $database_exists does not exist",1); |
667
|
|
|
|
|
|
|
} |
668
|
|
|
|
|
|
|
|
669
|
|
|
|
|
|
|
# safe to create the table |
670
|
0
|
|
|
|
|
|
$self->{db}->do_sql(qq{ |
671
|
|
|
|
|
|
|
create table $self->{config}{url_mappings_table} ( |
672
|
|
|
|
|
|
|
endpoint_uri varchar(200) primary key, |
673
|
|
|
|
|
|
|
handler_module varchar(200) not null |
674
|
|
|
|
|
|
|
) |
675
|
|
|
|
|
|
|
}); |
676
|
|
|
|
|
|
|
|
677
|
|
|
|
|
|
|
} |
678
|
|
|
|
|
|
|
|
679
|
|
|
|
|
|
|
# finally, create the mapping |
680
|
0
|
|
|
|
|
|
$self->{db}->do_sql(qq{ |
681
|
|
|
|
|
|
|
replace into $self->{config}{url_mappings_table} |
682
|
|
|
|
|
|
|
(endpoint_uri, handler_module) values (?, ?) |
683
|
|
|
|
|
|
|
}, [$endpoint_uri, $endpoint_handler] ); |
684
|
|
|
|
|
|
|
|
685
|
|
|
|
|
|
|
# save this change |
686
|
0
|
|
|
|
|
|
$self->{db}->commit(); |
687
|
|
|
|
|
|
|
|
688
|
|
|
|
|
|
|
# otherwise, save to a JSON file |
689
|
|
|
|
|
|
|
} else { |
690
|
|
|
|
|
|
|
|
691
|
0
|
|
|
|
|
|
my $url_mappings = $self->read_json_file( $self->{config}{url_mappings_file} ); |
692
|
0
|
|
|
|
|
|
$$url_mappings{$endpoint_uri} = $endpoint_handler; |
693
|
0
|
|
|
|
|
|
$self->write_json_file( $self->{config}{url_mappings_file}, $url_mappings ); |
694
|
|
|
|
|
|
|
|
695
|
|
|
|
|
|
|
} |
696
|
|
|
|
|
|
|
|
697
|
|
|
|
|
|
|
} |
698
|
|
|
|
|
|
|
|
699
|
|
|
|
|
|
|
# method to delete an endpoint mapping via 'pepper delete-endpoint' |
700
|
|
|
|
|
|
|
sub delete_endpoint_mapping { |
701
|
0
|
|
|
0
|
0
|
|
my ($self, $endpoint_uri) = @_; |
702
|
|
|
|
|
|
|
|
703
|
0
|
0
|
|
|
|
|
if (!$endpoint_uri) { |
704
|
0
|
|
|
|
|
|
$self->send_response('Error: The endpoint uri must be specified for delete_endpoint_mapping()',1); |
705
|
|
|
|
|
|
|
} |
706
|
|
|
|
|
|
|
|
707
|
|
|
|
|
|
|
# did they choose to store in a database table? |
708
|
0
|
0
|
|
|
|
|
if ($self->{config}{url_mappings_table}) { |
709
|
|
|
|
|
|
|
|
710
|
0
|
|
|
|
|
|
$self->{db}->do_sql(qq{ |
711
|
|
|
|
|
|
|
delete from $self->{config}{url_mappings_table} |
712
|
|
|
|
|
|
|
where endpoint_uri=? |
713
|
|
|
|
|
|
|
}, [$endpoint_uri] ); |
714
|
|
|
|
|
|
|
|
715
|
|
|
|
|
|
|
# save this change |
716
|
0
|
|
|
|
|
|
$self->{db}->commit(); |
717
|
|
|
|
|
|
|
|
718
|
|
|
|
|
|
|
# or a JSON file? |
719
|
|
|
|
|
|
|
} else { |
720
|
|
|
|
|
|
|
|
721
|
0
|
|
|
|
|
|
my $url_mappings = $self->read_json_file( $self->{config}{url_mappings_file} ); |
722
|
0
|
|
|
|
|
|
delete ( $$url_mappings{$endpoint_uri} ); |
723
|
0
|
|
|
|
|
|
$self->write_json_file( $self->{config}{url_mappings_file}, $url_mappings ); |
724
|
|
|
|
|
|
|
|
725
|
|
|
|
|
|
|
} |
726
|
|
|
|
|
|
|
|
727
|
|
|
|
|
|
|
} |
728
|
|
|
|
|
|
|
|
729
|
|
|
|
|
|
|
1; |
730
|
|
|
|
|
|
|
|
731
|
|
|
|
|
|
|
__END__ |
732
|
|
|
|
|
|
|
|
733
|
|
|
|
|
|
|
=head1 NAME |
734
|
|
|
|
|
|
|
|
735
|
|
|
|
|
|
|
Pepper::Utilities |
736
|
|
|
|
|
|
|
|
737
|
|
|
|
|
|
|
=head1 DESCRIPTION |
738
|
|
|
|
|
|
|
|
739
|
|
|
|
|
|
|
This package provides useful functions for web services and scripts built using the |
740
|
|
|
|
|
|
|
Pepper quick-start kit. These methods can be access via the main 'Pepper' object, |
741
|
|
|
|
|
|
|
and are all documented in that package. Please see 'perldoc Pepper' or the main |
742
|
|
|
|
|
|
|
documentation on MetaCPAN. |