/**********************************************************************
 *
 * collectoraction.cpp -- 
 * Copyright (C) 2000  The New Zealand Digital Library Project
 *
 * A component of the Greenstone digital library software
 * from the New Zealand Digital Library Project at the
 * University of Waikato, New Zealand.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *********************************************************************/

#include "gsdl_modules_cfg.h"
#ifdef GSDL_USE_COLLECTOR_ACTION

// note that the collectoraction relies on having direct access to a
// collections configuration file. this breaks the separation between
// receptionist and collection server and so is not suitable (at least
// in its current form) for use when collection servers are separate 
// from the receptionist (e.g. when using the CORBA protocol).

// following line required to get fstream.filedesc() on darwin (Mac OS X)
#define _STREAM_COMPAT  1
// required for utsname on solaris???
#ifndef _XOPEN_SOURCE
#define _XOPEN_SOURCE 1
#endif
// This was added for Solaris, but it makes things worse on Solaris for me...
// #define _XOPEN_SOURCE_EXTENDED 1

#include "collectoraction.h"
#include "OIDtools.h"
#include "fileutil.h"
#include "cfgread.h"
#include "gsdltools.h"
#include "gsdltimes.h"
#include "argdb.h"
#include "cgiutils.h"
#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>

#if !defined (__WIN32__)
#include <sys/utsname.h>
#include <unistd.h>
#endif

collectoraction::collectoraction () 
  : wizardaction()
{
  macro_prefix = "bc1";

  do_mkcol = false;
  badsources = false;
  failedsources.erase(failedsources.begin(), failedsources.end());

  cgiarginfo arg_ainfo;
  arg_ainfo.shortname = "a";
  arg_ainfo.longname = "action";
  arg_ainfo.multiplechar = true;
  arg_ainfo.multiplevalue = false;
  arg_ainfo.defaultstatus = cgiarginfo::weak;
  arg_ainfo.argdefault = "collector";
  arg_ainfo.savedarginfo = cgiarginfo::must;
  argsinfo.addarginfo (NULL, arg_ainfo);

  arg_ainfo.shortname = "p";
  arg_ainfo.longname = "page";
  arg_ainfo.multiplechar = true;
  arg_ainfo.multiplevalue = false;
  arg_ainfo.defaultstatus = cgiarginfo::weak;
  arg_ainfo.argdefault = "intro";
  arg_ainfo.savedarginfo = cgiarginfo::must;
  argsinfo.addarginfo (NULL, arg_ainfo);

  // temporary directory name for this collector
  // session
  arg_ainfo.shortname = "bc1tmp";
  arg_ainfo.longname = "collector specific";
  arg_ainfo.multiplechar = true;
  arg_ainfo.multiplevalue = false;
  arg_ainfo.defaultstatus = cgiarginfo::weak;
  arg_ainfo.argdefault = g_EmptyText;
  arg_ainfo.savedarginfo = cgiarginfo::must;
  argsinfo.addarginfo (NULL, arg_ainfo);

  arg_ainfo.shortname = "bc1fullname";
  arg_ainfo.longname = "collector specific";
  arg_ainfo.multiplechar = true;
  arg_ainfo.multiplevalue = false;
  arg_ainfo.defaultstatus = cgiarginfo::weak;
  arg_ainfo.argdefault = g_EmptyText;
  arg_ainfo.savedarginfo = cgiarginfo::mustnot; // saved on disk
  argsinfo.addarginfo (NULL, arg_ainfo);

  arg_ainfo.shortname = "bc1dirname";
  arg_ainfo.longname = "collector specific";
  arg_ainfo.multiplechar = true;
  arg_ainfo.multiplevalue = false;
  arg_ainfo.defaultstatus = cgiarginfo::weak;
  arg_ainfo.argdefault = g_EmptyText;
  arg_ainfo.savedarginfo = cgiarginfo::must;
  argsinfo.addarginfo (NULL, arg_ainfo);

  arg_ainfo.shortname = "bc1contactemail";
  arg_ainfo.longname = "collector specific";
  arg_ainfo.multiplechar = true;
  arg_ainfo.multiplevalue = false;
  arg_ainfo.defaultstatus = cgiarginfo::weak;
  arg_ainfo.argdefault = g_EmptyText;
  arg_ainfo.savedarginfo = cgiarginfo::mustnot; // saved on disk
  argsinfo.addarginfo (NULL, arg_ainfo);

  arg_ainfo.shortname = "bc1aboutdesc";
  arg_ainfo.longname = "collector specific";
  arg_ainfo.multiplechar = true;
  arg_ainfo.multiplevalue = false;
  arg_ainfo.defaultstatus = cgiarginfo::weak;
  arg_ainfo.argdefault = g_EmptyText;
  arg_ainfo.savedarginfo = cgiarginfo::mustnot; // saved on disk
  argsinfo.addarginfo (NULL, arg_ainfo);

  arg_ainfo.shortname = "bc1clone";
  arg_ainfo.longname = "collector specific";
  arg_ainfo.multiplechar = false;
  arg_ainfo.multiplevalue = false;
  arg_ainfo.defaultstatus = cgiarginfo::weak;
  arg_ainfo.argdefault = "0";
  arg_ainfo.savedarginfo = cgiarginfo::mustnot; // saved on disk
  argsinfo.addarginfo (NULL, arg_ainfo);

  arg_ainfo.shortname = "bc1clonecol";
  arg_ainfo.longname = "collector specific";
  arg_ainfo.multiplechar = true;
  arg_ainfo.multiplevalue = false;
  arg_ainfo.defaultstatus = cgiarginfo::weak;
  arg_ainfo.argdefault = g_EmptyText;
  arg_ainfo.savedarginfo = cgiarginfo::mustnot; // saved on disk
  argsinfo.addarginfo (NULL, arg_ainfo);

  // set when cloning option has changed
  arg_ainfo.shortname = "bc1clonechanged";
  arg_ainfo.longname = "collector specific";
  arg_ainfo.multiplechar = false;
  arg_ainfo.multiplevalue = false;
  arg_ainfo.defaultstatus = cgiarginfo::weak;
  arg_ainfo.argdefault = "0";
  arg_ainfo.savedarginfo = cgiarginfo::mustnot; 
  argsinfo.addarginfo (NULL, arg_ainfo);

  // only set when one of the fields was changed in
  // the "collection info" page
  arg_ainfo.shortname = "bc1infochanged";
  arg_ainfo.longname = "collector specific";
  arg_ainfo.multiplechar = false;
  arg_ainfo.multiplevalue = false;
  arg_ainfo.defaultstatus = cgiarginfo::weak;
  arg_ainfo.argdefault = "0";
  arg_ainfo.savedarginfo = cgiarginfo::mustnot;
  argsinfo.addarginfo (NULL, arg_ainfo);

  // only set when cfg file is altered from within
  // "configure collection" page
  arg_ainfo.shortname = "bc1cfgchanged";
  arg_ainfo.longname = "collector specific";
  arg_ainfo.multiplechar = false;
  arg_ainfo.multiplevalue = false;
  arg_ainfo.defaultstatus = cgiarginfo::weak;
  arg_ainfo.argdefault = "0";
  arg_ainfo.savedarginfo = cgiarginfo::mustnot;
  argsinfo.addarginfo (NULL, arg_ainfo);

  arg_ainfo.shortname = "cfgfile";
  arg_ainfo.longname = "configuration file contents";
  arg_ainfo.multiplechar = true;
  arg_ainfo.multiplevalue = false;
  arg_ainfo.defaultstatus = cgiarginfo::weak;
  arg_ainfo.argdefault = g_EmptyText;
  arg_ainfo.savedarginfo = cgiarginfo::mustnot;
  argsinfo.addarginfo (NULL, arg_ainfo);

  arg_ainfo.shortname = "bc1dodelete";
  arg_ainfo.longname = "collector specific";
  arg_ainfo.multiplechar = false;
  arg_ainfo.multiplevalue = false;
  arg_ainfo.defaultstatus = cgiarginfo::weak;
  arg_ainfo.argdefault = "0";
  arg_ainfo.savedarginfo = cgiarginfo::mustnot;
  argsinfo.addarginfo (NULL, arg_ainfo);

  // will be set if we arrived at the "configure collection" page
  // via the "changing an existing collection" page
  arg_ainfo.shortname = "bc1econf";
  arg_ainfo.longname = "collector specific";
  arg_ainfo.multiplechar = false;
  arg_ainfo.multiplevalue = false;
  arg_ainfo.defaultstatus = cgiarginfo::weak;
  arg_ainfo.argdefault = "0";
  arg_ainfo.savedarginfo = cgiarginfo::must;
  argsinfo.addarginfo (NULL, arg_ainfo);

  // will be set if we arrived at the "source data" page
  // via the "changing an existing collection" page
  arg_ainfo.shortname = "bc1esrce";
  arg_ainfo.longname = "collector specific";
  arg_ainfo.multiplechar = false;
  arg_ainfo.multiplevalue = false;
  arg_ainfo.defaultstatus = cgiarginfo::weak;
  arg_ainfo.argdefault = "0";
  arg_ainfo.savedarginfo = cgiarginfo::must;
  argsinfo.addarginfo (NULL, arg_ainfo);

  arg_ainfo.shortname = "bc1inputnum";
  arg_ainfo.longname = "collector specific";
  arg_ainfo.multiplechar = true;
  arg_ainfo.multiplevalue = false;
  arg_ainfo.defaultstatus = cgiarginfo::weak;
  arg_ainfo.argdefault = "3";
  arg_ainfo.savedarginfo = cgiarginfo::mustnot; // saved on disk
  argsinfo.addarginfo (NULL, arg_ainfo);

  arg_ainfo.shortname = "bc1input";
  arg_ainfo.longname = "collector specific";
  arg_ainfo.multiplechar = true;
  arg_ainfo.multiplevalue = true;
  arg_ainfo.defaultstatus = cgiarginfo::weak;
  arg_ainfo.argdefault = g_EmptyText;
  arg_ainfo.savedarginfo = cgiarginfo::mustnot; // saved on disk
  argsinfo.addarginfo (NULL, arg_ainfo);

  arg_ainfo.shortname = "bc1inputtype";
  arg_ainfo.longname = "collector specific";
  arg_ainfo.multiplechar = true;
  arg_ainfo.multiplevalue = true;
  arg_ainfo.defaultstatus = cgiarginfo::weak;
  arg_ainfo.argdefault = g_EmptyText;
  arg_ainfo.savedarginfo = cgiarginfo::mustnot; // saved on disk
  argsinfo.addarginfo (NULL, arg_ainfo);

  // will be set when we've just come from the "source data" page
  arg_ainfo.shortname = "bc1fromsrce";
  arg_ainfo.longname = "collector specific";
  arg_ainfo.multiplechar = false;
  arg_ainfo.multiplevalue = false;
  arg_ainfo.defaultstatus = cgiarginfo::weak;
  arg_ainfo.argdefault = "0";
  arg_ainfo.savedarginfo = cgiarginfo::mustnot;
  argsinfo.addarginfo (NULL, arg_ainfo);
}

