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

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

Made a few cosmetic changes to the "Administration" pages and added the
ability to view the last 100 lines of the usage log. This meant making a
change to the file_tail() function to allow for the very long lines that
occur in the log file.

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