source: trunk/gsdl/src/recpt/collectoraction.cpp@ 2379

Last change on this file since 2379 was 2379, checked in by jrm21, 23 years ago

added #define line for iostream stuff to work under Mac OS X.

  • Property svn:keywords set to Author Date Id Revision
File size: 58.6 KB
Line 
1/**********************************************************************
2 *
3 * collectoraction.cpp --
4 * Copyright (C) 2000 The New Zealand Digital Library Project
5 *
6 * A component of the Greenstone digital library software
7 * from the New Zealand Digital Library Project at the
8 * University of Waikato, New Zealand.
9 *
10 * This program is free software; you can redistribute 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// note that the collectoraction relies on having direct access to a
27// collections configuration file. this breaks the separation between
28// receptionist and collection server and so is not suitable (at least
29// in its current form) for use when collection servers are separate
30// from the receptionist (e.g. when using the CORBA protocol).
31
32// following line required to get fstream.filedesc() on darwin (Mac OS X)
33#define _STREAM_COMPAT 1
34
35#include "collectoraction.h"
36#include "OIDtools.h"
37#include "fileutil.h"
38#include "cfgread.h"
39#include "gsdltools.h"
40#include "gsdltimes.h"
41#include "nullproto.h"
42#include "argdb.h"
43
44#if !defined (__WIN32__)
45#include <sys/utsname.h>
46#include <unistd.h>
47#endif
48
49collectoraction::collectoraction () {
50
51 recpt = NULL;
52 disabled = true;
53 do_mkcol = false;
54 gsdlosc = NULL;
55 gsdlhomec = NULL;
56 pathc = NULL;
57
58 cgiarginfo arg_ainfo;
59 arg_ainfo.shortname = "a";
60 arg_ainfo.longname = "action";
61 arg_ainfo.multiplechar = true;
62 arg_ainfo.defaultstatus = cgiarginfo::weak;
63 arg_ainfo.argdefault = "collector";
64 arg_ainfo.savedarginfo = cgiarginfo::must;
65 argsinfo.addarginfo (NULL, arg_ainfo);
66
67 arg_ainfo.shortname = "p";
68 arg_ainfo.longname = "page";
69 arg_ainfo.multiplechar = true;
70 arg_ainfo.defaultstatus = cgiarginfo::weak;
71 arg_ainfo.argdefault = "intro";
72 arg_ainfo.savedarginfo = cgiarginfo::must;
73 argsinfo.addarginfo (NULL, arg_ainfo);
74
75 // temporary directory name for this collector
76 // session
77 arg_ainfo.shortname = "bc1tmp";
78 arg_ainfo.longname = "collector specific";
79 arg_ainfo.multiplechar = true;
80 arg_ainfo.defaultstatus = cgiarginfo::weak;
81 arg_ainfo.argdefault = "";
82 arg_ainfo.savedarginfo = cgiarginfo::must;
83 argsinfo.addarginfo (NULL, arg_ainfo);
84
85 arg_ainfo.shortname = "bc1fullname";
86 arg_ainfo.longname = "collector specific";
87 arg_ainfo.multiplechar = true;
88 arg_ainfo.defaultstatus = cgiarginfo::weak;
89 arg_ainfo.argdefault = "";
90 arg_ainfo.savedarginfo = cgiarginfo::mustnot; // saved on disk
91 argsinfo.addarginfo (NULL, arg_ainfo);
92
93 arg_ainfo.shortname = "bc1dirname";
94 arg_ainfo.longname = "collector specific";
95 arg_ainfo.multiplechar = true;
96 arg_ainfo.defaultstatus = cgiarginfo::weak;
97 arg_ainfo.argdefault = "";
98 arg_ainfo.savedarginfo = cgiarginfo::must;
99 argsinfo.addarginfo (NULL, arg_ainfo);
100
101 arg_ainfo.shortname = "bc1contactemail";
102 arg_ainfo.longname = "collector specific";
103 arg_ainfo.multiplechar = true;
104 arg_ainfo.defaultstatus = cgiarginfo::weak;
105 arg_ainfo.argdefault = "";
106 arg_ainfo.savedarginfo = cgiarginfo::mustnot; // saved on disk
107 argsinfo.addarginfo (NULL, arg_ainfo);
108
109 arg_ainfo.shortname = "bc1aboutdesc";
110 arg_ainfo.longname = "collector specific";
111 arg_ainfo.multiplechar = true;
112 arg_ainfo.defaultstatus = cgiarginfo::weak;
113 arg_ainfo.argdefault = "";
114 arg_ainfo.savedarginfo = cgiarginfo::mustnot; // saved on disk
115 argsinfo.addarginfo (NULL, arg_ainfo);
116
117 arg_ainfo.shortname = "bc1clone";
118 arg_ainfo.longname = "collector specific";
119 arg_ainfo.multiplechar = false;
120 arg_ainfo.defaultstatus = cgiarginfo::weak;
121 arg_ainfo.argdefault = "0";
122 arg_ainfo.savedarginfo = cgiarginfo::mustnot; // saved on disk
123 argsinfo.addarginfo (NULL, arg_ainfo);
124
125 arg_ainfo.shortname = "bc1clonecol";
126 arg_ainfo.longname = "collector specific";
127 arg_ainfo.multiplechar = true;
128 arg_ainfo.defaultstatus = cgiarginfo::weak;
129 arg_ainfo.argdefault = "";
130 arg_ainfo.savedarginfo = cgiarginfo::mustnot; // saved on disk
131 argsinfo.addarginfo (NULL, arg_ainfo);
132
133 // set when cloning option has changed
134 arg_ainfo.shortname = "bc1clonechanged";
135 arg_ainfo.longname = "collector specific";
136 arg_ainfo.multiplechar = false;
137 arg_ainfo.defaultstatus = cgiarginfo::weak;
138 arg_ainfo.argdefault = "0";
139 arg_ainfo.savedarginfo = cgiarginfo::mustnot;
140 argsinfo.addarginfo (NULL, arg_ainfo);
141
142 arg_ainfo.shortname = "bc1inputdir1";
143 arg_ainfo.longname = "collector specific";
144 arg_ainfo.multiplechar = true;
145 arg_ainfo.defaultstatus = cgiarginfo::weak;
146 arg_ainfo.argdefault = "";
147 arg_ainfo.savedarginfo = cgiarginfo::mustnot; // saved on disk
148 argsinfo.addarginfo (NULL, arg_ainfo);
149
150 arg_ainfo.shortname = "bc1inputdir2";
151 arg_ainfo.longname = "collector specific";
152 arg_ainfo.multiplechar = true;
153 arg_ainfo.defaultstatus = cgiarginfo::weak;
154 arg_ainfo.argdefault = "";
155 arg_ainfo.savedarginfo = cgiarginfo::mustnot; // saved on disk
156 argsinfo.addarginfo (NULL, arg_ainfo);
157
158 arg_ainfo.shortname = "bc1inputdir3";
159 arg_ainfo.longname = "collector specific";
160 arg_ainfo.multiplechar = true;
161 arg_ainfo.defaultstatus = cgiarginfo::weak;
162 arg_ainfo.argdefault = "";
163 arg_ainfo.savedarginfo = cgiarginfo::mustnot; // saved on disk
164 argsinfo.addarginfo (NULL, arg_ainfo);
165
166 arg_ainfo.shortname = "bc1inputdir4";
167 arg_ainfo.longname = "collector specific";
168 arg_ainfo.multiplechar = true;
169 arg_ainfo.defaultstatus = cgiarginfo::weak;
170 arg_ainfo.argdefault = "";
171 arg_ainfo.savedarginfo = cgiarginfo::mustnot; // saved on disk
172 argsinfo.addarginfo (NULL, arg_ainfo);
173
174 // only set when one of the fields was changed in
175 // the "collection info" page
176 arg_ainfo.shortname = "bc1infochanged";
177 arg_ainfo.longname = "collector specific";
178 arg_ainfo.multiplechar = false;
179 arg_ainfo.defaultstatus = cgiarginfo::weak;
180 arg_ainfo.argdefault = "0";
181 arg_ainfo.savedarginfo = cgiarginfo::mustnot;
182 argsinfo.addarginfo (NULL, arg_ainfo);
183
184 // only set when cfg file is altered from within
185 // "configure collection" page
186 arg_ainfo.shortname = "bc1cfgchanged";
187 arg_ainfo.longname = "collector specific";
188 arg_ainfo.multiplechar = false;
189 arg_ainfo.defaultstatus = cgiarginfo::weak;
190 arg_ainfo.argdefault = "0";
191 arg_ainfo.savedarginfo = cgiarginfo::mustnot;
192 argsinfo.addarginfo (NULL, arg_ainfo);
193
194 arg_ainfo.shortname = "cfgfile";
195 arg_ainfo.longname = "configuration file contents";
196 arg_ainfo.multiplechar = true;
197 arg_ainfo.defaultstatus = cgiarginfo::weak;
198 arg_ainfo.argdefault = "";
199 arg_ainfo.savedarginfo = cgiarginfo::mustnot;
200 argsinfo.addarginfo (NULL, arg_ainfo);
201
202 arg_ainfo.shortname = "bc1dodelete";
203 arg_ainfo.longname = "collector specific";
204 arg_ainfo.multiplechar = false;
205 arg_ainfo.defaultstatus = cgiarginfo::weak;
206 arg_ainfo.argdefault = "0";
207 arg_ainfo.savedarginfo = cgiarginfo::mustnot;
208 argsinfo.addarginfo (NULL, arg_ainfo);
209
210 // will be set if we arrived at the "configure collection" page
211 // via the "changing an existing collection" page
212 arg_ainfo.shortname = "bc1econf";
213 arg_ainfo.longname = "collector specific";
214 arg_ainfo.multiplechar = false;
215 arg_ainfo.defaultstatus = cgiarginfo::weak;
216 arg_ainfo.argdefault = "0";
217 arg_ainfo.savedarginfo = cgiarginfo::must;
218 argsinfo.addarginfo (NULL, arg_ainfo);
219
220 // will be set if we arrived at the "source data" page
221 // via the "changing an existing collection" page
222 arg_ainfo.shortname = "bc1esrce";
223 arg_ainfo.longname = "collector specific";
224 arg_ainfo.multiplechar = false;
225 arg_ainfo.defaultstatus = cgiarginfo::weak;
226 arg_ainfo.argdefault = "0";
227 arg_ainfo.savedarginfo = cgiarginfo::must;
228 argsinfo.addarginfo (NULL, arg_ainfo);
229}
230
231collectoraction::~collectoraction () {
232 if (gsdlosc != NULL) delete gsdlosc;
233 if (gsdlhomec != NULL) delete gsdlhomec;
234 if (pathc != NULL) delete pathc;
235}
236
237
238void collectoraction::configure (const text_t &key, const text_tarray &cfgline) {
239 if ((key == "collector") && (cfgline.size() == 1) &&
240 (cfgline[0] == "true" || cfgline[0] == "on" || cfgline[0] == "enabled")) {
241 disabled = false;
242 } else {
243 // call the parent class to deal with the things which
244 // are not dealt with here
245 action::configure (key, cfgline);
246 }
247}
248
249
250bool collectoraction::init (ostream & /*logout*/) {
251
252 // set up GSDLOS, GSDLHOME and PATH environment variables
253 text_t gsdlos, path;
254 unsigned int path_separator = ':';
255#if defined (__WIN32__)
256 gsdlos = "windows";
257 path_separator = ';';
258#else
259 utsname *buf = new utsname();
260 int i = uname (buf);
261 if (i == -1) gsdlos = "linux"; // uname failed
262 else gsdlos.setcstr (buf->sysname);
263 delete buf;
264 lc (gsdlos);
265#endif
266
267 pathc = getenv ("PATH");
268 path = filename_cat (gsdlhome, "bin", gsdlos);
269 path.push_back (path_separator);
270 path += filename_cat (gsdlhome, "bin", "script");
271 if (pathc != NULL) {
272 path.push_back (path_separator);
273 path += pathc;
274 }
275 path = "PATH=" + path;
276
277 gsdlos = "GSDLOS=" + gsdlos;
278 text_t setgsdlhome = "GSDLHOME=" + gsdlhome;
279
280 // these will be cleaned up in the destructor
281 gsdlosc = gsdlos.getcstr();
282 gsdlhomec = setgsdlhome.getcstr();
283 pathc = path.getcstr();
284
285 putenv (gsdlosc);
286 putenv (gsdlhomec);
287 putenv (pathc);
288
289 return true;
290}
291
292bool collectoraction::check_cgiargs (cgiargsinfoclass &argsinfo, cgiargsclass &args,
293 ostream &logout) {
294
295 text_t &current_page = args["p"];
296
297 // note that the "bildstatus" and "bildframe1" pages don't actually do anything
298 // functional so we don't need to worry about authenticating them (it's the
299 // underlying "bild" page that does the building (and creates the frameset))
300 // This helps us overcome a bit of a problem we have with multiple pages trying
301 // to read from the key.db database at the same time.
302 if (current_page != "intro" && current_page != "bildstatus" && current_page != "bildframe1") {
303 // authenticate the user if authentication is available
304 args["uan"] = 1;
305 args["ug"] = "colbuilder";
306 }
307
308 if (current_page == "new" || current_page == "existing") {
309
310 // assign (and create) a temporary directory
311 assign_tmpname (args, logout);
312
313 // clean up any old builds left laying about in the tmp directory
314 // (note that it's possible this could take some time if there's a huge
315 // partially built collection laying about so we'll make it an asynchronous
316 // system call)
317 gsdl_system ("perl -S cleantmp.pl", false, logout);
318 }
319
320 if (current_page != "intro" && current_page != "bildstatus" &&
321 current_page != "bildframe1" && current_page != "new") {
322 // update arguments that were saved to the harddrive
323 enum source_t {default_arg, compressed_arg, cgi_arg, program_arg};
324 text_tmap saved_args;
325 saved_args["bc1inputdir1"] = "";
326 saved_args["bc1inputdir2"] = "";
327 saved_args["bc1inputdir3"] = "";
328 saved_args["bc1inputdir4"] = "";
329 saved_args["bc1fullname"] = "";
330 saved_args["bc1contactemail"] = "";
331 saved_args["bc1aboutdesc"] = "";
332 saved_args["bc1clone"] = "";
333
334 // update the argdb database with any arguments that were set
335 // by previous page
336 text_tmap::iterator here = saved_args.begin();
337 text_tmap::iterator end = saved_args.end();
338 while (here != end) {
339 if (args.lookupcgiarg((*here).first).source != default_arg) {
340 (*here).second = args[(*here).first];
341 }
342 here++;
343 }
344
345 argdb *args_on_disk = new argdb(filename_cat(get_collectdir(args), "argdb.db"));
346 if (!args_on_disk->update_args(saved_args)) {
347 // error
348
349 }
350
351 // update args from argdb
352 saved_args.erase(saved_args.begin(), saved_args.end());
353 if (!args_on_disk->get_args(saved_args)) {
354 // error
355
356 }
357 delete args_on_disk;
358 here = saved_args.begin();
359 end = saved_args.end();
360 while (here != end) {
361 if (!(*here).second.empty()) {
362 args[(*here).first] = (*here).second;
363 }
364 here ++;
365 }
366 }
367
368 if (args["bc1infochanged"] == "1") {
369
370 if (args["bc1dirname"].empty()) {
371 // we've just come from the "collection information" page for the
372 // first time so we'll need to create the collection with mkcol.pl
373 // and set up bc1dirname - we do this part here instead of in do_action
374 // because the bc1dirname argument must be set to its new value before
375 // the compressedoptions macros are set.
376 args["bc1dirname"] = get_directory_name (args["bc1fullname"]);
377
378 text_t createfile = filename_cat (gsdlhome, "tmp", args["bc1tmp"], ".create");
379 if (!file_exists (createfile)) {
380 // we could do the mkcol.pl here but I guess it's nicer to do it in do_action()
381 do_mkcol = true;
382 } else {
383 // .create file already exists but bc1dirname wasn't set ... this should only be
384 // able to occur when the "reload" (and possibly the "back" and "forward" buttons)
385 // have been used to get us here.
386 // we'll check that the bc1dirname directory exists (in case of the unlikely
387 // possibility that get_directory_name returned a different value this time
388 // than it did originally).
389 text_t coldir = filename_cat (get_collectdir(args), args["bc1dirname"]);
390 if (!directory_exists (coldir)) {
391 message = "reloaderror";
392 return true;
393 }
394 }
395 } else {
396 // "collection information" has been changed after collection already exists
397 // so we'll need to update the cfg file.
398 update_cfgfile_partial (args, logout);
399 }
400 }
401
402 if (args["bc1cfgchanged"] == "1") {
403 // configuration file has been changed from the "configure collection"
404 // page. we need to update the file on disk and catch bc1 arguments up
405 // with changes.
406 update_cfgfile_complete (args, logout);
407 }
408
409 if (args["bc1clonechanged"] == "1") {
410 // cloning option has been changed on "source data" page. if it was turned
411 // on we want to create a new collect.cfg file using the bc1clonecol cfg file
412 // as a model (we'll save the old file as collect.cfg.org). if cloning was
413 // turned off we'll revert to using the collect.cfg.org file (which will need
414 // updating in case the bc1 arguments have been altered since cloning was
415 // turned on).
416 update_cfgfile_clone (args, logout);
417
418 // if cloning has just been turned on we'll also copy the rest of the files
419 // (excluding collect.cfg which we've already done) from the cloned collections
420 // etc directory to the new collection.
421 if (args["bc1clone"] == "1") {
422 text_t clone_etc = filename_cat(gsdlhome, "collect", args["bc1clonecol"], "etc");
423 text_t new_etc = filename_cat(get_collectdir(args), args["bc1dirname"], "etc");
424 text_tarray files;
425
426 if (read_dir (clone_etc, files)) {
427 text_tarray::const_iterator here = files.begin();
428 text_tarray::const_iterator end = files.end();
429 while (here != end) {
430 if (*here != "collect.cfg" && *here != "collect.cfg.org") {
431 file_copy (filename_cat(clone_etc, *here), filename_cat(new_etc, *here));
432 }
433 here ++;
434 }
435 } else {
436 outconvertclass text_t2ascii;
437 logout <<text_t2ascii << "collectoraction::check_cgiargs couldn't read from "
438 << clone_etc << " directory\n";
439 }
440 }
441 }
442
443 if (current_page == "bildstatus" || current_page == "bildcancel") {
444 // if .final file exists then build has finished
445 text_t fbld = filename_cat (gsdlhome, "tmp", args["bc1tmp"], args["bc1dirname"] + ".bld.final");
446 if (file_exists (fbld)) {
447 char *fbldc = fbld.getcstr();
448 ifstream fbld_in (fbldc);
449 if (fbld_in) {
450 char c = fbld_in.get();
451 fbld_in.close();
452 if (c == '0') {
453 // success - we need to create and configure a collection server for the
454 // newly built collection (for fastcgi and local library where
455 // initialization isn't going to be redone when the user clicks the
456 // "view your new collection" button
457 create_colserver (args["bc1dirname"], logout);
458 current_page = "bilddone";
459 }
460 else if (c == '4' || c == '5') message = "installfail";
461 else current_page = "bildfail";
462 } else {
463 // assume build failed (we shouldn't get here though ... right?)
464 current_page = "bildfail";
465 }
466 delete fbldc;
467 }
468 }
469
470 return true;
471}
472
473void collectoraction::update_cfgfile_clone (cgiargsclass &args, ostream &logout) {
474
475 text_t tmpdir = filename_cat(gsdlhome, "tmp", args["bc1tmp"]);
476 text_t cfgfile = filename_cat(tmpdir, args["bc1dirname"], "etc", "collect.cfg");
477 if (!file_exists (cfgfile)) {
478 message = "tmpfail";
479 return;
480 }
481
482 text_t cfgfile_org = filename_cat (tmpdir, "collect.cfg.org");
483
484 if (args["bc1clone"] == "1") {
485 // cloning was turned on
486
487 text_t cfgfile_clone = filename_cat(gsdlhome, "collect", args["bc1clonecol"], "etc", "collect.cfg");
488 if (file_exists (cfgfile_clone)) {
489 // if .org file doesn't exist already create it
490 if (!file_exists (cfgfile_org)) {
491 if (!file_copy (cfgfile, cfgfile_org)) {
492 message = "tmpfail";
493 return;
494 }
495 }
496 // copy clone collections cfg file to new collection
497 if (!file_copy (cfgfile_clone, cfgfile)) {
498 message = "tmpfail";
499 return;
500 }
501 // update the new cfg file
502 update_cfgfile_partial (args, logout);
503
504 } else {
505 // can't clone non-existant or read-protected collection
506 message = "clonefail";
507 }
508
509 } else {
510 // cloning has been turned off having been on at some point. the .org file
511 // should exist, if it doesn't we'll bail out and leave the user with the
512 // cloned copy
513 if (file_exists (cfgfile_org)) {
514 // copy original back again and update it with any recent changes
515 if (file_copy (cfgfile_org, cfgfile)) {
516 update_cfgfile_partial (args, logout);
517 } else {
518 message = "tmpfail";
519 }
520 }
521 }
522}
523
524// update configuration file on disk to match bc1 arguments
525void collectoraction::update_cfgfile_partial (cgiargsclass &args, ostream &logout) {
526
527 text_t cfgfile = filename_cat(get_collectdir(args), args["bc1dirname"], "etc", "collect.cfg");
528 char *cfgfilec = cfgfile.getcstr();
529
530 vector<text_tarray> cfgarray;
531
532 // read in cfg file
533 ifstream cfg_in (cfgfilec);
534 if (cfg_in) {
535 text_tarray cfgline;
536 while (read_cfg_line(cfg_in, cfgline) >= 0) {
537 if (cfgline.size () >= 2) {
538 if (cfgline[0] == "creator" || cfgline[0] == "maintainer") {
539 cfgline[1] = args["bc1contactemail"];
540 } else if (cfgline[0] == "collectionmeta") {
541 if (cfgline[1] == "collectionname") {
542 cfgline[2] = args["bc1fullname"];
543 } else if (cfgline[1] == "collectionextra") {
544 cfgline[2] = carriage_replace (args["bc1aboutdesc"], 0);
545 }
546 }
547 }
548 cfgarray.push_back (cfgline);
549 }
550 cfg_in.close();
551
552 // now write cfg file back out
553#ifdef __WIN32__
554 ofstream cfg_out (cfgfilec, ios::binary);
555#else
556 ofstream cfg_out (cfgfilec);
557#endif
558 if (cfg_out) {
559 // lock the file
560 int fd = GSDL_GET_FILEDESC(cfg_out);
561 int lock_val = 1;
562 GSDL_LOCK_FILE (fd);
563 if (lock_val != 0) {
564 logout << "Error: Couldn't lock file " << cfgfilec << "\n";
565 cfg_out.close();
566 message = "tmpfail";
567
568 } else {
569
570 vector<text_tarray>::const_iterator this_line = cfgarray.begin();
571 vector<text_tarray>::const_iterator end_line = cfgarray.end();
572 while (this_line != end_line) {
573 write_cfg_line (cfg_out, *this_line);
574 this_line ++;
575 }
576 GSDL_UNLOCK_FILE (fd);
577 cfg_out.close();
578 }
579
580 } else {
581 logout << "collectoraction::update_cfgfile_partial: unable to open "
582 << cfgfilec << " for output\n";
583 message = "tmpfail";
584 }
585
586 } else {
587 logout << "collectoraction::update_cfgfile_partial: unable to open "
588 << cfgfilec << " for input\n";
589 message = "tmpfail";
590 }
591 delete cfgfilec;
592}
593
594// replace configuration file on disk with that in the cfgfile argument and
595// catch other bc1 arguments up with those the new cfgfile contains
596void collectoraction::update_cfgfile_complete (cgiargsclass &args, ostream &logout) {
597
598 text_t cfgfile = filename_cat(get_collectdir(args), args["bc1dirname"], "etc", "collect.cfg");
599 char *cfgfilec = cfgfile.getcstr();
600#ifdef __WIN32__
601 ofstream cfg_out (cfgfilec, ios::binary);
602#else
603 ofstream cfg_out (cfgfilec);
604#endif
605
606 if (cfg_out) {
607 // lock the file
608 int fd = GSDL_GET_FILEDESC(cfg_out);
609 int lock_val = 1;
610 GSDL_LOCK_FILE (fd);
611 if (lock_val != 0) {
612 logout << "Error: Couldn't lock file " << cfgfilec << "\n";
613 cfg_out.close();
614 message = "tmpfail";
615
616 } else {
617
618 outconvertclass text_t2ascii;
619 cfg_out << text_t2ascii << args["cfgfile"];
620 GSDL_UNLOCK_FILE (fd);
621 cfg_out.close();
622
623 // now that we've written the file we'll read it back again and
624 // update our bc1 arguments
625 ifstream cfg_in (cfgfilec);
626 if (cfg_in) {
627 text_tarray cfgline;
628 while (read_cfg_line(cfg_in, cfgline) >= 0) {
629 if (cfgline.size () >= 2) {
630 if (cfgline[0] == "creator") {
631 args["bc1contactemail"] = cfgline[1];
632 } else if (cfgline[0] == "collectionmeta") {
633 if (cfgline[1] == "collectionname") {
634 args["bc1fullname"] = cfgline[2];
635 } else if (cfgline[1] == "collectionextra") {
636 args["bc1aboutdesc"] = carriage_replace (cfgline[2], 1);
637 }
638 }
639 }
640 }
641 cfg_in.close();
642 } else {
643 logout << "collectoraction::update_cfgfile_complete: unable to open "
644 << cfgfilec << " for input\n";
645 message = "tmpfail";
646 }
647 }
648 } else {
649 logout << "collectoraction::update_cfgfile_complete: unable to open "
650 << cfgfilec << " for output\n";
651 message = "tmpfail";
652 }
653 delete cfgfilec;
654}
655
656void collectoraction::get_cgihead_info (cgiargsclass &/*args*/, recptprotolistclass * /*protos*/,
657 response_t &response,text_t &response_data,
658 ostream &/*logout*/) {
659 response = content;
660 response_data = "text/html";
661}
662
663// return html for buttons used in collector bar
664// color may be "green", "grey", or "yellow"
665// type may be:
666// "info" --> "collection information" button
667// "srce" --> "source data" button
668// "conf" --> "configure collection" button
669// "bild" --> "build collection" button
670// "view" --> "view collection" button
671// if enabled is true button will be flashy rollover type and
672// will be hyperlinked
673
674text_t collectoraction::get_button (const text_t &thispage, const text_t &color,
675 const text_t &type, bool enabled) {
676
677 if ((color != "green" && color != "grey" && color != "yellow") ||
678 (type != "info" && type != "srce" && type != "conf" && type != "bild" && type != "view"))
679 return "";
680
681 text_t prefix = "gc";
682 if (color == "grey") prefix = "nc";
683 else if (color == "yellow") prefix = "yc";
684
685 text_t httpicon = "httpicon" + prefix + type;
686
687 if (enabled) {
688 text_t gsmacro = "_gsimage_";
689 if (thispage == "info" || thispage == "srce" || thispage == "conf" ||
690 thispage == "bildcancel" || thispage == "bildfail") {
691 gsmacro = "_gsjimage_";
692 } else if (type == "view") {
693 // view button is special case as it needs a target=_top
694 gsmacro = "_gstimage_";
695 }
696 return "<td>" + gsmacro + "(_collector:http" + type + "_,_collector:" + httpicon +
697 "of_,_collector:" + httpicon + "on_," + type + ",_collector:text" + type + "_)</td>\n";
698 } else {
699 return "<td>_icon" + prefix + type + "of_</td>\n";
700 }
701}
702
703// set the _fullnamemenu_ macro (and _warnindex_ if we're on the "srce" page)
704void collectoraction::set_fullnamemenu (displayclass &disp, cgiargsclass &args,
705 recptprotolistclass *protos, ostream &logout) {
706
707 if (recpt == NULL) {
708 logout << "ERROR (collectoraction::set_fullnamemenu): This action does not contain\n"
709 << " information about any receptionists. The method set_receptionist was\n"
710 << " probably not called from the module which instantiated this action.\n";
711 return;
712 }
713
714 text_t &current_page = args["p"];
715 text_t currentname = args["bc1dirname"];
716 if (current_page == "srce") currentname = args["bc1clonecol"];
717
718 text_tarray dirnames;
719 text_tarray fullnames;
720 vector<bool> write_protected;
721 int selected_index = 0;
722 int index = 0;
723
724 recptprotolistclass::iterator rprotolist_here = protos->begin();
725 recptprotolistclass::iterator rprotolist_end = protos->end();
726 while (rprotolist_here != rprotolist_end) {
727 if ((*rprotolist_here).p != NULL) {
728
729 // don't include z39.50 collection
730 comerror_t err = noError;
731 if ((*rprotolist_here).p->get_protocol_name (err) == "z3950proto") {
732 rprotolist_here ++;
733 continue;
734 }
735
736 text_tarray collist;
737 (*rprotolist_here).p->get_collection_list (collist, err, logout);
738 if (err == noError) {
739 text_tarray::iterator collist_here = collist.begin();
740 text_tarray::iterator collist_end = collist.end();
741 FilterResponse_t response;
742 text_tset metadata;
743 metadata.insert ("collectionname");
744 while (collist_here != collist_end) {
745 ColInfoResponse_t *cinfo = recpt->get_collectinfo_ptr ((*rprotolist_here).p, *collist_here, logout);
746 if (cinfo != NULL) {
747 text_t collectionname = *collist_here;
748 if (!cinfo->collectionmeta["collectionname"].empty()) {
749 // get collection name from the collection cfg file
750 collectionname = cinfo->collectionmeta["collectionname"];
751 } else if (get_info ("collection", *collist_here, metadata, false,
752 (*rprotolist_here).p, response, logout)) {
753 // get collection name from gdbm file
754 collectionname = response.docInfo[0].metadata["collectionname"].values[0];
755 }
756 dirnames.push_back(*collist_here);
757 fullnames.push_back(collectionname);
758 // check to see if the collection is writable
759 if (collection_protected (*collist_here)) write_protected.push_back(true);
760 else write_protected.push_back(false);
761
762 if (*collist_here == currentname) selected_index = index;
763 index ++;
764 }
765 collist_here ++;
766 }
767 }
768 }
769 rprotolist_here ++;
770 }
771
772 bool first = true;
773 text_t warnindex;
774 text_t fullnamemenu = "<select name=\"bc1dirname\">\n";
775 if (current_page == "srce") fullnamemenu = "<select name=\"bc1clonecol\" onChange=\"menuchange();\">\n";
776 for (int i = 0; i < index; i ++) {
777 // don't want write protected collections in list on "change existing
778 // collection" page
779 if (write_protected[i] && current_page == "existing") continue;
780 fullnamemenu += "<option value=\"" + dirnames[i] + "\"";
781 if (i == selected_index) fullnamemenu += " selected";
782 fullnamemenu.push_back ('>');
783 fullnamemenu += fullnames[i];
784 fullnamemenu.push_back ('\n');
785
786 // add to Warnindex if collection uses any dubious plugins
787 // (if creating clone collection list)
788 if (current_page == "srce") {
789 if (!first) warnindex.push_back(',');
790 if (uses_weird_plugin (dirnames[i])) {
791 warnindex += text_t (1);
792 } else {
793 warnindex += text_t (0);
794 }
795 }
796 first = false;
797 }
798 fullnamemenu += "</select>\n";
799
800 if (!first) {
801 disp.setmacro ("fullnamemenu", "collector", fullnamemenu);
802 if (current_page == "srce")
803 disp.setmacro ("warnindex", "collector", warnindex);
804 }
805}
806
807// set the _cfgfile_ macro
808void collectoraction::set_cfgfile (displayclass &disp, cgiargsclass &args, ostream &logout) {
809
810 text_t &collection = args["bc1dirname"];
811 if (collection.empty()) {
812 message = "nocollection";
813 return;
814 }
815
816 // read in collect.cfg
817 text_t cfgfile = filename_cat(get_collectdir(args), collection, "etc", "collect.cfg");
818 char *cfgfilec = cfgfile.getcstr();
819
820#ifdef GSDL_USE_IOS_H
821 ifstream cfg_ifs (cfgfilec, ios::in | ios::nocreate);
822#else
823 ifstream cfg_ifs (cfgfilec, ios::in);
824#endif
825
826 if (cfg_ifs) {
827 // read in collect.cfg
828 text_t cfgtext;
829 char c;
830 cfg_ifs.get(c);
831 while (!cfg_ifs.eof ()) {
832 cfgtext.push_back(c);
833 cfg_ifs.get(c);
834 }
835 cfg_ifs.close();
836
837 // define it as a macro
838 disp.setmacro("cfgfile","collector",dm_safe(cfgtext));
839
840 } else {
841 logout << "collectoraction::set_cfgfile: couldn't open configuration file ("
842 << cfgfilec << ") for reading\n";
843 message = "tmpfail";
844 }
845 delete cfgfilec;
846}
847
848// set the _statusline_ macro
849void collectoraction::set_statusline (displayclass &disp, cgiargsclass &args, ostream & /*logout*/) {
850
851 // the build command creates .bld.download, .bld.import, and .bld.build files (in that
852 // order) and deletes them (also in that order) when each stage is complete. the .bld
853 // file is the concatenation of all these files.
854 text_t bld_file = filename_cat (gsdlhome, "tmp", args["bc1tmp"], args["bc1dirname"] + ".bld");
855 text_t statusline;
856
857 if (file_exists (bld_file + ".download")) {
858 statusline = "Downloading files ...<br>\n";
859 statusline += file_tail (bld_file + ".download", 1, 0);
860 } else if (file_exists (bld_file + ".import")) {
861 statusline = "Importing collection ...<br>\n";
862 statusline += file_tail (bld_file + ".import", 1, 0);
863 } else if (file_exists (bld_file + ".build")) {
864 statusline = "Building collection ...<br>\n";
865 statusline += file_tail (bld_file + ".build", 1, 0);
866 } else {
867 statusline += "creating collection ...<br>\n";
868 statusline += file_tail (bld_file, 1, 0);
869 }
870
871 disp.setmacro ("statusline", "collector", dm_safe(statusline));
872
873}
874
875void collectoraction::define_internal_macros (displayclass &disp, cgiargsclass &args,
876 recptprotolistclass *protos, ostream &logout) {
877
878 // define_internal_macros sets the following macros:
879 // _collectorbar_
880 // _pagescriptextra_
881 // _fullnamemenu_ -- if displaying the "source data" page or the "changing existing
882 // collection" page
883 // _cfgfile_ -- if displaying the "configure collection" page
884 // _statusline_ -- if displaying the bildstatus page
885 // _header_ -- may be set for pages that require it
886 // _faillog_ - set to last 6 lines of .bld file if build failed
887 // _gsdlhome_ - the gsdlhome path (dm_safe)
888
889 text_t &collector_page = args["p"];
890 int esrce = args["bc1esrce"].getint();
891 int econf = args["bc1econf"].getint();
892
893 // set _pagescriptextra_ macro to _cpagescriptextra_
894 disp.setmacro ("pagescriptextra", "collector", "_" + collector_page + "scriptextra_");
895
896 if (collector_page == "bildstatus" || collector_page == "bilddone" ||
897 collector_page == "bildfail" || collector_page == "bildframe1") {
898 disp.setmacro ("header", "collector", "_" + collector_page + "header_");
899 }
900
901 // set the collectorbar macro
902 text_t collectorbar = "<table border=0 cellspacing=4 cellpadding=0><tr>\n";
903
904 if (collector_page == "new") {
905 collectorbar += "<td>_icongreyarrow_</td>\n";
906 collectorbar += get_button (collector_page, "green", "info", true);
907 collectorbar += "<td>_icongreyarrow_</td>\n";
908 collectorbar += get_button (collector_page, "grey", "srce", false);
909 collectorbar += "<td>_icongreyarrow_</td>\n";
910 collectorbar += get_button (collector_page, "grey", "conf", false);
911 collectorbar += "<td>_icongreyarrow_</td>\n";
912 collectorbar += get_button (collector_page, "grey", "bild", false);
913 collectorbar += "<td>_icongreyarrow_</td>\n";
914 collectorbar += get_button (collector_page, "grey", "view", false);
915
916 } else if (collector_page == "info") {
917 collectorbar += "<td>_icongreyarrow_</td>\n";
918 collectorbar += get_button (collector_page, "yellow", "info", false);
919 collectorbar += "<td>_icongreyarrow_</td>\n";
920 collectorbar += get_button (collector_page, "green", "srce", true);
921 collectorbar += "<td>_icongreyarrow_</td>\n";
922 collectorbar += get_button (collector_page, "grey", "conf", false);
923 collectorbar += "<td>_icongreyarrow_</td>\n";
924 collectorbar += get_button (collector_page, "grey", "bild", false);
925 collectorbar += "<td>_icongreyarrow_</td>\n";
926 collectorbar += get_button (collector_page, "grey", "view", false);
927 collectorbar += "</tr><tr><td></td><td align=center>_icongreyuparrow_</td><td colspan=8></td>\n";
928
929 } else if (collector_page == "srce") {
930 collectorbar += "<td>_icongreyarrow_</td>\n";
931 if (esrce == 1) {
932 // if we came from the "change an existing collection" page previous button(s)
933 // are disabled
934 collectorbar += get_button (collector_page, "grey", "info", false);
935 } else {
936 collectorbar += get_button (collector_page, "yellow", "info", true);
937 }
938 collectorbar += "<td>_icongreyarrow_</td>\n";
939 collectorbar += get_button (collector_page, "yellow", "srce", false);
940 collectorbar += "<td>_icongreyarrow_</td>\n";
941 collectorbar += get_button (collector_page, "green", "conf", true);
942 collectorbar += "<td>_icongreyarrow_</td>\n";
943 collectorbar += get_button (collector_page, "green", "bild", true);
944 collectorbar += "<td>_icongreyarrow_</td>\n";
945 collectorbar += get_button (collector_page, "grey", "view", false);
946 collectorbar += "</tr><tr><td colspan=3></td><td align=center>_icongreyuparrow_</td><td colspan=6></td>\n";
947
948 } else if (collector_page == "conf") {
949 collectorbar += "<td>_icongreyarrow_</td>\n";
950 // disable appropriate buttons if we came from "change an existing collection"
951 // page
952 if (esrce == 1 || econf == 1) {
953 collectorbar += get_button (collector_page, "grey", "info", false);
954 } else {
955 collectorbar += get_button (collector_page, "yellow", "info", true);
956 }
957 collectorbar += "<td>_icongreyarrow_</td>\n";
958 if (econf == 1) {
959 collectorbar += get_button (collector_page, "grey", "srce", false);
960 } else {
961 collectorbar += get_button (collector_page, "yellow", "srce", true);
962 }
963 collectorbar += "<td>_icongreyarrow_</td>\n";
964 collectorbar += get_button (collector_page, "yellow", "conf", false);
965 collectorbar += "<td>_icongreyarrow_</td>\n";
966 collectorbar += get_button (collector_page, "green", "bild", true);
967 collectorbar += "<td>_icongreyarrow_</td>\n";
968 collectorbar += get_button (collector_page, "grey", "view", false);
969 collectorbar += "</tr><tr><td colspan=5></td><td align=center>_icongreyuparrow_</td><td colspan=4></td>\n";
970
971 } else if (collector_page == "bilddone") {
972 collectorbar += "<td>_icongreyarrow_</td>\n";
973 // all previous buttons grey after build was completed
974 collectorbar += get_button (collector_page, "grey", "info", false);
975 collectorbar += "<td>_icongreyarrow_</td>\n";
976 collectorbar += get_button (collector_page, "grey", "srce", false);
977 collectorbar += "<td>_icongreyarrow_</td>\n";
978 collectorbar += get_button (collector_page, "grey", "conf", false);
979 collectorbar += "<td>_icongreyarrow_</td>\n";
980 collectorbar += get_button (collector_page, "yellow", "bild", false);
981 collectorbar += "<td>_icongreyarrow_</td>\n";
982 collectorbar += get_button (collector_page, "green", "view", true);
983 collectorbar += "</tr><tr><td colspan=7></td><td align=center>_icongreyuparrow_</td><td colspan=2></td>\n";
984
985 } else if (collector_page == "bildcancel" || collector_page == "bildfail") {
986 collectorbar += "<td>_icongreyarrow_</td>\n";
987 // disable appropriate buttons if we came from "change an existing collection"
988 // page
989 if (esrce == 1 || econf == 1) {
990 collectorbar += get_button (collector_page, "grey", "info", false);
991 } else {
992 collectorbar += get_button (collector_page, "yellow", "info", true);
993 }
994 collectorbar += "<td>_icongreyarrow_</td>\n";
995 if (econf == 1) {
996 collectorbar += get_button (collector_page, "grey", "srce", false);
997 } else {
998 collectorbar += get_button (collector_page, "yellow", "srce", true);
999 }
1000 collectorbar += "<td>_icongreyarrow_</td>\n";
1001 collectorbar += get_button (collector_page, "yellow", "conf", true);
1002 collectorbar += "<td>_icongreyarrow_</td>\n";
1003 collectorbar += get_button (collector_page, "yellow", "bild", true);
1004 collectorbar += "<td>_icongreyarrow_</td>\n";
1005 collectorbar += get_button (collector_page, "grey", "view", false);
1006 }
1007
1008 if (collector_page == "bildfail") {
1009 text_t bldlog = filename_cat(gsdlhome, "tmp", args["bc1tmp"], args["bc1dirname"] + ".bld");
1010 text_t rawlog = file_tail (bldlog, 6, 0);
1011 // we'll shove in some <br> tags where \n's occur
1012 text_t faillog;
1013 text_t::const_iterator here = rawlog.begin();
1014 text_t::const_iterator end = rawlog.end();
1015 while (here != end) {
1016 if (*here == '\n') faillog += "<br>";
1017 faillog.push_back (*here);
1018 here ++;
1019 }
1020 disp.setmacro ("faillog", "collector", dm_safe(faillog));
1021 }
1022
1023 collectorbar += "</tr></table>\n";
1024 disp.setmacro ("collectorbar", "collector", collectorbar);
1025
1026 if (collector_page == "srce" || collector_page == "existing")
1027 set_fullnamemenu (disp, args, protos, logout);
1028 if (collector_page == "conf")
1029 set_cfgfile (disp, args, logout);
1030 if (collector_page == "bildstatus")
1031 set_statusline (disp, args, logout);
1032
1033 disp.setmacro ("gsdlhome", "collector", dm_safe(gsdlhome));
1034}
1035
1036bool collectoraction::do_action (cgiargsclass &args, recptprotolistclass * /*protos*/,
1037 browsermapclass * /*browsers*/, displayclass &disp,
1038 outconvertclass &outconvert, ostream &textout,
1039 ostream &logout) {
1040
1041 // make sure the collector is enabled
1042 if (disabled) {
1043 textout << outconvert
1044 << "<html>\n"
1045 << "<head>\n"
1046 << "<title>Collector disabled</title>\n"
1047 << "</head>\n"
1048 << "<body bgcolor=\"#ffffff\" text=\"#000000\" link=\"#006666\" "
1049 << "alink=\"#cc9900\" vlink=\"#666633\">\n"
1050 << "<h2>Facility disabled</h2>\n"
1051 << "Sorry, the Collector end-user collection building facility is currently disabled\n"
1052 << "\n</body>\n"
1053 << "</html>\n";
1054 return true;
1055 }
1056
1057 text_t &collector_page = args["p"];
1058 text_t &collection = args["bc1dirname"];
1059
1060 // make sure we have perl (we won't bother with this check for the
1061 // building status pages to avoid slowing things down unneccessarily)
1062 if (collector_page != "bildstatus" && collector_page != "bildframe1" && !perl_ok(logout)) {
1063 textout << outconvert
1064 << "<html>\n"
1065 << "<head>\n"
1066 << "<title>Perl not found</title>\n"
1067 << "</head>\n"
1068 << "<body bgcolor=\"#ffffff\" text=\"#000000\" link=\"#006666\" "
1069 << "alink=\"#cc9900\" vlink=\"#666633\">\n"
1070 << "<h2>Perl not found</h2>\n"
1071 << "Greenstone could not detect perl on this system. The Collector\n"
1072 << "end-user collection building facility is therefore not available.\n"
1073 << "\n</body>\n"
1074 << "</html>\n";
1075 return true;
1076
1077 }
1078
1079 if (collector_page == "bild") {
1080 // do the work (download, import, build)
1081 gsdl_build (args, logout);
1082
1083 if (message.empty()) {
1084 // bild page is a frameset so we don't want headers and stuff
1085 textout << outconvert << disp << ("_collector:bildcontent_\n");
1086 }
1087 }
1088
1089 if (do_mkcol == true) {
1090 // execute mkcol.pl (do_mkcol is set from within check_cgiargs)
1091 gsdl_mkcol (args, logout);
1092 do_mkcol = false; // reset for fast-cgi
1093 }
1094
1095 if (args["bc1dodelete"] == "1") {
1096 // delete bcidirname collection
1097 if (collection_protected (collection)) {
1098 message = "delinvalid";
1099
1100 } else {
1101
1102 const recptconf &rcinfo = recpt->get_configinfo ();
1103 bool emailuserevents = rcinfo.EmailUserEvents;
1104
1105 // get collection maintainer email from collect.cfg before we
1106 // delete it
1107 text_t colmaintainer;
1108 text_t cfgfile = filename_cat(gsdlhome, "collect", collection, "etc", "collect.cfg");
1109 char *cfgfilec = cfgfile.getcstr();
1110 ifstream cfg_in (cfgfilec);
1111 delete cfgfilec;
1112 if (cfg_in) {
1113 text_tarray cfgline;
1114 while (read_cfg_line(cfg_in, cfgline) >= 0) {
1115 if (cfgline.size () == 2 && cfgline[0] == "maintainer") {
1116 colmaintainer = cfgline[1];
1117 break;
1118 }
1119 }
1120 cfg_in.close();
1121 }
1122 if (colmaintainer.empty()) {
1123 logout << outconvert
1124 << "collectoraction::do_action WARNING: Collection being deleted ("
1125 << collection << ") has no maintainer address. EmailUserEvents "
1126 << "disabled\n";
1127 emailuserevents = false;
1128 }
1129
1130 // first we need to free up the collection's collection server
1131 // we must do this for the local library (and I guess when using
1132 // fastcgi too) as you can't delete the gdbm file while it's
1133 // being kept open by the collection server
1134 remove_colservr (collection, logout);
1135
1136 text_t delete_cmd = "perl -S delcol.pl -f " + collection;
1137 int rv = gsdl_system (delete_cmd, true, logout);
1138 if (rv != 0) {
1139 // deletion failed -- permissions?
1140 message = "delpermission";
1141 } else {
1142 message = "delsuccess";
1143 }
1144
1145 // log the event
1146 if (rcinfo.LogEvents == CollectorEvents || rcinfo.LogEvents == AllEvents) {
1147
1148 text_t eventlog = filename_cat (gsdlhome, "etc", "events.txt");
1149 char *eventlogt = eventlog.getcstr();
1150 ofstream eventl (eventlogt, ios::app);
1151 delete eventlogt;
1152
1153 if (eventl) {
1154 eventl << outconvert << "[Collector Event]\n"
1155 << "Date: " << get_date (true) << "\n"
1156 << "Greenstone Username: " << args["un"] << "\n"
1157 << "Collection: " << collection << "\n"
1158 << "Collection Maintainer: " << colmaintainer << "\n"
1159 << "GSDLHOME: " << gsdlhome << "\n";
1160
1161 if (message == "delsuccess") {
1162 eventl << outconvert
1163 << "The " << collection << " collection was successfully deleted\n\n";
1164 } else {
1165 eventl << outconvert
1166 << "Attempt to delete the " << collection << " collection failed\n\n";
1167 }
1168 eventl.close();
1169
1170 } else {
1171 logout << outconvert << "collectoraction::do_action ERROR: Couldn't open "
1172 << "event log file " << eventlog << " for appending during collection "
1173 << "deletion. LogEvents disabled\n";
1174 }
1175 }
1176
1177 if (rcinfo.EmailEvents == CollectorEvents || rcinfo.EmailEvents == AllEvents || emailuserevents) {
1178 // use sendmail.pl perl script to send email events
1179 text_t tmpmailfile = filename_cat (gsdlhome, "tmp", args["bc1tmp"], "event.txt");
1180 char *tmpmailfilec = tmpmailfile.getcstr();
1181 ofstream tmpfile (tmpmailfilec);
1182 delete tmpmailfilec;
1183 if (tmpfile) {
1184 tmpfile << outconvert << "[Collector Event]\n"
1185 << "Date: " << get_date (true) << "\n"
1186 << "Greenstone Username: " << args["un"] << "\n"
1187 << "Collection: " << collection << "\n"
1188 << "Collection Maintainer: " << colmaintainer << "\n"
1189 << "GSDLHOME: " << gsdlhome << "\n";
1190 if (message == "delsuccess") {
1191 tmpfile << outconvert
1192 << "The " << collection << " collection was successfully deleted\n\n";
1193 } else {
1194 tmpfile << outconvert
1195 << "Attempt to delete the " << collection << " collection failed\n\n";
1196 }
1197 tmpfile.close();
1198 text_t to;
1199 if (rcinfo.EmailEvents == CollectorEvents || rcinfo.EmailEvents == AllEvents) to += rcinfo.maintainer;
1200 if (emailuserevents) {
1201 if (!to.empty()) to.push_back (',');
1202 to += colmaintainer;
1203 }
1204 text_t sendmail_cmd = "perl -S sendmail.pl -to \"" + to + "\" -from \"" + rcinfo.maintainer;
1205 sendmail_cmd += "\" -smtp \"" + rcinfo.MailServer + "\" -subject \"Greenstone Collector Event\"";
1206 sendmail_cmd += " -msgfile \"" + tmpmailfile + "\"";
1207
1208 gsdl_system (sendmail_cmd, false, logout);
1209
1210 } else {
1211 logout << outconvert << "collectoraction::do_action ERROR: Couldn't open "
1212 << "temporary event log file " << tmpmailfile << " during collection "
1213 << "deletion. EmailEvents and EmailUserEvents disabled\n";
1214 }
1215 }
1216 }
1217 }
1218
1219 if (collector_page == "bildcancel" || collector_page == "bildfail") {
1220 // cancel the build (we'll also use the cancel_build script to tidy
1221 // up if the build failed)
1222 gsdl_cancel_build (args, logout);
1223 }
1224
1225 if (collector_page == "expt") {
1226
1227 // export the collection - we'll do a synchronous system call to
1228 // exportcol.pl as that's the easiest way to do it. if it becomes a
1229 // problem that it's taking too long to export a large collection then
1230 // we may have to revisit this.
1231 text_t tmpfile = filename_cat (gsdlhome, "tmp", collection + "_export.txt");
1232 text_t export_cmd = "perl -S exportcol.pl -out " + tmpfile + " " + collection;
1233 gsdl_system (export_cmd, true, logout);
1234 if (file_exists (tmpfile)) {
1235 text_t returnline = file_tail (tmpfile, 1, 0);
1236 if (returnline.size() > 23 && (substr(returnline.begin(), returnline.begin()+23) == "exportcol.pl succeeded:")) {
1237 // success
1238 message = "exptsuccess";
1239 } else {
1240 message = "exptfail";
1241 }
1242 } else {
1243 message = "exptfail";
1244 }
1245 }
1246
1247 if (message.empty()) {
1248 if (collector_page != "bild") {
1249 // output page ("bild" page was already output above)
1250 textout << outconvert << disp << ("_collector:header_\n")
1251 << ("_collector:" + collector_page + "content_\n")
1252 << ("_collector:footer_\n");
1253 }
1254 } else {
1255 // message was set somewhere (probably an error), output message page
1256 textout << outconvert << disp << ("_collector:header_\n")
1257 << ("_collector:" + message + "content_\n")
1258 << ("_collector:footer_\n");
1259 message.clear();
1260 }
1261 return true;
1262}
1263
1264// if sw = 0 replace all carriage returns in intext with the string "\n"
1265// else replace all occurances of "\n" with a carriage return
1266text_t collectoraction::carriage_replace (const text_t &intext, int sw) {
1267
1268 text_t outtext;
1269 text_t::const_iterator here = intext.begin();
1270 text_t::const_iterator end = intext.end();
1271 while (here != end) {
1272 if (sw == 0) {
1273 if (*here == '\n') {
1274 if ((here+1) != end && *(here+1) == '\r') here ++;
1275 outtext += "\\n";
1276 } else if (*here == '\r') {
1277 if ((here+1) != end && *(here+1) == '\n') here ++;
1278 outtext += "\\n";
1279 } else {
1280 outtext.push_back (*here);
1281 }
1282 } else if (*here == '\\' && (here+1) != end && *(here+1) == 'n') {
1283 outtext.push_back ('\n');
1284 here ++;
1285 } else {
1286 outtext.push_back (*here);
1287 }
1288 here ++;
1289 }
1290 return outtext;
1291}
1292
1293// create a short directory name from fullname
1294text_t collectoraction::get_directory_name (const text_t &fullname) {
1295
1296 text_t shortname;
1297 if (fullname.empty()) {
1298 shortname = "coll";
1299
1300 } else {
1301
1302 // first make all lowercase and remove any dodgy characters
1303 // (i.e. anything not [a-z]
1304 text_t::const_iterator here = fullname.begin();
1305 text_t::const_iterator end = fullname.end();
1306 while (here != end) {
1307 if ((*here >= 'A' && *here <= 'Z') || (*here >= 'a' && *here <= 'z') ||
1308 (*here == ' ')) {
1309 if (*here >= 'A' && *here <= 'Z') shortname.push_back (*here+32);
1310 else if (*here == ' ') {
1311 while ((*(here+1)) == ' ') here ++;
1312 shortname.push_back (*here);
1313 } else shortname.push_back (*here);
1314 }
1315 here ++;
1316 }
1317
1318 text_tarray words;
1319 splitchar (shortname.begin(), shortname.end(), ' ', words);
1320 int num_words = words.size();
1321
1322 if (num_words == 0) {
1323 shortname = "coll";
1324
1325 } else {
1326
1327 shortname.clear();
1328 int use_words = (num_words <= 6) ? num_words : 6;
1329 int substr_len = 6 / use_words;
1330
1331 for (int i = 0; i < use_words; i++) {
1332 if (words[i].size() < substr_len) shortname += words[i];
1333 else shortname += substr (words[i].begin(), words[i].begin()+substr_len);
1334 }
1335 }
1336 }
1337
1338 // check to see if shortname is unique
1339 text_t fulldirname = filename_cat (gsdlhome, "collect", shortname);
1340 if (directory_exists (fulldirname)) {
1341 int version = 0;
1342 text_t newname;
1343 do {
1344 version ++;
1345 newname = shortname;
1346 newname.push_back ('v');
1347 newname.appendint (version);
1348 fulldirname = filename_cat (gsdlhome, "collect", newname);
1349 } while (directory_exists (fulldirname));
1350
1351 shortname = newname;
1352 }
1353
1354 return shortname;
1355}
1356
1357// tests if collection is write protected (currently just checks if
1358// collect.cfg file is writable
1359bool collectoraction::collection_protected (const text_t &collection) {
1360 text_t cfgfile = filename_cat(gsdlhome, "collect", collection, "etc", "collect.cfg");
1361 if (file_writable(cfgfile)) return false;
1362 return true;
1363}
1364
1365// assigns a temporary directory name for this collector session
1366// and creates temporary directory
1367void collectoraction::assign_tmpname (cgiargsclass &args, ostream &logout) {
1368
1369 int i = 0;
1370 text_t tmpname = "tbuild";
1371 while (directory_exists (filename_cat (gsdlhome, "tmp", tmpname + text_t(i)))) {
1372 i++;
1373 }
1374 tmpname.appendint (i);
1375
1376 text_t fulltmpdir = filename_cat (gsdlhome, "tmp", tmpname);
1377 if (!mk_dir (fulltmpdir)) {
1378 outconvertclass text_t2ascii;
1379 logout << text_t2ascii << "collectoraction::assign_tmpname unable to create directory ("
1380 << fulltmpdir << ")\n";
1381 }
1382
1383 args["bc1tmp"] = tmpname;
1384}
1385
1386void collectoraction::gsdl_mkcol (cgiargsclass &args, ostream &logout) {
1387
1388 text_t tmpdir = filename_cat (gsdlhome, "tmp", args["bc1tmp"]);
1389 if (!directory_exists (tmpdir)) {
1390 message = "tmpfail";
1391 return;
1392 }
1393
1394 text_t &collection = args["bc1dirname"];
1395 if (collection.empty()) {
1396 message = "nocollection";
1397 return;
1398 }
1399
1400 // check for a .create file - if it exists then we've already created the collection
1401 text_t createfile = filename_cat (tmpdir, ".create");
1402 if (file_exists (createfile)) {
1403 return;
1404 }
1405
1406 // set up options
1407 text_t options = "-creator \"" + args["bc1contactemail"] + "\"";
1408 options += " -title \"" + args["bc1fullname"] + "\"";
1409 options += " -about \"" + carriage_replace (args["bc1aboutdesc"] + "_collectorextra_", 0) + "\"";
1410 options += " -collectdir \"" + tmpdir + "\" ";
1411 text_t optionfile = filename_cat (tmpdir, "mkcol.opt");
1412 char *optionfilec = optionfile.getcstr();
1413 ofstream ofile_out (optionfilec);
1414 delete optionfilec;
1415 if (!ofile_out) {
1416 message = "tmpfail";
1417 return;
1418 }
1419 outconvertclass text_t2ascii;
1420 ofile_out << text_t2ascii << options << "\n";
1421 ofile_out.close();
1422
1423 // run mkcol.pl
1424 text_t mkcol_cmd = "perl -S mkcol.pl -optionfile \"" + optionfile;
1425 mkcol_cmd += "\" " + collection;
1426 gsdl_system (mkcol_cmd, true, logout);
1427
1428 // make sure it went ok
1429 text_t cfgfile = filename_cat (tmpdir, collection, "etc", "collect.cfg");
1430 if (!file_writable (cfgfile)) {
1431 message = "mkcolfail";
1432 } else {
1433 // create the .create file (this file is just a place holder to let any future
1434 // pages know that the collection already exists).
1435 char *createfilec = createfile.getcstr();
1436 ofstream cfile_out (createfilec);
1437 delete createfilec;
1438 if (cfile_out) {
1439 cfile_out << "collection created\n";
1440 cfile_out.close();
1441 } else {
1442 message = "tmpfail";
1443 return;
1444 }
1445 }
1446}
1447
1448void collectoraction::gsdl_build (cgiargsclass &args, ostream &logout) {
1449
1450 outconvertclass text_t2ascii;
1451
1452 text_t tmpdir = filename_cat (gsdlhome, "tmp", args["bc1tmp"]);
1453 if (!directory_exists (tmpdir)) {
1454 message = "tmpfail";
1455 return;
1456 }
1457
1458 text_t &collection = args["bc1dirname"];
1459 if (collection.empty()) {
1460 message = "nocollection";
1461 return;
1462 }
1463
1464 // check for a .build file - if it exists then we've already built
1465 // the collection (or are in the process of building it)
1466 text_t buildfile = filename_cat (tmpdir, ".build");
1467 if (file_exists (buildfile)) {
1468 return;
1469 } else {
1470 // create the .build file (this file is just a place holder to let any future
1471 // pages know that we've already been here)
1472 char *buildfilec = buildfile.getcstr();
1473 ofstream bfile_out (buildfilec);
1474 delete buildfilec;
1475 if (bfile_out) {
1476 bfile_out << "collection building\n";
1477 bfile_out.close();
1478 } else {
1479 message = "tmpfail";
1480 return;
1481 }
1482 }
1483
1484 const recptconf &rcinfo = recpt->get_configinfo ();
1485
1486 // create the event header file if LogEvents, EmailEvents or
1487 // EmailUserEvents options are turned on.
1488 bool logevents =
1489 (rcinfo.LogEvents == CollectorEvents || rcinfo.LogEvents == AllEvents ||
1490 rcinfo.EmailEvents == CollectorEvents || rcinfo.EmailEvents == AllEvents ||
1491 rcinfo.EmailUserEvents);
1492 text_t ehead_file = filename_cat (tmpdir, "ehead.txt");
1493 if (logevents) {
1494 if (!create_event_header_file (ehead_file, args, logout)) {
1495 logevents = false;
1496 }
1497 }
1498
1499 // set up build options
1500 text_t options = "-remove_import -out \"";
1501 options += filename_cat (tmpdir, collection + ".bld");
1502 options += "\" -collectdir \"" + get_collectdir (args) + "\"";
1503
1504 if (args["bc1esrce"] == 1) {
1505 // we're adding data to an existing collection
1506 options += " -save_archives -append";
1507 }
1508
1509 if (!args["bc1inputdir1"].empty())
1510 options += " -download \"" + args["bc1inputdir1"] + "\"";
1511 if (!args["bc1inputdir2"].empty())
1512 options += " -download \"" + args["bc1inputdir2"] + "\"";
1513 if (!args["bc1inputdir3"].empty())
1514 options += " -download \"" + args["bc1inputdir3"] + "\"";
1515 if (!args["bc1inputdir4"].empty())
1516 options += " -download \"" + args["bc1inputdir4"] + "\"";
1517
1518 if (logevents) {
1519 if (rcinfo.LogEvents == CollectorEvents || rcinfo.LogEvents == AllEvents)
1520 options += " -log_events";
1521 if (rcinfo.EmailEvents == CollectorEvents || rcinfo.EmailEvents == AllEvents) {
1522 options += " -mail_server " + rcinfo.MailServer;
1523 options += " -email_events " + rcinfo.maintainer;
1524 if (rcinfo.EmailUserEvents) options += "," + args["bc1contactemail"];
1525 } else if (rcinfo.EmailUserEvents) {
1526 options += " -mail_server " + rcinfo.MailServer;
1527 options += " -email_events " + args["bc1contactemail"];
1528 }
1529 options += " -event_header " + ehead_file;
1530 }
1531
1532 text_t optionfile = filename_cat (tmpdir, "build.opt");
1533 char *optionfilec = optionfile.getcstr();
1534 ofstream ofile_out (optionfilec);
1535 delete optionfilec;
1536 if (!ofile_out) {
1537 message = "tmpfail";
1538 return;
1539 }
1540 ofile_out << text_t2ascii << options << "\n";
1541 ofile_out.close();
1542
1543 // if we're altering an existing collection we need to kill off
1544 // the existing collection server - we do this for the local library
1545 // (and any other persistent version of the library) as the existing
1546 // gdbm file can't be deleted while the collection server holds it open
1547 if ((args["bc1econf"] == 1) || (args["bc1esrce"] == 1)) {
1548 remove_colservr (collection, logout);
1549 }
1550
1551 // set up the build command - build.bat has some issues with quoting
1552 // on win2k when gsdlhome contains spaces so we'll avoid using
1553 // "perl -S" here in favor of calling the "build" perl script explicitly
1554 text_t build_cmd = "perl \"" + filename_cat (gsdlhome, "bin", "script", "build");
1555 build_cmd += "\" -optionfile \"" + optionfile + "\" " + collection;
1556 // run build command in background (i.e. asynchronously)
1557 gsdl_system (build_cmd, false, logout);
1558}
1559
1560void collectoraction::gsdl_cancel_build (cgiargsclass &args, ostream &logout) {
1561 // I really wanted to do what this perl script does from within the library
1562 // c++ code. I ran into some problems though (like how do you write a portable
1563 // "rm -r" in c++?). One day I'll spend some time sorting it out ... maybe.
1564 text_t cancel_cmd = "perl -S cancel_build.pl -collectdir \"";
1565 cancel_cmd += filename_cat (gsdlhome, "tmp", args["bc1tmp"]) + "\" ";
1566 cancel_cmd += args["bc1dirname"];
1567 // To be on the safe side we'll make this a synchronous call
1568 // so that all tidying up is done before the user has a chance
1569 // to do anything else (like start rebuilding their collection).
1570 // This means that for a big collection where there's lots of
1571 // stuff to delete etc. it might take a while before the "build
1572 // cancelled" page appears.
1573 gsdl_system (cancel_cmd, true, logout);
1574}
1575
1576text_t collectoraction::get_collectdir (cgiargsclass &args) {
1577
1578 if ((args["bc1econf"] == 1) || (args["bc1esrce"] == 1)) {
1579 // we're adding to a collection in place
1580 return filename_cat(gsdlhome, "collect");
1581
1582 } else {
1583 return filename_cat (gsdlhome, "tmp", args["bc1tmp"]);
1584 }
1585}
1586
1587// checks to see if any of the plugins in pluginset occur in
1588// collections configuration file
1589bool collectoraction::uses_weird_plugin (const text_t &collection) {
1590
1591 text_tset pluginset;
1592 pluginset.insert ("HBPlug");
1593
1594 text_t cfgfile_content;
1595 text_t cfgfile_name = filename_cat (gsdlhome, "collect", collection, "etc", "collect.cfg");
1596 text_t pluginstr, pluginname;
1597
1598 if (read_file (cfgfile_name, cfgfile_content)) {
1599 text_t::const_iterator here = cfgfile_content.begin();
1600 text_t::const_iterator end = cfgfile_content.end();
1601 while (here != end) {
1602 here = findchar (here, end, 'p');
1603 if (here == end) break;
1604 if ((here+6 < end) && (substr (here, here+6) == "plugin")) {
1605 getdelimitstr (here+6, end, '\n', pluginstr);
1606 text_t::const_iterator hp = pluginstr.begin();
1607 text_t::const_iterator ep = pluginstr.end();
1608 bool found = false;
1609 // remove any leading whitespace, trailing options etc.
1610 while (hp != ep) {
1611 if (*hp == '\t' || *hp == ' ' || *hp == '\n') {
1612 if (found) break;
1613 } else {
1614 pluginname.push_back (*hp);
1615 found = true;
1616 }
1617 hp ++;
1618 }
1619 text_tset::const_iterator it = pluginset.find (pluginname);
1620 if (it != pluginset.end()) return true; // found matching plugin
1621 pluginname.clear();
1622 }
1623 here ++;
1624 }
1625 }
1626 return false;
1627}
1628
1629// create and initialize a new collection server and
1630// add it to the null protocol.
1631void collectoraction::create_colserver (const text_t &collection, ostream &logout) {
1632
1633 recptprotolistclass *protos = recpt->get_recptprotolist_ptr();
1634 recptprotolistclass::iterator rprotolist_here = protos->begin();
1635 recptprotolistclass::iterator rprotolist_end = protos->end();
1636 while (rprotolist_here != rprotolist_end) {
1637 comerror_t err = noError;
1638 if ((*rprotolist_here).p != NULL) {
1639 if ((*rprotolist_here).p->get_protocol_name (err) == "nullproto") {
1640 // create collection server and add it to nullproto
1641 (*rprotolist_here).p->add_collection (collection, recpt, gsdlhome, gsdlhome);
1642 // make sure gsdlhome is configured
1643 text_tarray tmp;
1644 tmp.push_back (gsdlhome);
1645 (*rprotolist_here).p->configure ("gsdlhome", tmp, err);
1646 // re-initialize the null protocol
1647 if (!(*rprotolist_here).p->init (err, logout)) {
1648 logout << "collectoraction::create_colserver: nullproto init failed\n";
1649 }
1650 return;
1651 }
1652 }
1653 rprotolist_here ++;
1654 }
1655
1656 logout << "collectoraction::create_colserver: no valid nullproto found\n";
1657}
1658
1659// delete a collection server from the null protocol
1660void collectoraction::remove_colservr (const text_t &collection, ostream &logout) {
1661
1662 recpt->uncache_collection (collection);
1663
1664 recptprotolistclass *protos = recpt->get_recptprotolist_ptr();
1665 recptprotolistclass::iterator rprotolist_here = protos->begin();
1666 recptprotolistclass::iterator rprotolist_end = protos->end();
1667 while (rprotolist_here != rprotolist_end) {
1668 comerror_t err = noError;
1669 if ((*rprotolist_here).p != NULL) {
1670 if ((*rprotolist_here).p->get_protocol_name (err) == "nullproto") {
1671 (*rprotolist_here).p->remove_collection (collection, logout);
1672 return;
1673 }
1674 }
1675 rprotolist_here ++;
1676 }
1677
1678 logout << "collectoraction::create_colserver: no valid nullproto found\n";
1679}
1680
1681bool collectoraction::create_event_header_file (const text_t &filename, cgiargsclass &args,
1682 ostream &logout) {
1683
1684 outconvertclass text_t2ascii;
1685 char *filenamec = filename.getcstr();
1686 ofstream eheadfile (filenamec);
1687 delete filenamec;
1688
1689 if (eheadfile) {
1690 eheadfile << text_t2ascii << get_event_header (args);
1691 eheadfile.close();
1692 return true;
1693 }
1694
1695 logout << text_t2ascii << "collectoraction::create_event_header ERROR: Couldn't create "
1696 << "Event Header file " << filename << ". Event logging disabled\n";
1697 return false;
1698}
1699
1700text_t collectoraction::get_event_header (cgiargsclass &args) {
1701 text_t header = "Greenstone Username: " + args["un"] + "\n";
1702 header += "Collection: " + args["bc1dirname"] + "\n";
1703 header += "Collection Creator: " + args["bc1contactemail"] + "\n";
1704 header += "GSDLHOME: " + gsdlhome + "\n";
1705 header += "Build Location: " + get_collectdir(args) + "\n";
1706
1707 return header;
1708}
Note: See TracBrowser for help on using the repository browser.