collectoraction::~collectoraction () {
}



bool collectoraction::check_cgiargs (cgiargsinfoclass &argsinfo, cgiargsclass &args, 
				     recptprotolistclass * /*protos*/, ostream &logout) {

  text_t &current_page = args["p"];

  // note that the "bildstatus" and "bildframe1" pages don't actually do anything
  // functional so we don't need to worry about authenticating them (it's the 
  // underlying "bild" page that does the building (and creates the frameset))
  // This helps us overcome a bit of a problem we have with multiple pages trying
  // to read from the key.gdb database at the same time.
  if (current_page != "intro" && current_page != "bildstatus" && current_page != "bildframe1") {
    // authenticate the user if authentication is available
    args["uan"] = 1;
    args["ug"] = "all-collections-editor";
  }
  
  if (current_page == "new" || current_page == "existing") {

    // assign (and create) a temporary directory 
    if (assign_tmpname (args, logout)==false) {
      // there was an error creating the tmp dir
      message="tmpfail";
      return true; // true because we could still parse the arguments
    }

    // clean up any old builds left laying about in the tmp directory
    // (note that it's possible this could take some time if there's a huge
    // partially built collecton laying about so we'll make it an asynchronous
    // system call)
    gsdl_system ("perl -S cleantmp.pl", false, logout);
  }

  if (current_page != "intro" && current_page != "bildstatus" && 
      current_page != "bildframe1" && current_page != "new") {
    // update arguments that were saved to the harddrive
    text_tmap saved_args;
    saved_args["bc1fullname"] = g_EmptyText;
    saved_args["bc1contactemail"] = g_EmptyText;
    saved_args["bc1aboutdesc"] = g_EmptyText;
    saved_args["bc1clone"] = g_EmptyText;
    saved_args["bc1clonecol"] = g_EmptyText;
    saved_args["bc1inputnum"] = g_EmptyText;
    saved_args["bc1input"] = g_EmptyText;
    saved_args["bc1inputtype"] = g_EmptyText;

    // update the argdb database with any arguments that were set
    // by previous page
    text_tmap::iterator here = saved_args.begin();
    text_tmap::iterator end = saved_args.end();
    while (here != end) {
      if (args.lookupcgiarg((*here).first).source != cgiarg_t::default_arg) {
	(*here).second = args[(*here).first];
      }
      ++here;
    }

    argdb *args_on_disk = new argdb(gsdlhome,args["bc1tmp"]);
    if (!args_on_disk->update_args(saved_args)) {
      // error
      logout << "collectoraction: argdb::update_args failed\n";
    }

    // update args from argdb
    saved_args.erase(saved_args.begin(), saved_args.end());
    if (!args_on_disk->get_args(saved_args)) {
      // error
      logout << "collectoraction: argdb::get_args failed\n";
    }
    delete args_on_disk;
    here = saved_args.begin();
    end = saved_args.end();
    while (here != end) {
      if (!(*here).second.empty()) {
	args[(*here).first] = (*here).second;
      }
      ++here;
    }
  }

  if (args["bc1infochanged"] == "1") {
    
    if (args["bc1dirname"].empty()) {
      // we've just come from the "collection information" page for the 
      // first time so we'll need to create the collection with mkcol.pl
      // and set up bc1dirname - we do this part here instead of in do_action
      // because the bc1dirname argument must be set to its new value before
      // the compressedoptions macros are set.
      args["bc1dirname"] = get_directory_name (args["bc1fullname"]);

      text_t createfile = filename_cat (gsdlhome, "tmp", args["bc1tmp"], ".create");
      if (!file_exists (createfile)) {
	// we could do the mkcol.pl here but I guess it's nicer to do it in do_action()
	do_mkcol = true; 
      } else {
	// .create file already exists but bc1dirname wasn't set ... this should only be
	// able to occur when the "reload" (and possibly the "back" and "forward" buttons)
	// have been used to get us here.
	// we'll check that the bc1dirname directory exists (in case of the unlikely 
	// possibility that get_directory_name returned a different value this time
	// than it did originally).
	text_t coldir = filename_cat (get_collectdir(args), args["bc1dirname"]);
	if (!directory_exists (coldir)) {
	  message = "reloaderror";
	  return true;
	}
      }
    } else {
      // "collection information" has been changed after collection already exists
      // so we'll need to update the cfg file.
      update_cfgfile_partial (args, false, logout);
    }
  }

  if (args["bc1cfgchanged"] == "1") {
    // configuration file has been changed from the "configure collection"
    // page. we need to update the file on disk and catch bc1 arguments up
    // with changes.
    update_cfgfile_complete (args, logout);
  }
  
  if (args["bc1clonechanged"] == "1") {
    // cloning option has been changed on "source data" page. if it was turned 
    // on we want to create a new collect.cfg file using the bc1clonecol cfg file
    // as a model (we'll save the old file as collect.cfg.org). if cloning was
    // turned off we'll revert to using the collect.cfg.org file (which will need
    // updating in case the bc1 arguments have been altered since cloning was 
    // turned on).
    update_cfgfile_clone (args, logout);

    // if cloning has just been turned on we'll also copy the rest of the files 
    // (excluding collect.cfg which we've already done) from the cloned collections
    // etc directory to the new collection.
    if (args["bc1clone"] == "1") {
      text_t clone_etc = filename_cat(collecthome, args["bc1clonecol"], "etc");
      text_t new_etc = filename_cat(get_collectdir(args), args["bc1dirname"], "etc");
      text_tarray files;

      if (read_dir (clone_etc, files)) {
	text_tarray::const_iterator here = files.begin();
	text_tarray::const_iterator end = files.end();
	while (here != end) {
	  if (*here != "collect.cfg" && *here != "collect.cfg.org") {
	    file_copy (filename_cat(clone_etc, *here), filename_cat(new_etc, *here));
	  }
	  ++here;
	}
      } else {
	outconvertclass text_t2ascii;
	logout <<text_t2ascii << "collectoraction::check_cgiargs couldn't read from " 
	       << clone_etc << " directory\n";
      }
    }
  }
  
  if (current_page == "bildstatus" || current_page == "bildcancel") {
    // if .final file exists then build has finished
    text_t fbld = filename_cat (gsdlhome, "tmp", args["bc1tmp"], args["bc1dirname"] + ".bld.final");
    if (file_exists (fbld)) {
      char *fbldc = fbld.getcstr();
      ifstream fbld_in (fbldc);
      if (fbld_in) {
	failcode = fbld_in.get();
	fbld_in.close();
	if (failcode == '0') {
	  // success - we need to create and configure a collection server for the
	  // newly built collection (for fastcgi and local library where
	  // initialization isn't going to be redone when the user clicks the
	  // "view your new collection" button
	  create_colserver (args["bc1dirname"], logout);
	  current_page = "bilddone";
	}
	else current_page = "bildfail";
      } else {
	// assume build failed (we shouldn't get here though ... right?)
	current_page = "bildfail";
      }
      delete []fbldc;
    }
  }

  if (args["bc1fromsrce"] == "1") {

    // we've just come from the "source data" page so we need to check that
    // input sources are valid
    if (!check_sources(args, logout)) {
      args["p"] = "srce";
    }
  }

  return true;
}

