/*///---------------------------------------------------------------------\\\*/
/*// Copyright (c)               (_)/=\~/_(_)         All rights reserved. \\*/
/*/  2005-2006                (_)  /=\  ~/_  (_)        Released under the  \*/
/*\  Jim Peters            (_)    /=\    ~/_    (_)             GNU GPL v2  /*/
/*\\ http://uazu.net    (_) ____ /=\ ____ ~/_ ____ (_)      http://fsf.org //*/
/*\\\---------------------------------------------------------------------///*/

//
//	Remote-end
//

#ifdef HEADER

#else

#include "all.h"

static void
dump(char *nam, char *out) {
   if (out && !isEOF(out))
      warn("%12.12s == %s", nam, out);
   else if (out) 
      warn("%12.12s ||", nam);
   else
      warn("%12.12s --", nam);
}
#define DUMP(xx) DEBUG dump(#xx, xx)
#define DUMP_NL() DEBUG warn("")

static void 
hup_handler(int sig) {
   error("Connection from controlling symirr broken.");
}

// Statics
static char *sessid;	// Session ID
static char *old_ts;	// Old time-stamp
static char *idts;	// ID/time-stamp directory
static char *old_idts;	// ID/time-stamp directory for previous session, or 0


//
//	Handle remote side
//

void 
do_remote() {
   char *cmd, *a0;
   struct stat sb;
   DIR *dir;

   // This is a sanity check to be sure that we don't have any o_*
   // variables already set by this point, due to running symirr via
   // fork() (as in older code)
   if (o_local) error("do_remote() called with o_local set");

   // Setup pipe_*() calls to use FD 0/1
   pipe_remote();

   // Ask for calls to error() to send an ERR string up to the controlling symirr also
   fatal_to_stdout= 1;

   // Setup signal handlers
   if (SIG_ERR == signal(SIGPIPE, hup_handler)) error("Can't set SIGPIPE signal handler");
   if (SIG_ERR == signal(SIGHUP, hup_handler)) error("Can't set SIGHUP signal handler");
   if (SIG_ERR == signal(SIGINT, SIG_IGN)) error("Can't set SIGINT signal to be ignored");

   { // Welcome banner
      char tmp[128];
      if (gethostname(tmp, sizeof(tmp))) strcpy(tmp, "???");
      pipe_wrlin("OK\tsymirr\t" VERSION "\ton\t%s", tmp);
   }

   // Initialised okay
   pipe_wrOK();		// @@@ Add capabilities here
   base= 0;

   // Main loop
   while ((cmd= pipe_rdlin())) {
      if (cmd[0] == '#') continue;
      a0= skipTSV(cmd);

      if (str_eq(cmd, "quit")) {
	 if (chlog) chlog_close();
	 tgz_close();
	 pipe_wrOK();
	 usleep(100000);  // Slight delay to attempt to help coverage results come out right ... I hope
	 return;
      }

      if (str_eq(cmd, "ping")) {
	 pipe_wrOK();
	 continue;
      }

      if (str_eq(cmd, "opt")) {
	 char *p= a0, ch;
	 char *nxt= skipTSV(p);

	 if (base) { pipe_wrlin("ERR Only one 'opt' command permitted"); continue; }

	 while (p && p[0] == '-') {
	    for (p++; (ch= *p++); ) switch (ch) {
	     case 'D':
		o_debug++; break;
	     case 'f':
		o_follow++; break;
	     case 'b':
		o_backup++; break;
	     case 'x':
	     case 'p':
		if (!nxt) { pipe_wrlin("ERR Expecting argument to option '%c'", *p); continue; }
		a0= nxt; nxt= skipTSV(nxt);
		list_add((*p == 'x') ? &o_excl : &o_prune, StrDup(a0));
		break;
	     case 'T':
		special_test_mode(atoi(p));
		p= ""; break;
	     default:
		pipe_wrlin("ERR Unknown option '%c'", *p); continue;
	    }
	    p= nxt; nxt= skipTSV(nxt);
	 }
	 if (!p) {
	    pipe_wrlin("ERR Missing remote-base-directory in 'opt' command"); continue;
	 }
	 if (nxt) {
	    pipe_wrlin("ERR Excess options/data after base directory in 'opt' command:\n  %s %s", p, nxt); continue;
	 }
	 base= StrDup(p);

	 { // Create base directory if doesn't exist
	    char *nam= StrF("%s/_SYMIRR_BASE", base);
	    DIR *dir= opendir(nam);
	    if (!dir) {
	       if (errno != ENOENT) {
		  pipe_wrlin("ERR Can't access directory (%s): %s", strerror(errno), nam); continue;
	       }
	       if (mkdir(nam, 0777)) { 
		  pipe_wrlin("ERR Can't create directory (%s): %s", strerror(errno), nam); continue;
	       }
	    }
	    closedir(dir);
	 }
	 pipe_wrOK();
	 continue;
      }

      if (!base) {
	 pipe_wrlin("ERR Need 'opt' command first:\n  %s", cmd); continue;
      }

      if (str_eq(cmd, "conn")) {
	 char *ts= a0;
	 char *newid= skipTSV(ts);
	 char *id0= skipTSV(newid);
	 char *id1= skipTSV(id0);

	 if (idts || chlog) {
	    pipe_wrlin("ERR Only one successful 'conn' permitted per session");
	    continue;
	 }

	 while (id0) {
	    if (0 != (dir= opendir(StrF("%s/_SYMIRR_BASE/%s", base, id0)))) {
	       struct dirent *ent;
	       free(old_ts); old_ts= 0;
	       while ((ent= readdir(dir))) {
		  // Record the latest valid session time-stamp
		  // directory with all files present ready to sync
		  if (isdigit(ent->d_name[0]) && 
		      isdigit(ent->d_name[1]) &&
		      (!old_ts || strcmp(old_ts, ent->d_name) < 0) &&
		      0 == stat(StrF("%s/_SYMIRR_BASE/%s/%s/local-pc-tree.gz", base, id0, ent->d_name), &sb) &&
		      0 == stat(StrF("%s/_SYMIRR_BASE/%s/%s/local-pc-changes", base, id0, ent->d_name), &sb)) {
		     free(old_ts);
		     old_ts= StrDup(ent->d_name);
		  }
	       }
	       closedir(dir);
	       if (old_ts) {
		  sessid= StrDup(id0);
		  break;
	       }
	    }	       
	    id0= id1; id1= skipTSV(id0);
	 }
	 if (sessid) {
	    pipe_wrlin("OK\t%s\t%s", sessid, old_ts);
	    old_idts= StrDupF("%s/_SYMIRR_BASE/%s/%s", base, sessid, old_ts);
	    idts= StrDupF("%s/_SYMIRR_BASE/%s/%s", base, sessid, ts);
	 } else {
	    char *tmp= StrF("%s/_SYMIRR_BASE/%s", base, newid);
	    if (0 != (dir= opendir(tmp))) {
	       closedir(dir);
	       pipe_wrlin("NO");
	       continue;
	    }
	    sessid= StrDup(newid);
	    if (mkdir(tmp, 0777)) { pipe_wrlin("ERR Can't create directory (%s): %s", strerror(errno), tmp); continue; }
	    idts= StrDupF("%s/_SYMIRR_BASE/%s/%s", base, sessid, ts);
	    pipe_wrlin("OK\t%s", sessid);
	 }

	 if (mkdir(idts, 0777)) { pipe_wrlin("ERR Can't create directory (%s): %s", strerror(errno), idts); continue; }
	 tgz_pathname= StrDupF("%s/backups.tgz", idts);

	 // Clean up history data, at the same time local end is
	 clean_and_pack(idts);
	 continue;
      }

      if (!idts) {
	 pipe_wrlin("ERR Need 'conn' command first: %s", cmd);
	 continue;
      }

      if (str_eq(cmd, "tree")) {
	 SMTree *tr= smtree(base);
	 SMTsave *sv0= smtsave(StrF("%s/local-pc-tree.gz", idts), &tr->out, 0);
	 SMTsave *sv1= smtsave(StrF("%s/local-tree.gz", idts), &sv0->out, 0);
	 while (1) {
	    smtree_do(tr);	DUMP(tr->out);
	    smtsave_do(sv0);	DUMP(sv0->out);
	    smtsave_do(sv1);	DUMP(sv1->out); DUMP_NL();

	    if (isEOF(sv1->out)) break;
	    if (sv1->out) {
	       pipe_wrlin("%s", sv1->out); 
	       free(sv1->out); sv1->out= 0;
	    }
	 }
	 smtree_del(tr);
	 smtsave_del(sv0);
	 smtsave_del(sv1);

	 // Create local-pc-changes file to flag that local-pc-tree.gz is complete
	 chlog_open(idts);
	 continue;
      }

      if (str_eq(cmd, "xtree") ||
	  (str_eq(cmd, "xtreediff") && 0 != stat(StrF("%s/local-tree.gz", old_idts), &sb))) {
	 // For an xtreediff when there is no local-tree.gz, just do a
	 // normal xtree
	 if (old_idts &&
	     0 == stat(StrF("%s/local-pc-tree.gz", old_idts), &sb) &&
	     0 == stat(StrF("%s/local-pc-changes", old_idts), &sb)) {
	    // Everything ready
	    SMLoad *ld1= smload(StrF("%s/local-pc-tree.gz", old_idts));
	    SMLoad *ld2= smload_sorted(StrF("%s/local-pc-changes", old_idts));
	    SMMapch *map= smmapch(&ld2->out);
	    SMMerge *mrg= smmerge(&ld1->out, &map->out);
	    SMTsave *sv0= smtsave(StrF("%s/local-pc-tree.gz", idts), &mrg->out, 0);
	    SMTree *tr= smtree(base);
	    SMTsave *sv1= smtsave(StrF("%s/local-tree.gz", idts), &tr->out, 0);
	    SMXtree *xtr= smxtree(&sv0->out, &sv1->out);

	    while (1) {
	       smload_do(ld1);	DUMP(ld1->out);
	       smload_do(ld2);	DUMP(ld2->out);
	       smmapch_do(map);	DUMP(map->out);
	       smmerge_do(mrg);	DUMP(mrg->out);
	       smtsave_do(sv0);	DUMP(sv0->out);
	       smtree_do(tr);	DUMP(tr->out);
	       smtsave_do(sv1);	DUMP(sv1->out);
	       smxtree_do(xtr);	DUMP(xtr->out); DUMP_NL();

	       if (isEOF(xtr->out)) break;
	       if (xtr->out) {
		  pipe_wrlin("%s", xtr->out);
		  free(xtr->out); xtr->out= 0;
	       }
	    }

	    smload_del(ld1);
	    smload_del(ld2);
	    smmapch_del(map);
	    smmerge_del(mrg);
	    smtsave_del(sv0);
	    smtree_del(tr);
	    smtsave_del(sv1);
	    smxtree_del(xtr);

	    // Create local-pc-changes file to flag that local-pc-tree.gz is complete
	    chlog_open(idts);
	    continue;
	 }
	 pipe_wrlin("ERR Can't do an 'xtree' without previous session information");
	 continue;
      }

      if (str_eq(cmd, "xtreediff")) {
	 if (old_idts &&
	     0 == stat(StrF("%s/local-tree.gz", old_idts), &sb) &&
	     0 == stat(StrF("%s/local-pc-tree.gz", old_idts), &sb) &&
	     0 == stat(StrF("%s/local-pc-changes", old_idts), &sb)) {
	    // Everything ready
	    SMLoad *ld0= smload(StrF("%s/local-tree.gz", old_idts));
	    SMLoad *ld1= smload(StrF("%s/local-pc-tree.gz", old_idts));
	    SMLoad *ld2= smload_sorted(StrF("%s/local-pc-changes", old_idts));
	    SMMapch *map= smmapch(&ld2->out);
	    SMMerge *mrg= smmerge(&ld1->out, &map->out);
	    SMTsave *sv0= smtsave(StrF("%s/local-pc-tree.gz", idts), &mrg->out, 0);
	    SMTree *tr= smtree(base);
	    SMTsave *sv1= smtsave(StrF("%s/local-tree.gz", idts), &tr->out, 0);
	    SMXtree *xtr= smxtree(&sv0->out, &sv1->out);
	    SMDiff *df= smdiff(&ld0->out, &xtr->out);

	    while (1) {
	       smload_do(ld0);	DUMP(ld0->out);
	       smload_do(ld1);	DUMP(ld1->out);
	       smload_do(ld2);	DUMP(ld2->out);
	       smmapch_do(map);	DUMP(map->out);
	       smmerge_do(mrg);	DUMP(mrg->out);
	       smtsave_do(sv0);	DUMP(sv0->out);
	       smtree_do(tr);	DUMP(tr->out);
	       smtsave_do(sv1);	DUMP(sv1->out);
	       smxtree_do(xtr);	DUMP(xtr->out);
	       smdiff_do(df);	DUMP(df->out); DUMP_NL();

	       if (isEOF(df->out)) break;
	       if (df->out) {
		  pipe_wrlin("%s", df->out);
		  free(df->out); df->out= 0;
	       }
	    }

	    smload_del(ld0);
	    smload_del(ld1);
	    smload_del(ld2);
	    smmapch_del(map);
	    smmerge_del(mrg);
	    smtsave_del(sv0);
	    smtree_del(tr);
	    smtsave_del(sv1);
	    smxtree_del(xtr);
	    smdiff_del(df);

	    // Create local-pc-changes file to flag that local-pc-tree.gz is complete
	    chlog_open(idts);
	    continue;
	 }
	 pipe_wrlin("ERR Can't do an 'xtreediff' without previous session information");
	 continue;
      }

      if (!chlog) {
	 pipe_wrlin("ERR Need tree/xtree/xtreediff command first (no chlog): %s", cmd);
	 continue;
      }

      if (str_eq(cmd, ">>mkdir")) {
	 char *mug= a0;
	 char *nam= skipTSV(mug);
	 char *path= StrDupF("%s%s", base, nam);
	 do_mkdir(path, mug);
	 wrlog(nam, path);
	 free(path);
	 pipe_wrOK();
	 continue;
      }

      if (str_eq(cmd, ">>chmug")) {
	 char *mug= a0;
	 char *nam= skipTSV(mug);
	 char *path= StrDupF("%s%s", base, nam);
	 do_chmug(path, mug);
	 wrlog(nam, path);
	 free(path);
	 pipe_wrOK();
	 continue;
      }

      if (str_eq(cmd, ">>rm")) {
	 char *nam= a0;
	 char *path= StrDupF("%s%s", base, nam);
	 do_rm(path);
	 wrlog(nam, path);
	 free(path); 
	 pipe_wrOK();
	 continue;
      }
	 
      if (str_eq(cmd, ">>mv")) {
	 char *nam0= a0;
	 char *nam1= skipTSV(nam0);
	 char *path0= StrDupF("%s%s", base, nam0);
	 char *path1= StrDupF("%s%s", base, nam1);
	 if (rename(path0, path1)) {
	    pipe_wrlin("ERR Rename failed (%s): %s -> %s", strerror(errno), path0, path1);
	 } else {
	    wrlog(nam0, path0);
	    wrlog(nam1, path1);
	    pipe_wrOK();
	 }
	 free(path0); free(path1);
	 continue;
      }

      if (str_eq(cmd, ">>cp")) {
	 char *p;
	 char *mug= StrDup(a0);		// pipe_rdmem/pipe_rdfile nukes our buffer, must StrDup
	 char *nam= skipTSV(mug);
	 char *szt= skipTSV(nam);
	 char *path= StrDupF("%s%s", base, nam);
	 int siz, mtim, sym;
	 decode_szt(szt, &siz, &mtim, &sym);

	 if (sym) {
	    char *buf= Alloc(siz + 1);
	    pipe_rdmem(buf, siz);
	    do_symlink(path, buf);
	    free(buf);
	 } else {
	    do_backup(path);
	    pipe_rdfile(path, siz);	
	    do_mtime(path, mtim);
	 }

	 do_chmug(path, mug);
	 wrlog(nam, path);
	 free(mug);

	 p= pipe_rdlin();
	 if (!p || *p)
	    error("Seem to have lost sync during a '>>cp' command for '%s':\n  %s", path, p);
	 pipe_wrOK();

	 free(path);
	 continue;
      }

      if (str_eq(cmd, "<<mkdir")) {
	 char *nam= skipTSV(a0);
	 char *path= StrDupF("%s%s", base, nam);
	 wrlog(nam, path);
	 free(path);
	 pipe_wrOK();
	 continue;
      }
      if (str_eq(cmd, "<<chmug")) {
	 char *nam= skipTSV(a0);
	 char *path= StrDupF("%s%s", base, nam);
	 wrlog(nam, path);
	 free(path);
	 pipe_wrOK();
	 continue;
      }
      if (str_eq(cmd, "<<rm")) {
	 char *nam= a0;
	 char *path= StrDupF("%s%s", base, nam);
	 wrlog(nam, path);
	 free(path); 
	 pipe_wrOK();
	 continue;
      }
      if (str_eq(cmd, "<<mv")) {
	 // We log both files, just in case; this will result in
	 // duplicate entries for some files in the log
	 char *nam0= a0;
	 char *nam1= skipTSV(a0);
	 char *path0= StrDupF("%s%s", base, nam0);
	 char *path1= StrDupF("%s%s", base, nam1);
	 wrlog(nam0, path0);
	 wrlog(nam1, path1);
	 free(path0); free(path1); 
	 pipe_wrOK();
	 continue;
      }

      if (str_eq(cmd, "<<cp")) {
	 char *mug= a0;
	 char *nam= skipTSV(mug);
	 char *szt= skipTSV(nam);
	 char *path= StrDupF("%s%s", base, nam);
	 char *lkup, *k1, *k2, *k3;
	 int siz, mtim, sym;
	 if (0 != lstat(path, &sb) ||
	     !(lkup= smtree_lookup(path, 0, "")) ||
	     !lkup[0] || 
	     0 == strcmp("DEL", lkup)) {
	    pipe_wrlin("ERR File seems to have disappeared: %s", path);
	    continue;
	 }
	 lkup= StrDup(lkup);
	 k1= skipTSV(lkup);
	 k2= skipTSV(k1);
	 k3= skipTSV(k2);
	 if (!str_eq(k1, mug)) {
	    pipe_wrlin("ERR Mode/user/group seem to have changed (%s -> %s) for file: %s", mug, k1, nam);
	    free(path); free(lkup); continue;
	 }
	 if (!str_eq(k3, szt)) {
	    pipe_wrlin("ERR Size/mtime seem to have changed (%s -> %s) for file: %s", szt, k3, nam);
	    free(path); free(lkup); continue;
	 }
	 decode_szt(szt, &siz, &mtim, &sym);
	 
	 pipe_wrlin("BIN");
	 if (!sym) {
	    pipe_wrfile(path, siz);
	 } else {
	    char *buf= Alloc(siz + 1);
	    if (siz != readlink(path, buf, siz+1))
	       error("symlink readlink() call failed in 'get' command (%s): %s", strerror(errno), path);
	    pipe_wrmem(buf, siz);
	    free(buf);
	 }
	 pipe_wrlin("");	// Blank line for sync-check 
	 wrlog(nam, path);
	 free(path); free(lkup); continue;
      }

      pipe_wrlin("ERR Command not known: %s", cmd);
   }

   return;
}

#endif

/*///---------------------------------------------------------------------\\\*/
/*// )`( )()/)(`/\`//\`          (_)/=\~/_(_)           '/\\'/\')(\()( )'( \\*/
/*/ ()(')()\)(/`/\\ `         (_)  /=\  ~/_  (_)          ' //\'\)(/()(`)() \*/
/*\  )`()( )(/'            (_)    /=\    ~/_    (_)             `\)( )()'(  /*/
/*\\  ()()(`)(/         (_) ____ /=\ ____ ~/_ ____ (_)          \)(')()()  //*/
/*\\\---------------------------------------------------------------------///*/
