/* 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,
                    const 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 = rel_table_nil;
      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, rel_peek_str (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 (const t_uchar * name,
                           const t_uchar * uri,
                           const t_uchar * version,
                           const 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);
}


int 
arch_valid_uri (const 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 (const 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,
                        const 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,
                          const 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,
                      const t_uchar * path)
{
  return pfs->vtable->file_exists (pfs, path);
}


int
arch_pfs_get_file (struct arch_pfs_session * pfs,
                   int out_fd,
                   const 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,
                   const 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,
                     const 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,
                const 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,
                 const t_uchar * from,
                 const 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,
                 const t_uchar * path)
{
  return pfs->vtable->is_dir (pfs, path);
}


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


int
arch_pfs_rm (struct arch_pfs_session * pfs,
             const 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,
                              const 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 = rel_table_nil;
  rel_table files_after = rel_table_nil;

  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 (rel_n_records (files_before))
        rel_sort_table_by_field (0, files_before, 0);

      for (x = 0; x < rel_n_records (files_before); ++x)
        {
          const t_uchar * file_name;

          file_name = rel_peek_str (files_before, x, 0);

          if (   (('.' != file_name[0]) && (',' != file_name[0]))
              || !str_cmp (".archive-version", file_name))
            {
              safe_printfmt (tmp_fd, "%s\r\n", rel_peek_str (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 (rel_n_records (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 = rel_table_nil;
          files_after = rel_table_nil;
        }
      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 ((!rel_n_records (a) && rel_n_records (b)) || (rel_n_records (a) && !rel_n_records (b)))
    return 0;

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

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

      if (str_cmp (rel_peek_str (a, ax, 0), rel_peek_str (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)
 */