void collectoraction::update_cfgfile_clone (cgiargsclass &args, ostream &logout) {

  text_t tmpdir = filename_cat(gsdlhome, "tmp", args["bc1tmp"]);
  text_t cfgfile = filename_cat(tmpdir, args["bc1dirname"], "etc", "collect.cfg");
  if (!file_exists (cfgfile)) {
    message = "tmpfail";
    return;
  }

  text_t cfgfile_org = filename_cat (tmpdir, "collect.cfg.org");

  if (args["bc1clone"] == "1") {
    // cloning was turned on

    text_t cfgfile_clone = filename_cat(collecthome, args["bc1clonecol"], "etc", "collect.cfg");
    if (file_exists (cfgfile_clone)) {
      // if .org file doesn't exist already create it
      if (!file_exists (cfgfile_org)) {
	if (!file_copy (cfgfile, cfgfile_org)) {
	  message = "tmpfail";
	  return;
	}
      }
      // copy clone collections cfg file to new collection
      if (!file_copy (cfgfile_clone, cfgfile)) {
	message = "tmpfail";
	return;
      }
      // update the new cfg file
      update_cfgfile_partial (args, true, logout);
      
    } else {
      // can't clone non-existant or read-protected collection
      message = "clonefail";
    }

  } else {
    // cloning has been turned off having been on at some point. the .org file
    // should exist, if it doesn't we'll bail out and leave the user with the 
    // cloned copy
    if (file_exists (cfgfile_org)) {
      // copy original back again and update it with any recent changes
      if (file_copy (cfgfile_org, cfgfile)) {
	update_cfgfile_partial (args, false, logout);
      } else {
	message = "tmpfail";
      }
    }
  }
}

// update configuration file on disk to match bc1 arguments
// there's a special case if the clone option is true as certain parts of a
// config file should not be cloned (e.g. the iconcollection stuff)
void collectoraction::update_cfgfile_partial (cgiargsclass &args, bool clone, ostream &logout) {

  text_t cfgfile = filename_cat(get_collectdir(args), args["bc1dirname"], "etc", "collect.cfg");
  char *cfgfilec = cfgfile.getcstr();

#if defined (__WIN32__)
  // make sure collect.cfg isn't read-only
  _chmod (cfgfilec, _S_IREAD | _S_IWRITE);
#endif

  vector<text_tarray> cfgarray;

  // read in cfg file
  ifstream cfg_in (cfgfilec);
  if (cfg_in) {
    text_tarray cfgline;
    while (read_cfg_line(cfg_in, cfgline) >= 0) {
      if (cfgline.size () >= 2) {
	if (cfgline[0] == "creator" || cfgline[0] == "maintainer") {
	  cfgline[1] = args["bc1contactemail"];
	} else if (cfgline[0] == "collectionmeta") {
	  if (cfgline[1] == "collectionname") {
	    cfgline[2] = args["bc1fullname"];
	  } else if (cfgline[1] == "collectionextra") {
	    cfgline[2] = carriage_replace (args["bc1aboutdesc"], 0);
	  } else if (clone && (cfgline[1] == "iconcollection" ||
			       cfgline[1] == "iconcollectionsmall")) {
	    cfgline[2] = g_EmptyText;
	  }
	}
      }
      cfgarray.push_back (cfgline);
    }
    cfg_in.close();
    
    // now write cfg file back out
    int fd=open(cfgfilec, O_WRONLY | O_CREAT | O_TRUNC
#if defined(__WIN32__)
		| O_BINARY
#endif
		, 432 );

    if (fd != -1) {
      // lock the file
      int lock_val = 1;
      GSDL_LOCK_FILE (fd);
      if (lock_val != 0) {
	logout << "Error: Couldn't lock file " << cfgfilec << "\n";
	close(fd);
	message = "tmpfail";
	
      } else {

	vector<text_tarray>::const_iterator this_line = cfgarray.begin();
	vector<text_tarray>::const_iterator end_line = cfgarray.end();
	while (this_line != end_line) {
	  write_cfg_line (fd, *this_line);
	  ++this_line;
	}
	GSDL_UNLOCK_FILE (fd);
	close(fd);
      }

    } else {
      logout << "collectoraction::update_cfgfile_partial: unable to open "
	     << cfgfilec << " for output\n";
      message = "tmpfail";
    }
    
  } else {
    logout << "collectoraction::update_cfgfile_partial: unable to open "
	   << cfgfilec << " for input\n";
    message = "tmpfail";
  }

  delete []cfgfilec;
}

