/*
 * dbcompat.c -- part of filesys.mod
 *   Compability functions to convert older DBs to the newest version.
 *
 * Written for filedb3 by Fabian Knittel <fknittel@gmx.de>
 *
 * $Id: dbcompat.c,v 1.19 2006-03-28 02:35:51 wcc Exp $
 */
/*
 * Copyright (C) 1997 Robey Pointer
 * Copyright (C) 1999 - 2006 Eggheads Development Team
 *
 * 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.
 */

/* Convert '.files' db to newest db. Returns 1 if a valid file is
 * found and could be converted, 0 in all other cases.
 *
 * '.files' is a text file which contains file records built up in the
 * following way:
 *      '<filename> <nick> <tm> <gots>\n'
 *      '- <comment>\n'
 *      '- <comment>\n'
 *      ...
 */
static int convert_old_files(char *path, char *newfiledb)
{
  FILE *f, *fdb;
  char *s, *fn, *nick, *tm, *s1;
  filedb_entry *fdbe = NULL;
  int in_file = 0, i;
  struct stat st;

  s = nmalloc(strlen(path) + 8);
  sprintf(s, "%s/.files", path);
  f = fopen(s, "r");
  my_free(s);
  if (f == NULL)
    return 0;

  fdb = fopen(newfiledb, "w+b");
  if (!fdb) {
    putlog(LOG_MISC, "(!) Can't create filedb in %s", newfiledb);
    fclose(f);
    return 0;
  }
  lockfile(fdb);
  lockfile(f);
  filedb_initdb(fdb);

  putlog(LOG_FILES, "*", FILES_CONVERT, path);
  /* Scan contents of .files and painstakingly create .filedb entries */
  while (!feof(f)) {
    s = nmalloc(121);
    s1 = s;
    fgets(s, 120, f);
    if (s[strlen(s) - 1] == '\n')
      s[strlen(s) - 1] = 0;
    if (!feof(f)) {
      fn = newsplit(&s1);
      rmspace(fn);
      if ((fn[0]) && (fn[0] != ';') && (fn[0] != '#')) {
        /* Not comment */
        if (fn[0] == '-') {
          /* Adjust comment for current file */
          if (in_file && fdbe) {
            rmspace(s);
            if (fdbe->desc) {
              fdbe->desc = nrealloc(fdbe->desc,
                                    strlen(fdbe->desc) + strlen(s) + 2);
              strcat(fdbe->desc, "\n");
            } else
              fdbe->desc = nmalloc(strlen(s) + 2);
            strcat(fdbe->desc, s);
          }
        } else {
          if (fdbe) {
            /* File pending. Write to DB */
            filedb_addfile(fdb, fdbe);
            free_fdbe(&fdbe);
          }
          fdbe = malloc_fdbe();
          in_file = 1;
          nick = newsplit(&s1);
          rmspace(nick);
          tm = newsplit(&s1);
          rmspace(tm);
          rmspace(s1);
          i = strlen(fn) - 1;
          if (fn[i] == '/')
            fn[i] = 0;
          malloc_strcpy(fdbe->filename, fn);
          malloc_strcpy(fdbe->uploader, nick);
          fdbe->gots = atoi(s1);
          fdbe->uploaded = atoi(tm);
          sprintf(s, "%s/%s", path, fn);
          if (stat(s, &st) == 0) {
            /* File is okay */
            if (S_ISDIR(st.st_mode)) {
              fdbe->stat |= FILE_DIR;
              if (nick[0] == '+') {
                char x[100];

                /* Only do global flags, it's an old one */
                struct flag_record fr = { FR_GLOBAL, 0, 0, 0, 0, 0 };

                break_down_flags(nick + 1, &fr, NULL);
                build_flags(x, &fr, NULL);
                /* We only want valid flags */
                malloc_strcpy(fdbe->flags_req, x);
              }
            }
            fdbe->size = st.st_size;
          } else
            in_file = 0;        /* skip */
        }
      }
    }
    my_free(s);
  }
  if (fdbe) {
    /* File pending. Write to DB */
    filedb_addfile(fdb, fdbe);
    free_fdbe(&fdbe);
  }
  fseek(fdb, 0L, SEEK_END);
  unlockfile(f);
  unlockfile(fdb);
  fclose(fdb);
  fclose(f);
  return 1;
}

/* Reads file DB v1 entries from fdb_s and saves them to fdb_t in
 * v3 format.
 */
