#!/usr/local/bin/pike

inherit Stdio.Port;

#if !constant(Stdio.PROP_IPC)
#define NO_IPC
#endif

// Bugfix for some older versions of Pike..
string combine_path(string s, string ... rest)
{
  for(int e=0;e<sizeof(rest);e++)
  {
    if(sscanf(rest[e],"%*[a-zA-Z]:%*s")==2)
    {
      s=rest[e];
    }else{
      s=predef::combine_path(s,rest[e]);
    }
  }
  return s;
}

// Bugfix for some older versions of Pike..
#define BLOCK 65536
int cp(string from, string to)
{
  if(!Stdio.cp(from,to))
  {
    werror("Backup cp function in effect.\n");

    string data;
    object tmp=Stdio.File();
    if(!tmp->open(from,"r"))
    {
      werror(sprintf("Open %s failed.\n",from));
      return 0;
    }
    function r=tmp->read;
    tmp=Stdio.File();
    if(!tmp->open(to,"wct"))
    {
      werror(sprintf("Open %s failed.\n",to));
      return 0;
    }
    function w=tmp->write;
    do
    {
      data=r(BLOCK);
      if(!data)
      {
	werror("Read failed.\n");
	return 0;
      }
      if(w(data)!=strlen(data))
      {
	werror("Write failed.\n");
	return 0;
      }
    }while(strlen(data) == BLOCK);
  }
  return 1;
}

void monitor(object(Stdio.File) io, object proc)
{
  proc->wait();
  if(io)
  {
    io->close("rw");
    io->close();
    destruct(io);
  }
}

#ifdef WINE
void my_proxy(Stdio.File from, Stdio.File to)
{
  while(string s=from->read(128,1))
    if(to->write(s)!=strlen(s))
      return;

}
#endif

void handle_incoming_connection(object(Stdio.File) io)
{
  object p;
  sscanf(io->read(4),"%4c",int args);
  string *cmd=allocate(args);
  for(int e=0;e<args;e++)
  {
    sscanf(io->read(4),"%4c",int len);
    cmd[e]=io->read(len);
  }

  object pi=Stdio.File();
#ifdef NO_IPC
  object p2=pi->pipe();
#else
  object p2=pi->pipe(Stdio.PROP_IPC);
#endif
  string dir=cmd[0];
  cmd=cmd[1..];

  write("Doing "+cmd*" "+"\n");

  switch(lower_case(cmd[0]))
  {
    case "mkdir":
    {
      io->write(sprintf("%4c",0));
      mkdir(combine_path(combine_path(getcwd(),dir),cmd[1]));
      io->write(sprintf("%4c",0));
      break;
    }

    case "copy":
    {
      string from=combine_path(combine_path(getcwd(),dir),cmd[1]);
      string to=combine_path(combine_path(getcwd(),dir),cmd[2]);

      if(mixed stat=file_stat(to))
      {
	if(stat[1]==-2)
	{
	  to=combine_path(to,basename(cmd[1]));
	}
      }


      int ret=cp(from,to);
      if(!ret)
      {
	string x=sprintf("Errno is %d\n"
			 "CWD=%s\n"
			 "from=%s\n"
			 "to=%s\n"
			 "dir=%s (%s)\n",
			 errno(),
			 getcwd(),
			 from,
			 to,
			 dir, combine_path(getcwd(),dir));
	io->write(sprintf("%4c%s",strlen(x),x));
      }
      io->write(sprintf("%4c",0));
      io->write(sprintf("%4c",!ret));
      break;
    }
    case "getenv":
    {
      string s=getenv(cmd[1])+"\n";
      io->write(sprintf("%4c%s",strlen(s),s));
      io->write(sprintf("%4c",0));
      io->write(sprintf("%4c",0));
      break;
    }

    default:
#ifdef WINE
    {
      werror("Proxying.....\n");
      object p3=Stdio.File();
#ifdef NO_IPC
      object p4=p3->pipe();
#else
      object p4=p3->pipe(Stdio.PROP_IPC);
#endif
      thread_create(my_proxy,io,p4);
      io=p3;
    }
#endif

      mixed err=catch {
	p=Process.create_process(cmd,
				 ([
#ifndef WINE
				   "stdin":io,
				   "stdout":p2,
				   "stderr":p2,
#endif
				   "cwd":dir,
				   ]));
      };
      destruct(p2);
      if(!err)
      {
#ifdef NO_IPC
	thread_create(monitor,p2,p);
#endif
	while(1)
	{
	  string s=pi->read(1000,1);
	  if(!s || !strlen(s)) break;
	  io->write(sprintf("%4c%s",strlen(s),s));
	}
	
	io->write(sprintf("%4c",0));
	io->write(sprintf("%4c",p->wait()));
      }else{
	werror(master()->describe_backtrace(err));
	destruct(p2);
	io->write(sprintf("%4c",0));
	io->write(sprintf("%4c",69));
      }
  }
  io->close("w");
  destruct(io);
}

void handle_connections(string *hosts)
{
  while(1)
  {
    if(object io=accept())
    {
      sscanf(io->query_address(),"%s ",string ip);
      if(search(hosts, ip)==-1)
      {
	destruct(io);
	continue;
      }
      thread_create(handle_incoming_connection,io);
    }else{
      werror("Accept failed "+errno()+"\n");
    }
  }
}

int main(int argc, string *argv)
{
#ifdef WINE
  werror("Running in WINE mode.\n");
#endif
  if(argc<2)
  {
    werror("Usage: sprshd <port> <hosts to accept connections from>\n");
    exit(1);
  }
  if(!bind((int)argv[1]))
  {
    werror("Failed to bind port.\n");
    exit(1);
  }

  string *hosts=({});
  for(int e=2;e<sizeof(argv);e++)
  {
    if(sscanf(argv[e],"%*d.%*d")==2)
    {
      hosts+=({argv[e]});
      continue;
    }
    mixed tmp=gethostbyname(argv[e]);
    if(!tmp)
    {
      werror("Gethostbyname("+argv[e]+") failed.\n");
      exit(1);
    }
    hosts+=tmp[1];
  }

  write("Ready ("+version()+").\n");

#ifdef WINE
  thread_create(handle_connections,hosts);
  werror("main returning...\n");
  return -1;
#else
  handle_connection(hosts);
  return 0;
#endif
}