// replace configuration file on disk with that in the cfgfile argument and 
// catch other bc1 arguments up with those the new cfgfile contains
void collectoraction::update_cfgfile_complete (cgiargsclass &args, ostream &logout) {

  text_t cfgfile = filename_cat(get_collectdir(args), args["bc1dirname"], "etc", "collect.cfg");
  char *cfgfilec = cfgfile.getcstr();

#ifdef __WIN32__
  // make sure collect.cfg isn't read-only
  _chmod (cfgfilec, _S_IREAD | _S_IWRITE);
#endif

  int fd=open(cfgfilec, O_WRONLY | O_CREAT | O_TRUNC
#if defined(__WIN32__)
	      | O_BINARY
#endif
	      , 432 );

  if (fd) {
    // lock the file
    int lock_val = 1;
    GSDL_LOCK_FILE (fd);
    if (lock_val != 0) {
      logout << "Error: Couldn't lock file " << cfgfilec << "\n";
      close(fd);
      message = "tmpfail";
      
    } else {
      
      outconvertclass text_t2ascii;
      text_t2ascii.setinput(&args["cfgfile"]);
      size_t buffersize=args["cfgfile"].size();
      char *buffer=new char[buffersize];
      buffer[0]='\n'; // just in case something goes wrong...
      size_t num_chars;
      convertclass::status_t status;
      text_t2ascii.convert(buffer, buffersize, num_chars, status);
      // ignore status - assume it is "finished" as buffer is big enough
      write(fd, buffer, num_chars);
      GSDL_UNLOCK_FILE (fd);
      close(fd);
      delete []buffer;
      
      // now that we've written the file we'll read it back again and
      // update our bc1 arguments
      ifstream cfg_in (cfgfilec);
      if (cfg_in) {
	text_tarray cfgline;
	while (read_cfg_line(cfg_in, cfgline) >= 0) {
	  if (cfgline.size () >= 2) {
	    if (cfgline[0] == "creator") {
	      args["bc1contactemail"] = cfgline[1];
	    } else if (cfgline[0] == "collectionmeta") {
	      if (cfgline[1] == "collectionname") {
		args["bc1fullname"] = cfgline[2];
	      } else if (cfgline[1] == "collectionextra") {
		args["bc1aboutdesc"] = carriage_replace (cfgline[2], 1);
	      }
	    }
	  }
	}
	cfg_in.close();
      } else {
	logout << "collectoraction::update_cfgfile_complete: unable to open " 
	       << cfgfilec << " for input\n";
	message = "tmpfail";
      }
    }
  } else {
    logout << "collectoraction::update_cfgfile_complete: unable to open " 
	   << cfgfilec << " for output\n";
    message = "tmpfail";
  }

  delete []cfgfilec;
}


// return html for buttons used in collector bar
// color may be "green", "grey", or "yellow"
// type may be:
//   "info" --> "collection information" button
//   "srce" --> "source data" button
//   "conf" --> "configure collection" button
//   "bild" --> "build collection" button
//   "view" --> "view collection" button
// if enabled is true button will be flashy rollover type and 
// will be hyperlinked

// Wendy left a comment suggesting this be move, but where to
// was not specified.  Into wizardaction.cpp?

// set the _fullnamemenu_ macro (and _warnindex_ and _selectedindex_ if
// we're on the "srce" page)
void collectoraction::set_fullnamemenu (displayclass &disp, cgiargsclass &args, 
					recptprotolistclass *protos, ostream &logout) {

  if (recpt == NULL) {
    logout << "ERROR (collectoraction::set_fullnamemenu): This action does not contain\n"
	   << "       information about any receptionists. The method set_receptionist was\n"
	   << "       probably not called from the module which instantiated this action.\n";
    return;
  }

  text_t &current_page = args["p"];
  text_t currentname = args[macro_prefix+"dirname"];
  if (current_page == "srce") currentname = args[macro_prefix + "clonecol"];

  text_tarray dirnames;
  text_tarray fullnames;
  vector<bool> write_protected;
  bool is_selected = false;
  int selected_index = 0;
  int index = 0;

  recptprotolistclass::iterator rprotolist_here = protos->begin();
  recptprotolistclass::iterator rprotolist_end = protos->end();
  while (rprotolist_here != rprotolist_end) {
    if ((*rprotolist_here).p != NULL) {

      // don't include z39.50 collections
      comerror_t err = noError;
      if ((*rprotolist_here).p->get_protocol_name (err) == "z3950proto") {
	++rprotolist_here;
	continue;
      }

      text_tarray collist;
      (*rprotolist_here).p->get_collection_list (collist, err, logout);
      if (err == noError) {
	text_tarray::iterator collist_here = collist.begin();
	text_tarray::iterator collist_end = collist.end();
	while (collist_here != collist_end) {
	  ColInfoResponse_t *cinfo = recpt->get_collectinfo_ptr ((*rprotolist_here).p, *collist_here, logout);
	  if (cinfo != NULL) {
	    text_t collectionname = cinfo->get_collectionmeta("collectionname", args["l"]);
	    if (collectionname.empty()) {
	      collectionname = *collist_here;
	    }
	    dirnames.push_back(*collist_here);
	    fullnames.push_back(collectionname);
	    // check to see if the collection is writable
	    if (collection_protected (*collist_here)) write_protected.push_back(true);
	    else write_protected.push_back(false);

	    if (*collist_here == currentname) {
	      is_selected = true;
	      selected_index = index; 	    
	    }
	    ++index;
	  }
	  ++collist_here;
	}
      }
    }
    ++rprotolist_here;
  }

  bool first = true;

  text_t warnindex;
  text_t fullnamemenu = "<select name=\""+macro_prefix+"dirname\" onChange=\"menuchange();\">\n";

  if (current_page == "srce") {
    fullnamemenu = "<select name=\""+macro_prefix+"clonecol\" onChange=\"menuchange();\">\n";
    fullnamemenu += "<option value=defaultstructure";
    if (!is_selected) fullnamemenu += " selected>";
    else fullnamemenu.push_back('>');
    fullnamemenu += "_collector:textdefaultstructure_\n";
  }

  fullnamemenu += "<option value=\"\"></option>\n";

  for (int i = 0; i < index; ++i) {
    // don't want write protected collections in list on "change existing
    // collection" page
    if (write_protected[i] && current_page == "existing") continue;
    fullnamemenu += "<option value=\"" + dirnames[i] + "\"";
    if ((i == 0 && !is_selected && current_page != "srce") || 
	(is_selected && i == selected_index)) {
      fullnamemenu += " selected";
      ++selected_index;
      is_selected = false;
    }
    fullnamemenu.push_back ('>');
    fullnamemenu += fullnames[i];
    fullnamemenu.push_back ('\n');
    
    // add to Warnindex if collection uses any dubious plugins 
    // (if creating clone collection list)
    if (current_page == "srce") {
      if (first) warnindex += "0,";
      else warnindex.push_back(',');
      if (uses_weird_plugin (dirnames[i])) {
	warnindex += text_t (1);
      } else {
	warnindex += text_t (0);
      }
    }
    first = false;
  }
  fullnamemenu += "</select>\n";
  text_t action_name = get_action_name();
  disp.setmacro ("fullnamemenu", action_name, fullnamemenu);
  if (current_page == "srce") {
    disp.setmacro ("warnindex",action_name , warnindex);
    disp.setmacro ("selectedindex", action_name, text_t(selected_index));
  }
}


