/* Jungle Monkey Search Server
 * Copyright (C) 2000-2001  The Regents of the University of Michigan
 *
 * 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
 */


#include "config.h"

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include <errno.h>
#include <unistd.h>
#include <time.h>
#include <sys/types.h>
#include <sys/mman.h>

#ifdef JM_ENABLE_GNOME
#include <popt-gnome.h>
#else
#include <popt.h>
#endif
#include "jmintl.h"
#include "util/util.h"

#include "mtp_server.h"
#include "mtp_mirror.h"
#include "mtp_debug.h"


#define DUMP_SERVER_TIMEOUT	5 * 60 * 1000	/* 5 minutes */

static gboolean dump_server_cb (gpointer data);

static guint read_func(MtpLocalMirror* mirror, 
		       guint offset, gchar* buffer, guint length,
		       gpointer user_data);

static gboolean timeout_func (gpointer p);




/* ******************** */
/* Globals		*/

static int   		verbose = 0;
static const gchar* 	filename = NULL;
static gboolean 	sha = FALSE;
static gint		timeout = 30;

static MtpServer*	server = NULL;

static GURL* 		url = NULL;
static gchar* 		buffer = NULL;
static gint 		length = 0;
static FILE* 		file = NULL;
static guint		timer = 0;

static MtpLocalMirror* 	mirror = NULL;
static MtpLocalMirror* 	sha_mirror = NULL;

int main (int argc, char* argv[])
{
  const gchar* url_str = NULL;
  GMainLoop* main_loop;

  GInetAddr* iface = NULL;
  gchar* interface_name = NULL;

  gint port = 0;
  gboolean force_port = FALSE;

  gboolean rendezvous = FALSE;
  gboolean path = FALSE;


  struct poptOption jm_options[] = 
  {
    {"help", 'h', POPT_ARG_NONE, NULL, 1, 
     "Show this help message", NULL},
    {"usage", '\0', POPT_ARG_NONE, NULL, 2, 
     "Display brief usage message", NULL},
    {"version", 0, POPT_ARG_NONE, NULL, 3,
     "Output verson information and exit", NULL},
    {"verbose", 'v', POPT_ARG_NONE, &verbose, 0,
     "Print lots of stuff", NULL},

    {"interface", '\0', POPT_ARG_STRING, &interface_name, 0,
     "Interface address for listening socket", "ADDR"},
    {"port", '\0', POPT_ARG_INT, &port, 0,
     "Port for listening socket", "PORT"},
    {"force-port", '\0', POPT_ARG_NONE, &force_port, 0,
     "Fail if can't set port", NULL},

    {"rendezvous", '\0', POPT_ARG_NONE, &rendezvous, 0,
     "Act as rendezvous server also", NULL},
    {"sha", '\0', POPT_ARG_NONE, &sha, 0,
     "Mirror using SHA as name too", NULL},
    {"path", '\0', POPT_ARG_NONE, &path, 0,
     "Use mtp_mirror_path function", NULL},
    {"timeout", '\0', POPT_ARG_INT, &timeout, 0,
     "Remove file from memory after TIMEOUT seconds", "TIMEOUT"},

    {"debug-flags", 'd', POPT_ARG_INT, &mtp_debug_flags, 0, 
     "Set the MTP debug flags", "FLAGS"},

    {NULL, '\0', 0, NULL, 0, NULL, NULL}
  };

  poptContext ctx;
  int rc;
  int exit_status = EXIT_SUCCESS;


  /* ******************** */
  /* Initialize NLS 	  */
#ifdef ENABLE_NLS
  {
/*      gtk_set_locale(); */
    bindtextdomain (PACKAGE, LOCALEDIR);
    textdomain (PACKAGE);
  }
#endif

  /* ******************** */
  /* Parse command line	  */

  ctx = poptGetContext (NULL, argc, (const char**) argv, jm_options, 0);
  poptSetOtherOptionHelp (ctx, _("[OPTION]... [FILENAME] [RENDEZVOUS URL]"));

  while ((rc = poptGetNextOpt(ctx)) > 0)
    {
      switch (rc)
	{
	case 1:	  goto help;	  break;
	case 2:	  goto usage;	  break;
	case 3:   goto version;	  break;
	}
    }

  filename = poptGetArg (ctx);
  url_str = poptGetArg (ctx);

  poptFreeContext (ctx);

  /* Must have filename */
  if (!filename)
    my_error (_("You must specify a file to mirror.\n"));

  /* Must have URL */
  if (!url_str)
    my_error (_("You must specify a URL of a rendezvous server.\n"));

  if (timeout < 10)
    timeout = 10;

  url = gnet_url_new (url_str);
  if (!url || !url->resource)
    my_error (_("Malformed rendezvous URL: %s\n"), url_str);


  /* If an interface was specified, make sure it resoves to an address */
  if (interface_name)
    {
      /* Convert interface_name to InetAddr. */
      iface = gnet_inetaddr_new (interface_name, 0);
      if (!iface)
	my_error (_("Bad interface name: %s.\n"), interface_name);
    }

  /* Otherwise, auto-detect the interface */
  else
    {
      iface = gnet_inetaddr_autodetect_internet_interface ();
      if (!iface)
	my_error (_("Could not auto-detect an internet interface. "
		    "Use the --interface option to set it manually.\n"));
    }


  /* ******************** */
  /* Open the file	  */

  length = file_size (filename);
  if (length < 0)
    my_error (_("Could not get length of file %s\n"), filename);


  /* ******************** */
  /* Create the server    */

  gnet_inetaddr_set_port (iface, port);
  server = mtp_server_new (NULL, iface, force_port, MTP_SERVER_TYPE_MIRROR, TRUE);
  if (!server)
    my_error (_("Could not start mirror server.\n"));

  if (rendezvous)
    {
      mtp_server_set_type (server, MTP_SERVER_TYPE_BOTH);
      server->is_global = TRUE;
    }

  server->verbose = verbose;
  if (verbose)
    {
      g_print (_("MTP mirror server started on port %d\n"), server->server->port);

      g_timeout_add (DUMP_SERVER_TIMEOUT, dump_server_cb, NULL);
      dump_server_cb (NULL);
    }

  if (!path)
    mirror = mtp_mirror (server, url, read_func, NULL, length);
  else
    mirror = mtp_mirror_path (server, url, filename);

  if (!mirror)
    g_warning ("Could not create mirror\n");

  /* ******************** */
  /* Start the main loop  */

  main_loop = g_main_new (FALSE);
  g_main_run (main_loop);

  exit (EXIT_SUCCESS);

 usage:
  poptPrintUsage (ctx, stdout, 0);
  exit (exit_status);

 help:
  g_print (_("`mtpmirror' - MTP Mirror Server (NOT USEFUL - FOR TESTING ONLY)\n"));
  poptPrintHelp (ctx, stdout, 0);
  g_print (_("\n"
	     "Report bugs to <jm-dev@umich.edu>\n"));
  exit (exit_status);

 version:
  g_print (_("mtpmirror %s\n"
	     "Written by David A. Helder\n"
	     "\n"
	     "Copyright (C) 2000-2001 The Regents of the University of Michigan\n"
	     "This is free software; see the source for copying conditions.  There is NO\n"
	     "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"),
	     VERSION);
  exit (exit_status);
}


