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

Last change on this file since 1804 was 1796, checked in by sjboddie, 23 years ago

Removed the "cp" and "sp" cgi arguments and used the "p" argument instead
(i.e. the "p" argument is now used in the page, status and collector
actions). I'd like to do this type of thing in lots of places where
different actions are using different cgi arguments to perform very
similar tasks. Fewer arguments are becoming more of a priority with our
ever expanding URLs, I'll leave it for a while though and make sure this
change doesn't cause any problems.

  • Property svn:keywords set to Author Date Id Revision
File size: 55.7 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 -- may need to think about doing
312 // this in the background).
313 gsdl_call_perl ("cleantmp.pl", NULL);
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);
809 } else if (file_exists (bld_file + ".import")) {
810 statusline = "Importing collection ...<br>\n";
811 statusline += file_tail (bld_file + ".import", 1);
812 } else if (file_exists (bld_file + ".build")) {
813 statusline = "Building collection ...<br>\n";
814 statusline += file_tail (bld_file + ".build", 1);
815 } else {
816 statusline += "creating collection ...<br>\n";
817 statusline += file_tail (bld_file, 1);
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);
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'll only do this when entering
1010 // the intro page to minimise the number of annoying console
1011 // windows that get popped up on windows -- this should be ok
1012 // for now as the intro page is the only access point into the
1013 // collector)
1014 if (collector_page == "intro" && !perl_ok()) {
1015 textout << outconvert
1016 << "<html>\n"
1017 << "<head>\n"
1018 << "<title>Perl not found</title>\n"
1019 << "</head>\n"
1020 << "<body bgcolor=\"#ffffff\" text=\"#000000\" link=\"#006666\" "
1021 << "alink=\"#cc9900\" vlink=\"#666633\">\n"
1022 << "<h2>Perl not found</h2>\n"
1023 << "Greenstone could not detect perl on this system. The Collector\n"
1024 << "end-user collection building facility is therefore not available.\n"
1025 << "\n</body>\n"
1026 << "</html>\n";
1027 return true;
1028
1029 }
1030
1031 if (collector_page == "bild") {
1032 // do the work (download, import, build)
1033 gsdl_build (args, logout);
1034
1035 if (message.empty()) {
1036 // bild page is a frameset so we don't want headers and stuff
1037 textout << outconvert << disp << ("_collector:bildcontent_\n");
1038 }
1039 }
1040
1041 if (do_mkcol == true) {
1042 // execute mkcol.pl (do_mkcol is set from within check_cgiargs)
1043 gsdl_mkcol (args, logout);
1044 do_mkcol = false; // reset for fast-cgi
1045 }
1046
1047 if (args["bc1dodelete"] == "1") {
1048 // delete bcidirname collection
1049 if (collection_protected (collection)) {
1050 message = "delinvalid";
1051
1052 } else {
1053
1054 const recptconf &rcinfo = recpt->get_configinfo ();
1055 bool emailuserevents = rcinfo.EmailUserEvents;
1056
1057 // get collection maintainer email from collect.cfg before we
1058 // delete it
1059 text_t colmaintainer;
1060 text_t cfgfile = filename_cat(gsdlhome, "collect", collection, "etc", "collect.cfg");
1061 char *cfgfilec = cfgfile.getcstr();
1062 ifstream cfg_in (cfgfilec);
1063 delete cfgfilec;
1064 if (cfg_in) {
1065 text_tarray cfgline;
1066 while (read_cfg_line(cfg_in, cfgline) >= 0) {
1067 if (cfgline.size () == 2 && cfgline[0] == "maintainer") {
1068 colmaintainer = cfgline[1];
1069 break;
1070 }
1071 }
1072 cfg_in.close();
1073 }
1074 if (colmaintainer.empty()) {
1075 logout << outconvert
1076 << "collectoraction::do_action WARNING: Collection being deleted ("
1077 << collection << ") has no maintainer address. EmailUserEvents "
1078 << "disabled\n";
1079 emailuserevents = false;
1080 }
1081
1082 // first we need to free up the collection's collection server
1083 // we must do this for the local library (and I guess when using
1084 // fastcgi too) as you can't delete the gdbm file while it's
1085 // being kept open by the collection server
1086 remove_colservr (collection, logout);
1087
1088
1089 char *collectionc = collection.getcstr();
1090 int rv = gsdl_call_perl ("delcol.pl", "-f", collectionc, NULL);
1091 delete collectionc;
1092 if (rv != 0) {
1093 // deletion failed -- permissions?
1094 message = "delpermission";
1095 } else {
1096 message = "delsuccess";
1097 }
1098
1099 // log the event
1100 if (rcinfo.LogEvents == CollectorEvents || rcinfo.LogEvents == AllEvents) {
1101
1102 text_t eventlog = filename_cat (gsdlhome, "etc", "events.txt");
1103 char *eventlogt = eventlog.getcstr();
1104 ofstream eventl (eventlogt, ios::app);
1105 delete eventlogt;
1106
1107 if (eventl) {
1108 eventl << outconvert << "[Collector Event]\n"
1109 << "Date: " << get_date (true) << "\n"
1110 << "Greenstone Username: " << args["un"] << "\n"
1111 << "Collection: " << collection << "\n"
1112 << "Collection Maintainer: " << colmaintainer << "\n"
1113 << "GSDLHOME: " << gsdlhome << "\n";
1114
1115 if (message == "delsuccess") {
1116 eventl << outconvert
1117 << "The " << collection << " collection was successfully deleted\n\n";
1118 } else {
1119 eventl << outconvert
1120 << "Attempt to delete the " << collection << " collection failed\n\n";
1121 }
1122 eventl.close();
1123
1124 } else {
1125 logout << outconvert << "collectoraction::do_action ERROR: Couldn't open "
1126 << "event log file " << eventlog << " for appending during collection "
1127 << "deletion. LogEvents disabled\n";
1128 }
1129 }
1130
1131 if (rcinfo.EmailEvents == CollectorEvents || rcinfo.EmailEvents == AllEvents || emailuserevents) {
1132 // use sendmail.pl perl script to send email events
1133 text_t tmpmailfile = filename_cat (gsdlhome, "tmp", args["bc1tmp"], "event.txt");
1134 char *tmpmailfilec = tmpmailfile.getcstr();
1135 ofstream tmpfile (tmpmailfilec);
1136 if (tmpfile) {
1137 tmpfile << outconvert << "[Collector Event]\n"
1138 << "Date: " << get_date (true) << "\n"
1139 << "Greenstone Username: " << args["un"] << "\n"
1140 << "Collection: " << collection << "\n"
1141 << "Collection Maintainer: " << colmaintainer << "\n"
1142 << "GSDLHOME: " << gsdlhome << "\n";
1143 if (message == "delsuccess") {
1144 tmpfile << outconvert
1145 << "The " << collection << " collection was successfully deleted\n\n";
1146 } else {
1147 tmpfile << outconvert
1148 << "Attempt to delete the " << collection << " collection failed\n\n";
1149 }
1150 tmpfile.close();
1151 text_t to;
1152 if (rcinfo.EmailEvents == CollectorEvents || rcinfo.EmailEvents == AllEvents) to += rcinfo.maintainer;
1153 if (emailuserevents) {
1154 if (!to.empty()) to.push_back (',');
1155 to += colmaintainer;
1156 }
1157 char *to_c = to.getcstr();
1158 char *from_c = rcinfo.maintainer.getcstr();
1159 char *smtp_c = rcinfo.MailServer.getcstr();
1160 gsdl_call_perl ("sendmail.pl", "-to", to_c, "-from", from_c, "-smtp", smtp_c,
1161 "-subject", "Greenstone Collector Event", "-msgfile", tmpmailfilec, NULL);
1162 delete (to_c);
1163 delete (from_c);
1164 delete (smtp_c);
1165
1166 } else {
1167 logout << outconvert << "collectoraction::do_action ERROR: Couldn't open "
1168 << "temporary event log file " << tmpmailfile << " during collection "
1169 << "deletion. EmailEvents and EmailUserEvents disabled\n";
1170 }
1171 delete (tmpmailfilec);
1172 }
1173 }
1174 }
1175
1176 if (collector_page == "bildcancel" || collector_page == "bildfail") {
1177 // cancel the build (we'll also use the cancel_build script to tidy
1178 // up if the build failed)
1179 gsdl_cancel_build (args, logout);
1180 }
1181
1182 if (message.empty()) {
1183 if (collector_page != "bild") {
1184 // output page ("bild" page was already output above)
1185 textout << outconvert << disp << ("_collector:header_\n")
1186 << ("_collector:" + collector_page + "content_\n")
1187 << ("_collector:footer_\n");
1188 }
1189 } else {
1190 // message was set somewhere (probably an error), output message page
1191 textout << outconvert << disp << ("_collector:header_\n")
1192 << ("_collector:" + message + "content_\n")
1193 << ("_collector:footer_\n");
1194 message.clear();
1195 }
1196 return true;
1197}
1198
1199// if sw = 0 replace all carriage returns in intext with the string "\n"
1200// else replace all occurances of "\n" with a carriage return
1201text_t collectoraction::carriage_replace (const text_t &intext, int sw) {
1202
1203 text_t outtext;
1204 text_t::const_iterator here = intext.begin();
1205 text_t::const_iterator end = intext.end();
1206 while (here != end) {
1207 if (sw == 0) {
1208 if (*here == '\n') {
1209 if ((here+1) != end && *(here+1) == '\r') here ++;
1210 outtext += "\\n";
1211 } else if (*here == '\r') {
1212 if ((here+1) != end && *(here+1) == '\n') here ++;
1213 outtext += "\\n";
1214 } else {
1215 outtext.push_back (*here);
1216 }
1217 } else if (*here == '\\' && (here+1) != end && *(here+1) == 'n') {
1218 outtext.push_back ('\n');
1219 here ++;
1220 } else {
1221 outtext.push_back (*here);
1222 }
1223 here ++;
1224 }
1225 return outtext;
1226}
1227
1228// create a short directory name from fullname
1229text_t collectoraction::get_directory_name (const text_t &fullname) {
1230
1231 text_t shortname;
1232 if (fullname.empty()) {
1233 shortname = "coll";
1234
1235 } else {
1236
1237 // first make all lowercase and remove any dodgy characters
1238 // (i.e. anything not [a-z]
1239 text_t::const_iterator here = fullname.begin();
1240 text_t::const_iterator end = fullname.end();
1241 while (here != end) {
1242 if ((*here >= 'A' && *here <= 'Z') || (*here >= 'a' && *here <= 'z') ||
1243 (*here == ' ')) {
1244 if (*here >= 'A' && *here <= 'Z') shortname.push_back (*here+32);
1245 else if (*here == ' ') {
1246 while ((*(here+1)) == ' ') here ++;
1247 shortname.push_back (*here);
1248 } else shortname.push_back (*here);
1249 }
1250 here ++;
1251 }
1252
1253 text_tarray words;
1254 splitchar (shortname.begin(), shortname.end(), ' ', words);
1255 int num_words = words.size();
1256
1257 if (num_words == 0) {
1258 shortname = "coll";
1259
1260 } else {
1261
1262 shortname.clear();
1263 int use_words = (num_words <= 6) ? num_words : 6;
1264 int substr_len = 6 / use_words;
1265
1266 for (int i = 0; i < use_words; i++) {
1267 if (words[i].size() < substr_len) shortname += words[i];
1268 else shortname += substr (words[i].begin(), words[i].begin()+substr_len);
1269 }
1270 }
1271 }
1272
1273 // check to see if shortname is unique
1274 text_t fulldirname = filename_cat (gsdlhome, "collect", shortname);
1275 if (directory_exists (fulldirname)) {
1276 int version = 0;
1277 text_t newname;
1278 do {
1279 version ++;
1280 newname = shortname;
1281 newname.push_back ('v');
1282 newname.appendint (version);
1283 fulldirname = filename_cat (gsdlhome, "collect", newname);
1284 } while (directory_exists (fulldirname));
1285
1286 shortname = newname;
1287 }
1288
1289 return shortname;
1290}
1291
1292// tests if collection is write protected (currently just checks if
1293// collect.cfg file is writable
1294bool collectoraction::collection_protected (const text_t &collection) {
1295 text_t cfgfile = filename_cat(gsdlhome, "collect", collection, "etc", "collect.cfg");
1296 if (file_writable(cfgfile)) return false;
1297 return true;
1298}
1299
1300// assigns a temporary directory name for this collector session
1301// and creates temporary directory
1302void collectoraction::assign_tmpname (cgiargsclass &args, ostream &logout) {
1303
1304 int i = 0;
1305 text_t tmpname = "tbuild";
1306 while (directory_exists (filename_cat (gsdlhome, "tmp", tmpname + text_t(i)))) {
1307 i++;
1308 }
1309 tmpname.appendint (i);
1310
1311 text_t fulltmpdir = filename_cat (gsdlhome, "tmp", tmpname);
1312 if (!mk_dir (fulltmpdir)) {
1313 outconvertclass text_t2ascii;
1314 logout << text_t2ascii << "collectoraction::assign_tmpname unable to create directory ("
1315 << fulltmpdir << ")\n";
1316 }
1317
1318 args["bc1tmp"] = tmpname;
1319}
1320
1321void collectoraction::gsdl_mkcol (cgiargsclass &args, ostream &logout) {
1322
1323 text_t tmpdir = filename_cat (gsdlhome, "tmp", args["bc1tmp"]);
1324 if (!directory_exists (tmpdir)) {
1325 message = "tmpfail";
1326 return;
1327 }
1328
1329 text_t &collection = args["bc1dirname"];
1330 if (collection.empty()) {
1331 message = "nocollection";
1332 return;
1333 }
1334
1335 // check for a .create file - if it exists then we've already created the collection
1336 text_t createfile = filename_cat (tmpdir, ".create");
1337 if (file_exists (createfile)) {
1338 return;
1339 }
1340
1341 // set up options
1342 text_t options = "-creator \"" + args["bc1contactemail"] + "\"";
1343 options += " -title \"" + args["bc1fullname"] + "\"";
1344 options += " -about \"" + carriage_replace (args["bc1aboutdesc"], 0) + "\"";
1345 options += " -collectdir \"" + tmpdir + "\" ";
1346 text_t optionfile = filename_cat (tmpdir, "mkcol.opt");
1347 char *optionfilec = optionfile.getcstr();
1348 ofstream ofile_out (optionfilec);
1349 if (!ofile_out) {
1350 message = "tmpfail";
1351 delete optionfilec;
1352 return;
1353 }
1354 outconvertclass text_t2ascii;
1355 ofile_out << text_t2ascii << options << "\n";
1356 ofile_out.close();
1357
1358 char *collectionc = collection.getcstr();
1359
1360 // this is just an ordinary system call running in the foreground. we're assuming (hoping??)
1361 // that mkcol.pl will run through fast enough that nothing more elaborate is required.
1362 gsdl_call_perl ("mkcol.pl", "-optionfile", optionfilec, collectionc, NULL);
1363
1364 delete optionfilec;
1365 delete collectionc;
1366
1367 // make sure it went ok
1368 text_t cfgfile = filename_cat (tmpdir, collection, "etc", "collect.cfg");
1369 if (!file_writable (cfgfile)) {
1370 message = "mkcolfail";
1371 } else {
1372 // create the .create file (this file is just a place holder to let any future
1373 // pages know that the collection already exists).
1374 char *createfilec = createfile.getcstr();
1375 ofstream cfile_out (createfilec);
1376 delete createfilec;
1377 if (cfile_out) {
1378 cfile_out << "collection created\n";
1379 cfile_out.close();
1380 } else {
1381 message = "tmpfail";
1382 return;
1383 }
1384 }
1385}
1386
1387void collectoraction::gsdl_build (cgiargsclass &args, ostream &logout) {
1388
1389 outconvertclass text_t2ascii;
1390
1391 text_t tmpdir = filename_cat (gsdlhome, "tmp", args["bc1tmp"]);
1392 if (!directory_exists (tmpdir)) {
1393 message = "tmpfail";
1394 return;
1395 }
1396
1397 text_t &collection = args["bc1dirname"];
1398 if (collection.empty()) {
1399 message = "nocollection";
1400 return;
1401 }
1402
1403 // check for a .build file - if it exists then we've already built
1404 // the collection (or are in the process of building it)
1405 text_t buildfile = filename_cat (tmpdir, ".build");
1406 if (file_exists (buildfile)) {
1407 return;
1408 } else {
1409 // create the .build file (this file is just a place holder to let any future
1410 // pages know that we've already been here)
1411 char *buildfilec = buildfile.getcstr();
1412 ofstream bfile_out (buildfilec);
1413 delete buildfilec;
1414 if (bfile_out) {
1415 bfile_out << "collection building\n";
1416 bfile_out.close();
1417 } else {
1418 message = "tmpfail";
1419 return;
1420 }
1421 }
1422
1423 const recptconf &rcinfo = recpt->get_configinfo ();
1424
1425 // create the event header file if LogEvents, EmailEvents or
1426 // EmailUserEvents options are turned on.
1427 bool logevents =
1428 (rcinfo.LogEvents == CollectorEvents || rcinfo.LogEvents == AllEvents ||
1429 rcinfo.EmailEvents == CollectorEvents || rcinfo.EmailEvents == AllEvents ||
1430 rcinfo.EmailUserEvents);
1431 text_t ehead_file = filename_cat (tmpdir, "ehead.txt");
1432 if (logevents) {
1433 if (!create_event_header_file (ehead_file, args, logout)) {
1434 logevents = false;
1435 }
1436 }
1437
1438 // set up build options
1439 text_t options = "-remove_import -out \"";
1440 options += filename_cat (tmpdir, collection + ".bld");
1441 options += "\" -collectdir \"" + get_collectdir (args) + "\"";
1442
1443 if (args["bc1esrce"] == 1) {
1444 // we're adding data to an existing collection
1445 options += "\" -save_archives -append";
1446 }
1447
1448 if (!args["bc1inputdir1"].empty())
1449 options += " -download \"" + args["bc1inputdir1"] + "\"";
1450 if (!args["bc1inputdir2"].empty())
1451 options += " -download \"" + args["bc1inputdir2"] + "\"";
1452 if (!args["bc1inputdir3"].empty())
1453 options += " -download \"" + args["bc1inputdir3"] + "\"";
1454 if (!args["bc1inputdir4"].empty())
1455 options += " -download \"" + args["bc1inputdir4"] + "\"";
1456
1457 if (logevents) {
1458 if (rcinfo.LogEvents == CollectorEvents || rcinfo.LogEvents == AllEvents)
1459 options += " -log_events";
1460 if (rcinfo.EmailEvents == CollectorEvents || rcinfo.EmailEvents == AllEvents) {
1461 options += " -mail_server " + rcinfo.MailServer;
1462 options += " -email_events " + rcinfo.maintainer;
1463 if (rcinfo.EmailUserEvents) options += "," + args["bc1contactemail"];
1464 } else if (rcinfo.EmailUserEvents) {
1465 options += " -mail_server " + rcinfo.MailServer;
1466 options += " -email_events " + args["bc1contactemail"];
1467 }
1468 options += " -event_header " + ehead_file;
1469 }
1470
1471 text_t optionfile = filename_cat (tmpdir, "build.opt");
1472 char *optionfilec = optionfile.getcstr();
1473 ofstream ofile_out (optionfilec);
1474 delete optionfilec;
1475 if (!ofile_out) {
1476 message = "tmpfail";
1477 return;
1478 }
1479 ofile_out << text_t2ascii << options << "\n";
1480 ofile_out.close();
1481
1482 // set up the build command
1483 // Can't seem to get "perl -S" to work properly here on windows 95...
1484 //text_t build_cmd = "perl -S build -optionfile \"" + optionfile + "\" " + collection;
1485 text_t build_cmd = "perl \"" + filename_cat(gsdlhome, "bin", "script", "build") +
1486 "\" -optionfile \"" + optionfile + "\" " + collection;
1487 char *build_cmdc = build_cmd.getcstr();
1488 // run build command in background
1489 gsdl_system (build_cmdc, logout);
1490 delete build_cmdc;
1491}
1492
1493void collectoraction::gsdl_cancel_build (cgiargsclass &args, ostream &logout) {
1494 // I really wanted to do what this perl script does from within the library
1495 // c++ code. I ran into some problems though (like how do you write a portable
1496 // "rm -r" in c++?). One day I'll spend some time sorting it out ... maybe.
1497 text_t collectdir = filename_cat (gsdlhome, "tmp", args["bc1tmp"]);
1498 char *collectdirc = collectdir.getcstr();
1499 char *collectionc = args["bc1dirname"].getcstr();
1500
1501 gsdl_call_perl ("cancel_build.pl", "-collectdir", collectdirc, collectionc, NULL);
1502 delete (collectdirc);
1503 delete (collectionc);
1504}
1505
1506text_t collectoraction::get_collectdir (cgiargsclass &args) {
1507
1508 if ((args["bc1econf"] == 1) || (args["bc1esrce"] == 1)) {
1509 // we're adding to a collection in place
1510 return filename_cat(gsdlhome, "collect");
1511
1512 } else {
1513 return filename_cat (gsdlhome, "tmp", args["bc1tmp"]);
1514 }
1515}
1516
1517// checks to see if any of the plugins in pluginset occur in
1518// collections configuration file
1519bool collectoraction::uses_weird_plugin (const text_t &collection) {
1520
1521 text_tset pluginset;
1522 pluginset.insert ("HBPlug");
1523
1524 text_t cfgfile_content;
1525 text_t cfgfile_name = filename_cat (gsdlhome, "collect", collection, "etc", "collect.cfg");
1526 text_t pluginstr, pluginname;
1527
1528 if (read_file (cfgfile_name, cfgfile_content)) {
1529 text_t::const_iterator here = cfgfile_content.begin();
1530 text_t::const_iterator end = cfgfile_content.end();
1531 while (here != end) {
1532 here = findchar (here, end, 'p');
1533 if (here == end) break;
1534 if ((here+6 < end) && (substr (here, here+6) == "plugin")) {
1535 getdelimitstr (here+6, end, '\n', pluginstr);
1536 text_t::const_iterator hp = pluginstr.begin();
1537 text_t::const_iterator ep = pluginstr.end();
1538 bool found = false;
1539 // remove any leading whitespace, trailing options etc.
1540 while (hp != ep) {
1541 if (*hp == '\t' || *hp == ' ' || *hp == '\n') {
1542 if (found) break;
1543 } else {
1544 pluginname.push_back (*hp);
1545 found = true;
1546 }
1547 hp ++;
1548 }
1549 text_tset::const_iterator it = pluginset.find (pluginname);
1550 if (it != pluginset.end()) return true; // found matching plugin
1551 pluginname.clear();
1552 }
1553 here ++;
1554 }
1555 }
1556 return false;
1557}
1558
1559// create and initialize a new collection server and
1560// add it to the null protocol.
1561void collectoraction::create_colserver (const text_t &collection, ostream &logout) {
1562
1563 recptprotolistclass *protos = recpt->get_recptprotolist_ptr();
1564 recptprotolistclass::iterator rprotolist_here = protos->begin();
1565 recptprotolistclass::iterator rprotolist_end = protos->end();
1566 while (rprotolist_here != rprotolist_end) {
1567 if ((*rprotolist_here).p != NULL) {
1568 if ((*rprotolist_here).p->get_protocol_name () == "nullproto") {
1569 // create collection server and add it to nullproto
1570 (*rprotolist_here).p->add_collection (collection, recpt, gsdlhome, gsdlhome);
1571 // make sure gsdlhome is configured
1572 text_tarray tmp;
1573 tmp.push_back (gsdlhome);
1574 (*rprotolist_here).p->configure ("gsdlhome", tmp);
1575 // re-initialize the null protocol
1576 if (!(*rprotolist_here).p->init (logout)) {
1577 logout << "collectoraction::create_colserver: nullproto init failed\n";
1578 }
1579 return;
1580 }
1581 }
1582 rprotolist_here ++;
1583 }
1584
1585 logout << "collectoraction::create_colserver: no valid nullproto found\n";
1586}
1587
1588// delete a collection server from the null protocol
1589void collectoraction::remove_colservr (const text_t &collection, ostream &logout) {
1590
1591 recptprotolistclass *protos = recpt->get_recptprotolist_ptr();
1592 recptprotolistclass::iterator rprotolist_here = protos->begin();
1593 recptprotolistclass::iterator rprotolist_end = protos->end();
1594 while (rprotolist_here != rprotolist_end) {
1595 if ((*rprotolist_here).p != NULL) {
1596 if ((*rprotolist_here).p->get_protocol_name () == "nullproto") {
1597 (*rprotolist_here).p->remove_collection (collection, logout);
1598 return;
1599 }
1600 }
1601 rprotolist_here ++;
1602 }
1603
1604 logout << "collectoraction::create_colserver: no valid nullproto found\n";
1605}
1606
1607bool collectoraction::create_event_header_file (const text_t &filename, cgiargsclass &args,
1608 ostream &logout) {
1609
1610 outconvertclass text_t2ascii;
1611 char *filenamec = filename.getcstr();
1612 ofstream eheadfile (filenamec);
1613 delete filenamec;
1614
1615 if (eheadfile) {
1616 eheadfile << text_t2ascii << get_event_header (args);
1617 eheadfile.close();
1618 return true;
1619 }
1620
1621 logout << text_t2ascii << "collectoraction::create_event_header ERROR: Couldn't create "
1622 << "Event Header file " << filename << ". Event logging disabled\n";
1623 return false;
1624}
1625
1626text_t collectoraction::get_event_header (cgiargsclass &args) {
1627 text_t header = "Greenstone Username: " + args["un"] + "\n";
1628 header += "Collection: " + args["bc1dirname"] + "\n";
1629 header += "Collection Creator: " + args["bc1contactemail"] + "\n";
1630 header += "GSDLHOME: " + gsdlhome + "\n";
1631 header += "Build Location: " + get_collectdir(args) + "\n";
1632
1633 return header;
1634}
Note: See TracBrowser for help on using the repository browser.