source: main/trunk/greenstone2/perllib/cgiactions/baseaction.pm@ 23477

Last change on this file since 23477 was 23477, checked in by davidb, 13 years ago

"un" cgi argument is now compulsory if authentication is on, and made options when not.

File size: 13.7 KB
Line 
1###########################################################################
2#
3# baseaction.pm --
4# A component of the Greenstone digital library software
5# from the New Zealand Digital Library Project at the
6# University of Waikato, New Zealand.
7#
8# Copyright (C) 2009 New Zealand Digital Library Project
9#
10# This program is free software; you can redistr te it and/or modify
11# it under the terms of the GNU General Public License as published by
12# the Free Software Foundation; either version 2 of the License, or
13# (at your option) any later version.
14#
15# This program is distributed in the hope that it will be useful,
16# but WITHOUT ANY WARRANTY; without even the implied warranty of
17# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18# GNU General Public License for more details.
19#
20# You should have received a copy of the GNU General Public License
21# along with this program; if not, write to the Free Software
22# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23#
24###########################################################################
25
26
27package baseaction;
28
29use strict;
30
31use inexport;
32
33our $authenication_enabled = 0;
34our $mail_enabled = 0;
35
36
37# change this to get these values from a config file
38my $mail_to_address = "user\@server"; # Set this appropriately
39my $mail_from_address = "user\@server"; # Set this appropriately
40my $mail_smtp_server = "smtp.server"; # Set this appropriately
41
42
43
44# Required CGI arguments: "a" for action
45# "c" for collection
46# Optional CGI arguemnts: "ts" for timestamp (auto generated is missing)
47# "site" (used by Greenstone3)
48
49# allow "un" for username to be optional for now
50
51sub new
52{
53 my $class = shift (@_);
54 my ($action_table,$gsdl_cgi,$iis6_mode) = @_;
55
56 my $self = { 'gsdl_cgi' => $gsdl_cgi,
57 'iis6_mode' => $iis6_mode,
58 'gsdlhome' => $ENV{'GSDLHOME'} };
59
60 # Retrieve the (required) command CGI argument
61 my $action = $gsdl_cgi->clean_param("a");
62
63 if (!defined $action) {
64 my $err_mess = "No action (a=...) specified.\n";
65 $err_mess .= "\nPossible actions are:\n";
66
67 $err_mess .= " check-installation\n\n";
68
69 foreach my $a (keys %$action_table) {
70 $err_mess .= " $a:\n";
71 $err_mess .= " Compulsory args: ";
72 my @comp_args = ("c");
73 push(@comp_args,"un") if ($authenication_enabled);
74 push(@comp_args,@{$action_table->{$a}->{'compulsory-args'}});
75 $err_mess .= join(", ", @comp_args);
76
77 $err_mess .= "\n";
78
79 my @opt_args = ();
80 push(@opt_args,"un") if (!$authenication_enabled);
81 push(@opt_args,@{$action_table->{$a}->{'optional-args'}});
82
83 if (scalar(@opt_args)>0) {
84
85 $err_mess .= " Optional args : ";
86 $err_mess .= join(", ", @opt_args);
87 $err_mess .= "\n";
88 }
89 $err_mess .= "\n";
90 }
91
92 $gsdl_cgi->generate_message($err_mess);
93 exit(-1);
94
95 }
96 $gsdl_cgi->delete("a");
97
98 $self = bless $self, $class;
99
100 # The check-installation command has no arguments
101 if ($action eq "check-installation") {
102 $self->check_installation($gsdl_cgi,$iis6_mode);
103 exit 0;
104 }
105
106
107 if (!defined $action_table->{$action}) {
108 my $valid_actions = join(", ", keys %$action_table);
109
110 my $err_mess = "Unrecognised action (a=$action) specified.\n";
111 $err_mess .= "Valid actions are: $valid_actions\n";
112
113 $gsdl_cgi->generate_error($err_mess);
114 }
115
116
117 my $collect = $gsdl_cgi->clean_param("c");
118 if ((!defined $collect) || ($collect =~ m/^\s*$/)) {
119 $gsdl_cgi->generate_error("No collection specified.");
120 }
121 $gsdl_cgi->delete("c");
122
123 # allow un to be optional for now
124 my $username = $gsdl_cgi->clean_param("un");
125
126
127 # Get then remove the ts (timestamp) argument (since this can mess up
128 # other scripts)
129 my $timestamp = $gsdl_cgi->clean_param("ts");
130 if ((!defined $timestamp) || ($timestamp =~ m/^\s*$/)) {
131 # Fall back to using the Perl time() function to generate a timestamp
132 $timestamp = time();
133 }
134 $gsdl_cgi->delete("ts");
135
136 my $site = undef;
137 if($gsdl_cgi->greenstone_version() != 2) {
138 # all GS versions after 2 may define site
139 $site = $gsdl_cgi->clean_param("site");
140 if (!defined $site) {
141 $gsdl_cgi->generate_error("No site specified.");
142 }
143 $gsdl_cgi->delete("site");
144 }
145
146 $self->{'action'} = $action;
147 $self->{'collect'} = $collect;
148 $self->{'username'} = $username;
149 $self->{'timestamp'} = $timestamp;
150 $self->{'site'} = $site;
151
152 # Locate and store compulsory arguments
153 my $comp_args = $action_table->{$action}->{'compulsory-args'};
154 foreach my $ca (@$comp_args) {
155 if (!defined $gsdl_cgi->param($ca)) {
156 $gsdl_cgi->generate_error("Compulsory argument '$ca' missing");
157 }
158 else {
159 $self->{$ca} = $gsdl_cgi->clean_param($ca);
160 $gsdl_cgi->delete($ca);
161 }
162 }
163
164 # Locate and store optional args if present
165 my $opt_args = $action_table->{$action}->{'optional-args'};
166 foreach my $oa (@$opt_args) {
167 if (defined $gsdl_cgi->param($oa)) {
168
169 $self->{$oa} = $gsdl_cgi->clean_param($oa);
170 $gsdl_cgi->delete($oa);
171 }
172 }
173
174 # Retrieve infodb-type
175 if (defined $collect) {
176 my $inexport = newCGI inexport(ref $self,$collect);
177 my ($config_filename,$collect_cfg) = $inexport->read_collection_cfg($collect);
178 $self->{'infodbtype'} = $collect_cfg->{'infodbtype'};
179 }
180
181 return $self;
182}
183
184
185sub do_action
186{
187 my $self = shift @_;
188 my $action = $self->{'action'};
189
190 $action =~ s/-/_/g;
191
192
193 $self->$action();
194
195}
196
197
198
199
200sub authenticate_user
201{
202 my $self = shift @_;
203 my $username = shift(@_);
204 my $collection = shift(@_);
205
206 my $gsdl_cgi = $self->{'gsdl_cgi'};
207
208 # Remove the pw argument (since this can mess up other scripts)
209 my $user_password = $gsdl_cgi->clean_param("pw");
210 $gsdl_cgi->delete("pw");
211
212 if ((!defined $user_password) || ($user_password =~ m/^\s*$/)) {
213 $gsdl_cgi->generate_error("Authentication failed: no password specified.");
214 }
215
216 my $gsdlhome = $ENV{'GSDLHOME'};
217 my $etc_directory = &util::filename_cat($gsdlhome, "etc");
218 my $users_db_file_path = &util::filename_cat($etc_directory, "users.db");
219
220 # Use db2txt to get the user accounts information
221 my $users_db_content = "";
222 open(USERS_DB, "db2txt \"$users_db_file_path\" |");
223 while (<USERS_DB>) {
224 $users_db_content .= $_;
225 }
226
227 # Get the user account information from the users.db database
228 my %users_db_data = ();
229 foreach my $users_db_entry (split(/-{70}/, $users_db_content)) {
230 if ($users_db_entry =~ /\n?\[(.+)\]\n/) {
231 $users_db_data{$1} = $users_db_entry;
232 }
233 }
234
235 # Check username
236 my $user_data = $users_db_data{$username};
237 if (!defined $user_data) {
238 $gsdl_cgi->generate_error("Authentication failed: no account for user '$username'.");
239 }
240
241 # Check password
242 my ($valid_user_password) = ($user_data =~ /\<password\>(.*)/);
243 if ($user_password ne $valid_user_password) {
244 $gsdl_cgi->generate_error("Authentication failed: incorrect password.");
245 }
246
247 # Check group
248 my ($user_groups) = ($user_data =~ /\<groups\>(.*)/);
249 if ($collection eq "") {
250 # If we're not editing a collection then the user doesn't need to be in a particular group
251 return $user_groups; # Authentication successful
252 }
253 foreach my $user_group (split(/\,/, $user_groups)) {
254 # Does this user have access to all collections?
255 if ($user_group eq "all-collections-editor") {
256 return $user_groups; # Authentication successful
257 }
258 # Does this user have access to personal collections, and is this one?
259 if ($user_group eq "personal-collections-editor" && $collection =~ /^$username\-/) {
260 return $user_groups; # Authentication successful
261 }
262 # Does this user have access to this collection
263 if ($user_group eq "$collection-collection-editor") {
264 return $user_groups; # Authentication successful
265 }
266 }
267
268 $gsdl_cgi->generate_error("Authentication failed: user is not in the required group.");
269}
270
271
272
273sub check_installation
274{
275 my $self = shift @_;
276 my $iis6_mode = shift(@_);
277
278 my $gsdl_cgi = $self->{'gsdl_cgi'};
279
280 my $installation_ok = 1;
281 my $installation_status = "";
282
283 print STDOUT "Content-type:text/plain\n\n";
284
285 # Check that Java is installed and accessible
286 my $java = $gsdl_cgi->get_java_path();
287 my $java_command = "$java -version 2>&1";
288
289 # IIS 6: redirecting output from STDERR to STDOUT just doesn't work, so we have to let it go
290 # directly out to the page
291 if ($iis6_mode)
292 {
293 $java_command = "java -version";
294 }
295
296 my $java_output = `$java_command`;
297 my $java_status = $?;
298 if ($java_status < 0) {
299 # The Java command failed
300 $installation_status = "Java failed -- do you have the Java run-time installed?\n" . $gsdl_cgi->check_java_home() . "\n";
301 $installation_ok = 0;
302 }
303 else {
304 $installation_status = "Java found: $java_output";
305 }
306
307 # Show the values of some important environment variables
308 $installation_status .= "\n";
309 $installation_status .= "GSDLHOME: " . $ENV{'GSDLHOME'} . "\n";
310 $installation_status .= "GSDLOS: " . $ENV{'GSDLOS'} . "\n";
311 $installation_status .= "PATH: " . $ENV{'PATH'} . "\n";
312
313 if ($installation_ok) {
314 print STDOUT $installation_status . "\nInstallation OK!";
315 }
316 else {
317 print STDOUT $installation_status;
318 }
319}
320
321sub lock_collection
322{
323 my $self = shift @_;
324 my $username = shift(@_);
325 my $collection = shift(@_);
326
327 my $gsdl_cgi = $self->{'gsdl_cgi'};
328
329 my $steal_lock = $gsdl_cgi->clean_param("steal_lock");
330 $gsdl_cgi->delete("steal_lock");
331
332 if (!defined $username) {
333 # don't have any user details for current user to compare with
334 # even if there is a lock file
335 # For now, allow the current user access. Might want to
336 # revisit this in the future.
337 return;
338 }
339
340 my $gsdlhome = $ENV{'GSDLHOME'};
341 my $collection_directory = &util::filename_cat($gsdlhome, "collect", $collection);
342 $gsdl_cgi->checked_chdir($collection_directory);
343
344 # Check if a lock file already exists for this collection
345 my $lock_file_name = "gli.lck";
346 if (-e $lock_file_name) {
347 # A lock file already exists... check if it's ours
348 my $lock_file_content = "";
349 open(LOCK_FILE, "<$lock_file_name");
350 while (<LOCK_FILE>) {
351 $lock_file_content .= $_;
352 }
353 close(LOCK_FILE);
354
355 # Pick out the owner of the lock file
356 $lock_file_content =~ /\<User\>(.*?)\<\/User\>/;
357 my $lock_file_owner = $1;
358
359 # The lock file is ours, so there is no problem
360 if ($lock_file_owner eq $username) {
361 return;
362 }
363
364 # The lock file is not ours, so throw an error unless "steal_lock" is set
365 unless (defined $steal_lock) {
366 $gsdl_cgi->generate_error("Collection is locked by: $lock_file_owner");
367 }
368 }
369
370 my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime(time);
371 my $current_time = sprintf("%02d/%02d/%d %02d:%02d:%02d", $mday, $mon + 1, $year + 1900, $hour, $min, $sec);
372
373 # Create a lock file for us (in the same format as the GLI) and we're done
374 open(LOCK_FILE, ">$lock_file_name");
375 print LOCK_FILE "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
376 print LOCK_FILE "<LockFile>\n";
377 print LOCK_FILE " <User>" . $username . "</User>\n";
378 print LOCK_FILE " <Machine>(Remote)</Machine>\n";
379 print LOCK_FILE " <Date>" . $current_time . "</Date>\n";
380 print LOCK_FILE "</LockFile>\n";
381 close(LOCK_FILE);
382}
383
384
385# Release the gli.lck otherwise no one else will be able to use the collection again.
386sub unlock_collection
387{
388 my $self = shift @_;
389 my ($username, $collection) = @_;
390 my $gsdl_cgi = $self->{'gsdl_cgi'};
391
392 # Obtain the path to the collection GLI lock file
393 my $lock_file_path = &util::filename_cat($ENV{'GSDLHOME'}, "collect", $collection, "gli.lck");
394
395 # If the lock file does exist, check if it is ours
396 if (-e $lock_file_path)
397 {
398 my $lock_file_content = "";
399 open(LOCK_FILE, "<$lock_file_path");
400 while (<LOCK_FILE>) {
401 $lock_file_content .= $_;
402 }
403 close(LOCK_FILE);
404
405 # Pick out the owner of the lock file
406 $lock_file_content =~ /\<User\>(.*?)\<\/User\>/;
407 my $lock_file_owner = $1;
408
409 # If we are the owner of this lock, we have the right to delete it
410 if ($lock_file_owner eq $username) {
411 unlink($lock_file_path );
412 }
413 else {
414 $gsdl_cgi->generate_error("Collection is locked by: $lock_file_owner. Cannot be unlocked");
415 }
416 }
417}
418
419
420sub send_mail
421{
422 my $self = shift @_;
423
424 my ($mail_subject,$mail_content) = @_;
425
426 my $gsdl_cgi = $self->{'gsdl_cgi'};
427
428 my $sendmail_command = "perl -S sendmail.pl";
429 $sendmail_command .= " -to \"" . $mail_to_address . "\"";
430 $sendmail_command .= " -from \"" . $mail_from_address . "\"";
431 $sendmail_command .= " -smtp \"" . $mail_smtp_server . "\"";
432 $sendmail_command .= " -subject \"" . $mail_subject . "\"";
433
434 if (!open(POUT, "| $sendmail_command")) {
435 $gsdl_cgi->generate_error("Unable to execute command: $sendmail_command");
436 }
437 print POUT $mail_content . "\n";
438 close(POUT);
439}
440
441
442
443
444sub run_script
445{
446 my $self = shift @_;
447
448 my ($collect, $site, $script) = @_;
449
450 my $gsdl_cgi = $self->{'gsdl_cgi'};
451
452 my $perl_args = $collect;
453
454 my $collect_dir = $gsdl_cgi->get_collection_dir($site);
455 $perl_args = "-collectdir \"$collect_dir\" " . $perl_args;
456
457 my $perl_command = "perl -S $script $perl_args";
458
459
460 # IIS 6: redirecting output from STDERR to STDOUT just doesn't work, so
461 # we have to let it go directly out to the page
462
463 if (!$self->{'iis6_mode'})
464 {
465 $perl_command .= " 2>&1";
466 }
467
468 if (!open(PIN, "$perl_command |")) {
469 $gsdl_cgi->generate_error("Unable to execute command: $perl_command");
470 }
471
472 print STDOUT "Content-type:text/plain\n\n";
473 print "$perl_command \n";
474
475 while (defined (my $perl_output_line = <PIN>)) {
476 print STDOUT $perl_output_line;
477 }
478 close(PIN);
479
480 my $perl_status = $?;
481 if ($perl_status > 0) {
482 $gsdl_cgi->generate_error("Perl failed: $perl_command\n--\nExit status: " . ($perl_status / 256));
483 }
484}
485
486
487
488
4891;
Note: See TracBrowser for help on using the repository browser.