/* pfs.c:
 *
 ****************************************************************
 * Copyright (C) 2003 Tom Lord
 *
 * See the file "COPYING" for further information about
 * the copyright and warranty status of this work.
 */


#include "hackerlab/char/str.h"
#include "hackerlab/fs/file-names.h"
#include "hackerlab/vu/safe.h"
#include "tla/libfsutils/tmp-files.h"
#include "tla/libarch/archive.h"
#include "tla/libarch/archives.h"
#include "tla/libarch/pfs-dav.h"
#include "tla/libarch/pfs-sftp.h"
#include "tla/libarch/pfs-ftp.h"
#include "tla/libarch/pfs-fs.h"
#include "tla/libarch/pfs.h"


/* __STDC__ prototypes for static functions */
static int dot_listings_equal (rel_table a, rel_table b);



void
arch_pfs_rmrf_file (struct arch_pfs_session * pfs, t_uchar * path)
{
  int dir_check;

  dir_check = arch_pfs_is_dir (pfs, path);

  if (!dir_check)
    {
      arch_pfs_rm (pfs, path, 1);
    }
  else if (dir_check > 0)
    {
      rel_table contents = 0;
      int x;

      contents = arch_pfs_directory_files (pfs, path, 1);

      for (x = 0; x < rel_n_records (contents); ++x)
        {
          t_uchar * subpath = 0;

          subpath = file_name_in_vicinity (0, path, contents[x][0]);
          arch_pfs_rmrf_file (pfs, subpath);

          lim_free (0, subpath);
        }

      arch_pfs_rmdir (pfs, path, 1);

      rel_free_table (contents);
    }

  /* dir_check < 0 indicates an error, presumably ENOENT */
}




void
arch_pfs_pfs_make_archive (t_uchar * name, t_uchar * uri, t_uchar * version, t_uchar *mirror_of, int dot_listing_lossage, int signed_archive)
{
  struct arch_pfs_sftp_session * p;
  struct arch_pfs_session *pfs;
  t_uchar * meta_info_path = 0;
  t_uchar * name_file_path = 0;
  t_uchar * archive_version_path = 0;
  t_uchar * tmp_path;
  t_uchar * archdir;
  t_uchar * hosturi;
  int name_fd;
  int version_fd;

  /* TODO: handle URIs like 'sftp://amit@cvs:~f' semi-reasonably
   * The following code can mangle URIs, since it doesn't ignore the protocol
   * part. IMHO, it doesn't belong in a general function like this
   * one. - aaron.bentley@utoronto.ca
   */ 
  tmp_path = file_name_from_directory (0, uri); /* remove any trailing '/' */
  archdir = file_name_tail (0, tmp_path);
  hosturi = file_name_directory (0, tmp_path);
  lim_free (0, tmp_path);

  pfs = arch_pfs_connect (hosturi);
  p = (struct arch_pfs_sftp_session *)pfs;
  pfs->vtable->mkdir (pfs, archdir, 0777, 0);

  meta_info_path = file_name_in_vicinity (0, archdir, "=meta-info");
  archive_version_path = file_name_in_vicinity (0, archdir, ".archive-version");
  name_file_path = file_name_in_vicinity (0, meta_info_path, "name");

  pfs->vtable->mkdir (pfs, meta_info_path, 0777, 0);

  if (mirror_of)
    {
      tmp_path = tmp_file_name ("/tmp", ",,pfs-sftp-file-contents");
      name_fd = safe_open (tmp_path, O_RDWR | O_CREAT | O_EXCL, 0000);
      safe_unlink (tmp_path);
      safe_printfmt (name_fd, "%s\n", mirror_of);

      safe_lseek (name_fd, (off_t)0, SEEK_SET);
      pfs->vtable->put_file (pfs, name_file_path, 0666, name_fd, 0);

      name_file_path = file_name_in_vicinity (0, meta_info_path, "mirror");
      safe_lseek (name_fd, (off_t)0, SEEK_SET);
      pfs->vtable->put_file (pfs, name_file_path, 0666, name_fd, 0);

      safe_close (name_fd);
    }
  else
    {
      tmp_path = tmp_file_name ("/tmp", ",,pfs-sftp-file-contents");
      name_fd = safe_open (tmp_path, O_RDWR | O_CREAT | O_EXCL, 0000);
      safe_unlink (tmp_path);
      safe_printfmt (name_fd, "%s\n", name);
      safe_lseek (name_fd, (off_t)0, SEEK_SET);
      pfs->vtable->put_file (pfs, name_file_path, 0666, name_fd, 0);
      safe_close (name_fd);
    }

  if (dot_listing_lossage)
    {
      t_uchar * tmp_blowage_path = 0;
      int tmp_blowage_fd = 0;
      t_uchar * http_blows_path = 0;

      tmp_blowage_path = tmp_file_name ("/tmp", ",,pfs-http-blows");
      tmp_blowage_fd = safe_open (tmp_blowage_path, O_RDWR | O_CREAT | O_EXCL, 0000);
      safe_unlink (tmp_blowage_path);
      safe_printfmt (tmp_blowage_fd, "it sure does\n");

      http_blows_path = file_name_in_vicinity (0, meta_info_path, "http-blows");
      safe_lseek (tmp_blowage_fd, (off_t)0, SEEK_SET);
      pfs->vtable->put_file (pfs, http_blows_path, 0666, tmp_blowage_fd, 0);

      lim_free (0, tmp_blowage_path);
      safe_close (tmp_blowage_fd);
      lim_free (0, http_blows_path);
    }

  if (signed_archive)
    {
      t_uchar * tmp_signage_path = 0;
      int tmp_signage_fd = 0;
      t_uchar * http_signed_path = 0;

      tmp_signage_path = tmp_file_name ("/tmp", ",,pfs-signed-archive");
      tmp_signage_fd = safe_open (tmp_signage_path, O_RDWR | O_CREAT | O_EXCL, 0000);
      safe_unlink (tmp_signage_path);
      safe_printfmt (tmp_signage_fd, "system cracking is (nearly always) lame\n");

      http_signed_path = file_name_in_vicinity (0, meta_info_path, "signed-archive");
      safe_lseek (tmp_signage_fd, (off_t)0, SEEK_SET);
      pfs->vtable->put_file (pfs, http_signed_path, 0666, tmp_signage_fd, 0);

      lim_free (0, tmp_signage_path);
      safe_close (tmp_signage_fd);
      lim_free (0, http_signed_path);
    }


  tmp_path = tmp_file_name ("/tmp", ",,pfs-sftp-file-contents");
  version_fd = safe_open (tmp_path, O_RDWR | O_CREAT | O_EXCL, 0000);
  safe_unlink (tmp_path);
  safe_printfmt (version_fd, "%s\n", version);
  safe_lseek (version_fd, (off_t)0, SEEK_SET);
  pfs->vtable->put_file (pfs, archive_version_path, 0666, name_fd, 0);
  safe_close (version_fd);

  if (dot_listing_lossage)
    {
      arch_pfs_update_listing_file (pfs, meta_info_path);
      arch_pfs_update_listing_file (pfs, archdir);
    }

  arch_set_archive_location (name, uri, 0, ARCH_REG_FAIL_NORM);

  lim_free (0, meta_info_path);
  lim_free (0, name_file_path);
  lim_free (0, archive_version_path);
  lim_free (0, hosturi);
  lim_free (0, archdir);
}