// set _sourcelist_ and _badsources_ macros
void collectoraction::set_inputsourceboxes (displayclass &disp, cgiargsclass &args, 
					    ostream &logout) {
  
  if (badsources) disp.setmacro ("badsources", "collector", "1");

  text_t sourcelist = get_source_box(args["bc1input"], args["bc1inputnum"].getint(),
				     args["bc1inputtype"]);

  disp.setmacro("sourcelist", "collector", sourcelist);

  // reset badsources and failedsources variables
  badsources = false;
  failedsources.erase(failedsources.begin(), failedsources.end());
}

text_t collectoraction::get_source_box (text_t inputarglist, int numboxes,
					text_t inputtypelist) {

  text_tarray inputvalues;
  splitchar (inputarglist.begin(), inputarglist.end(), ',', inputvalues);
  // remove any empty values from the end of the array
  if (inputvalues.size()) {
    text_tarray::iterator l = inputvalues.end() - 1;
    text_tarray::iterator b = inputvalues.begin();
    while ((*l).empty() && l >= b) {
      --l;
    }
    inputvalues.erase(l+1, inputvalues.end());
  }

  text_tarray inputtypes;
  splitchar (inputtypelist.begin(), inputtypelist.end(), ',', inputtypes);

  int numvalues = inputvalues.size();
  int numtypes = inputtypes.size();

  text_t last = "file://";
  text_t rv;
  for (int i = 0; i < numboxes; ++i) {
    rv += "<nobr><select name=\"bc1inputtype\">\n";
    rv += "<option value=\"file://\"";
    if ((i < numtypes && inputtypes[i] == "file://") ||
	(numboxes == 3 && i == 0 && numvalues == 0) ||
	(i >= 3 && i >= numvalues && last == "file://")) {
      rv += " selected";
      last = "file://";
    }
    rv += ">file://\n";
    rv += "<option value=\"http://\"";
    if ((i < numtypes && inputtypes[i] == "http://") ||
	(numboxes == 3 && i == 1 && numvalues == 0) ||
	(i >= 3 && i >= numvalues && last == "http://")) {
      rv += " selected";
      last = "http://";
    }
    rv += ">http://\n";
    rv += "<option value=\"ftp://\"";
    if ((i < numtypes && inputtypes[i] == "ftp://") ||
	(numboxes == 3 && i == 2 && numvalues == 0) ||
	(i >= 3 && i >= numvalues && last == "ftp://")) {
      rv += " selected";
      last = "ftp://";
    }
    rv += ">ftp://\n";
    rv += "</select>\n";
    rv += "<input type=text name=\"bc1input\" value=\"";
    if (i < numvalues) {
      //rv += dm_safe(decode_commas(inputvalues[i]));
      text_t websafe = encodeForHTMLAttr(inputvalues[i]);
      rv += dm_safe(decode_commas(websafe));
    }
    rv += "\" size=50>";
    if (badsources) {
      if ((i < numvalues) && (!inputvalues[i].empty())) {
	if (failedsources[decode_commas(inputvalues[i])] == "1") {
	  rv += "_iconcross_";
	} else {
	  rv += "_icontick_";
	}
      } else {
	rv += "_iconblank_";
      }
    }
    if (i+1 == numboxes) {
      if (!badsources) rv += "_iconblank_";
      rv += "_imagemore_</nobr><br>";
    } else {
      rv += "</nobr><br>\n";
    }
  }
  
  return rv;
}

// set the _cfgfile_ macro
void collectoraction::set_cfgfile (displayclass &disp, cgiargsclass &args, ostream &logout) {

  text_t &collection = args["bc1dirname"];
  if (collection.empty()) {
    message = "nocollection";
    return;
  }

  // read in collect.cfg      
  text_t cfgfile = filename_cat(get_collectdir(args), collection, "etc", "collect.cfg");  
  char *cfgfilec = cfgfile.getcstr();

#ifdef GSDL_USE_IOS_H
  ifstream cfg_ifs (cfgfilec, ios::in | ios::nocreate);
#else
  ifstream cfg_ifs (cfgfilec, ios::in);
#endif
  
  if (cfg_ifs) {  
    // read in collect.cfg      
    text_t cfgtext;
    char c;
    cfg_ifs.get(c);
    while (!cfg_ifs.eof ()) {
      cfgtext.push_back(c);
      cfg_ifs.get(c);
    }
    cfg_ifs.close();
    
    // define it as a macro
    disp.setmacro("cfgfile", "collector", dm_safe(cfgtext));

  } else {
    logout << "collectoraction::set_cfgfile: couldn't open configuration file (" 
	   << cfgfilec << ") for reading\n";
    message = "tmpfail";
  }    
  delete []cfgfilec;
}


