#!/usr//share/steam/steam

/* Copyright (C) 2000-2004  Thomas Bopp, Thorsten Hampel, Ludger Merkens
 *
 *  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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 * 
 * $Id: spm.in,v 1.7 2005/03/26 15:14:11 mbaehr Exp $
 */

constant cvs_version="$Id: spm.in,v 1.7 2005/03/26 15:14:11 mbaehr Exp $";

//! steam package manager main script
//! Installs packages into the main server 
//! Uploads files by coal and runs pike scripts for preinst and postinst
inherit "../config";

string tmp_dir = "/tmp";

static object oInstall;

object find_object(int id)
{
    if ( objectp(oInstall) )
	oInstall->find_object(id);
    return 0;
}

void list_packages(mapping options)
{
  object conn = ((program)"connection.pike")();

  conn->start("www.open-steam.org", 1900, "guest", "guest");
  conn->list_packages();
}

int get_package(string pck_name, string filename)
{
    object conn = ((program)"connection.pike")();
    
    conn->start("www.open-steam.org", 1900, "guest", "guest");
    string content = conn->get_package(pck_name);
    if ( !stringp(content) )
    return 0;
    conn->close();
    object f = Stdio.File(filename, "wct", 0600);
    f->write(content);
    f->close();
    return 1;
}

static string|int get_value(string val)
{
   int      d;
   if ( sscanf(val, "%d", d) == 1 && (string)d == val )
     return d;
   return val;
}

void read_configs(string fname)
{
    string configs = Stdio.read_file(fname);
    mapping conf = ([ ]);

    if ( !stringp(configs) )
    error("failed to find config file at "+ fname);
    array lines = configs / "\n";
    foreach(lines, string line) {
     if ( strlen(line) == 0 )
       continue;
     if ( line[0] == '#' ) // comment
       continue;
     string key;
     mixed  val;
 
     if ( sscanf(line, "%s=%s", key, val) != 2 ) {
       continue;
     } 
     string v;
     if ( sscanf(val, "hbs(%s)", v) > 0 ) {
       conf[key] = get_value(v);
       continue;
     }
     conf[key] = get_value(val);
    }
    vars = conf;
}

//! Generate a random valid filename for a new file
//! Returns the string of a valid filename in the given directory and with the
//! given prefix and suffix. The filename will thus be dir+prefix+random+suffix.
//! At the time of calling, no file with this name existed.
string make_tmp_filename ( string dir, string prefix, string suffix ) {
  string full_prefix = dir;
  if ( ! has_suffix( full_prefix, "/" ) ) full_prefix += "/";
  full_prefix += prefix;
  string random_fname = "";
  Stdio.File tmp_file;
  mixed err = 0;
  do {
    random_fname = full_prefix + replace(MIME.encode_base64(random_string(20),1) + suffix,"/","X");
  } while ( Stdio.exist(random_fname) );
  return random_fname;
}