extern int 
arch_valid_uri (t_uchar * uri)
{
  int answer = 1;
  if (arch_pfs_sftp_supported_protocol (uri))
    {
      t_uchar * user = 0, * hostname = 0, * port = 0;
      char * path = 0;
      answer = (arch_pfs_sftp_parse_uri (&user, &hostname, &port, &path, uri) == 0);
      lim_free (0, user);
      lim_free (0, hostname);
      lim_free (0, port);
      lim_free (0, path);
    }
  return answer;
}

struct arch_pfs_session *
arch_pfs_connect (t_uchar * uri)
{
  struct arch_pfs_session * answer = 0;

  if (arch_pfs_dav_supported_protocol (uri))
    {
      return arch_pfs_dav_connect (uri);
    }
  else if (arch_pfs_sftp_supported_protocol (uri))
    {
      return arch_pfs_sftp_connect (uri);
    }
  else if (arch_pfs_ftp_supported_protocol (uri))
    {
      return arch_pfs_ftp_connect (uri);
    }
  else
    {
      return arch_pfs_fs_connect (uri);
    }

  return answer;
}

t_uchar *
arch_pfs_file_contents (struct arch_pfs_session * pfs, t_uchar * path, int soft_errors)
{
  return pfs->vtable->file_contents (pfs, path, soft_errors);
}

rel_table
arch_pfs_directory_files (struct arch_pfs_session * pfs, t_uchar * path, int soft_errors)
{
  return pfs->vtable->directory_files (pfs, path, soft_errors);
}

int
arch_pfs_file_exists (struct arch_pfs_session * pfs, t_uchar * path)
{
  return pfs->vtable->file_exists (pfs, path);
}

int
arch_pfs_get_file (struct arch_pfs_session * pfs, int out_fd, t_uchar * path, int soft_errors)
{
  return pfs->vtable->get_file (pfs, out_fd, path, soft_errors);
}

int
arch_pfs_put_file (struct arch_pfs_session * pfs, t_uchar * path, mode_t perms, int in_fd, int soft_errors)
{
  return pfs->vtable->put_file (pfs, path, perms, in_fd, soft_errors);
}