void collectoraction::define_internal_macros (displayclass &disp, cgiargsclass &args, 
					      recptprotolistclass *protos, ostream &logout) {

  // define_internal_macros sets the following macros:
  // _collectorbar_
  // _pagescriptextra_
  // _fullnamemenu_ -- if displaying the "source data" page or the "changing existing 
  //                   collection" page
  // _cfgfile_ -- if displaying the "configure collection" page
  // _statusline_ -- if displaying the bildstatus page
  // _header_ -- may be set for pages that require it
  // _textfailmsg_ -- set to different messages depending on failcode returned
  //                  by build script (if build fails)
  // _faillog_ - set to last 6 lines of .bld file if build failed
  // _gsdlhome_ - the gsdlhome path (dm_safe)
  // _sourcelist_ -- "input source" text boxes
  // _badsources_ -- will be set to "1" if we've come from the 
  //                 "source data" page and there's a problem 
  //                 with the input sources

  text_t &collector_page = args["p"];
  int esrce = args["bc1esrce"].getint();
  int econf = args["bc1econf"].getint();

  // set _pagescriptextra_ macro to _cpagescriptextra_
  disp.setmacro ("pagescriptextra", "collector", "_" + encodeForHTML(collector_page) + "scriptextra_");

  if (collector_page == "bildstatus" || collector_page == "bilddone" || 
      collector_page == "bildfail" || collector_page == "bildframe1") {
    disp.setmacro ("header", "collector", "_" + encodeForHTML(collector_page) + "header_");
  }

  // set the collectorbar macro
  text_t collectorbar = "<table class=wizardbar border=0 cellspacing=4 cellpadding=0><tr>\n";

  if (collector_page == "new") {
    collectorbar += "<td>_icongreyarrow_</td>\n";
    collectorbar += get_button (args,collector_page, "green", "info", true);
    collectorbar += "<td>_icongreyarrow_</td>\n";
    collectorbar += get_button (args,collector_page, "grey", "srce", false);
    collectorbar += "<td>_icongreyarrow_</td>\n";
    collectorbar += get_button (args,collector_page, "grey", "conf", false);
    collectorbar += "<td>_icongreyarrow_</td>\n";
    collectorbar += get_button (args,collector_page, "grey", "bild", false);
    collectorbar += "<td>_icongreyarrow_</td>\n";
    collectorbar += get_button (args,collector_page, "grey", "view", false);

  } else if (collector_page == "info") {
    collectorbar += "<td>_icongreyarrow_</td>\n";
    collectorbar += get_button (args,collector_page, "yellow", "info", false);
    collectorbar += "<td>_icongreyarrow_</td>\n";
    collectorbar += get_button (args,collector_page, "green", "srce", true);
    collectorbar += "<td>_icongreyarrow_</td>\n";
    collectorbar += get_button (args,collector_page, "grey", "conf", false);
    collectorbar += "<td>_icongreyarrow_</td>\n";
    collectorbar += get_button (args,collector_page, "grey", "bild", false);
    collectorbar += "<td>_icongreyarrow_</td>\n";
    collectorbar += get_button (args,collector_page, "grey", "view", false);
    collectorbar += "</tr><tr><td></td><td align=center>_icongreyuparrow_</td><td colspan=8></td>\n";

  } else if (collector_page == "srce") {
    collectorbar += "<td>_icongreyarrow_</td>\n";
    if (esrce == 1) {
      // if we came from the "change an existing collection" page previous button(s)
      // are disabled
      collectorbar += get_button (args,collector_page, "grey", "info", false);
    } else {
      collectorbar += get_button (args,collector_page, "yellow", "info", true);
    }
    collectorbar += "<td>_icongreyarrow_</td>\n";
    collectorbar += get_button (args,collector_page, "yellow", "srce", false);
    collectorbar += "<td>_icongreyarrow_</td>\n";
    collectorbar += get_button (args,collector_page, "green", "conf", true);
    collectorbar += "<td>_icongreyarrow_</td>\n";
    collectorbar += get_button (args,collector_page, "green", "bild", true);
    collectorbar += "<td>_icongreyarrow_</td>\n";
    collectorbar += get_button (args,collector_page, "grey", "view", false);
    collectorbar += "</tr><tr><td colspan=3></td><td align=center>_icongreyuparrow_</td><td colspan=6></td>\n";

  } else if (collector_page == "conf") {
    collectorbar += "<td>_icongreyarrow_</td>\n";
    // disable appropriate buttons if we came from "change an existing collection"
    // page
    if (esrce == 1 || econf == 1) {
      collectorbar += get_button (args,collector_page, "grey", "info", false);
    } else {
      collectorbar += get_button (args,collector_page, "yellow", "info", true);
    }
    collectorbar += "<td>_icongreyarrow_</td>\n";
    if (econf == 1) {
      collectorbar += get_button (args,collector_page, "grey", "srce", false);
    } else {
      collectorbar += get_button (args,collector_page, "yellow", "srce", true);
    }
    collectorbar += "<td>_icongreyarrow_</td>\n";
    collectorbar += get_button (args,collector_page, "yellow", "conf", false);
    collectorbar += "<td>_icongreyarrow_</td>\n";
    collectorbar += get_button (args,collector_page, "green", "bild", true);
    collectorbar += "<td>_icongreyarrow_</td>\n";
    collectorbar += get_button (args,collector_page, "grey", "view", false);
    collectorbar += "</tr><tr><td colspan=5></td><td align=center>_icongreyuparrow_</td><td colspan=4></td>\n";

  } else if (collector_page == "bilddone") {
    collectorbar += "<td>_icongreyarrow_</td>\n";
    // all previous buttons grey after build was completed
    collectorbar += get_button (args,collector_page, "grey", "info", false);
    collectorbar += "<td>_icongreyarrow_</td>\n";
    collectorbar += get_button (args,collector_page, "grey", "srce", false);
    collectorbar += "<td>_icongreyarrow_</td>\n";
    collectorbar += get_button (args,collector_page, "grey", "conf", false);
    collectorbar += "<td>_icongreyarrow_</td>\n";
    collectorbar += get_button (args,collector_page, "yellow", "bild", false);
    collectorbar += "<td>_icongreyarrow_</td>\n";
    collectorbar += get_button (args,collector_page, "green", "view", true);
    collectorbar += "</tr><tr><td colspan=7></td><td align=center>_icongreyuparrow_</td><td colspan=2></td>\n";

  } else if (collector_page == "bildcancel" || collector_page == "bildfail") {
    collectorbar += "<td>_icongreyarrow_</td>\n";
    // disable appropriate buttons if we came from "change an existing collection"
    // page
    if (esrce == 1 || econf == 1) {
      collectorbar += get_button (args,collector_page, "grey", "info", false);
    } else {
      collectorbar += get_button (args,collector_page, "yellow", "info", true);
    }
    collectorbar += "<td>_icongreyarrow_</td>\n";
    if (econf == 1) {
      collectorbar += get_button (args,collector_page, "grey", "srce", false);
    } else {
      collectorbar += get_button (args,collector_page, "yellow", "srce", true);
    }
    collectorbar += "<td>_icongreyarrow_</td>\n";
    collectorbar += get_button (args,collector_page, "yellow", "conf", true);
    collectorbar += "<td>_icongreyarrow_</td>\n";
    collectorbar += get_button (args,collector_page, "yellow", "bild", true);
    collectorbar += "<td>_icongreyarrow_</td>\n";
    collectorbar += get_button (args,collector_page, "grey", "view", false);
  }

  collectorbar += "</tr></table>\n";
  disp.setmacro ("collectorbar", "collector", collectorbar);

  if (collector_page == "bildfail") {

    text_t textfailmsg = "_textfailmsg";
    textfailmsg.push_back(failcode);
    textfailmsg.push_back('_');
    disp.setmacro("textfailmsg", "collector", textfailmsg);

    text_t bldlog = filename_cat(gsdlhome, "tmp", args["bc1tmp"], args["bc1dirname"] + ".bld");
    text_t rawlog = file_tail (bldlog, 6, 0);
    // we'll shove in some <br> tags where \n's occur
    text_t faillog;
    text_t::const_iterator here = rawlog.begin();
    text_t::const_iterator end = rawlog.end();
    while (here != end) {
      if (*here == '\n') faillog += "<br>";
      faillog.push_back (*here);
      ++here;
    }
    disp.setmacro ("faillog", "collector", dm_safe(faillog));
  }

  if (collector_page == "srce" || collector_page == "existing") 
    set_fullnamemenu (disp, args, protos, logout);
  if (collector_page == "conf") 
    set_cfgfile (disp, args, logout);
  if (collector_page == "bildstatus")
    set_statusline (disp, args, logout);
  if (collector_page == "srce") {
    set_inputsourceboxes (disp, args, logout);
  }

  disp.setmacro ("gsdlhome", "collector", dm_safe(gsdlhome));
}