static void convert_version1(FILE *fdb_s, FILE *fdb_t)
{
  filedb1 fdb1;

  fseek(fdb_s, 0L, SEEK_SET);
  while (!feof(fdb_s)) {
    fread(&fdb1, sizeof(filedb1), 1, fdb_s);
    if (!feof(fdb_s)) {
      if (!(fdb1.stat & FILE_UNUSED)) {
        filedb_entry *fdbe = malloc_fdbe();

        fdbe->stat = fdb1.stat;
        if (fdb1.filename[0])
          malloc_strcpy(fdbe->filename, fdb1.filename);
        if (fdb1.desc[0])
          malloc_strcpy(fdbe->desc, fdb1.desc);
        if (fdb1.uploader[0])
          malloc_strcpy(fdbe->uploader, fdb1.uploader);
        if (fdb1.flags_req[0])
          malloc_strcpy(fdbe->flags_req, fdb1.flags_req);
        fdbe->uploaded = fdb1.uploaded;
        fdbe->size = fdb1.size;
        fdbe->gots = fdb1.gots;
        if (fdb1.sharelink[0])
          malloc_strcpy(fdbe->sharelink, fdb1.sharelink);
        filedb_addfile(fdb_s, fdbe);
        free_fdbe(&fdbe);
      }
    }
  }
}

/* Reads file DB v2 entries from fdb_s and saves them to fdb_t in
 * v3 format.
 */
static void convert_version2(FILE *fdb_s, FILE *fdb_t)
{
  filedb2 fdb2;

  fseek(fdb_s, 0L, SEEK_SET);
  while (!feof(fdb_s)) {
    fread(&fdb2, sizeof(filedb2), 1, fdb_s);
    if (!feof(fdb_s)) {
      if (!(fdb2.stat & FILE_UNUSED)) {
        filedb_entry *fdbe = malloc_fdbe();

        fdbe->stat = fdb2.stat;
        if (fdb2.filename[0])
          malloc_strcpy(fdbe->filename, fdb2.filename);
        if (fdb2.desc[0])
          malloc_strcpy(fdbe->desc, fdb2.desc);
        if (fdb2.chname[0])
          malloc_strcpy(fdbe->chan, fdb2.chname);
        if (fdb2.uploader[0])
          malloc_strcpy(fdbe->uploader, fdb2.uploader);
        if (fdb2.flags_req[0])
          malloc_strcpy(fdbe->flags_req, fdb2.flags_req);
        fdbe->uploaded = fdb2.uploaded;
        fdbe->size = fdb2.size;
        fdbe->gots = fdb2.gots;
        if (fdb2.sharelink[0])
          malloc_strcpy(fdbe->sharelink, fdb2.sharelink);
        filedb_addfile(fdb_t, fdbe);
        free_fdbe(&fdbe);
      }
    }
  }
}

/* Converts old versions of the filedb to the newest. Returns 1 if all went
 * well and otherwise 0. The new db is first written to a temporary place
 * and then moved over to the original db's position.
 *
 * Note: Unfortunately there is a small time-frame where aren't locking the
 *       DB, but want to replace it with a new one, using movefile().
 *       TODO: Copy old db to tmp file and then build the new db directly
 *             in the original file. This solves the tiny locking problem.
 *
 * Also remember to check the returned *fdb_s on failure, as it could be
 * NULL.
 */
static int convert_old_db(FILE ** fdb_s, char *filedb)
{
  filedb_top fdbt;
  FILE *fdb_t;
  int ret = 0;                  /* Default to 'failure' */

  filedb_readtop(*fdb_s, &fdbt);
  /* Old DB version? */
  if (fdbt.version > 0 && fdbt.version < FILEDB_VERSION3) {
    char *tempdb;

    putlog(LOG_MISC, "*", "Converting old filedb %s to newest format.",
           filedb);
    /* Create temp DB name */
    tempdb = nmalloc(strlen(filedb) + 5);
    simple_sprintf(tempdb, "%s-tmp", filedb);

    fdb_t = fopen(tempdb, "w+b");       /* Open temp DB         */
    if (fdb_t) {
      filedb_initdb(fdb_t);     /* Initialise new DB    */

      /* Convert old database to new one, saving
       * in temporary db file
       */
      if (fdbt.version == FILEDB_VERSION1)
        convert_version1(*fdb_s, fdb_t);        /* v1 -> v3             */
      else
        convert_version2(*fdb_s, fdb_t);        /* v2 -> v3             */

      unlockfile(*fdb_s);
      fclose(fdb_t);
      fclose(*fdb_s);

      /* Move over db to new location */
      if (movefile(tempdb, filedb))
        putlog(LOG_MISC, "*", "(!) Moving file db from %s to %s failed.",
               tempdb, filedb);

      *fdb_s = fopen(filedb, "r+b");    /* Reopen new db        */
      if (*fdb_s) {
        lockfile(*fdb_s);
        /* Now we should have recreated the original situation,
         * with the file pointer just pointing to the new version
         * of the DB instead of the original one.
         */
        ret = 1;
      } else
        putlog(LOG_MISC, "*", "(!) Reopening db %s failed.", filedb);
    }
    my_free(tempdb);
    /* Database already at the newest version? */
  } else if (fdbt.version == FILEDB_VERSION3)
    ret = 1;
  else
    putlog(LOG_MISC, "*", "(!) Unknown db version: %d", fdbt.version);
  if (!ret)
    putlog(LOG_MISC, "*", "Conversion of filedb %s failed.", filedb);
  return ret;
}
