#include "sfsrosd.h"
#include "rxx.h"
#include "sfsrodb_core.h"

#ifdef DMALLOC
#include "dmalloc.h"
#endif

#ifndef MAINTAINER
enum { dumptrace = 0 };
const int asrvtrace (getenv ("ASRV_TRACE") ? atoi (getenv ("ASRV_TRACE")) : 0);
#else /* MAINTAINER */
const bool dumptrace (getenv ("SFSRO_TRACE"));
enum { asrvtrace = 0 };
#endif /* MAINTAINER */


#ifdef SFSRO_PROXY
#include "/home/fubob/src/proxyfs/miracl/elliptic.h"
#include "/home/fubob/src/proxyfs/miracl/monty.h"
#include "/home/fubob/src/proxyfs/miracl/zzn2.h"
extern Miracl precision;
#include "/home/fubob/src/proxyfs/pairing.h"
static CurveParams gParams;
extern ProxyPK proxy_PublicKey;
extern ProxySK proxy_SecretKey;
extern ProxyPK proxy_DelegatePublicKey;
extern ProxySK proxy_DelegateSecretKey;
extern CurveParams proxy_params;
extern ECn proxy_delegationKey;
#endif /* SFSRO_PROXY */

client::client (ptr<axprt_stream> _x)
  : x (_x), conargs (NULL), destroyed (New refcounted<bool> (false))
{
  rosrv = asrv::alloc (x, sfsro_program_2,
			 wrap (this, &client::dispatch, destroyed));
  sfssrv = asrv::alloc (x, sfs_program_1,
			wrap (this, &client::dispatch, destroyed));
}

client::~client()
{
  //  warn << "Calling destructor\n";
  assert (!*destroyed);
  rosrv = sfssrv = NULL;
  *destroyed = true;
}