bool collectoraction::do_action (cgiargsclass &args, recptprotolistclass * /*protos*/, 
				 browsermapclass * /*browsers*/, displayclass &disp, 
				 outconvertclass &outconvert, ostream &textout, 
				 ostream &logout) {

  // make sure the collector is enabled
  if (disabled) {
    textout << outconvert 
	    << "<html>\n"
	    << "<head>\n"
	    << "<title>Collector disabled</title>\n"
	    << "</head>\n"
	    << "<body bgcolor=\"#ffffff\" text=\"#000000\" link=\"#006666\" "
	    << "alink=\"#cc9900\" vlink=\"#666633\">\n"
	    << "<h2>Facility disabled</h2>\n" 
	    << "Sorry, the Collector end-user collection building facility is currently disabled\n"
	    << "\n</body>\n"
	    << "</html>\n";
    return true;
  }

  text_t &collector_page = args["p"];
  text_t &collection = args["bc1dirname"];

  // make sure we have perl (we won't bother with this check for the 
  // building status pages to avoid slowing things down unneccessarily)
  if (collector_page != "bildstatus" && collector_page != "bildframe1" && !perl_ok(logout)) {
    textout << outconvert 
	    << "<html>\n"
	    << "<head>\n"
	    << "<title>Perl not found</title>\n"
	    << "</head>\n"
	    << "<body bgcolor=\"#ffffff\" text=\"#000000\" link=\"#006666\" "
	    << "alink=\"#cc9900\" vlink=\"#666633\">\n"
	    << "<h2>Perl not found</h2>\n" 
	    << "Greenstone could not detect perl on this system. It is therefore not\n"
	    << "possible to build a Greenstone collection, either from the Collector or the \n"
	    << "command-line tools, or to use the Collector for any other task.\n"
	    << "<p>Please refer to the Greenstone Installer's Guide for details on\n"
	    << "installing perl on your system.\n"
	    << "\n</body>\n"
	    << "</html>\n";
    return true;

  }

  if (collector_page == "bild") {
    // do the work (download, import, build)
    gsdl_build (args, logout);

    if (message.empty()) {
      // bild page is a frameset so we don't want headers and stuff
      textout << outconvert << disp << ("_collector:bildcontent_\n");
    }
  }

  if (do_mkcol == true) {
    // execute mkcol.pl (do_mkcol is set from within check_cgiargs)
    gsdl_mkcol (args, logout);
    do_mkcol = false; // reset for fast-cgi
  }

  if (args["bc1dodelete"] == "1") {
    // delete bcidirname collection
    if (collection_protected (collection)) {
      message = "delinvalid";
	
    } else {

      const recptconf &rcinfo = recpt->get_configinfo ();
      bool emailuserevents = rcinfo.EmailUserEvents;

      // get collection maintainer email from collect.cfg before we
      // delete it
      text_t colmaintainer;
      text_t cfgfile = filename_cat(collecthome, collection, "etc", "collect.cfg");
      char *cfgfilec = cfgfile.getcstr();
      ifstream cfg_in (cfgfilec);
      delete []cfgfilec;
      if (cfg_in) {
	text_tarray cfgline;
	while (read_cfg_line(cfg_in, cfgline) >= 0) {
	  if (cfgline.size () == 2 && cfgline[0] == "maintainer") {
	    colmaintainer = cfgline[1];
	    break;
	  }
	}
	cfg_in.close();
      }
      if (colmaintainer.empty()) {
	logout << outconvert 
	       << "collectoraction::do_action WARNING: Collection being deleted (" 
	       << collection << ") has no maintainer address. EmailUserEvents "
	       << "disabled\n";
	emailuserevents = false;
      }
      
      // first we need to free up the collection's collection server
      // we must do this for the local library (and I guess when using
      // fastcgi too) as you can't delete the database file while it's
      // being kept open by the collection server
      remove_colservr (collection, logout);

      text_t delete_cmd = "perl -S delcol.pl -f " + collection;
      int rv = gsdl_system (delete_cmd, true, logout);
      if (rv != 0) {
	// deletion failed -- permissions?
	message = "delpermission";
      } else {
	message = "delsuccess";
      }

      // log the event
      if (rcinfo.LogEvents == CollectorEvents || rcinfo.LogEvents == AllEvents) {
	
	text_t eventlog = filename_cat (gsdlhome, "etc", "events.txt");
	char *eventlogt = eventlog.getcstr();
	ofstream eventl (eventlogt, ios::app);
	delete []eventlogt;

	if (eventl) {
	  eventl << outconvert << "[Collector Event]\n"
		 << "Date: " << get_date (true) << "\n"
		 << "Greenstone Username: " << args["un"] << "\n"
		 << "Collection: " << collection << "\n"
		 << "Collection Maintainer: " << colmaintainer << "\n"
		 << "GSDLHOME: " << gsdlhome << "\n";

	  if (message == "delsuccess") {
	    eventl << outconvert 
		   << "The " << collection << " collection was successfully deleted\n\n";
	  } else {
	    eventl << outconvert 
		   << "Attempt to delete the " << collection << " collection failed\n\n";
	  }
	  eventl.close();

	} else {
	  logout << outconvert << "collectoraction::do_action ERROR: Couldn't open "
		 << "event log file " << eventlog << " for appending during collection "
		 << "deletion. LogEvents disabled\n";
	}
      }
      
      if (rcinfo.EmailEvents == CollectorEvents || rcinfo.EmailEvents == AllEvents || emailuserevents) {
	// use sendmail.pl perl script to send email events
	text_t tmpmailfile = filename_cat (gsdlhome, "tmp", args["bc1tmp"], "event.txt");
	char *tmpmailfilec = tmpmailfile.getcstr();
	ofstream tmpfile (tmpmailfilec);
	delete []tmpmailfilec;
	if (tmpfile) {
	  tmpfile << outconvert << "[Collector Event]\n"
	    	  << "Date: " << get_date (true) << "\n"
		  << "Greenstone Username: " << args["un"] << "\n"
		  << "Collection: " << collection << "\n"
		  << "Collection Maintainer: " << colmaintainer << "\n"
		  << "GSDLHOME: " << gsdlhome << "\n";
	  if (message == "delsuccess") {
	    tmpfile << outconvert 
		    << "The " << collection << " collection was successfully deleted\n\n";
	  } else {
	    tmpfile << outconvert 
		    << "Attempt to delete the " << collection << " collection failed\n\n";
	  }
	  tmpfile.close();
	  text_t to;
	  if (rcinfo.EmailEvents == CollectorEvents || rcinfo.EmailEvents == AllEvents) to += rcinfo.maintainer;
	  if (emailuserevents) {
	    if (!to.empty()) to.push_back (',');
	    to += colmaintainer;
	  }
	  text_t sendmail_cmd = "perl -S sendmail.pl -to \"" + to + "\" -from \"" + rcinfo.maintainer;
	  sendmail_cmd += "\" -smtp \"" + rcinfo.MailServer + "\" -subject \"Greenstone Collector Event\"";
	  sendmail_cmd += " -msgfile \"" + tmpmailfile + "\"";

	  gsdl_system (sendmail_cmd, false, logout);
	  
	} else {
	  logout << outconvert << "collectoraction::do_action ERROR: Couldn't open "
		 << "temporary event log file " << tmpmailfile << " during collection "
		 << "deletion. EmailEvents and EmailUserEvents disabled\n";
	}
      }
    }
  }
  
  if (collector_page == "bildcancel" || collector_page == "bildfail") {
    // cancel the build (we'll also use the cancel_build script to tidy
    // up if the build failed)
    gsdl_cancel_build (args, logout);
  }

  if (collector_page == "expt") {

    // export the collection - we'll do a synchronous system call to
    // exportcol.pl as that's the easiest way to do it. if it becomes a
    // problem that it's taking too long to export a large collection then
    // we may have to revisit this.
    text_t tmpfile = filename_cat (gsdlhome, "tmp", collection + "_export.txt");
    text_t export_cmd = "perl -S exportcol.pl -out \"" + tmpfile + "\" " + collection;
    gsdl_system (export_cmd, true, logout);
    if (file_exists (tmpfile)) {
      text_t returnline = file_tail (tmpfile, 1, 0);
      if (returnline.size() > 23 && (substr(returnline.begin(), returnline.begin()+23) == "exportcol.pl succeeded:")) {
	// success
	message = "exptsuccess";
      } else {
	message = "exptfail";
      }
    } else {
      message = "exptfail";
    }
  }

  if (message.empty()) {
    if (collector_page != "bild") {
      // output page ("bild" page was already output above)
      textout << outconvert << disp << ("_collector:header_\n")
	      << ("_collector:" + encodeForHTML(collector_page) + "content_\n")
	      << ("_collector:footer_\n");
    }
  } else {
    // message was set somewhere (probably an error), output message page
    textout << outconvert << disp << ("_collector:header_\n")
	    << ("_collector:" + message + "content_\n")
	    << ("_collector:footer_\n");
    message.clear();
  }
  return true;
}

