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

Last change on this file since 3007 was 3007, checked in by jrm21, 22 years ago

Replaced old code to get file descriptors from ofstreams for locking with
c-style open() functions instead. Now works with gcc3.

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