# Up2us -- RPM package manager manager for linux and beyond
# THIS MODULE: Command line interface.
# $Id: cmdline.pm,v 1.6 2003/04/08 20:35:01 tomj Exp $

# Copyright Tom Jennings 2002-2003
# tomj@wps.com, http://wps.com
# 
# 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.


#############################################################
#
# $error= &cmdline();
#
# The main code for silent, command-line interface. Supports
# only immediate update from all enabled repositories.
#

sub cmdline {

	&load_config();					# read config files
	
# For each repository, run up2date. Skips disabled repositories, but
# has no concept of "DONE" as does the Gtk interface.

	foreach $rep (&rep_list()) {			# all of 'em
		print "\n--------------------------------------------------\n";
		print "--------------------------------------------------\n";
		print STDERR "Repository $rep:\n";
		if ( &rep_item ("$rep.disable")) {
			print STDERR "  disabled, skipped.\n";
			next;
		}

		print STDERR ("  Invoking up2date for repository $rep\n");
		$status= &run_prog ("up2date-nox", $rep); # do actual work,
		if ($status eq $OK) {			# whew
			&set_rep_item ("$rep.last-up2date", "OK " . &gtod);
			print STDERR ("Repository $rep completed.\n");

		} else {
			&set_rep_item ("$rep.last-up2date", "ERROR " . &gtod);
			print STDERR ("  ERROR: $status\n");
			last;				# time to die
		}
	}
	if ($status eq $OK) {
		print STDERR ("All updates completed.\n");
	}
	&leave ($status eq $OK ? $noerror : $generror);
}

#############################################################
#
# &load_config();
#
# This local routine loads and checks configuration for the
# command line functions. Unlike all other code in up2us, this
# exits upon error.

sub load_config {

# First we need to read our main config file, from whence all 
# knowledge flows; die now elsewise.

# eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
# This code can't use leave() which flushes config files) since
# they are AFU if it needs to exit here.

	my $r= &read_up2us_config;			# read config files,
	if ($r ne $OK) {				# oops,
		print STDERR ("$readconfigerr: $r\n");
		exit ($rcferror);
	}
	exit ($crcerror) if &c_crash_recovery ne $OK;	# check for crash.

# Before we start, we move the original up2date config file
# to a safe place; we'll create one form scratch for eac
# repository. The original is restored upon exit.

	$r= &up2date_original_save;			# save up2date config
	if ($r ne $OK) {				# oops
		print STDERR ("$savefail_title: $r\n");	# bad error,
		exit ($suderror);			# can't continue.
	}	

# eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee

# Now load up2date's config files.

	$r= &read_up2date_config;			# read config files,
	if ($r ne $OK) {				# oops,
		print STDERR ("$readconfig_title: " . 
		    sprintf ($readconfigerr, $r) . "\n");
		&leave ($rcferror);			# sigh, error out.
	}
}


# Perform crash recovery if required; return OK if success (or
# not needed).

sub c_crash_recovery {
my $r;

	return $OK if &up2date_original_check eq $OK;	# no problemo,
	print STDERR "WARNING: Previous invokation of up2us crashed, recovering.\n";
	return &up2date_original_recover ();		# needs recovery,
}


#############################################################
#
#  &leave (code)
#
# Exit the program, flush out dirty files and restore things to a
# pristine state, complaining carefully on error. We make every
# attempt to back out gracefully.  The build_config_for creates
# a RedHat-functional (though not pristine) config file should
# recovery fail. None if this is likely, but it's a pain in the
# ass to lose config data, and code is cheap.

sub leave {
my $code= shift;

	my $e= &build_config_for (&RedHat());		# make backup config,
	&ooyay ($code, "build_config_for", $e, $wcferror) if $e ne $OK;

	$e= &write_config_data;				# flush changes to disk,
	&ooyay ($code, "write_config_data", $e, $mfferror) if $e ne $OK;

	$e= &up2date_original_restore ();		# restore up2date conf,
	&ooyay ($code, "up2date_original_restore", $e, $ruderror) if $e ne $OK;

	exit $code;
}

# Local convenience.

sub ooyay {
my $code= shift;
my $routine= shift;
my $e= shift;
my $erp= shift;

	print "Return code was going to be $code, but we failed ";
	print "during leave()'s $routine, which said \"$e\"!\n";
	exit $erp;
}



#############################################################
#
# &cmdlinelist ();
#
# Display information on all of the repositories.

sub cmdlinelist {

my $n= 0;

# repository SSL noSSL last-update registered

my $f= "
Repository: %s (%s)
    Server:        %s
    No SSL server: %s
    Last update:   %s
    Registered on: %s
";
my ($foo, $en, $h1, $h2, $r, $u);

	&load_config();					# read config files
	foreach (&rep_list) {
		$en= &rep_item ("$_.disable") ? "disabled" : "enabled";

		$h1= &rep_item ("$_.serverURL"); $h1 =~ s|\w+://(.*)/.*|$1|; 
		$h2= &rep_item ("$_.noSSLServerURL"); $h2 =~ s|\w+://(.*)/.*|$1|; 
		$u= &rep_item ("$_.last-up2date");
		$r= &rep_item ("$_.last-register");

		print sprintf ($f,
		    $_,					# repository,
		    $en,				# en/dis-able,
		    $h1, $h2,				# servers,
		    $u, $r,				# updated, registered,
		);
	}
	&leave ($noerror);
}

