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

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

text strings weren't being properly converted for file output via the
convertclass. Input is now properly set so that we don't get garbage output.

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