/* $Id: sfskeyctl.C,v 1.36 2002/12/03 21:56:47 max Exp $ */

/*
 *
 * Copyright (C) 1999 David Mazieres (dm@uun.org)
 *
 * 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, 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
 *
 */


#include "sfskey.h"
#include "rxx.h"
#include "sfscrypt.h"
#include "sfskeymgr.h"

void sfskey_add_cb1 (sfskeymgr *kmgr, sfskeyinfo *ki, time_t ex, sfskey *k);
void sfskey_add_cb2 (sfskeymgr *kmgr, sfskeyinfo *ki, ptr<bool> res, 
		     clnt_stat err);
void sfskey_add_cb3 (clnt_stat err);

void
sfskey_add (int argc, char **argv)
{
  time_t expire = 0;

  int ch;
  while ((ch = getopt (argc, argv, "2t:")) != -1)
    switch (ch) {
    case 't':
      {
	rxx hmrx ("^((\\d+):)?(\\d+)?$");
	int hrs = 0, min = 0;
	if (!hmrx.search (optarg)
	    || (hmrx[3] && !convertint (hmrx[3], &min))
	    || (hmrx[2] && (!convertint (hmrx[2], &hrs) || min > 60))
	    || min < 0 || hrs < 0)
	  usage ();
	expire = 3600 * hrs + 60 * min;
	if (expire)
	  expire += time (NULL);
	break;
      }
    default:
      usage ();
      break;
    }

  agent_setsock ();
  str keyname;
  if (optind + 1 == argc)
    keyname = argv[optind];
  else if (optind != argc)
    usage ();

  sfskeymgr *kmgr = New sfskeymgr (NULL, (KM_NOCRT | KM_NODCHK));
  sfskeyinfo *ki = kmgr->getkeyinfo_list (keyname, 0);
  if (!ki) 
    fatal << "No suitable key found\n";
  kmgr->fetch_from_list (ki, (wrap (sfskey_add_cb1, kmgr, ki, expire)));
}

void
sfskey_add_cb1 (sfskeymgr *kmgr, sfskeyinfo *ki, time_t expire, sfskey *k)
{
  if (!k) 
    fatal << "No suitable key found\n";

  sfs_addkey_arg arg;
  k->key->export_privkey (&arg.privkey);
  arg.expire = expire;
  arg.name = k->keyname;
  ptr<bool> res = New refcounted<bool> ();
  aconn->cagent_ctl ()->call (AGENTCTL_ADDKEY, &arg, res,
			      wrap (sfskey_add_cb2, kmgr, ki, res));
}

void
sfskey_add_cb2 (sfskeymgr *kmgr, sfskeyinfo *ki, ptr<bool> res, clnt_stat err)
{
  if (err)
    fatal << err << "\n";
  if (!*res)
    warn << "agent refused private key\n";
  ptr<sfscon> sc = kmgr->getsrpcon (ki);
  if (sc) { 
    sfsagent_symlink_arg arg;
    arg.name = sc->servinfo->get_hostname ();
    arg.contents = sc->path;
    aconn->cagent_ctl ()->call (AGENTCTL_SYMLINK, &arg, NULL, 
				wrap (sfskey_add_cb3));
  }
  exit (0);
}

void
sfskey_add_cb3 (clnt_stat err)
{
  exit (0);
}

void
sfskey_list (int argc, char **argv)
{
  bool opt_l = false, opt_q = false;
  int ch;
  while ((ch = getopt (argc, argv, "lq")) != -1)
    switch (ch) {
    case 'l':
      opt_l = true;
      break;
    case 'q':
      opt_q = true;
      break;
    default:
      usage ();
      break;
    }
  if (optind < argc)
    usage ();

  sfs_keylist kl;
  if (clnt_stat err = 
      aconn->cagent_ctl ()->scall (AGENTCTL_DUMPKEYS, NULL, &kl))
    fatal << "agent: " << err << "\n";

  sfs_time now = time (NULL);
  strbuf list;
  ptr<sfspub> pubkey;
  strbuf kstr;

  if (!opt_q)
    list.fmt ("%-50s  %9s%s\n", "NAME", "EXPIRE",
	      opt_l ? "  KEY/PID" : "");
  for (sfs_keylistelm *e = kl; e; e = e->next) {
    list.fmt ("%-50s", e->name.cstr ());
    if (!e->expire)
      list.fmt ("  %9s", "never");
    else {
      sfs_time life = e->expire < 0 ? 0 : e->expire - now;
      list.fmt ("  % 3" U64F "d:%02" U64F "d'%02" U64F "d",
		life / 3600, life / 60 % 60, life % 60);
    }
    if (opt_l && e->desc)
      list << " " << e->desc ;
    
    list << "\n";
  }

  make_sync (1);
  list.tosuio ()->output (1);
  exit (0);
}