//! Call Script with package to install, for example
//! install web.spm
//! spm is a packed archive with the following directories:
//! sources/ - files to upload on the server with the present structure
//! xml/ - xml code to set attributes, access and so on in server files
//! steam/ - meta information of the package and installation perequesites
//!
int main(int argc, array(string) argv)
{
  string server_path = "/usr//share/steam";
  string config_path = "/etc/steam";

  if ( config_path == "" )
    config_path = server_path + "/config";

  if ( search(argv, "--help") >= 0 ) { 
      write("SPM: sTeam package manager.\n"+
	"This utility connects to a running local sTeam server and\n"+
	"installs new packages. Usage is spm <options>:\n"+
	" -i --install <package-name>: Installs a new package.\n"+
	"    The package is either retrieved from the main sTeam server\n"+
	"    or located on the local disk (package-name can be a path name).\n"+
	" -l --list: Lists all packages available for installation on the\n"+
	"    main sTeam server (www.open-steam.org).\n"+
	" -d --dest: Install the package to the destination directory.\n"+
	" -c --configure: Just configure a package (if already installed).\n");
      return 0;
  }

  mapping values = ([ "i":"install", 
		      "l":"list",
		      "d":"dest",
		      "c":"configure",
		      ]);
			
  mapping options = handle_options(argv[1..], values);

  master()->add_include_path(server_path+"/server/include");
  master()->add_program_path(server_path+"/server/");
  master()->add_program_path(server_path+"/spm/");
  master()->add_module_path(server_path+"/server/libraries");
  master()->add_program_path(server_path+"/server/net/coal");
  add_constant("find_object", find_object);
  read_configs(config_path+"/steam.cnf");

  if ( options["list"] ) {
      list_packages(options);
      return 0;
  }
  
  werror("OPTIONS\n"+sprintf("%O\n", options));
  if ( sizeof(options->options) < 1 ) {
      werror("Missing filename or URL for installation...\n");
      return 1;
  }


  string url, pck_name;
  pck_name = options->options[0];
  mkdir(dirname(tmp_dir));

  if ( sscanf(pck_name, "http://%s", url) > 0 ) {
      // the temporary file for download...
      
      mapping headers = ([ ]);
      string user, pass;
      if ( sscanf(url, "%s:%s@%s", user, pass, url) == 3 ) {
      werror("User: "+ user + "\n");
      headers = ([ "authorization": "Basic " + 
        MIME.encode_base64(user+":"+pass), ]);
      }
      werror("Connecting to http://" + url+"\n");
      object query = Protocols.HTTP.get_url("http://"+url, ([ ]), headers);
      mapping d = query->cast("mapping");
      werror("RESULT=\n"+sprintf("%O\n",indices(d)));
      string data = d->data;
      string fname;
      sscanf(url, "%*s/%s", fname);
      werror("Transfered " + strlen(data) + " bytes...\n");
      pck_name = make_tmp_filename(tmp_dir,"spm_",basename(fname));
      object f = Stdio.File(pck_name, "wct",0600);
      f->write(data);
      f->close();
      
      werror("Package received...\n");
  }

  if ( !Stdio.exist(pck_name) ) {
      werror("Getting file from package server...\n");
      string tmp_filename = make_tmp_filename(tmp_dir,"spm_",basename(pck_name));
      if ( !get_package(pck_name, tmp_filename) ) {
        werror("Package not found on server ... Try -l to list packages.\n");
        return 1;
      }
      pck_name = tmp_filename;
  }
  master()->add_program_path("../spm");

	if ( sscanf(pck_name, "%s.gz", pck_name) >= 0 ) {
		werror("Unzipping...\n");
		string destname = pck_name;
		if ( search(destname,tmp_dir) != 0 )
			destname = make_tmp_filename(tmp_dir,"spm_",basename(destname));
		object f = Stdio.File(destname, "wct");
		int ret = Process.create_process( 
			({ "gunzip", "-c", "-f", pck_name+".gz" }), ([ "stdout" : f ]))->wait();
		f->close();
		if ( search(pck_name,tmp_dir) == 0 )
			Filesystem.System()->rm( pck_name+".gz" );
		pck_name = destname;
	}

  werror("Opening Tar Filesystem...");
  object fsystem = Filesystem.Tar(pck_name);
  werror("ok.\n");

  object conn = ((program)"connection.pike")();
  string server = "localhost";
  int port = (int)vars->port;

  if ( options->server )
      server = options->server;
  if ( options->port )
      port = (int)options->port;
  write("Connecting sTeam on "+server+":"+port+"\n");

  string pw = read_input("Root Password for server", "steam");

  string pname;
  if ( sscanf(pck_name, "%s.spm", pname) != 1 ) {
      pname = pck_name;
  }

  vars["package"] = (pname / "/")[-1];
  vars["fs"] = pname+".spm";
  if ( !stringp(options["dest"]) )
	vars["dest"] = "/";
  else 
	vars["dest"] = options["dest"];

  conn->set_fsystem(fsystem, vars);
  conn->start(server, port, "root", pw);
  if ( options->configure ) {
	conn->configure_web();
	werror("CONFIGURATION of package completed !");
	return 1;
  }
  /* 
   * connect to the server and register the new package module
   */ 
  conn->upload_package(vars);
  werror("\nINSTALLATION Successfull, "+conn->iInstall + " new Files, "+
	 conn->iUpdate + " updated.\n\n");

	// delete the temporary file:
	if ( search(pck_name,tmp_dir) == 0 )
		Filesystem.System()->rm( pck_name );
}