void
client::dispatch (ref<bool> d, svccb *sbp)
{
  if (!sbp) {
    if (*d)
      return;
    //  warn << "dispatch delete\n";
    delete this; 
    return;
  }

  if (sbp->prog () == SFS_PROGRAM) {
    switch (sbp->proc ()) {
    case SFSPROC_NULL:
      sbp->reply (NULL);
      return;
    case SFSPROC_CONNECT:
      {
	if (asrvtrace >= 5) {
	  warnx << "client: handled connect\n";
	}
	conargs = New refcounted<sfs_connectarg>
	  (*sbp->Xtmpl getarg<sfs_connectarg> ());

	replica *r;
	if (conargs->civers == 5) {
	  warn << "ci5: " << conargs->ci5->sname << "\n";

	  str host;
	  if (!sfs_parsepath (conargs->ci5->sname, &host)) {
	    warn << "unable to parsepath " << conargs->ci5->sname << "\n";
	    sfs_connectres cres (SFS_NOTSUPP);
	    sbp->reply (&cres);
	  }

	  if ((r = replicatab[host]) != NULL) {
	    sbp->reply (&r->cres);
	  } else {
	    warn << "unable to find replica with hostname=" << host << "\n";
	    sfs_connectres cres (SFS_NOTSUPP);
	    sbp->reply (&cres);
	  }
	} else if (conargs->civers == 4) {
	  warn << "ci4: " << conargs->ci4->name << "\n";
	    // << "|" <<  
	    //	    conargs->ci4->hostid << "\n";
	  // XXXX not  supported yet!
	  warn << "unsupported ci4 connect!\n";
	  sfs_connectres cres (SFS_NOTSUPP);
	  sbp->reply (&cres);
	} else {
	  sfs_connectres cres (SFS_NOTSUPP);
	  sbp->reply (&cres);
	}
	return;
      }
    case SFSPROC_GETFSINFO:
      {
	if (asrvtrace >= 5) {
	  warnx << "client: handling request w/ id" << sbp->xid() << "\n";
	}
	
	if (conargs->civers == 5) {
	  warn << "ci5: " << conargs->ci5->sname << "\n";

	  str host;
	  if (!sfs_parsepath (conargs->ci5->sname, &host)) {
	    warn << "unable to parsepath " << conargs->ci5->sname << "\n";
	    sfs_connectres cres (SFS_NOTSUPP);
	    sbp->reply (&cres);
	  }

	  replica *r;
	  if ((r = replicatab[host]) != NULL) {
	    sbp->reply (&r->fsinfores, xdr_sfsro_fsinfo);
	  } else {
	    warn << "unable to find replica with hostname=" << host << "\n";
	    sfsro_fsinfo res (SFSRO_ERRNOENT);
	    sbp->reply (&res, xdr_sfsro_fsinfo);
	  }
	}
      }
      return;
    default:
      warn << "client: unknown proc\n";
      sbp->reject (PROC_UNAVAIL);
      return;
    }
  }
  else if (sbp->prog () == SFSRO_PROGRAM 
	   && sbp->vers () == SFSRO_VERSION_V2) {
    switch (sbp->proc ()) {
    case SFSROPROC2_NULL:
      sbp->reply (NULL);
      return;
    case SFSROPROC2_GETDATA:
      {
	if (asrvtrace >= 5) {
	  warn << "client: handling request w/ id" << sbp->xid() << "\n";
	}
	
	sfsro_getdataargs *args = sbp->Xtmpl getarg<sfsro_getdataargs> ();
	
	if (dumptrace) {
	  u_char *cp = reinterpret_cast<u_char *> (args->fh.base ());
	  u_char *lim = cp + args->fh.size ();
	  printf ("  { 0x%02x", *cp);
	  while (++cp < lim)
	    printf (", 0x%02x", *cp);
	  printf (" },\n");
	}

	if (asrvtrace >= 5) {
	  warn << "calling getdata w fh=" << 
	    hexdump(args->fh.base (), args->fh.size ()) 
		<< "\n";
	}

	str host;
	if (!sfs_parsepath (args->sname, &host)) {
	  warn << "unable to parsepath " << args->sname << "\n";
	  sfsro_datares res (SFSRO_ERRNOENT);
	  sbp->reply (&res);
	}

	replica *r = replicatab[host];
	if (r) {
	  sfsro_datares *res = New sfsro_datares();
	  r->getdata (&args->fh, res, wrap (this, &client::getdata_cb, 
					    sbp, res, destroyed));
	} else {
	  warn << "unable to find replica with hostname " << host << "\n";
	  sfsro_datares res (SFSRO_ERRNOENT);
	  sbp->reply (&res);
	}
	return;
      }
    case SFSROPROC2_GETKEY:
      {
	rsa_pub rp (bigint ("a50e41c593b3b866bc4c72d0476611baab9bd54a22c62e11f536f87861ce592e7a101aea8652d3b949e66271b4497f91a861404eb5f3cba23f22b9b46fadda6cd327e3773eb23795e73ee06c16e5df18cf12e812fcd1bdbf3a4d7cca4fecd95fcbf248ac0534a3ebc67ebb06f9ca77d3ce1a5c4920da6d211b5f242e80d03661",16));
	
	chefs_keyres res (SFSRO_OK);
	res.enc->msglen = mm.len ();
	res.enc->c = rp.encrypt (mm);
	//Should use OAEP to defend against Hastad's attack remarked on in
        //Section 5 of http://theory.lcs.mit.edu/~rivest/RivestKaliski-RSAProblem.pdf


	if (dumptrace) {
	  warn << "GETKEY\n";
	}

	sbp->reply (&res);
      }
      return;		
    case SFSROPROC2_PROXYREENC:
      {
	sfsro_proxyreenc res;
	
#ifdef SFSRO_PROXY
	sfsro_proxyreenc *pargs = sbp->Xtmpl getarg<sfsro_proxyreenc> ();

	if (dumptrace) {
	  u_char *cp = reinterpret_cast<u_char *> (pargs->data.base ());
	  u_char *lim = cp + pargs->data.size ();
	  printf ("  { 0x%02x", *cp);
	  while (++cp < lim)
	    printf (", 0x%02x", *cp);
	  printf (" },\n");
	}

	// Unmarshall c1 here
	char *bufa = (char*)pargs->data.base ();
	ECn c1;
	ZZn2 Zc1;
	c1 = charToECn (bufa);
	
	if (proxy_reencrypt(proxy_params, c1, proxy_delegationKey, Zc1) 
	    == FALSE) {
	  fatal << "Re-encryption failed\n";
	}

	//return Zc1 here
	char buf[1024];
	int buflen = 0;
	buflen = ZZn2Tochar (Zc1, buf, 1024);
	if (buflen <= 0)
	  fatal << "ZZn2Tochar failed\n";
	res.data.setsize (buflen);
	memcpy (res.data.base (), buf, buflen);
	sbp->reply (&res);

#endif /* SFSRO_PROXY */
	return;
      }
    default:
      warn << "rejected unavailable proc " << sbp->proc () << "version "
	    << sbp->vers () << "\n";
      sbp->reject (PROC_UNAVAIL);
      break;
    }
  } else {
    warn << "client: unknown program\n";
    sbp->reject (PROC_UNAVAIL);
    return;
  }

}

void
client::getdata_cb(svccb *sbp, sfsro_datares *res, ref<bool> d) {
  xdrproc_t xdr = sfsro_program_2.tbl[sbp->proc ()].xdr_res;

  if (!*d) {
    if (sbp && res)
      sbp->reply (res);
  }
   
  if (res)
    xdr_delete (xdr, res);
  return;
}