void collectoraction::gsdl_mkcol (cgiargsclass &args, ostream &logout) {

  text_t tmpdir = filename_cat (gsdlhome, "tmp", args["bc1tmp"]);
  if (!directory_exists (tmpdir)) {
    message = "tmpfail";
    cerr << "WE CANNOT CREATE THE DIRECTORY!!! " << endl; 
    return;
  }

  text_t &collection = args["bc1dirname"];
  if (collection.empty()) {
    message = "nocollection";
    return;
  }

  // check for a .create file - if it exists then we've already created the collection
  text_t createfile = filename_cat (tmpdir, ".create");
  if (file_exists (createfile)) {
    return;
  }

  // set up options
  text_t options = "-quiet -creator \"" + args["bc1contactemail"] + "\"";
  options += " -title \"" + args["bc1fullname"] + "\"";
  options += " -about \"" + carriage_replace (args["bc1aboutdesc"] + "_collectorextra_", 0) + "\"";
  options += " -collectdir \"" + remove_trailing_slashes(tmpdir) + "\" ";

  text_t optionfile = filename_cat (tmpdir, "mkcol.opt");
  char *optionfilec = optionfile.getcstr();
  ofstream ofile_out (optionfilec);
  delete []optionfilec;
  if (!ofile_out) {
    message = "tmpfail";
    return;
  }
  outconvertclass text_t2ascii;
  ofile_out << text_t2ascii << options << "\n";
  ofile_out.close();

  // run mkcol.pl
  text_t mkcol_cmd = "perl -S mkcol.pl -optionfile \"" + optionfile;
  mkcol_cmd += "\" " + collection;


  gsdl_system (mkcol_cmd, true, logout);

  // make sure it went ok
  text_t cfgfile = filename_cat (tmpdir, collection, "etc", "collect.cfg");
  if (!file_writable (cfgfile)) {
    message = "mkcolfail";
  } else {
    // create the .create file (this file is just a place holder to let any future
    // pages know that the collection already exists).
    char *createfilec = createfile.getcstr();
    ofstream cfile_out (createfilec);
    delete []createfilec;
    if (cfile_out) {
      cfile_out << "collection created\n";
      cfile_out.close();
    } else {
      message = "tmpfail";
      return;
    }
  }
}


bool collectoraction::check_sources (cgiargsclass &args, ostream &logout) {

  bool found = false;

  text_tarray inputvalues;
  splitchar (args["bc1input"].begin(), args["bc1input"].end(), ',', inputvalues);

  text_tarray inputtypes;
  splitchar (args["bc1inputtype"].begin(), args["bc1inputtype"].end(), ',', inputtypes);

  int numvalues = inputvalues.size();
  int numtypes = inputtypes.size();

  for (int i = 0; i < numvalues; ++i) {
    text_t value = format_url(decode_commas(inputvalues[i]));
    text_t type = "file://"; // default
    if (!value.empty()) {
      found = true;
      if (i >= numtypes || inputtypes[i].empty()) {
	logout << "collectoraction::check_sources: WARNING type not set\n";
      } else {
	type = inputtypes[i];
      }
      if (type == "file://") {
	if (!file_exists(value) && !directory_exists(value)) {
	  failedsources[decode_commas(inputvalues[i])] = "1";
	  badsources = true;
	}
      } else if (type == "http://") {
	if (gsdl_system ("perl -S ping.pl -quiet http://" + value, true, logout)) {
	  failedsources[decode_commas(inputvalues[i])] = "1";
	  badsources = true;
	}
      } else if (type == "ftp://") {
	if (gsdl_system ("perl -S ping.pl -quiet ftp://" + value, true, logout)) {
	  failedsources[decode_commas(inputvalues[i])] = "1";
	  badsources = true;
	}
      }
    }
  }

  // set badsources if there weren't any sources at all
  if (!found) badsources = true;

  if (badsources) return false;
  return true;
}

text_t collectoraction::get_button(cgiargsclass &args, const text_t &thispage, 
				   const text_t &color, 
				   const text_t &type, bool enabled) 
{

  if ((color != "green" && color != "grey" && color != "yellow") ||
      (type != "info" && type != "srce" && type != "conf" && type != "bild" && type != "view"))
    return g_EmptyText;

  text_t href = "_http"+type+"_";
  text_t target = "";

  if (thispage == "info" || thispage == "srce" || thispage == "conf" || 
      thispage == "bildcancel" || thispage == "bildfail") {
    // call the check submit macro instead of linking directly to the page
    href="\"javascript:check_submit('"+type+"');\"";
  } else if (type == "view") {
    // view button is special case as it needs a target=_top
    target = " target=_top";
  }

  text_t tdclass = "wizardbar"+color; 
  if (enabled) {
    // link to the appropriate page
    return "<td class="+tdclass+"><a href="+href+target+">_text"+type+"_</a></td>";
  }
  else {
    // just display the text
    return "<td class="+tdclass+">_text"+type+"_</td>";
  }
}

#endif //GSDL_USE_COLLECTOR_ACTION
