source: main/tags/2.32/gsdl/src/recpt/collectoraction.cpp@ 25600

Last change on this file since 25600 was 2303, checked in by sjboddie, 23 years ago

* empty log message *

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