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

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

The recent bug fix to prevent collector problems when source directories
ended in a '\' didn't work on some systems (it seems that each different
version of windows has different rules where command line quoting is
converned!). It now simply removes any trailing '\' from each path rather
than attempting to escape all '\'s

  • Property svn:keywords set to Author Date Id Revision
File size: 65.5 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 char c = fbld_in.get();
460 fbld_in.close();
461 if (c == '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 if (c == '4' || c == '5') message = "installfail";
470 else current_page = "bildfail";
471 } else {
472 // assume build failed (we shouldn't get here though ... right?)
473 current_page = "bildfail";
474 }
475 delete fbldc;
476 }
477 }
478
479 if (args["bc1fromsrce"] == "1") {
480
481 // we've just come from the "source data" page so we need to check that
482 // input sources are valid
483 if (!check_sources(args, logout)) {
484 args["p"] = "srce";
485 }
486 }
487
488 return true;
489}
490
491void collectoraction::update_cfgfile_clone (cgiargsclass &args, ostream &logout) {
492
493 text_t tmpdir = filename_cat(gsdlhome, "tmp", args["bc1tmp"]);
494 text_t cfgfile = filename_cat(tmpdir, args["bc1dirname"], "etc", "collect.cfg");
495 if (!file_exists (cfgfile)) {
496 message = "tmpfail";
497 return;
498 }
499
500 text_t cfgfile_org = filename_cat (tmpdir, "collect.cfg.org");
501
502 if (args["bc1clone"] == "1") {
503 // cloning was turned on
504
505 text_t cfgfile_clone = filename_cat(gsdlhome, "collect", args["bc1clonecol"], "etc", "collect.cfg");
506 if (file_exists (cfgfile_clone)) {
507 // if .org file doesn't exist already create it
508 if (!file_exists (cfgfile_org)) {
509 if (!file_copy (cfgfile, cfgfile_org)) {
510 message = "tmpfail";
511 return;
512 }
513 }
514 // copy clone collections cfg file to new collection
515 if (!file_copy (cfgfile_clone, cfgfile)) {
516 message = "tmpfail";
517 return;
518 }
519 // update the new cfg file
520 update_cfgfile_partial (args, true, logout);
521
522 } else {
523 // can't clone non-existant or read-protected collection
524 message = "clonefail";
525 }
526
527 } else {
528 // cloning has been turned off having been on at some point. the .org file
529 // should exist, if it doesn't we'll bail out and leave the user with the
530 // cloned copy
531 if (file_exists (cfgfile_org)) {
532 // copy original back again and update it with any recent changes
533 if (file_copy (cfgfile_org, cfgfile)) {
534 update_cfgfile_partial (args, false, logout);
535 } else {
536 message = "tmpfail";
537 }
538 }
539 }
540}
541
542// update configuration file on disk to match bc1 arguments
543// there's a special case if the clone option is true as certain parts of a
544// config file should not be cloned (e.g. the iconcollection stuff)
545void collectoraction::update_cfgfile_partial (cgiargsclass &args, bool clone, ostream &logout) {
546
547 text_t cfgfile = filename_cat(get_collectdir(args), args["bc1dirname"], "etc", "collect.cfg");
548 char *cfgfilec = cfgfile.getcstr();
549
550#if defined (__WIN32__)
551 // make sure collect.cfg isn't read-only
552 _chmod (cfgfilec, _S_IREAD | _S_IWRITE);
553#endif
554
555 vector<text_tarray> cfgarray;
556
557 // read in cfg file
558 ifstream cfg_in (cfgfilec);
559 if (cfg_in) {
560 text_tarray cfgline;
561 while (read_cfg_line(cfg_in, cfgline) >= 0) {
562 if (cfgline.size () >= 2) {
563 if (cfgline[0] == "creator" || cfgline[0] == "maintainer") {
564 cfgline[1] = args["bc1contactemail"];
565 } else if (cfgline[0] == "collectionmeta") {
566 if (cfgline[1] == "collectionname") {
567 cfgline[2] = args["bc1fullname"];
568 } else if (cfgline[1] == "collectionextra") {
569 cfgline[2] = carriage_replace (args["bc1aboutdesc"], 0);
570 } else if (clone && (cfgline[1] == "iconcollection" ||
571 cfgline[1] == "iconcollectionsmall")) {
572 cfgline[2] = "";
573 }
574 }
575 }
576 cfgarray.push_back (cfgline);
577 }
578 cfg_in.close();
579
580 // now write cfg file back out
581#ifdef __WIN32__
582 ofstream cfg_out (cfgfilec, ios::binary);
583#else
584 ofstream cfg_out (cfgfilec);
585#endif
586 if (cfg_out) {
587 // lock the file
588 int fd = GSDL_GET_FILEDESC(cfg_out);
589 int lock_val = 1;
590 GSDL_LOCK_FILE (fd);
591 if (lock_val != 0) {
592 logout << "Error: Couldn't lock file " << cfgfilec << "\n";
593 cfg_out.close();
594 message = "tmpfail";
595
596 } else {
597
598 vector<text_tarray>::const_iterator this_line = cfgarray.begin();
599 vector<text_tarray>::const_iterator end_line = cfgarray.end();
600 while (this_line != end_line) {
601 write_cfg_line (cfg_out, *this_line);
602 this_line ++;
603 }
604 GSDL_UNLOCK_FILE (fd);
605 cfg_out.close();
606 }
607
608 } else {
609 logout << "collectoraction::update_cfgfile_partial: unable to open "
610 << cfgfilec << " for output\n";
611 message = "tmpfail";
612 }
613
614 } else {
615 logout << "collectoraction::update_cfgfile_partial: unable to open "
616 << cfgfilec << " for input\n";
617 message = "tmpfail";
618 }
619
620 delete cfgfilec;
621}
622
623// replace configuration file on disk with that in the cfgfile argument and
624// catch other bc1 arguments up with those the new cfgfile contains
625void collectoraction::update_cfgfile_complete (cgiargsclass &args, ostream &logout) {
626
627 text_t cfgfile = filename_cat(get_collectdir(args), args["bc1dirname"], "etc", "collect.cfg");
628 char *cfgfilec = cfgfile.getcstr();
629
630#ifdef __WIN32__
631 // make sure collect.cfg isn't read-only
632 _chmod (cfgfilec, _S_IREAD | _S_IWRITE);
633 ofstream cfg_out (cfgfilec, ios::binary);
634#else
635 ofstream cfg_out (cfgfilec);
636#endif
637
638 if (cfg_out) {
639 // lock the file
640 int fd = GSDL_GET_FILEDESC(cfg_out);
641 int lock_val = 1;
642 GSDL_LOCK_FILE (fd);
643 if (lock_val != 0) {
644 logout << "Error: Couldn't lock file " << cfgfilec << "\n";
645 cfg_out.close();
646 message = "tmpfail";
647
648 } else {
649
650 outconvertclass text_t2ascii;
651 cfg_out << text_t2ascii << args["cfgfile"];
652 GSDL_UNLOCK_FILE (fd);
653 cfg_out.close();
654
655 // now that we've written the file we'll read it back again and
656 // update our bc1 arguments
657 ifstream cfg_in (cfgfilec);
658 if (cfg_in) {
659 text_tarray cfgline;
660 while (read_cfg_line(cfg_in, cfgline) >= 0) {
661 if (cfgline.size () >= 2) {
662 if (cfgline[0] == "creator") {
663 args["bc1contactemail"] = cfgline[1];
664 } else if (cfgline[0] == "collectionmeta") {
665 if (cfgline[1] == "collectionname") {
666 args["bc1fullname"] = cfgline[2];
667 } else if (cfgline[1] == "collectionextra") {
668 args["bc1aboutdesc"] = carriage_replace (cfgline[2], 1);
669 }
670 }
671 }
672 }
673 cfg_in.close();
674 } else {
675 logout << "collectoraction::update_cfgfile_complete: unable to open "
676 << cfgfilec << " for input\n";
677 message = "tmpfail";
678 }
679 }
680 } else {
681 logout << "collectoraction::update_cfgfile_complete: unable to open "
682 << cfgfilec << " for output\n";
683 message = "tmpfail";
684 }
685
686 delete cfgfilec;
687}
688
689void collectoraction::get_cgihead_info (cgiargsclass &/*args*/, recptprotolistclass * /*protos*/,
690 response_t &response,text_t &response_data,
691 ostream &/*logout*/) {
692 response = content;
693 response_data = "text/html";
694}
695
696// return html for buttons used in collector bar
697// color may be "green", "grey", or "yellow"
698// type may be:
699// "info" --> "collection information" button
700// "srce" --> "source data" button
701// "conf" --> "configure collection" button
702// "bild" --> "build collection" button
703// "view" --> "view collection" button
704// if enabled is true button will be flashy rollover type and
705// will be hyperlinked
706
707text_t collectoraction::get_button (const text_t &thispage, const text_t &color,
708 const text_t &type, bool enabled) {
709
710 if ((color != "green" && color != "grey" && color != "yellow") ||
711 (type != "info" && type != "srce" && type != "conf" && type != "bild" && type != "view"))
712 return "";
713
714 text_t prefix = "gc";
715 if (color == "grey") prefix = "nc";
716 else if (color == "yellow") prefix = "yc";
717
718 text_t httpicon = "httpicon" + prefix + type;
719
720 if (enabled) {
721 text_t gsmacro = "_gsimage_";
722 if (thispage == "info" || thispage == "srce" || thispage == "conf" ||
723 thispage == "bildcancel" || thispage == "bildfail") {
724 gsmacro = "_gsjimage_";
725 } else if (type == "view") {
726 // view button is special case as it needs a target=_top
727 gsmacro = "_gstimage_";
728 }
729 return "<td>" + gsmacro + "(_collector:http" + type + "_,_collector:" + httpicon +
730 "of_,_collector:" + httpicon + "on_," + type + ",_collector:text" + type + "_)</td>\n";
731 } else {
732 return "<td>_icon" + prefix + type + "of_</td>\n";
733 }
734}
735
736// set the _fullnamemenu_ macro (and _warnindex_ and _selectedindex_ if
737// we're on the "srce" page)
738void collectoraction::set_fullnamemenu (displayclass &disp, cgiargsclass &args,
739 recptprotolistclass *protos, ostream &logout) {
740
741 if (recpt == NULL) {
742 logout << "ERROR (collectoraction::set_fullnamemenu): This action does not contain\n"
743 << " information about any receptionists. The method set_receptionist was\n"
744 << " probably not called from the module which instantiated this action.\n";
745 return;
746 }
747
748 text_t &current_page = args["p"];
749 text_t currentname = args["bc1dirname"];
750 if (current_page == "srce") currentname = args["bc1clonecol"];
751
752 text_tarray dirnames;
753 text_tarray fullnames;
754 vector<bool> write_protected;
755 bool is_selected = false;
756 int selected_index = 0;
757 int index = 0;
758
759 recptprotolistclass::iterator rprotolist_here = protos->begin();
760 recptprotolistclass::iterator rprotolist_end = protos->end();
761 while (rprotolist_here != rprotolist_end) {
762 if ((*rprotolist_here).p != NULL) {
763
764 // don't include z39.50 collections
765 comerror_t err = noError;
766 if ((*rprotolist_here).p->get_protocol_name (err) == "z3950proto") {
767 rprotolist_here ++;
768 continue;
769 }
770
771 text_tarray collist;
772 (*rprotolist_here).p->get_collection_list (collist, err, logout);
773 if (err == noError) {
774 text_tarray::iterator collist_here = collist.begin();
775 text_tarray::iterator collist_end = collist.end();
776 FilterResponse_t response;
777 text_tset metadata;
778 metadata.insert ("collectionname");
779 while (collist_here != collist_end) {
780 ColInfoResponse_t *cinfo = recpt->get_collectinfo_ptr ((*rprotolist_here).p, *collist_here, logout);
781 if (cinfo != NULL) {
782 text_t collectionname = *collist_here;
783 if (!cinfo->collectionmeta["collectionname"].empty()) {
784 // get collection name from the collection cfg file
785 collectionname = cinfo->collectionmeta["collectionname"];
786 } else if (get_info ("collection", *collist_here, metadata, false,
787 (*rprotolist_here).p, response, logout)) {
788 // get collection name from gdbm file
789 collectionname = response.docInfo[0].metadata["collectionname"].values[0];
790 }
791 dirnames.push_back(*collist_here);
792 fullnames.push_back(collectionname);
793 // check to see if the collection is writable
794 if (collection_protected (*collist_here)) write_protected.push_back(true);
795 else write_protected.push_back(false);
796
797 if (*collist_here == currentname) {
798 is_selected = true;
799 selected_index = index;
800 }
801 index ++;
802 }
803 collist_here ++;
804 }
805 }
806 }
807 rprotolist_here ++;
808 }
809
810 bool first = true;
811 text_t warnindex;
812 text_t fullnamemenu = "<select name=\"bc1dirname\">\n";
813 if (current_page == "srce") {
814 fullnamemenu = "<select name=\"bc1clonecol\" onChange=\"menuchange();\">\n";
815 fullnamemenu += "<option value=defaultstructure";
816 if (!is_selected) fullnamemenu += " selected>";
817 else fullnamemenu.push_back('>');
818 fullnamemenu += "default structure\n";
819 }
820 for (int i = 0; i < index; i ++) {
821 // don't want write protected collections in list on "change existing
822 // collection" page
823 if (write_protected[i] && current_page == "existing") continue;
824 fullnamemenu += "<option value=\"" + dirnames[i] + "\"";
825 if ((i == 0 && !is_selected && current_page != "srce") ||
826 (is_selected && i == selected_index)) {
827 fullnamemenu += " selected";
828 selected_index++;
829 is_selected = false;
830 }
831 fullnamemenu.push_back ('>');
832 fullnamemenu += fullnames[i];
833 fullnamemenu.push_back ('\n');
834
835 // add to Warnindex if collection uses any dubious plugins
836 // (if creating clone collection list)
837 if (current_page == "srce") {
838 if (first) warnindex += "0,";
839 else warnindex.push_back(',');
840 if (uses_weird_plugin (dirnames[i])) {
841 warnindex += text_t (1);
842 } else {
843 warnindex += text_t (0);
844 }
845 }
846 first = false;
847 }
848 fullnamemenu += "</select>\n";
849
850 disp.setmacro ("fullnamemenu", "collector", fullnamemenu);
851 if (current_page == "srce") {
852 disp.setmacro ("warnindex", "collector", warnindex);
853 disp.setmacro ("selectedindex", "collector", text_t(selected_index));
854 }
855}
856
857// set _sourcelist_ and _badsources_ macros
858void collectoraction::set_inputsourceboxes (displayclass &disp, cgiargsclass &args,
859 ostream &logout) {
860
861 if (badsources) disp.setmacro ("badsources", "collector", "1");
862
863 text_t sourcelist = get_source_box(args["bc1input"], args["bc1inputnum"].getint(),
864 args["bc1inputtype"]);
865
866 disp.setmacro("sourcelist", "collector", sourcelist);
867
868 // reset badsources and failedsources variables
869 badsources = false;
870 failedsources.erase(failedsources.begin(), failedsources.end());
871}
872
873text_t collectoraction::get_source_box (text_t inputarglist, int numboxes,
874 text_t inputtypelist) {
875
876 text_tarray inputvalues;
877 splitchar (inputarglist.begin(), inputarglist.end(), ',', inputvalues);
878 // remove any empty values from the end of the array
879 if (inputvalues.size()) {
880 text_tarray::iterator l = inputvalues.end() - 1;
881 text_tarray::iterator b = inputvalues.begin();
882 while ((*l).empty() && l >= b) {
883 l--;
884 }
885 inputvalues.erase(l+1, inputvalues.end());
886 }
887
888 text_tarray inputtypes;
889 splitchar (inputtypelist.begin(), inputtypelist.end(), ',', inputtypes);
890
891 int numvalues = inputvalues.size();
892 int numtypes = inputtypes.size();
893
894 text_t last = "file://";
895 text_t rv;
896 for (int i = 0; i < numboxes; i++) {
897 rv += "<nobr><select name=\"bc1inputtype\">\n";
898 rv += "<option value=\"file://\"";
899 if ((i < numtypes && inputtypes[i] == "file://") ||
900 (numboxes == 3 && i == 0 && numvalues == 0) ||
901 (i >= 3 && i >= numvalues && last == "file://")) {
902 rv += " selected";
903 last = "file://";
904 }
905 rv += ">file://\n";
906 rv += "<option value=\"http://\"";
907 if ((i < numtypes && inputtypes[i] == "http://") ||
908 (numboxes == 3 && i == 1 && numvalues == 0) ||
909 (i >= 3 && i >= numvalues && last == "http://")) {
910 rv += " selected";
911 last = "http://";
912 }
913 rv += ">http://\n";
914 rv += "<option value=\"ftp://\"";
915 if ((i < numtypes && inputtypes[i] == "ftp://") ||
916 (numboxes == 3 && i == 2 && numvalues == 0) ||
917 (i >= 3 && i >= numvalues && last == "ftp://")) {
918 rv += " selected";
919 last = "ftp://";
920 }
921 rv += ">ftp://\n";
922 rv += "</select>\n";
923 rv += "<input type=text name=\"bc1input\" value=\"";
924 if (i < numvalues) {
925 rv += dm_safe(decode_commas(inputvalues[i]));
926 }
927 rv += "\" size=50>";
928 if (badsources) {
929 if ((i < numvalues) && (!inputvalues[i].empty())) {
930 if (failedsources[decode_commas(inputvalues[i])] == "1") {
931 rv += "_iconcross_";
932 } else {
933 rv += "_icontick_";
934 }
935 } else {
936 rv += "_iconblank_";
937 }
938 }
939 if (i+1 == numboxes) {
940 if (!badsources) rv += "_iconblank_";
941 rv += "_imagemore_</nobr><br>";
942 } else {
943 rv += "</nobr><br>\n";
944 }
945 }
946
947 return rv;
948}
949
950// set the _cfgfile_ macro
951void collectoraction::set_cfgfile (displayclass &disp, cgiargsclass &args, ostream &logout) {
952
953 text_t &collection = args["bc1dirname"];
954 if (collection.empty()) {
955 message = "nocollection";
956 return;
957 }
958
959 // read in collect.cfg
960 text_t cfgfile = filename_cat(get_collectdir(args), collection, "etc", "collect.cfg");
961 char *cfgfilec = cfgfile.getcstr();
962
963#ifdef GSDL_USE_IOS_H
964 ifstream cfg_ifs (cfgfilec, ios::in | ios::nocreate);
965#else
966 ifstream cfg_ifs (cfgfilec, ios::in);
967#endif
968
969 if (cfg_ifs) {
970 // read in collect.cfg
971 text_t cfgtext;
972 char c;
973 cfg_ifs.get(c);
974 while (!cfg_ifs.eof ()) {
975 cfgtext.push_back(c);
976 cfg_ifs.get(c);
977 }
978 cfg_ifs.close();
979
980 // define it as a macro
981 disp.setmacro("cfgfile", "collector", dm_safe(cfgtext));
982
983 } else {
984 logout << "collectoraction::set_cfgfile: couldn't open configuration file ("
985 << cfgfilec << ") for reading\n";
986 message = "tmpfail";
987 }
988 delete cfgfilec;
989}
990
991// set the _statusline_ macro
992void collectoraction::set_statusline (displayclass &disp, cgiargsclass &args, ostream & /*logout*/) {
993
994 // the build command creates .bld.download, .bld.import, and .bld.build files (in that
995 // order) and deletes them (also in that order) when each stage is complete. the .bld
996 // file is the concatenation of all these files.
997 text_t bld_file = filename_cat (gsdlhome, "tmp", args["bc1tmp"], args["bc1dirname"] + ".bld");
998 text_t statusline;
999
1000 if (file_exists (bld_file + ".download")) {
1001 statusline = "Downloading files ...<br>\n";
1002 statusline += file_tail (bld_file + ".download", 1, 0);
1003 } else if (file_exists (bld_file + ".import")) {
1004 statusline = "Importing collection ...<br>\n";
1005 statusline += file_tail (bld_file + ".import", 1, 0);
1006 } else if (file_exists (bld_file + ".build")) {
1007 statusline = "Building collection ...<br>\n";
1008 statusline += file_tail (bld_file + ".build", 1, 0);
1009 } else {
1010 statusline += "creating collection ...<br>\n";
1011 statusline += file_tail (bld_file, 1, 0);
1012 }
1013
1014 disp.setmacro ("statusline", "collector", dm_safe(statusline));
1015
1016}
1017
1018void collectoraction::define_internal_macros (displayclass &disp, cgiargsclass &args,
1019 recptprotolistclass *protos, ostream &logout) {
1020
1021 // define_internal_macros sets the following macros:
1022 // _collectorbar_
1023 // _pagescriptextra_
1024 // _fullnamemenu_ -- if displaying the "source data" page or the "changing existing
1025 // collection" page
1026 // _cfgfile_ -- if displaying the "configure collection" page
1027 // _statusline_ -- if displaying the bildstatus page
1028 // _header_ -- may be set for pages that require it
1029 // _faillog_ - set to last 6 lines of .bld file if build failed
1030 // _gsdlhome_ - the gsdlhome path (dm_safe)
1031
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 if (collector_page == "bildfail") {
1157 text_t bldlog = filename_cat(gsdlhome, "tmp", args["bc1tmp"], args["bc1dirname"] + ".bld");
1158 text_t rawlog = file_tail (bldlog, 6, 0);
1159 // we'll shove in some <br> tags where \n's occur
1160 text_t faillog;
1161 text_t::const_iterator here = rawlog.begin();
1162 text_t::const_iterator end = rawlog.end();
1163 while (here != end) {
1164 if (*here == '\n') faillog += "<br>";
1165 faillog.push_back (*here);
1166 here ++;
1167 }
1168 disp.setmacro ("faillog", "collector", dm_safe(faillog));
1169 }
1170
1171 collectorbar += "</tr></table>\n";
1172 disp.setmacro ("collectorbar", "collector", collectorbar);
1173
1174 if (collector_page == "srce" || collector_page == "existing")
1175 set_fullnamemenu (disp, args, protos, logout);
1176 if (collector_page == "conf")
1177 set_cfgfile (disp, args, logout);
1178 if (collector_page == "bildstatus")
1179 set_statusline (disp, args, logout);
1180 if (collector_page == "srce") {
1181 set_inputsourceboxes (disp, args, logout);
1182 }
1183
1184 disp.setmacro ("gsdlhome", "collector", dm_safe(gsdlhome));
1185}
1186
1187bool collectoraction::do_action (cgiargsclass &args, recptprotolistclass * /*protos*/,
1188 browsermapclass * /*browsers*/, displayclass &disp,
1189 outconvertclass &outconvert, ostream &textout,
1190 ostream &logout) {
1191
1192 // make sure the collector is enabled
1193 if (disabled) {
1194 textout << outconvert
1195 << "<html>\n"
1196 << "<head>\n"
1197 << "<title>Collector disabled</title>\n"
1198 << "</head>\n"
1199 << "<body bgcolor=\"#ffffff\" text=\"#000000\" link=\"#006666\" "
1200 << "alink=\"#cc9900\" vlink=\"#666633\">\n"
1201 << "<h2>Facility disabled</h2>\n"
1202 << "Sorry, the Collector end-user collection building facility is currently disabled\n"
1203 << "\n</body>\n"
1204 << "</html>\n";
1205 return true;
1206 }
1207
1208 text_t &collector_page = args["p"];
1209 text_t &collection = args["bc1dirname"];
1210
1211 // make sure we have perl (we won't bother with this check for the
1212 // building status pages to avoid slowing things down unneccessarily)
1213 if (collector_page != "bildstatus" && collector_page != "bildframe1" && !perl_ok(logout)) {
1214 textout << outconvert
1215 << "<html>\n"
1216 << "<head>\n"
1217 << "<title>Perl not found</title>\n"
1218 << "</head>\n"
1219 << "<body bgcolor=\"#ffffff\" text=\"#000000\" link=\"#006666\" "
1220 << "alink=\"#cc9900\" vlink=\"#666633\">\n"
1221 << "<h2>Perl not found</h2>\n"
1222 << "Greenstone could not detect perl on this system. The Collector\n"
1223 << "end-user collection building facility is therefore not available.\n"
1224 << "\n</body>\n"
1225 << "</html>\n";
1226 return true;
1227
1228 }
1229
1230 if (collector_page == "bild") {
1231 // do the work (download, import, build)
1232 gsdl_build (args, logout);
1233
1234 if (message.empty()) {
1235 // bild page is a frameset so we don't want headers and stuff
1236 textout << outconvert << disp << ("_collector:bildcontent_\n");
1237 }
1238 }
1239
1240 if (do_mkcol == true) {
1241 // execute mkcol.pl (do_mkcol is set from within check_cgiargs)
1242 gsdl_mkcol (args, logout);
1243 do_mkcol = false; // reset for fast-cgi
1244 }
1245
1246 if (args["bc1dodelete"] == "1") {
1247 // delete bcidirname collection
1248 if (collection_protected (collection)) {
1249 message = "delinvalid";
1250
1251 } else {
1252
1253 const recptconf &rcinfo = recpt->get_configinfo ();
1254 bool emailuserevents = rcinfo.EmailUserEvents;
1255
1256 // get collection maintainer email from collect.cfg before we
1257 // delete it
1258 text_t colmaintainer;
1259 text_t cfgfile = filename_cat(gsdlhome, "collect", collection, "etc", "collect.cfg");
1260 char *cfgfilec = cfgfile.getcstr();
1261 ifstream cfg_in (cfgfilec);
1262 delete cfgfilec;
1263 if (cfg_in) {
1264 text_tarray cfgline;
1265 while (read_cfg_line(cfg_in, cfgline) >= 0) {
1266 if (cfgline.size () == 2 && cfgline[0] == "maintainer") {
1267 colmaintainer = cfgline[1];
1268 break;
1269 }
1270 }
1271 cfg_in.close();
1272 }
1273 if (colmaintainer.empty()) {
1274 logout << outconvert
1275 << "collectoraction::do_action WARNING: Collection being deleted ("
1276 << collection << ") has no maintainer address. EmailUserEvents "
1277 << "disabled\n";
1278 emailuserevents = false;
1279 }
1280
1281 // first we need to free up the collection's collection server
1282 // we must do this for the local library (and I guess when using
1283 // fastcgi too) as you can't delete the gdbm file while it's
1284 // being kept open by the collection server
1285 remove_colservr (collection, logout);
1286
1287 text_t delete_cmd = "perl -S delcol.pl -f " + collection;
1288 int rv = gsdl_system (delete_cmd, true, logout);
1289 if (rv != 0) {
1290 // deletion failed -- permissions?
1291 message = "delpermission";
1292 } else {
1293 message = "delsuccess";
1294 }
1295
1296 // log the event
1297 if (rcinfo.LogEvents == CollectorEvents || rcinfo.LogEvents == AllEvents) {
1298
1299 text_t eventlog = filename_cat (gsdlhome, "etc", "events.txt");
1300 char *eventlogt = eventlog.getcstr();
1301 ofstream eventl (eventlogt, ios::app);
1302 delete eventlogt;
1303
1304 if (eventl) {
1305 eventl << outconvert << "[Collector Event]\n"
1306 << "Date: " << get_date (true) << "\n"
1307 << "Greenstone Username: " << args["un"] << "\n"
1308 << "Collection: " << collection << "\n"
1309 << "Collection Maintainer: " << colmaintainer << "\n"
1310 << "GSDLHOME: " << gsdlhome << "\n";
1311
1312 if (message == "delsuccess") {
1313 eventl << outconvert
1314 << "The " << collection << " collection was successfully deleted\n\n";
1315 } else {
1316 eventl << outconvert
1317 << "Attempt to delete the " << collection << " collection failed\n\n";
1318 }
1319 eventl.close();
1320
1321 } else {
1322 logout << outconvert << "collectoraction::do_action ERROR: Couldn't open "
1323 << "event log file " << eventlog << " for appending during collection "
1324 << "deletion. LogEvents disabled\n";
1325 }
1326 }
1327
1328 if (rcinfo.EmailEvents == CollectorEvents || rcinfo.EmailEvents == AllEvents || emailuserevents) {
1329 // use sendmail.pl perl script to send email events
1330 text_t tmpmailfile = filename_cat (gsdlhome, "tmp", args["bc1tmp"], "event.txt");
1331 char *tmpmailfilec = tmpmailfile.getcstr();
1332 ofstream tmpfile (tmpmailfilec);
1333 delete tmpmailfilec;
1334 if (tmpfile) {
1335 tmpfile << outconvert << "[Collector Event]\n"
1336 << "Date: " << get_date (true) << "\n"
1337 << "Greenstone Username: " << args["un"] << "\n"
1338 << "Collection: " << collection << "\n"
1339 << "Collection Maintainer: " << colmaintainer << "\n"
1340 << "GSDLHOME: " << gsdlhome << "\n";
1341 if (message == "delsuccess") {
1342 tmpfile << outconvert
1343 << "The " << collection << " collection was successfully deleted\n\n";
1344 } else {
1345 tmpfile << outconvert
1346 << "Attempt to delete the " << collection << " collection failed\n\n";
1347 }
1348 tmpfile.close();
1349 text_t to;
1350 if (rcinfo.EmailEvents == CollectorEvents || rcinfo.EmailEvents == AllEvents) to += rcinfo.maintainer;
1351 if (emailuserevents) {
1352 if (!to.empty()) to.push_back (',');
1353 to += colmaintainer;
1354 }
1355 text_t sendmail_cmd = "perl -S sendmail.pl -to \"" + to + "\" -from \"" + rcinfo.maintainer;
1356 sendmail_cmd += "\" -smtp \"" + rcinfo.MailServer + "\" -subject \"Greenstone Collector Event\"";
1357 sendmail_cmd += " -msgfile \"" + tmpmailfile + "\"";
1358
1359 gsdl_system (sendmail_cmd, false, logout);
1360
1361 } else {
1362 logout << outconvert << "collectoraction::do_action ERROR: Couldn't open "
1363 << "temporary event log file " << tmpmailfile << " during collection "
1364 << "deletion. EmailEvents and EmailUserEvents disabled\n";
1365 }
1366 }
1367 }
1368 }
1369
1370 if (collector_page == "bildcancel" || collector_page == "bildfail") {
1371 // cancel the build (we'll also use the cancel_build script to tidy
1372 // up if the build failed)
1373 gsdl_cancel_build (args, logout);
1374 }
1375
1376 if (collector_page == "expt") {
1377
1378 // export the collection - we'll do a synchronous system call to
1379 // exportcol.pl as that's the easiest way to do it. if it becomes a
1380 // problem that it's taking too long to export a large collection then
1381 // we may have to revisit this.
1382 text_t tmpfile = filename_cat (gsdlhome, "tmp", collection + "_export.txt");
1383 text_t export_cmd = "perl -S exportcol.pl -out \"" + tmpfile + "\" " + collection;
1384 gsdl_system (export_cmd, true, logout);
1385 if (file_exists (tmpfile)) {
1386 text_t returnline = file_tail (tmpfile, 1, 0);
1387 if (returnline.size() > 23 && (substr(returnline.begin(), returnline.begin()+23) == "exportcol.pl succeeded:")) {
1388 // success
1389 message = "exptsuccess";
1390 } else {
1391 message = "exptfail";
1392 }
1393 } else {
1394 message = "exptfail";
1395 }
1396 }
1397
1398 if (message.empty()) {
1399 if (collector_page != "bild") {
1400 // output page ("bild" page was already output above)
1401 textout << outconvert << disp << ("_collector:header_\n")
1402 << ("_collector:" + collector_page + "content_\n")
1403 << ("_collector:footer_\n");
1404 }
1405 } else {
1406 // message was set somewhere (probably an error), output message page
1407 textout << outconvert << disp << ("_collector:header_\n")
1408 << ("_collector:" + message + "content_\n")
1409 << ("_collector:footer_\n");
1410 message.clear();
1411 }
1412 return true;
1413}
1414
1415// if sw = 0 replace all carriage returns in intext with the string "\n"
1416// else replace all occurances of "\n" with a carriage return
1417text_t collectoraction::carriage_replace (const text_t &intext, int sw) {
1418
1419 text_t outtext;
1420 text_t::const_iterator here = intext.begin();
1421 text_t::const_iterator end = intext.end();
1422 while (here != end) {
1423 if (sw == 0) {
1424 if (*here == '\n') {
1425 if ((here+1) != end && *(here+1) == '\r') here ++;
1426 outtext += "\\n";
1427 } else if (*here == '\r') {
1428 if ((here+1) != end && *(here+1) == '\n') here ++;
1429 outtext += "\\n";
1430 } else {
1431 outtext.push_back (*here);
1432 }
1433 } else if (*here == '\\' && (here+1) != end && *(here+1) == 'n') {
1434 outtext.push_back ('\n');
1435 here ++;
1436 } else {
1437 outtext.push_back (*here);
1438 }
1439 here ++;
1440 }
1441 return outtext;
1442}
1443
1444// create a short directory name from fullname
1445text_t collectoraction::get_directory_name (const text_t &fullname) {
1446
1447 text_t shortname;
1448 if (fullname.empty()) {
1449 shortname = "coll";
1450
1451 } else {
1452
1453 // first make all lowercase and remove any dodgy characters
1454 // (i.e. anything not [a-z]
1455 text_t::const_iterator here = fullname.begin();
1456 text_t::const_iterator end = fullname.end();
1457 while (here != end) {
1458 if ((*here >= 'A' && *here <= 'Z') || (*here >= 'a' && *here <= 'z') ||
1459 (*here == ' ')) {
1460 if (*here >= 'A' && *here <= 'Z') shortname.push_back (*here+32);
1461 else if (*here == ' ') {
1462 while ((*(here+1)) == ' ') here ++;
1463 shortname.push_back (*here);
1464 } else shortname.push_back (*here);
1465 }
1466 here ++;
1467 }
1468
1469 text_tarray words;
1470 splitchar (shortname.begin(), shortname.end(), ' ', words);
1471 int num_words = words.size();
1472
1473 if (num_words == 0) {
1474 shortname = "coll";
1475
1476 } else {
1477
1478 shortname.clear();
1479 int use_words = (num_words <= 6) ? num_words : 6;
1480 int substr_len = 6 / use_words;
1481
1482 for (int i = 0; i < use_words; i++) {
1483 if (words[i].size() < substr_len) shortname += words[i];
1484 else shortname += substr (words[i].begin(), words[i].begin()+substr_len);
1485 }
1486 }
1487 }
1488
1489 // check to see if shortname is unique
1490 text_t fulldirname = filename_cat (gsdlhome, "collect", shortname);
1491 if (directory_exists (fulldirname)) {
1492 int version = 0;
1493 text_t newname;
1494 do {
1495 version ++;
1496 newname = shortname;
1497 newname.push_back ('v');
1498 newname.appendint (version);
1499 fulldirname = filename_cat (gsdlhome, "collect", newname);
1500 } while (directory_exists (fulldirname));
1501
1502 shortname = newname;
1503 }
1504
1505 return shortname;
1506}
1507
1508// tests if collection is write protected (currently just checks if
1509// collect.cfg file is writable
1510bool collectoraction::collection_protected (const text_t &collection) {
1511 text_t cfgfile = filename_cat(gsdlhome, "collect", collection, "etc", "collect.cfg");
1512 if (file_writable(cfgfile)) return false;
1513 return true;
1514}
1515
1516// assigns a temporary directory name for this collector session
1517// and creates temporary directory
1518void collectoraction::assign_tmpname (cgiargsclass &args, ostream &logout) {
1519
1520 int i = 0;
1521 text_t tmpname = "tbuild";
1522 while (directory_exists (filename_cat (gsdlhome, "tmp", tmpname + text_t(i)))) {
1523 i++;
1524 }
1525 tmpname.appendint (i);
1526
1527 text_t fulltmpdir = filename_cat (gsdlhome, "tmp", tmpname);
1528 if (!mk_dir (fulltmpdir)) {
1529 outconvertclass text_t2ascii;
1530 logout << text_t2ascii << "collectoraction::assign_tmpname unable to create directory ("
1531 << fulltmpdir << ")\n";
1532 }
1533
1534 args["bc1tmp"] = tmpname;
1535}
1536
1537void collectoraction::gsdl_mkcol (cgiargsclass &args, ostream &logout) {
1538
1539 text_t tmpdir = filename_cat (gsdlhome, "tmp", args["bc1tmp"]);
1540 if (!directory_exists (tmpdir)) {
1541 message = "tmpfail";
1542 return;
1543 }
1544
1545 text_t &collection = args["bc1dirname"];
1546 if (collection.empty()) {
1547 message = "nocollection";
1548 return;
1549 }
1550
1551 // check for a .create file - if it exists then we've already created the collection
1552 text_t createfile = filename_cat (tmpdir, ".create");
1553 if (file_exists (createfile)) {
1554 return;
1555 }
1556
1557 // set up options
1558 text_t options = "-quiet -creator \"" + args["bc1contactemail"] + "\"";
1559 options += " -title \"" + args["bc1fullname"] + "\"";
1560 options += " -about \"" + carriage_replace (args["bc1aboutdesc"] + "_collectorextra_", 0) + "\"";
1561 options += " -collectdir \"" + remove_trailing_slashes(tmpdir) + "\" ";
1562
1563 text_t optionfile = filename_cat (tmpdir, "mkcol.opt");
1564 char *optionfilec = optionfile.getcstr();
1565 ofstream ofile_out (optionfilec);
1566 delete optionfilec;
1567 if (!ofile_out) {
1568 message = "tmpfail";
1569 return;
1570 }
1571 outconvertclass text_t2ascii;
1572 ofile_out << text_t2ascii << options << "\n";
1573 ofile_out.close();
1574
1575 // run mkcol.pl
1576 text_t mkcol_cmd = "perl -S mkcol.pl -optionfile \"" + optionfile;
1577 mkcol_cmd += "\" " + collection;
1578 gsdl_system (mkcol_cmd, true, logout);
1579
1580 // make sure it went ok
1581 text_t cfgfile = filename_cat (tmpdir, collection, "etc", "collect.cfg");
1582 if (!file_writable (cfgfile)) {
1583 message = "mkcolfail";
1584 } else {
1585 // create the .create file (this file is just a place holder to let any future
1586 // pages know that the collection already exists).
1587 char *createfilec = createfile.getcstr();
1588 ofstream cfile_out (createfilec);
1589 delete createfilec;
1590 if (cfile_out) {
1591 cfile_out << "collection created\n";
1592 cfile_out.close();
1593 } else {
1594 message = "tmpfail";
1595 return;
1596 }
1597 }
1598}
1599
1600void collectoraction::gsdl_build (cgiargsclass &args, ostream &logout) {
1601
1602 outconvertclass text_t2ascii;
1603
1604 text_t tmpdir = filename_cat (gsdlhome, "tmp", args["bc1tmp"]);
1605 if (!directory_exists (tmpdir)) {
1606 message = "tmpfail";
1607 return;
1608 }
1609
1610 text_t &collection = args["bc1dirname"];
1611 if (collection.empty()) {
1612 message = "nocollection";
1613 return;
1614 }
1615
1616 // check for a .build file - if it exists then we've already built
1617 // the collection (or are in the process of building it)
1618 text_t buildfile = filename_cat (tmpdir, ".build");
1619 if (file_exists (buildfile)) {
1620 return;
1621 } else {
1622 // create the .build file (this file is just a place holder to let any future
1623 // pages know that we've already been here)
1624 char *buildfilec = buildfile.getcstr();
1625 ofstream bfile_out (buildfilec);
1626 delete buildfilec;
1627 if (bfile_out) {
1628 bfile_out << "collection building\n";
1629 bfile_out.close();
1630 } else {
1631 message = "tmpfail";
1632 return;
1633 }
1634 }
1635
1636 const recptconf &rcinfo = recpt->get_configinfo ();
1637
1638 // create the event header file if LogEvents, EmailEvents or
1639 // EmailUserEvents options are turned on.
1640 bool logevents =
1641 (rcinfo.LogEvents == CollectorEvents || rcinfo.LogEvents == AllEvents ||
1642 rcinfo.EmailEvents == CollectorEvents || rcinfo.EmailEvents == AllEvents ||
1643 rcinfo.EmailUserEvents);
1644 text_t ehead_file = filename_cat (tmpdir, "ehead.txt");
1645 if (logevents) {
1646 if (!create_event_header_file (ehead_file, args, logout)) {
1647 logevents = false;
1648 }
1649 }
1650
1651 // set up build options
1652 text_t options = "-make_writable -remove_import -out \"";
1653 options += filename_cat (tmpdir, collection + ".bld");
1654 options += "\" -collectdir \"" + get_collectdir (args) + "\"";
1655
1656 if (args["bc1esrce"] == 1) {
1657 // we're adding data to an existing collection
1658 options += " -save_archives -append";
1659 }
1660
1661 text_tarray inputvalues, inputtypes;
1662 splitchar (args["bc1input"].begin(), args["bc1input"].end(), ',', inputvalues);
1663 splitchar (args["bc1inputtype"].begin(), args["bc1inputtype"].end(), ',', inputtypes);
1664 int numvalues = inputvalues.size();
1665 int numtypes = inputtypes.size();
1666 for (int i = 0; i < numvalues; i++) {
1667 if (!inputvalues[i].empty()) {
1668 text_t type = "file://"; // default
1669 if (i < numtypes) type = inputtypes[i];
1670 options += " -download \"" +
1671 remove_trailing_slashes(type + format_url(decode_commas(inputvalues[i]))) + "\"";
1672 }
1673 }
1674
1675 if (logevents) {
1676 if (rcinfo.LogEvents == CollectorEvents || rcinfo.LogEvents == AllEvents)
1677 options += " -log_events";
1678 if (rcinfo.EmailEvents == CollectorEvents || rcinfo.EmailEvents == AllEvents) {
1679 options += " -mail_server " + rcinfo.MailServer;
1680 options += " -email_events " + rcinfo.maintainer;
1681 if (rcinfo.EmailUserEvents) options += "," + args["bc1contactemail"];
1682 } else if (rcinfo.EmailUserEvents) {
1683 options += " -mail_server " + rcinfo.MailServer;
1684 options += " -email_events " + args["bc1contactemail"];
1685 }
1686 options += " -event_header " + ehead_file;
1687 }
1688
1689 text_t optionfile = filename_cat (tmpdir, "build.opt");
1690 char *optionfilec = optionfile.getcstr();
1691 ofstream ofile_out (optionfilec);
1692 delete optionfilec;
1693 if (!ofile_out) {
1694 message = "tmpfail";
1695 return;
1696 }
1697 ofile_out << text_t2ascii << options << "\n";
1698 ofile_out.close();
1699
1700 // if we're altering an existing collection we need to kill off
1701 // the existing collection server - we do this for the local library
1702 // (and any other persistent version of the library) as the existing
1703 // gdbm file can't be deleted while the collection server holds it open
1704 if ((args["bc1econf"] == 1) || (args["bc1esrce"] == 1)) {
1705 remove_colservr (collection, logout);
1706 }
1707
1708 // set up the build command - build.bat has some issues with quoting
1709 // on win2k when gsdlhome contains spaces so we'll avoid using
1710 // "perl -S" here in favor of calling the "build" perl script explicitly
1711 text_t build_cmd = "perl \"" + filename_cat (gsdlhome, "bin", "script", "build");
1712 build_cmd += "\" -optionfile \"" + optionfile + "\" " + collection;
1713 // run build command in background (i.e. asynchronously)
1714 gsdl_system (build_cmd, false, logout);
1715}
1716
1717void collectoraction::gsdl_cancel_build (cgiargsclass &args, ostream &logout) {
1718 // I really wanted to do what this perl script does from within the library
1719 // c++ code. I ran into some problems though (like how do you write a portable
1720 // "rm -r" in c++?). One day I'll spend some time sorting it out ... maybe.
1721 text_t cancel_cmd = "perl -S cancel_build.pl -collectdir \"";
1722 cancel_cmd += filename_cat (gsdlhome, "tmp", args["bc1tmp"]) + "\" ";
1723 cancel_cmd += args["bc1dirname"];
1724 // To be on the safe side we'll make this a synchronous call
1725 // so that all tidying up is done before the user has a chance
1726 // to do anything else (like start rebuilding their collection).
1727 // This means that for a big collection where there's lots of
1728 // stuff to delete etc. it might take a while before the "build
1729 // cancelled" page appears.
1730 gsdl_system (cancel_cmd, true, logout);
1731}
1732
1733text_t collectoraction::get_collectdir (cgiargsclass &args) {
1734
1735 if ((args["bc1econf"] == 1) || (args["bc1esrce"] == 1)) {
1736 // we're adding to a collection in place
1737 return filename_cat(gsdlhome, "collect");
1738
1739 } else {
1740 return filename_cat (gsdlhome, "tmp", args["bc1tmp"]);
1741 }
1742}
1743
1744// checks to see if any of the plugins in pluginset occur in
1745// collections configuration file
1746bool collectoraction::uses_weird_plugin (const text_t &collection) {
1747
1748 text_tset pluginset;
1749 pluginset.insert ("HBPlug");
1750
1751 text_t cfgfile_content;
1752 text_t cfgfile_name = filename_cat (gsdlhome, "collect", collection, "etc", "collect.cfg");
1753 text_t pluginstr, pluginname;
1754
1755 if (read_file (cfgfile_name, cfgfile_content)) {
1756 text_t::const_iterator here = cfgfile_content.begin();
1757 text_t::const_iterator end = cfgfile_content.end();
1758 while (here != end) {
1759 here = findchar (here, end, 'p');
1760 if (here == end) break;
1761 if ((here+6 < end) && (substr (here, here+6) == "plugin")) {
1762 getdelimitstr (here+6, end, '\n', pluginstr);
1763 text_t::const_iterator hp = pluginstr.begin();
1764 text_t::const_iterator ep = pluginstr.end();
1765 bool found = false;
1766 // remove any leading whitespace, trailing options etc.
1767 while (hp != ep) {
1768 if (*hp == '\t' || *hp == ' ' || *hp == '\n') {
1769 if (found) break;
1770 } else {
1771 pluginname.push_back (*hp);
1772 found = true;
1773 }
1774 hp ++;
1775 }
1776 text_tset::const_iterator it = pluginset.find (pluginname);
1777 if (it != pluginset.end()) return true; // found matching plugin
1778 pluginname.clear();
1779 }
1780 here ++;
1781 }
1782 }
1783 return false;
1784}
1785
1786// create and initialize a new collection server and
1787// add it to the null protocol.
1788void collectoraction::create_colserver (const text_t &collection, ostream &logout) {
1789
1790 recptprotolistclass *protos = recpt->get_recptprotolist_ptr();
1791 recptprotolistclass::iterator rprotolist_here = protos->begin();
1792 recptprotolistclass::iterator rprotolist_end = protos->end();
1793 while (rprotolist_here != rprotolist_end) {
1794 comerror_t err = noError;
1795 if ((*rprotolist_here).p != NULL) {
1796 if ((*rprotolist_here).p->get_protocol_name (err) == "nullproto") {
1797 // create collection server and add it to nullproto
1798 (*rprotolist_here).p->add_collection (collection, recpt, gsdlhome, gsdlhome);
1799 // make sure gsdlhome is configured
1800 text_tarray tmp;
1801 tmp.push_back (gsdlhome);
1802 (*rprotolist_here).p->configure ("gsdlhome", tmp, err);
1803 // re-initialize the null protocol
1804 if (!(*rprotolist_here).p->init (err, logout)) {
1805 logout << "collectoraction::create_colserver: nullproto init failed\n";
1806 }
1807 return;
1808 }
1809 }
1810 rprotolist_here ++;
1811 }
1812
1813 logout << "collectoraction::create_colserver: no valid nullproto found\n";
1814}
1815
1816// delete a collection server from the null protocol
1817void collectoraction::remove_colservr (const text_t &collection, ostream &logout) {
1818
1819 recpt->uncache_collection (collection);
1820
1821 recptprotolistclass *protos = recpt->get_recptprotolist_ptr();
1822 recptprotolistclass::iterator rprotolist_here = protos->begin();
1823 recptprotolistclass::iterator rprotolist_end = protos->end();
1824 while (rprotolist_here != rprotolist_end) {
1825 comerror_t err = noError;
1826 if ((*rprotolist_here).p != NULL) {
1827 if ((*rprotolist_here).p->get_protocol_name (err) == "nullproto") {
1828 (*rprotolist_here).p->remove_collection (collection, logout);
1829 return;
1830 }
1831 }
1832 rprotolist_here ++;
1833 }
1834
1835 logout << "collectoraction::create_colserver: no valid nullproto found\n";
1836}
1837
1838bool collectoraction::create_event_header_file (const text_t &filename, cgiargsclass &args,
1839 ostream &logout) {
1840
1841 outconvertclass text_t2ascii;
1842 char *filenamec = filename.getcstr();
1843 ofstream eheadfile (filenamec);
1844 delete filenamec;
1845
1846 if (eheadfile) {
1847 eheadfile << text_t2ascii << get_event_header (args);
1848 eheadfile.close();
1849 return true;
1850 }
1851
1852 logout << text_t2ascii << "collectoraction::create_event_header ERROR: Couldn't create "
1853 << "Event Header file " << filename << ". Event logging disabled\n";
1854 return false;
1855}
1856
1857text_t collectoraction::get_event_header (cgiargsclass &args) {
1858 text_t header = "Greenstone Username: " + args["un"] + "\n";
1859 header += "Collection: " + args["bc1dirname"] + "\n";
1860 header += "Collection Creator: " + args["bc1contactemail"] + "\n";
1861 header += "GSDLHOME: " + gsdlhome + "\n";
1862 header += "Build Location: " + get_collectdir(args) + "\n";
1863
1864 return header;
1865}
1866
1867bool collectoraction::check_sources (cgiargsclass &args, ostream &logout) {
1868
1869 bool found = false;
1870
1871 text_tarray inputvalues;
1872 splitchar (args["bc1input"].begin(), args["bc1input"].end(), ',', inputvalues);
1873
1874 text_tarray inputtypes;
1875 splitchar (args["bc1inputtype"].begin(), args["bc1inputtype"].end(), ',', inputtypes);
1876
1877 int numvalues = inputvalues.size();
1878 int numtypes = inputtypes.size();
1879
1880 for (int i = 0; i < numvalues; i++) {
1881 text_t value = format_url(decode_commas(inputvalues[i]));
1882 text_t type = "file://"; // default
1883 if (!value.empty()) {
1884 found = true;
1885 if (i >= numtypes || inputtypes[i].empty()) {
1886 logout << "collectoraction::check_sources: WARNING type not set\n";
1887 } else {
1888 type = inputtypes[i];
1889 }
1890 if (type == "file://") {
1891 if (!file_exists(value) && !directory_exists(value)) {
1892 failedsources[decode_commas(inputvalues[i])] = "1";
1893 badsources = true;
1894 }
1895 } else if (type == "http://") {
1896 if (gsdl_system ("perl -S ping.pl -quiet http://" + value, true, logout)) {
1897 failedsources[decode_commas(inputvalues[i])] = "1";
1898 badsources = true;
1899 }
1900 } else if (type == "ftp://") {
1901 if (gsdl_system ("perl -S ping.pl -quiet ftp://" + value, true, logout)) {
1902 failedsources[decode_commas(inputvalues[i])] = "1";
1903 badsources = true;
1904 }
1905 }
1906 }
1907 }
1908
1909 // set badsources if there weren't any sources at all
1910 if (!found) badsources = true;
1911
1912 if (badsources) return false;
1913 return true;
1914}
1915
1916// format_url simply strips "http://", "ftp://", or "file://" off the
1917// beginning of url if they're there
1918text_t collectoraction::format_url (const text_t &url) {
1919 text_t::const_iterator begin = url.begin();
1920 text_t::const_iterator end = url.end();
1921
1922 if (url.size() >= 7) {
1923 text_t prefix = substr(begin, begin+7);
1924 if (prefix == "http://" || prefix == "file://") {
1925 return substr(begin+7, end);
1926 }
1927 }
1928 if (url.size() >= 6) {
1929 if (substr(begin, begin+6) == "ftp://") {
1930 return substr(begin+6, end);
1931 }
1932 }
1933 return url;
1934}
1935
1936text_t collectoraction::remove_trailing_slashes (text_t str) {
1937
1938 while (*(str.end()-1) == '\\') {
1939 str.pop_back();
1940 }
1941 return str;
1942}
Note: See TracBrowser for help on using the repository browser.