/* **************************************** */

static gboolean
dump_server_cb (gpointer data)
{
  time_t t;
  char* time_str;

  t = time(NULL);
  t = mktime(gmtime(&t));
  time_str = ctime(&t);		/* don't free string */
  time_str[strlen(time_str) - 1] = '\0';

  g_print ("time: %s UTC\n", time_str);
  mtp_server_print (server, stderr);

  return TRUE;
}


static guint
read_func (MtpLocalMirror* mirror, guint offset, gchar* buf, guint blength,
	   gpointer user_data)
{
  g_return_val_if_fail (mirror, 0);
  g_return_val_if_fail (buf, 0);

  MTPP (1, "read_func offset = %d, length = %d\n", offset, blength);

  /* Load file if not loaded */
  if (!file)
    {
      MTPP (1, "LOADING FILE\n");

      /* Open file */
      file = fopen (filename, "r");
      if (!file)
	goto error;

      /* Verify length */
      if (length != file_size (filename))
	goto error;

      buffer = mmap (NULL, length, PROT_READ, MAP_PRIVATE, fileno(file), 0);
      if (buffer == NULL || ((int) buffer == -1))
	goto error;

      /* Get SHA if we don't have it */
      if (!sha_mirror)
	{
	  GSHA* sha;
	  gchar* str;
	  GURL* sha_url;

	  sha = gnet_sha_new (buffer, length);
	  str = gnet_sha_get_string (sha);
	  sha_url = gnet_url_clone (url);
	  gnet_url_set_resource (sha_url, str);

	  sha_mirror = mtp_mirror (server, sha_url, read_func, NULL, length);
	  if (!sha_mirror)
	    g_warning (_("Unable to mirror file by SHA.\n"));

	  gnet_sha_delete (sha);
	  g_free (str);
	  gnet_url_delete (sha_url);
	}
    }

  if (!buffer)
    goto error;

  if (offset + blength > length)
    goto error;

  /* Reset timer */
  if (timer)
    g_source_remove (timer);
  timer = g_timeout_add (timeout * 1000, timeout_func, NULL);


  memcpy (buf, &buffer[offset], blength);
  return blength;

 error:
  MTPP (1, "error\n");

  if (file)
    fclose (file);
  file = NULL;
  return 0;
}


static gboolean
timeout_func (gpointer p)
{
  g_return_val_if_fail (buffer, FALSE);
  g_return_val_if_fail (file,   FALSE);

  munmap (buffer, length);
  buffer = NULL;

  fclose (file);
  file = NULL;

  return FALSE;
}


syntax highlighted by Code2HTML, v. 0.9.1