int
arch_pfs_put_atomic (struct arch_pfs_session * pfs, t_uchar ** errstr, t_uchar * path, mode_t perms, int in_fd, int replace, int soft_errors)
{
  int result = 0;
  t_uchar * tmp_dir = file_name_directory_file (0, path);
  t_uchar * tmp_file = tmp_file_name (tmp_dir, ",,upload-tmp");

  arch_pfs_rmrf_file (pfs, tmp_file);
  if (!arch_pfs_put_file (pfs, tmp_file, perms, in_fd, soft_errors))
    {
      if (replace)
	  arch_pfs_rmrf_file (pfs, path);
      if (arch_pfs_rename (pfs, errstr, tmp_file, path, soft_errors))
	result = -1;
    }
  else
    result = -1;
  lim_free (0, tmp_dir);
  lim_free (0, tmp_file);
  return result;
}

int
arch_pfs_mkdir (struct arch_pfs_session * pfs, t_uchar * path, mode_t perms, int soft_errors)
{
  return pfs->vtable->mkdir (pfs, path, perms, soft_errors);
}

int
arch_pfs_rename (struct arch_pfs_session * pfs, t_uchar ** errstr, t_uchar * from, t_uchar * to, int soft_errors)
{
  return pfs->vtable->rename (pfs, errstr, from, to, soft_errors);
}

int
arch_pfs_is_dir (struct arch_pfs_session * pfs, t_uchar * path)
{
  return pfs->vtable->is_dir (pfs, path);
}

int
arch_pfs_rmdir (struct arch_pfs_session * pfs, t_uchar * path, int soft_errors)
{
  return pfs->vtable->rmdir (pfs, path, soft_errors);
}

int
arch_pfs_rm (struct arch_pfs_session * pfs, t_uchar * path, int soft_errors)
{
  return pfs->vtable->rm (pfs, path, soft_errors);
}

void
arch_pfs_update_listing_file (struct arch_pfs_session * session, t_uchar * dir)
{
  t_uchar * tmp_path = 0;
  int tmp_fd = 0;
  t_uchar * dot_listing_path = 0;
  t_uchar * dot_listing_tmp = 0;
  rel_table files_before = 0;
  rel_table files_after = 0;

  tmp_path = tmp_file_name ("/tmp", ",,pfs-dot-listing");
  tmp_fd = safe_open (tmp_path, O_RDWR | O_CREAT | O_EXCL, 0000);
  safe_unlink (tmp_path);

  dot_listing_path = file_name_in_vicinity (0, dir, ".listing");
  dot_listing_tmp = archive_tmp_file_name (dir, ",,dot-listing");

  while (1)
    {
      int x;

      files_before = arch_pfs_directory_files (session, dir, 0);
      if (files_before) rel_sort_table_by_field (0, files_before, 0);

      for (x = 0; x < rel_n_records (files_before); ++x)
        {
          if (('.' != files_before[x][0][0]) && (',' != files_before[x][0][0]))
            {
              safe_printfmt (tmp_fd, "%s\r\n", files_before[x][0]);
            }
        }

      safe_lseek (tmp_fd, (off_t)0, SEEK_SET);
      arch_pfs_put_atomic (session, 0, dot_listing_path, 0444, tmp_fd, 1, 0);

      files_after = arch_pfs_directory_files (session, dir, 0);
      if (files_after) rel_sort_table_by_field (0, files_after, 0);

      if (!dot_listings_equal (files_before, files_after))
        {
          safe_ftruncate (tmp_fd, (long)0);
          rel_free_table (files_before);
          rel_free_table (files_after);
          files_before = 0;
          files_after = 0;
        }
      else
        {
          break;
        }
    }

  lim_free (0, tmp_path);
  safe_close (tmp_fd);
  lim_free (0, dot_listing_path);
  lim_free (0, dot_listing_tmp);
  rel_free_table (files_before);
  rel_free_table (files_after);
}


static int
dot_listings_equal (rel_table a, rel_table b)
{
  int ax;
  int bx;

  if ((!a && b) || (a && !b))
    return 0;

  ax = 0;
  bx = 0;
  while ((ax < rel_n_records (a))  || (bx < rel_n_records (b)))
    {
      if ((ax < rel_n_records (a)) && (a[ax][0][0] == '.'))
        {
          ++ax;
          continue;
        }
      if ((ax < rel_n_records (a)) && (a[ax][0][0] == ','))
        {
          ++ax;
          continue;
        }
      if ((bx < rel_n_records (b)) && (b[bx][0][0] == '.'))
        {
          ++bx;
          continue;
        }
      if ((bx < rel_n_records (b)) && (b[bx][0][0] == ','))
        {
          ++bx;
          continue;
        }

      if ((ax == rel_n_records (a)) || (bx == rel_n_records (b)))
        return 0;

      if (str_cmp (a[ax][0], b[bx][0]))
        return 0;

      ++ax;
      ++bx;
    }

  if ((ax != rel_n_records (a)) || (bx != rel_n_records (b)))
    return 0;

  return 1;
}



/* tag: Tom Lord Thu Jun  5 15:12:22 2003 (pfs.c)
 */
