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

Last change on this file since 1474 was 1474, checked in by sjboddie, 24 years ago

* empty log message *

  • Property svn:keywords set to Author Date Id Revision
File size: 41.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
38#if !defined (__WIN32__)
39#include <sys/utsname.h>
40#include <unistd.h>
41#endif
42
43collectoraction::collectoraction () {
44
45 recpt = NULL;
46 do_mkcol = false;
47
48 cgiarginfo arg_ainfo;
49 arg_ainfo.shortname = "a";
50 arg_ainfo.longname = "action";
51 arg_ainfo.multiplechar = true;
52 arg_ainfo.defaultstatus = cgiarginfo::weak;
53 arg_ainfo.argdefault = "collector";
54 arg_ainfo.savedarginfo = cgiarginfo::must;
55 argsinfo.addarginfo (NULL, arg_ainfo);
56
57 arg_ainfo.shortname = "cp";
58 arg_ainfo.longname = "collector page";
59 arg_ainfo.multiplechar = true;
60 arg_ainfo.defaultstatus = cgiarginfo::weak;
61 arg_ainfo.argdefault = "intro";
62 arg_ainfo.savedarginfo = cgiarginfo::must;
63 argsinfo.addarginfo (NULL, arg_ainfo);
64
65 // temporary directory name for this collector
66 // session
67 arg_ainfo.shortname = "bc1tmp";
68 arg_ainfo.longname = "collector specific";
69 arg_ainfo.multiplechar = true;
70 arg_ainfo.defaultstatus = cgiarginfo::weak;
71 arg_ainfo.argdefault = "";
72 arg_ainfo.savedarginfo = cgiarginfo::must;
73 argsinfo.addarginfo (NULL, arg_ainfo);
74
75 arg_ainfo.shortname = "bc1fullname";
76 arg_ainfo.longname = "collector specific";
77 arg_ainfo.multiplechar = true;
78 arg_ainfo.defaultstatus = cgiarginfo::weak;
79 arg_ainfo.argdefault = "";
80 arg_ainfo.savedarginfo = cgiarginfo::must;
81 argsinfo.addarginfo (NULL, arg_ainfo);
82
83 arg_ainfo.shortname = "bc1dirname";
84 arg_ainfo.longname = "collector specific";
85 arg_ainfo.multiplechar = true;
86 arg_ainfo.defaultstatus = cgiarginfo::weak;
87 arg_ainfo.argdefault = "";
88 arg_ainfo.savedarginfo = cgiarginfo::must;
89 argsinfo.addarginfo (NULL, arg_ainfo);
90
91 arg_ainfo.shortname = "bc1contactemail";
92 arg_ainfo.longname = "collector specific";
93 arg_ainfo.multiplechar = true;
94 arg_ainfo.defaultstatus = cgiarginfo::weak;
95 arg_ainfo.argdefault = "";
96 arg_ainfo.savedarginfo = cgiarginfo::must;
97 argsinfo.addarginfo (NULL, arg_ainfo);
98
99 arg_ainfo.shortname = "bc1aboutdesc";
100 arg_ainfo.longname = "collector specific";
101 arg_ainfo.multiplechar = true;
102 arg_ainfo.defaultstatus = cgiarginfo::weak;
103 arg_ainfo.argdefault = "";
104 arg_ainfo.savedarginfo = cgiarginfo::must;
105 argsinfo.addarginfo (NULL, arg_ainfo);
106
107 arg_ainfo.shortname = "bc1clone";
108 arg_ainfo.longname = "collector specific";
109 arg_ainfo.multiplechar = false;
110 arg_ainfo.defaultstatus = cgiarginfo::weak;
111 arg_ainfo.argdefault = "0";
112 arg_ainfo.savedarginfo = cgiarginfo::must;
113 argsinfo.addarginfo (NULL, arg_ainfo);
114
115 arg_ainfo.shortname = "bc1clonecol";
116 arg_ainfo.longname = "collector specific";
117 arg_ainfo.multiplechar = true;
118 arg_ainfo.defaultstatus = cgiarginfo::weak;
119 arg_ainfo.argdefault = "";
120 arg_ainfo.savedarginfo = cgiarginfo::must;
121 argsinfo.addarginfo (NULL, arg_ainfo);
122
123 // set when cloning option has changed
124 arg_ainfo.shortname = "bc1clonechanged";
125 arg_ainfo.longname = "collector specific";
126 arg_ainfo.multiplechar = false;
127 arg_ainfo.defaultstatus = cgiarginfo::weak;
128 arg_ainfo.argdefault = "0";
129 arg_ainfo.savedarginfo = cgiarginfo::mustnot;
130 argsinfo.addarginfo (NULL, arg_ainfo);
131
132 arg_ainfo.shortname = "bc1inputdir1";
133 arg_ainfo.longname = "collector specific";
134 arg_ainfo.multiplechar = true;
135 arg_ainfo.defaultstatus = cgiarginfo::weak;
136 arg_ainfo.argdefault = "";
137 arg_ainfo.savedarginfo = cgiarginfo::must;
138 argsinfo.addarginfo (NULL, arg_ainfo);
139
140 arg_ainfo.shortname = "bc1inputdir2";
141 arg_ainfo.longname = "collector specific";
142 arg_ainfo.multiplechar = true;
143 arg_ainfo.defaultstatus = cgiarginfo::weak;
144 arg_ainfo.argdefault = "";
145 arg_ainfo.savedarginfo = cgiarginfo::must;
146 argsinfo.addarginfo (NULL, arg_ainfo);
147
148 arg_ainfo.shortname = "bc1inputdir3";
149 arg_ainfo.longname = "collector specific";
150 arg_ainfo.multiplechar = true;
151 arg_ainfo.defaultstatus = cgiarginfo::weak;
152 arg_ainfo.argdefault = "";
153 arg_ainfo.savedarginfo = cgiarginfo::must;
154 argsinfo.addarginfo (NULL, arg_ainfo);
155
156 arg_ainfo.shortname = "bc1inputdir4";
157 arg_ainfo.longname = "collector specific";
158 arg_ainfo.multiplechar = true;
159 arg_ainfo.defaultstatus = cgiarginfo::weak;
160 arg_ainfo.argdefault = "";
161 arg_ainfo.savedarginfo = cgiarginfo::must;
162 argsinfo.addarginfo (NULL, arg_ainfo);
163
164 // only set when one of the fields was changed in
165 // the "collection info" page
166 arg_ainfo.shortname = "bc1infochanged";
167 arg_ainfo.longname = "collector specific";
168 arg_ainfo.multiplechar = false;
169 arg_ainfo.defaultstatus = cgiarginfo::weak;
170 arg_ainfo.argdefault = "0";
171 arg_ainfo.savedarginfo = cgiarginfo::mustnot;
172 argsinfo.addarginfo (NULL, arg_ainfo);
173
174 // only set when cfg file is altered from within
175 // "configure collection" page
176 arg_ainfo.shortname = "bc1cfgchanged";
177 arg_ainfo.longname = "collector specific";
178 arg_ainfo.multiplechar = false;
179 arg_ainfo.defaultstatus = cgiarginfo::weak;
180 arg_ainfo.argdefault = "0";
181 arg_ainfo.savedarginfo = cgiarginfo::mustnot;
182 argsinfo.addarginfo (NULL, arg_ainfo);
183
184 arg_ainfo.shortname = "bc1dodelete";
185 arg_ainfo.longname = "collector specific";
186 arg_ainfo.multiplechar = false;
187 arg_ainfo.defaultstatus = cgiarginfo::weak;
188 arg_ainfo.argdefault = "0";
189 arg_ainfo.savedarginfo = cgiarginfo::mustnot;
190 argsinfo.addarginfo (NULL, arg_ainfo);
191
192 // will be set if we arrived at the "configure collection" page
193 // via the "changing an existing collection" page
194 arg_ainfo.shortname = "bc1econf";
195 arg_ainfo.longname = "collector specific";
196 arg_ainfo.multiplechar = false;
197 arg_ainfo.defaultstatus = cgiarginfo::weak;
198 arg_ainfo.argdefault = "0";
199 arg_ainfo.savedarginfo = cgiarginfo::must;
200 argsinfo.addarginfo (NULL, arg_ainfo);
201
202 // will be set if we arrived at the "source data" page
203 // via the "changing an existing collection" page
204 arg_ainfo.shortname = "bc1esrce";
205 arg_ainfo.longname = "collector specific";
206 arg_ainfo.multiplechar = false;
207 arg_ainfo.defaultstatus = cgiarginfo::weak;
208 arg_ainfo.argdefault = "0";
209 arg_ainfo.savedarginfo = cgiarginfo::must;
210 argsinfo.addarginfo (NULL, arg_ainfo);
211}
212
213collectoraction::~collectoraction () {
214 delete gsdlosc;
215 delete gsdlhomec;
216}
217
218bool collectoraction::init (ostream & /*logout*/) {
219
220 // set up GSDLOS and GSDLHOME environment variables
221 text_t gsdlos;
222#if defined (__WIN32__)
223 gsdlos = "windows";
224#else
225 utsname *buf = new utsname();
226 int i = uname (buf);
227 if (i == -1) gsdlos = "linux"; // uname failed
228 else gsdlos.setcstr (buf->sysname);
229 delete buf;
230 lc (gsdlos);
231#endif
232
233 gsdlos = "GSDLOS=" + gsdlos;
234 text_t setgsdlhome = "GSDLHOME=" + gsdlhome;
235
236 // these will be cleaned up in the destructor
237 gsdlosc = gsdlos.getcstr();
238 gsdlhomec = setgsdlhome.getcstr();
239
240 putenv (gsdlosc);
241 putenv (gsdlhomec);
242
243 return true;
244}
245
246bool collectoraction::check_cgiargs (cgiargsinfoclass &/*argsinfo*/, cgiargsclass &args,
247 ostream &logout) {
248
249 text_t &current_page = args["cp"];
250
251 // note that the "bildstatus" and "bildframe1" pages don't actually do anything
252 // functional so we don't need to worry about authenticating them (it's the
253 // underlying "bild" page that does the building (anc creates the frameset))
254 // This helps us overcome a bit of a problem we have with multiple pages trying
255 // to read from the key.db database at the same time.
256 if (current_page != "intro" && current_page != "bildstatus" && current_page != "bildframe1") {
257 // authenticate the user if authentication is available
258 args["uan"] = 1;
259 args["ug"] = "colbuilder";
260 }
261
262 if (current_page == "new" || current_page == "existing") {
263
264 // assign (and create) a temporary directory
265 assign_tmpname (args, logout);
266
267 // clean up any old builds left laying about in the tmp directory
268 // (note that it's possible this could take some time if there's a huge
269 // partially built collection laying about -- may need to think about doing
270 // this in the background).
271 text_t cleantmp_cmd = "perl " + filename_cat (gsdlhome, "bin", "script", "cleantmp.pl");
272 char *cleantmp_cmdc = cleantmp_cmd.getcstr();
273 system (cleantmp_cmdc);
274 delete cleantmp_cmdc;
275 }
276
277 if (args["bc1infochanged"] == "1") {
278
279 if (args["bc1dirname"].empty()) {
280 // we've just come from the "collection information" page for the
281 // first time so we'll need to create the collection with mkcol.pl
282 // and set up bc1dirname - we do this part here instead of in do_action
283 // because the bc1dirname argument must be set to its new value before
284 // the compressedoptions macros are set.
285 args["bc1dirname"] = get_directory_name (args["bc1fullname"]);
286
287 text_t createfile = filename_cat (gsdlhome, "tmp", args["bc1tmp"], ".create");
288 if (!file_exists (createfile)) {
289 // we could do the mkcol.pl here but I guess it's nicer to do it in do_action()
290 do_mkcol = true;
291 } else {
292 // .create file already exists but bc1dirname wasn't set ... this should only be
293 // able to occur when the "reload" (and possibly the "back" and "forward" buttons)
294 // have been used to get us here.
295 // we'll check that the bc1dirname directory exists (in case of the unlikely
296 // possibility that get_directory_name returned a different value this time
297 // than it did originally).
298 text_t coldir = filename_cat (get_collectdir(args), args["bc1dirname"]);
299 if (!directory_exists (coldir)) {
300 message = "reloaderror";
301 return true;
302 }
303 }
304 } else {
305 // "collection information" has been changed after collection already exists
306 // so we'll need to update the cfg file.
307 update_cfgfile_partial (args, logout);
308 }
309 }
310
311 if (args["bc1cfgchanged"] == "1") {
312 // configuration file has been changed from the "configure collection"
313 // page. we need to update the file on disk and catch bc1 arguments up
314 // with changes.
315 update_cfgfile_complete (args, logout);
316 }
317
318 if (args["bc1clonechanged"] == "1") {
319 // cloning option has been changed on "source data" page. if it was turned
320 // on we want to create a new collect.cfg file using the bc1clonecol cfg file
321 // as a model (we'll save the old file as collect.cfg.org). if cloning was
322 // turned off we'll revert to using the collect.cfg.org file (which will need
323 // updating in case the bc1 arguments have been altered since cloning was
324 // turned on).
325 update_cfgfile_clone (args, logout);
326 }
327
328 if (current_page == "bildstatus") {
329 // if .final file exists then build has finished
330 text_t fbld = filename_cat (gsdlhome, "tmp", args["bc1tmp"], args["bc1dirname"] + ".bld.final");
331 if (file_exists (fbld)) {
332 char *fbldc = fbld.getcstr();
333 ifstream fbld_in (fbldc);
334 if (fbld_in) {
335 char c = fbld_in.get();
336 fbld_in.close();
337 if (c == '0') current_page = "bilddone";
338 else if (c == '4' || c == '5') message = "installfail";
339 else current_page = "bildfail";
340 } else {
341 // assume build failed (we shouldn't get here though ... right?)
342 current_page = "bildfail";
343 }
344 delete fbldc;
345 }
346 }
347
348 return true;
349}
350
351void collectoraction::update_cfgfile_clone (cgiargsclass &args, ostream &logout) {
352
353 text_t tmpdir = filename_cat(gsdlhome, "tmp", args["bc1tmp"]);
354 text_t cfgfile = filename_cat(tmpdir, args["bc1dirname"], "etc", "collect.cfg");
355 if (!file_exists (cfgfile)) {
356 message = "tmpfail";
357 return;
358 }
359
360 text_t cfgfile_org = filename_cat (tmpdir, "collect.cfg.org");
361
362 if (args["bc1clone"] == "1") {
363 // cloning was turned on
364
365 text_t cfgfile_clone = filename_cat(gsdlhome, "collect", args["bc1clonecol"], "etc", "collect.cfg");
366 if (file_exists (cfgfile_clone)) {
367 // if .org file doesn't exist already create it
368 if (!file_exists (cfgfile_org)) {
369 if (!file_copy (cfgfile, cfgfile_org)) {
370 message = "tmpfail";
371 return;
372 }
373 }
374 // copy clone collections cfg file to new collection
375 if (!file_copy (cfgfile_clone, cfgfile)) {
376 message = "tmpfail";
377 return;
378 }
379 // update the new cfg file
380 update_cfgfile_partial (args, logout);
381
382 } else {
383 // can't clone non-existant or read-protected collection
384 message = "clonefail";
385 }
386
387 } else {
388 // cloning has been turned off having been on at some point. the .org file
389 // should exist, if it doesn't we'll bail out and leave the user with the
390 // cloned copy
391 if (file_exists (cfgfile_org)) {
392 // copy original back again and update it with any recent changes
393 if (file_copy (cfgfile_org, cfgfile)) {
394 update_cfgfile_partial (args, logout);
395 } else {
396 message = "tmpfail";
397 }
398 }
399 }
400}
401
402// update configuration file on disk to match bc1 arguments
403void collectoraction::update_cfgfile_partial (cgiargsclass &args, ostream &logout) {
404
405 text_t cfgfile = filename_cat(get_collectdir(args), args["bc1dirname"], "etc", "collect.cfg");
406 char *cfgfilec = cfgfile.getcstr();
407
408 vector<text_tarray> cfgarray;
409
410 // read in cfg file
411 ifstream cfg_in (cfgfilec);
412 if (cfg_in) {
413 text_tarray cfgline;
414 while (read_cfg_line(cfg_in, cfgline) >= 0) {
415 if (cfgline.size () >= 2) {
416 if (cfgline[0] == "creator" || cfgline[0] == "maintainer") {
417 cfgline[1] = args["bc1contactemail"];
418 } else if (cfgline[0] == "collectionmeta") {
419 if (cfgline[1] == "collectionname") {
420 cfgline[2] = args["bc1fullname"];
421 } else if (cfgline[1] == "collectionextra") {
422 cfgline[2] = carriage_replace (args["bc1aboutdesc"], 0);
423 }
424 }
425 }
426 cfgarray.push_back (cfgline);
427 }
428 cfg_in.close();
429
430 // now write cfg file back out
431#ifdef __WIN32__
432 ofstream cfg_out (cfgfilec, ios::binary);
433#else
434 ofstream cfg_out (cfgfilec);
435#endif
436 if (cfg_out) {
437 // lock the file
438 int fd = GSDL_GET_FILEDESC(cfg_out);
439 int lock_val = 1;
440 GSDL_LOCK_FILE (fd);
441 if (lock_val != 0) {
442 logout << "Error: Couldn't lock file " << cfgfilec << "\n";
443 cfg_out.close();
444 message = "tmpfail";
445
446 } else {
447
448 vector<text_tarray>::const_iterator this_line = cfgarray.begin();
449 vector<text_tarray>::const_iterator end_line = cfgarray.end();
450 while (this_line != end_line) {
451 write_cfg_line (cfg_out, *this_line);
452 this_line ++;
453 }
454 GSDL_UNLOCK_FILE (fd);
455 cfg_out.close();
456 }
457
458 } else {
459 logout << "collectoraction::update_cfgfile_partial: unable to open "
460 << cfgfilec << " for output\n";
461 message = "tmpfail";
462 }
463
464 } else {
465 logout << "collectoraction::update_cfgfile_partial: unable to open "
466 << cfgfilec << " for input\n";
467 message = "tmpfail";
468 }
469 delete cfgfilec;
470}
471
472// replace configuration file on disk with that in the bc1cfgfile argument and
473// catch other bc1 arguments up with those the new cfgfile contains
474void collectoraction::update_cfgfile_complete (cgiargsclass &args, ostream &logout) {
475
476 text_t cfgfile = filename_cat(get_collectdir(args), args["bc1dirname"], "etc", "collect.cfg");
477 char *cfgfilec = cfgfile.getcstr();
478#ifdef __WIN32__
479 ofstream cfg_out (cfgfilec, ios::binary);
480#else
481 ofstream cfg_out (cfgfilec);
482#endif
483
484 if (cfg_out) {
485 // lock the file
486 int fd = GSDL_GET_FILEDESC(cfg_out);
487 int lock_val = 1;
488 GSDL_LOCK_FILE (fd);
489 if (lock_val != 0) {
490 logout << "Error: Couldn't lock file " << cfgfilec << "\n";
491 cfg_out.close();
492 message = "tmpfail";
493
494 } else {
495
496 outconvertclass text_t2ascii;
497 cfg_out << text_t2ascii << args["bc1cfgfile"];
498 GSDL_UNLOCK_FILE (fd);
499 cfg_out.close();
500
501 // now that we've written the file we'll read it back again and
502 // update our bc1 arguments
503 ifstream cfg_in (cfgfilec);
504 if (cfg_in) {
505 text_tarray cfgline;
506 while (read_cfg_line(cfg_in, cfgline) >= 0) {
507 if (cfgline.size () >= 2) {
508 if (cfgline[0] == "creator") {
509 args["bc1contactemail"] = cfgline[1];
510 } else if (cfgline[0] == "collectionmeta") {
511 if (cfgline[1] == "collectionname") {
512 args["bc1fullname"] = cfgline[2];
513 } else if (cfgline[1] == "collectionextra") {
514 args["bc1aboutdesc"] = carriage_replace (cfgline[2], 1);
515 }
516 }
517 }
518 }
519 cfg_in.close();
520 } else {
521 logout << "collectoraction::update_cfgfile_complete: unable to open "
522 << cfgfilec << " for input\n";
523 message = "tmpfail";
524 }
525 }
526 } else {
527 logout << "collectoraction::update_cfgfile_complete: unable to open "
528 << cfgfilec << " for output\n";
529 message = "tmpfail";
530 }
531 delete cfgfilec;
532}
533
534void collectoraction::get_cgihead_info (cgiargsclass &/*args*/, recptprotolistclass * /*protos*/,
535 response_t &response,text_t &response_data,
536 ostream &/*logout*/) {
537 response = content;
538 response_data = "text/html";
539}
540
541// return html for buttons used in collector bar
542// color may be "green", "grey", or "yellow"
543// type may be:
544// "info" --> "collection information" button
545// "srce" --> "source data" button
546// "conf" --> "configure collection" button
547// "bild" --> "build collection" button
548// "view" --> "view collection" button
549// if enabled is true button will be flashy rollover type and
550// will be hyperlinked
551
552text_t collectoraction::get_button (const text_t &thispage, const text_t &color,
553 const text_t &type, bool enabled) {
554
555 if ((color != "green" && color != "grey" && color != "yellow") ||
556 (type != "info" && type != "srce" && type != "conf" && type != "bild" && type != "view"))
557 return "";
558
559 text_t prefix = "gc";
560 if (color == "grey") prefix = "nc";
561 else if (color == "yellow") prefix = "yc";
562
563 text_t httpicon = "httpicon" + prefix + type;
564
565 if (enabled) {
566 text_t gsmacro = "_gsimage_";
567 if (thispage == "info" || thispage == "srce" || thispage == "conf") {
568 gsmacro = "_gsjimage_";
569 } else if (type == "view") {
570 // view button is special case as it needs a target=_top
571 gsmacro = "_gstimage_";
572 }
573 return "<td>" + gsmacro + "(_collector:http" + type + "_,_collector:" + httpicon +
574 "of_,_collector:" + httpicon + "on_," + type + ",_collector:text" + type + "_)</td>\n";
575 } else {
576 return "<td>_icon" + prefix + type + "of_</td>\n";
577 }
578}
579
580// set the _fullnamemenu_ macro
581void collectoraction::set_fullnamemenu (displayclass &disp, cgiargsclass &args,
582 recptprotolistclass *protos, ostream &logout) {
583
584 if (recpt == NULL) {
585 logout << "ERROR (collectoraction::set_fullnamemenu): This action does not contain\n"
586 << " information about any receptionists. The method set_receptionist was\n"
587 << " probably not called from the module which instantiated this action.\n";
588 return;
589 }
590
591 text_t &current_page = args["cp"];
592 text_t currentname = args["bc1dirname"];
593 if (current_page == "srce") currentname = args["bc1clonecol"];
594
595 text_tarray dirnames;
596 text_tarray fullnames;
597 vector<bool> write_protected;
598 int selected_index = 0;
599 int index = 0;
600
601 recptprotolistclass::iterator rprotolist_here = protos->begin();
602 recptprotolistclass::iterator rprotolist_end = protos->end();
603 while (rprotolist_here != rprotolist_end) {
604 if ((*rprotolist_here).p != NULL) {
605
606 // don't include z39.50 collection
607 if ((*rprotolist_here).p->get_protocol_name () == "z3950proto") {
608 rprotolist_here ++;
609 continue;
610 }
611
612 text_tarray collist;
613 comerror_t err;
614 (*rprotolist_here).p->get_collection_list (collist, err, logout);
615 if (err == noError) {
616 text_tarray::iterator collist_here = collist.begin();
617 text_tarray::iterator collist_end = collist.end();
618 FilterResponse_t response;
619 text_tset metadata;
620 metadata.insert ("collectionname");
621 while (collist_here != collist_end) {
622 ColInfoResponse_t *cinfo = recpt->get_collectinfo_ptr ((*rprotolist_here).p, *collist_here, logout);
623 if (cinfo != NULL) {
624 text_t collectionname = *collist_here;
625 if (!cinfo->collectionmeta["collectionname"].empty()) {
626 // get collection name from the collection cfg file
627 collectionname = cinfo->collectionmeta["collectionname"];
628 } else if (get_info ("collection", *collist_here, metadata, false,
629 (*rprotolist_here).p, response, logout)) {
630 // get collection name from gdbm file
631 collectionname = response.docInfo[0].metadata["collectionname"].values[0];
632 }
633 dirnames.push_back(*collist_here);
634 fullnames.push_back(collectionname);
635 // check to see if the "collection" is writable
636 if (collection_protected (*collist_here)) write_protected.push_back(true);
637 else write_protected.push_back(false);
638 if (*collist_here == currentname) selected_index = index;
639
640 index ++;
641 }
642 collist_here ++;
643 }
644 }
645 }
646 rprotolist_here ++;
647 }
648
649 bool have_one = false;
650 text_t fullnamemenu = "<select name=\"bc1dirname\">\n";
651 if (current_page == "srce") fullnamemenu = "<select name=\"bc1clonecol\">\n";
652 for (int i = 0; i < index; i ++) {
653 // don't want write protected collections in list on "change existing
654 // collection" page
655 if (write_protected[i] && current_page == "existing") continue;
656 have_one = true;
657 fullnamemenu += "<option value=\"" + dirnames[i] + "\"";
658 if (i == selected_index) fullnamemenu += " selected";
659 fullnamemenu.push_back ('>');
660 fullnamemenu += fullnames[i];
661 if (write_protected[i]) fullnamemenu += " <b>(write protected)</b>";
662 }
663 fullnamemenu += "</select>\n";
664 if (have_one) disp.setmacro ("fullnamemenu", "collector", fullnamemenu);
665}
666
667// set the _cfgfile_ macro
668void collectoraction::set_cfgfile (displayclass &disp, cgiargsclass &args, ostream &logout) {
669
670 text_t &collection = args["bc1dirname"];
671 if (collection.empty()) {
672 message = "nocollection";
673 return;
674 }
675
676 // read in collect.cfg
677 text_t cfgfile = filename_cat(get_collectdir(args), collection, "etc", "collect.cfg");
678 char *cfgfilec = cfgfile.getcstr();
679
680#ifdef GSDL_USE_IOS_H
681 ifstream cfg_ifs (cfgfilec, ios::in | ios::nocreate);
682#else
683 ifstream cfg_ifs (cfgfilec, ios::in);
684#endif
685
686 if (cfg_ifs) {
687 // read in collect.cfg
688 text_t cfgtext;
689 char c;
690 cfg_ifs.get(c);
691 while (!cfg_ifs.eof ()) {
692 if (c=='\\') cfgtext.push_back('\\');
693 cfgtext.push_back(c);
694 cfg_ifs.get(c);
695 }
696 cfg_ifs.close();
697
698 // define it as a macro
699 disp.setmacro("cfgfile","collector",cfgtext);
700
701 } else {
702 logout << "collectoraction::set_cfgfile: couldn't open configuration file ("
703 << cfgfilec << ") for reading\n";
704 message = "tmpfail";
705 }
706 delete cfgfilec;
707}
708
709// set the _statusline_ macro
710void collectoraction::set_statusline (displayclass &disp, cgiargsclass &args, ostream & /*logout*/) {
711
712 // the build command creates .bld.download, .bld.import, and .bld.build files (in that
713 // order) and deletes them (also in that order) when each stage is complete. the .bld
714 // file is the concatenation of all these files.
715 text_t bld_file = filename_cat (gsdlhome, "tmp", args["bc1tmp"], args["bc1dirname"] + ".bld");
716 text_t statusline;
717
718 if (file_exists (bld_file + ".download")) {
719 statusline = "Downloading files ...<br>\n";
720 statusline += file_tail (bld_file + ".download");
721 } else if (file_exists (bld_file + ".import")) {
722 statusline = "Importing collection ...<br>\n";
723 statusline += file_tail (bld_file + ".import");
724 } else if (file_exists (bld_file + ".build")) {
725 statusline = "Building collection ...<br>\n";
726 statusline += file_tail (bld_file + ".build");
727 } else {
728 statusline += "creating collection ...<br>\n";
729 statusline += file_tail (bld_file);
730 }
731
732 disp.setmacro ("statusline", "collector", statusline);
733
734}
735
736void collectoraction::define_internal_macros (displayclass &disp, cgiargsclass &args,
737 recptprotolistclass *protos, ostream &logout) {
738
739 // define_internal_macros sets the following macros:
740 // _collectorbar_
741 // _pagescriptextra_
742 // _fullnamemenu_ -- if displaying the "source data" page or the "changing existing
743 // collection" page
744 // _cfgfile_ -- if displaying the "configure collection" page
745 // _statusline_ -- if displaying the bildstatus page
746 // _header_ -- may be set for pages that require it
747
748 text_t &collector_page = args["cp"];
749 int esrce = args["bc1esrce"].getint();
750 int econf = args["bc1econf"].getint();
751
752 // set _pagescriptextra_ macro to _cpagescriptextra_
753 disp.setmacro ("pagescriptextra", "collector", "_" + collector_page + "scriptextra_");
754
755 if (collector_page == "bildstatus" || collector_page == "bilddone" ||
756 collector_page == "bildfail" || collector_page == "bildframe1") {
757 disp.setmacro ("header", "collector", "_" + collector_page + "header_");
758 }
759
760 // set the collectorbar macro
761 text_t collectorbar = "<table border=0 cellspacing=4 cellpadding=0><tr>\n";
762
763 if (collector_page == "new") {
764 collectorbar += get_button (collector_page, "green", "info", true);
765 collectorbar += "<td>_icongreyarrow_</td>\n";
766 collectorbar += get_button (collector_page, "grey", "srce", false);
767 collectorbar += "<td>_icongreyarrow_</td>\n";
768 collectorbar += get_button (collector_page, "grey", "conf", false);
769 collectorbar += "<td>_icongreyarrow_</td>\n";
770 collectorbar += get_button (collector_page, "grey", "bild", false);
771 collectorbar += "<td>_icongreyarrow_</td>\n";
772 collectorbar += get_button (collector_page, "grey", "view", false);
773
774 } else if (collector_page == "info") {
775 collectorbar += get_button (collector_page, "yellow", "info", false);
776 collectorbar += "<td>_icongreyarrow_</td>\n";
777 collectorbar += get_button (collector_page, "green", "srce", true);
778 collectorbar += "<td>_icongreyarrow_</td>\n";
779 collectorbar += get_button (collector_page, "grey", "conf", false);
780 collectorbar += "<td>_icongreyarrow_</td>\n";
781 collectorbar += get_button (collector_page, "grey", "bild", false);
782 collectorbar += "<td>_icongreyarrow_</td>\n";
783 collectorbar += get_button (collector_page, "grey", "view", false);
784 collectorbar += "</tr><tr><td align=center>_icongreyuparrow_</td><td colspan=8></td>\n";
785
786 } else if (collector_page == "srce") {
787 if (esrce == 1) {
788 // if we came from the "change an existing collection" page previous button(s)
789 // are disabled
790 collectorbar += get_button (collector_page, "grey", "info", false);
791 } else {
792 collectorbar += get_button (collector_page, "yellow", "info", true);
793 }
794 collectorbar += "<td>_icongreyarrow_</td>\n";
795 collectorbar += get_button (collector_page, "yellow", "srce", false);
796 collectorbar += "<td>_icongreyarrow_</td>\n";
797 collectorbar += get_button (collector_page, "green", "conf", true);
798 collectorbar += "<td>_icongreyarrow_</td>\n";
799 collectorbar += get_button (collector_page, "grey", "bild", false);
800 collectorbar += "<td>_icongreyarrow_</td>\n";
801 collectorbar += get_button (collector_page, "grey", "view", false);
802 collectorbar += "</tr><tr><td colspan=2></td><td align=center>_icongreyuparrow_</td><td colspan=6></td>\n";
803
804 } else if (collector_page == "conf") {
805 // disable appropriate buttons if we came from "change an existing collection"
806 // page
807 if (esrce == 1 || econf == 1) {
808 collectorbar += get_button (collector_page, "grey", "info", false);
809 } else {
810 collectorbar += get_button (collector_page, "yellow", "info", true);
811 }
812 collectorbar += "<td>_icongreyarrow_</td>\n";
813 if (econf == 1) {
814 collectorbar += get_button (collector_page, "grey", "srce", false);
815 } else {
816 collectorbar += get_button (collector_page, "yellow", "srce", true);
817 }
818 collectorbar += "<td>_icongreyarrow_</td>\n";
819 collectorbar += get_button (collector_page, "yellow", "conf", false);
820 collectorbar += "<td>_icongreyarrow_</td>\n";
821 collectorbar += get_button (collector_page, "green", "bild", true);
822 collectorbar += "<td>_icongreyarrow_</td>\n";
823 collectorbar += get_button (collector_page, "grey", "view", false);
824 collectorbar += "</tr><tr><td colspan=4></td><td align=center>_icongreyuparrow_</td><td colspan=4></td>\n";
825
826 } else if (collector_page == "bilddone") {
827 // all previous buttons grey after build was completed
828 collectorbar += get_button (collector_page, "grey", "info", false);
829 collectorbar += "<td>_icongreyarrow_</td>\n";
830 collectorbar += get_button (collector_page, "grey", "srce", false);
831 collectorbar += "<td>_icongreyarrow_</td>\n";
832 collectorbar += get_button (collector_page, "grey", "conf", false);
833 collectorbar += "<td>_icongreyarrow_</td>\n";
834 collectorbar += get_button (collector_page, "yellow", "bild", false);
835 collectorbar += "<td>_icongreyarrow_</td>\n";
836 collectorbar += get_button (collector_page, "green", "view", true);
837 collectorbar += "</tr><tr><td colspan=6></td><td align=center>_icongreyuparrow_</td><td colspan=2></td>\n";
838
839 } else if (collector_page == "bildcancel") {
840 // disable appropriate buttons if we came from "change an existing collection"
841 // page
842 if (esrce == 1 || econf == 1) {
843 collectorbar += get_button (collector_page, "grey", "info", false);
844 } else {
845 collectorbar += get_button (collector_page, "yellow", "info", true);
846 }
847 collectorbar += "<td>_icongreyarrow_</td>\n";
848 if (econf == 1) {
849 collectorbar += get_button (collector_page, "grey", "srce", false);
850 } else {
851 collectorbar += get_button (collector_page, "yellow", "srce", true);
852 }
853 collectorbar += "<td>_icongreyarrow_</td>\n";
854 collectorbar += get_button (collector_page, "yellow", "conf", true);
855 collectorbar += "<td>_icongreyarrow_</td>\n";
856 collectorbar += get_button (collector_page, "yellow", "bild", true);
857 collectorbar += "<td>_icongreyarrow_</td>\n";
858 collectorbar += get_button (collector_page, "grey", "view", false);
859 }
860
861 collectorbar += "</tr></table>\n";
862 disp.setmacro ("collectorbar", "collector", collectorbar);
863
864 if (collector_page == "srce" || collector_page == "existing")
865 set_fullnamemenu (disp, args, protos, logout);
866 if (collector_page == "conf")
867 set_cfgfile (disp, args, logout);
868 if (collector_page == "bildstatus")
869 set_statusline (disp, args, logout);
870}
871
872bool collectoraction::do_action (cgiargsclass &args, recptprotolistclass * /*protos*/,
873 browsermapclass * /*browsers*/, displayclass &disp,
874 outconvertclass &outconvert, ostream &textout,
875 ostream &logout) {
876
877 text_t &collector_page = args["cp"];
878 text_t &collection = args["bc1dirname"];
879
880 if (collector_page == "bild") {
881 // do the work (download, import, build)
882 gsdl_build (args, logout);
883
884 if (message.empty()) {
885 // bild page is a frameset so we don't want headers and stuff
886 textout << outconvert << disp << ("_collector:bildcontent_\n");
887 }
888 }
889
890 if (do_mkcol == true) {
891 // execute mkcol.pl (do_mkcol is set from within check_cgiargs)
892 gsdl_mkcol (args, logout);
893 do_mkcol = false; // reset for fast-cgi
894 }
895
896 if (args["bc1dodelete"] == "1") {
897 // delete bcidirname collection
898 if (collection_protected (collection)) {
899 message = "delinvalid";
900
901 } else {
902
903 text_t delete_cmd = "perl " + filename_cat (gsdlhome, "bin", "script", "delcol.pl");
904 delete_cmd += " -f " + collection;
905 char *delete_cmdc = delete_cmd.getcstr();
906 int rv = system (delete_cmdc);
907 delete delete_cmdc;
908 if (rv != 0) {
909 // deletion failed -- permissions?
910 message = "delpermission";
911 } else {
912 message = "delsuccess";
913 }
914 }
915 }
916
917 if (collector_page == "bildcancel") {
918 // cancel the build
919 gsdl_cancel_build (args, logout);
920 }
921
922 if (message.empty()) {
923 if (collector_page != "bild") {
924 // output page ("bild" page was already output above)
925 textout << outconvert << disp << ("_collector:header_\n")
926 << ("_collector:" + collector_page + "content_\n")
927 << ("_collector:footer_\n");
928 }
929 } else {
930 // message was set somewhere (probably an error), output message page
931 textout << outconvert << disp << ("_collector:header_\n")
932 << ("_collector:" + message + "content_\n")
933 << ("_collector:footer_\n");
934 message.clear();
935 }
936 return true;
937}
938
939// if sw = 0 replace all carriage returns in intext with the string "\n"
940// else replace all occurances of "\n" with a carriage return
941text_t collectoraction::carriage_replace (const text_t &intext, int sw) {
942
943 text_t outtext;
944 text_t::const_iterator here = intext.begin();
945 text_t::const_iterator end = intext.end();
946 while (here != end) {
947 if (sw == 0) {
948 if (*here == '\n') {
949 if ((here+1) != end && *(here+1) == '\r') here ++;
950 outtext += "\\n";
951 } else if (*here == '\r') {
952 if ((here+1) != end && *(here+1) == '\n') here ++;
953 outtext += "\\n";
954 } else {
955 outtext.push_back (*here);
956 }
957 } else if (*here == '\\' && (here+1) != end && *(here+1) == 'n') {
958 outtext.push_back ('\n');
959 here ++;
960 } else {
961 outtext.push_back (*here);
962 }
963 here ++;
964 }
965 return outtext;
966}
967
968// create a short directory name from fullname
969text_t collectoraction::get_directory_name (const text_t &fullname) {
970
971 text_t shortname;
972 if (fullname.empty()) {
973 shortname = "coll";
974
975 } else {
976
977 // first make all lowercase and remove any dodgy characters
978 // (i.e. anything not [a-z]
979 text_t::const_iterator here = fullname.begin();
980 text_t::const_iterator end = fullname.end();
981 while (here != end) {
982 if ((*here >= 'A' && *here <= 'Z') || (*here >= 'a' && *here <= 'z') ||
983 (*here == ' ')) {
984 if (*here >= 'A' && *here <= 'Z') shortname.push_back (*here+32);
985 else if (*here == ' ') {
986 while ((*(here+1)) == ' ') here ++;
987 shortname.push_back (*here);
988 } else shortname.push_back (*here);
989 }
990 here ++;
991 }
992
993 text_tarray words;
994 splitchar (shortname.begin(), shortname.end(), ' ', words);
995 int num_words = words.size();
996
997 if (num_words == 0) {
998 shortname = "coll";
999
1000 } else {
1001
1002 shortname.clear();
1003 int use_words = (num_words <= 6) ? num_words : 6;
1004 int substr_len = 6 / use_words;
1005
1006 for (int i = 0; i < use_words; i++) {
1007 if (words[i].size() < substr_len) shortname += words[i];
1008 else shortname += substr (words[i].begin(), words[i].begin()+substr_len);
1009 }
1010 }
1011 }
1012
1013 // check to see if shortname is unique
1014 text_t fulldirname = filename_cat (gsdlhome, "collect", shortname);
1015 if (directory_exists (fulldirname)) {
1016 int version = 0;
1017 text_t newname;
1018 do {
1019 version ++;
1020 newname = shortname;
1021 newname.push_back ('v');
1022 newname.appendint (version);
1023 fulldirname = filename_cat (gsdlhome, "collect", newname);
1024 } while (directory_exists (fulldirname));
1025
1026 shortname = newname;
1027 }
1028
1029 return shortname;
1030}
1031
1032// tests if collection is write protected (currently just checks if
1033// collect.cfg file is writable
1034bool collectoraction::collection_protected (const text_t &collection) {
1035 text_t cfgfile = filename_cat(gsdlhome, "collect", collection, "etc", "collect.cfg");
1036 if (file_writable(cfgfile)) return false;
1037 return true;
1038}
1039
1040// assigns a temporary directory name for this collector session
1041// and creates temporary directory
1042void collectoraction::assign_tmpname (cgiargsclass &args, ostream &logout) {
1043
1044 int i = 0;
1045 text_t tmpname = "tbuild";
1046 while (directory_exists (filename_cat (gsdlhome, "tmp", tmpname + text_t(i)))) {
1047 i++;
1048 }
1049 tmpname.appendint (i);
1050
1051 text_t fulltmpdir = filename_cat (gsdlhome, "tmp", tmpname);
1052 if (!mk_dir (fulltmpdir)) {
1053 outconvertclass text_t2ascii;
1054 logout << text_t2ascii << "collectoraction::assign_tmpname unable to create directory ("
1055 << fulltmpdir << ")\n";
1056 }
1057
1058 args["bc1tmp"] = tmpname;
1059}
1060
1061void collectoraction::gsdl_mkcol (cgiargsclass &args, ostream &logout) {
1062
1063 text_t tmpdir = filename_cat (gsdlhome, "tmp", args["bc1tmp"]);
1064 if (!directory_exists (tmpdir)) {
1065 message = "tmpfail";
1066 return;
1067 }
1068
1069 text_t &collection = args["bc1dirname"];
1070 if (collection.empty()) {
1071 message = "nocollection";
1072 return;
1073 }
1074
1075 // check for a .create file - if it exists then we've already created the collection
1076 text_t createfile = filename_cat (tmpdir, ".create");
1077 if (file_exists (createfile)) {
1078 return;
1079 } else {
1080 // create the .create file (this file is just a place holder to let any future
1081 // pages know that the collection already exists).
1082 char *createfilec = createfile.getcstr();
1083 ofstream cfile_out (createfilec);
1084 delete createfilec;
1085 if (cfile_out) {
1086 cfile_out << "collection created\n";
1087 cfile_out.close();
1088 } else {
1089 message = "tmpfail";
1090 return;
1091 }
1092 }
1093
1094 // set up options
1095 text_t options = "-creator \"" + args["bc1contactemail"] + "\"";
1096 options += " -title \"" + args["bc1fullname"] + "\"";
1097 options += " -about \"" + carriage_replace (args["bc1aboutdesc"], 0) + "\"";
1098 options += " -collectdir \"" + tmpdir + "\" ";
1099 text_t optionfile = filename_cat (tmpdir, "mkcol.opt");
1100 char *optionfilet = optionfile.getcstr();
1101 ofstream ofile_out (optionfilet);
1102 delete optionfilet;
1103 if (!ofile_out) {
1104 message = "tmpfail";
1105 return;
1106 }
1107 outconvertclass text_t2ascii;
1108 ofile_out << text_t2ascii << options << "\n";
1109 ofile_out.close();
1110
1111 // set up the mkcol.pl command
1112 text_t mkcol_cmd = "perl ";
1113 mkcol_cmd += filename_cat (gsdlhome, "bin", "script", "mkcol.pl");
1114 mkcol_cmd += " -optionfile \"" + optionfile + "\" " + collection;
1115 char *mkcol_cmdc = mkcol_cmd.getcstr();
1116
1117 // just an ordinary system call running in the foreground. we're assuming (hoping??)
1118 // that mkcol.pl will run through fast enough that nothing more elaborate is required.
1119 system (mkcol_cmdc);
1120 delete mkcol_cmdc;
1121
1122 // make sure it went ok
1123 text_t cfgfile = filename_cat (tmpdir, collection, "etc", "collect.cfg");
1124 if (!file_writable (cfgfile)) {
1125 message = "mkcolfail";
1126 }
1127}
1128
1129void collectoraction::gsdl_build (cgiargsclass &args, ostream &logout) {
1130
1131 text_t tmpdir = filename_cat (gsdlhome, "tmp", args["bc1tmp"]);
1132 if (!directory_exists (tmpdir)) {
1133 message = "tmpfail";
1134 return;
1135 }
1136
1137 text_t &collection = args["bc1dirname"];
1138 if (collection.empty()) {
1139 message = "nocollection";
1140 return;
1141 }
1142
1143 // check for a .build file - if it exists then we've already built
1144 // the collection (or are in the process of building it)
1145 text_t buildfile = filename_cat (tmpdir, ".build");
1146 if (file_exists (buildfile)) {
1147 return;
1148 } else {
1149 // create the .build file (this file is just a place holder to let any future
1150 // pages know that we've already been here)
1151 char *buildfilec = buildfile.getcstr();
1152 ofstream bfile_out (buildfilec);
1153 delete buildfilec;
1154 if (bfile_out) {
1155 bfile_out << "collection building\n";
1156 bfile_out.close();
1157 } else {
1158 message = "tmpfail";
1159 return;
1160 }
1161 }
1162
1163 // set up build options
1164 text_t options = "-remove_import -out \"";
1165 options += filename_cat (tmpdir, collection + ".bld");
1166 options += "\" -collectdir \"" + get_collectdir (args) + "\"";
1167
1168 if (args["bc1esrce"] == 1) {
1169 // we're adding data to an existing collection
1170 options += "\" -save_archives -append";
1171 }
1172
1173 if (!args["bc1inputdir1"].empty())
1174 options += " -download \"" + args["bc1inputdir1"] + "\"";
1175 if (!args["bc1inputdir2"].empty())
1176 options += " -download \"" + args["bc1inputdir2"] + "\"";
1177 if (!args["bc1inputdir3"].empty())
1178 options += " -download \"" + args["bc1inputdir3"] + "\"";
1179 if (!args["bc1inputdir4"].empty())
1180 options += " -download \"" + args["bc1inputdir4"] + "\"";
1181 text_t optionfile = filename_cat (tmpdir, "build.opt");
1182 char *optionfilet = optionfile.getcstr();
1183 ofstream ofile_out (optionfilet);
1184 delete optionfilet;
1185 if (!ofile_out) {
1186 message = "tmpfail";
1187 return;
1188 }
1189 outconvertclass text_t2ascii;
1190 ofile_out << text_t2ascii << options << "\n";
1191 ofile_out.close();
1192
1193 // set up the build command
1194 text_t build_cmd = "perl " + filename_cat (gsdlhome, "bin", "script", "build");
1195 build_cmd += " -optionfile \"" + optionfile + "\" " + collection;
1196#if !defined (__WIN32__)
1197 // run in background on unix systems
1198 // build_cmd += " &";
1199#endif
1200 char *build_cmdc = build_cmd.getcstr();
1201 //#if defined (__WIN32__)
1202 gsdl_system (build_cmdc, logout);
1203 //#else
1204 // system (build_cmdc);
1205 //#endif
1206 delete build_cmdc;
1207}
1208
1209void collectoraction::gsdl_cancel_build (cgiargsclass &args, ostream &logout) {
1210 // I really wanted to do what this perl script does from within the library
1211 // c++ code. I ran into some problems though (like how do you write a portable
1212 // "rm -r" in c++?). One day I'll spend some time sorting it out ... maybe.
1213 text_t cancel_cmd = "perl " + filename_cat (gsdlhome, "bin", "script", "cancel_build.pl");
1214 cancel_cmd += " -collectdir \"" + filename_cat (gsdlhome, "tmp", args["bc1tmp"]) +
1215 "\" " + args["bc1dirname"];
1216 char *cancel_cmdc = cancel_cmd.getcstr();
1217 system (cancel_cmdc);
1218 delete (cancel_cmdc);
1219}
1220
1221text_t collectoraction::get_collectdir (cgiargsclass &args) {
1222
1223 if ((args["bc1econf"] == 1) || (args["bc1esrce"] == 1)) {
1224 // we're adding to a collection in place
1225 return filename_cat(gsdlhome, "collect");
1226
1227 } else {
1228 return filename_cat (gsdlhome, "tmp", args["bc1tmp"]);
1229 }
1230}
Note: See TracBrowser for help on using the repository browser.