#############################################################
#
# &cmdlinedel ($rep);
#
# Remove the repository from the list. Not much else to say.

sub cmdlinedel {
my $rep= shift;
my $n= 0;

	if ($rep eq "") {
		print "Can't remove nothing, please specify a repository to remove!\n";
		&leave ($generror);
	}

	&load_config();					# read config files

	foreach (&rep_list) {
		if (uc ($rep) eq uc ($_)) {
			&del_repository ($_);		# delete it,
			print "Removed repository \"$rep\"\n";
			++$n;
		}
	}
	print "There is no repository \"$rep\", nothing happens.\n" if $n == 0;
	&leave ($noerror);
}




#############################################################
#
# &cmdlineadd ($rep, $server, $noSSLserver);
#
# Add and register with the given repository. Exits with an
# error code indicating success.

sub cmdlineadd {
my $rep= shift;		# new repository name,
my $H1= shift;		# SSL server name,
my $H2= shift;		# no SSL server name,


# Error-check input, add to our config if OK.

	&load_config();					# read config files

	$rep= uc $rep;					# MUST BE upper case
	print "Adding new repository \"$rep\"\n";
	my $e= &addrep ($rep, $H1, $H2);		# error-check input,
	if ($e ne $OK) {				# oops,
		print "ERROR: $e\n";
		&leave ($generror);
	}

# Before we can register we need to fetch the SSL certificate
# from the server; we'll fetch the GPG keyring at the same time.

	print "1. Fetching authentication and signature information\n";
	$e= &get_auth_junk ($rep);
	if ($e ne $OK) {
		print "ERROR: Failed\n";
		&del_repository ($rep);			# failed, remove it,
		&leave ($generror);
	}

# Now register with this server. We try three times, if we fail 
# the new repository is deleted.

	print "2. Registering at this repository. This may take a few minutes.\n";
	foreach (1..3) {
		$status= &run_prog ("register", $rep);
		last if $status eq $OK;
		print "   $status; retrying\n";
	}

# Either successful or we gave up in frustration.

	if ($status eq $OK) {				# if completed OK,
		print "3. Saving new system ID\n";
		$status= &save_rep_systemid ($rep);	# save created file,
	}

# If status is OK, clean up and return success.

	if ($status eq $OK) {
		&set_rep_item ("$rep.last-register", "OK " . &gtod);
		print "Registration at repository $rep successful.\n";
		&leave ($noerror);

	} else {
		print "ERROR: $status\n";		# went awry,
		&del_repository ($rep);			# failed, remove it,
		&leave ($generror);
	}
	print "OH CRAP\n";				# only programmer error
	&leave (1);
}

#############################################################
#
# $msg= &addrep ("name", "SSL URL", "noSSL URL");
#
# Given the above items, error-check, build and store repository
# config. Returns $OK if no error else an error message.

sub addrep {
my $rep= shift;
my $H1= shift;
my $H2= shift;

# Make sure the repository name exists and is unique.

	$rep= uc $rep;					# force upper case
	return "You must provide a name for this repository." if $rep eq "";

	foreach (&rep_list) {
		return "There is already a repository named $rep."
		    if uc ($rep) eq uc ($_);
	}

# Check that at least one of the server fields is filled in,
# and that filled-in servers are syntactically correct.

	return "You must provide at least one server."
	    if not ($H1 or $H2);
	$H2= $H1 if not $H2;				# make the blank one
	$H1= $H2 if not $H1;				# the other.

	my $error= "";					# total error(s),
	my $e= "";					# local error,

	$error= "Server $H1: $e. "			# build error message,
	     if $e= &checkfqdn ($H1, "0S"); 		# if FQDN error,
	$error .= "Server $H2: $e. "
	    if $e= &checkfqdn ($H2, "0S");
	return $error if $error;

# Server(s) are syntactically correct; see if they exist. If not, issue
# FIXME a warning before continuing.

	$error= "Server $H1: $e."
	    if $e= &checkfqdn ($H1, "0E");		# check it resolves,
	$error .= "Server $H2: $e."
	    if $e= &checkfqdn ($H2, "0E");
	return $error if $error;

# Build the complete URL from the server name(s) entered.

	$H1= "https://$H1/XMLRPC" if $H1;		# SSL 
	$H2= "http://$H2/XMLRPC" if $H2;		# no SSL

# Now store the data we just built into the repository
# config file. 

	&rep_add ($rep);				# add to the list,
	&set_rep_item ("$rep.serverURL", $H1);
	&set_rep_item ("$rep.noSSLServerURL", $H2);
	&set_rep_item ("$rep.last-up2date", "ADDED " . &gtod);
	&set_rep_item ("$rep.last-register", "NEVER " . &gtod);

# Note that the data that these items points to DOESN'T YET
# EXIST. They will be fetched/created in subsequent steps.
# The routines that do that need to know where the data goes.

	&set_rep_item ("$rep.systemIdPath", &upd_item ("systemIdPath") . ".$rep");
	&set_rep_item ("$rep.sslCACert", &upd_item ("sslCACert") . ".$rep");
	&set_rep_item ("$rep.gpgKeyRing", &upd_item ("gpgKeyRing") . ".$rep");

	return $OK;
}



	return 1;

