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

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

The build process now creates a summary of how many files were included,
which were rejected, etc. A link to a page containing this summary is
provided from the final page of the collector (once the collection is built
successfully) and from the default "about this collection" text for
collections built by the collector.

Also did a little bit of tidying in a couple of places

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