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

Last change on this file since 5599 was 5599, checked in by jrm21, 21 years ago

added important piece of feedback so that if the tmp dir isn't writeable, we
immediately say so instead of failing silently and causing later problems.

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