void
sfskey_clear (int argc, char **argv)
{
  nularg (argc, argv);
  if (clnt_stat err = 
      aconn->cagent_ctl ()->scall (AGENTCTL_REMALLKEYS, NULL, NULL))
    fatal << "agent: " << err << "\n";
  exit (0);
}

void
sfskey_delete (int argc, char **argv)
{
  if (getopt (argc, argv, "") != -1 || optind + 1 != argc)
    usage ();
  sfs_remauth_arg arg (SFS_REM_NAME);
  *arg.name = argv[optind];
  bool res;
  if (clnt_stat err = 
      aconn->cagent_ctl ()->scall (AGENTCTL_REMAUTH, &arg, &res))
    fatal << "agent: " << err << "\n";
  else if (!res)
    fatal << "agent: could not delete key\n";
  exit (0);
}

void
sfskey_reset (int argc, char **argv)
{
  nularg (argc, argv);
  if (clnt_stat err = 
      aconn->cagent_ctl ()->scall (AGENTCTL_RESET, NULL, NULL))
    fatal << "agent: " << err << "\n";
  exit (0);
}

static void
sfskey_gethashcb (int base, str err, ptr<sfspub> k)
{
  if (err)
    fatal << err << "\n";
  assert (k);
  
  str h;
  if (!(h = k->get_pubkey_hash ())) {
    fatal << "error in sha1_hashxdr of public key\n";
    return;
  }
  h = (base == 32) ? armor32 (h) : armor64 (h);
  printf ("%s\n", h.cstr ());
  exit (0);
}

void
sfskey_gethash (int argc, char **argv)
{
  int ch;
  int base = 32;
  while ((ch = getopt (argc, argv, "6")) != -1)
    switch (ch) {
    case '6':
      base = 64;
      break;
    default:
      usage ();
    }
  str keyname;
  if (argc == optind + 1)
    keyname = argv[optind];
  else if (argc > optind + 1)
    usage();

  sfskeymgr *km = New sfskeymgr (NULL, (KM_NOCRT | KM_NODCHK));
  sfskeyinfo *ki;

  if (!(ki = km->getkeyinfo (keyname, KM_PKONLY)))
    fatal << "No suitable key found\n";
  km->fetchpub (ki, wrap (&sfskey_gethashcb, base));
}

void
sfskey_select (int argc, char **argv)
{
  int ch;
  u_int32_t opts = 0;
  while ((ch = getopt (argc, argv, "f")) != -1)
    switch (ch) {
    case 'f':
      opts |= KM_FRCLNK;
      break;
    default:
      usage ();
    }
  str keyname;
  if (argc == optind + 1)
    keyname = argv[optind];
  else if (argc > optind + 1)
    usage ();
  
  sfskeymgr km;
  sfskeyinfo *ki;

  if (!(ki = km.getkeyinfo (keyname, opts)))
    fatal << "No suitable key found\n";
  if (ki->remote)
    fatal << "Cannot select a remote key\n";
  str raw;
  str fn = ki->afn ();
  if (!(raw = file2str (fn)))
    fatal << fn << ": cannot access key\n";
  ptr<sfspub> p;
  if (!(p = sfscrypt.alloc_from_priv (raw)))
    fatal << fn << ": cannot parse key\n";
  if (!km.select (ki, opts))
    fatal << "Key select failed; consider -f flag to force overwrite\n";
  exit (0);
}

