/* ------------------------------------------------------------ */
/*
HTTrack Website Copier, Offline Browser for Windows and Unix
Copyright (C) 1998 Xavier Roche and other contributors

SPDX-License-Identifier: GPL-3.0-or-later

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 3 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, see <http://www.gnu.org/licenses/>.

Please visit our Website: http://www.httrack.com
*/

/* ------------------------------------------------------------ */
/* File: ProxyTrack, httrack cache-based proxy                  */
/* Author: Xavier Roche                                         */
/* ------------------------------------------------------------ */

#ifndef WEBHTTRACK_PROXYTRACK
#define WEBHTTRACK_PROXYTRACK

/* Version */
#define PROXYTRACK_VERSION "0.5"

/* Store manager */
#include "../minizip/mztools.h"
#include "store.h"
#include "../htsdate.h"

#include <sys/stat.h>
#ifdef _WIN32
#include <sys/utime.h>
#else
#include <utime.h>
#endif
#ifndef _WIN32
#include <pthread.h>
#endif
#include <stdarg.h>

/* generic */

int proxytrack_main(char *proxyAddr, int proxyPort, char *icpAddr, int icpPort,
                    PT_Indexes index);

/* Spaces: CR,LF,TAB,FF */
#define  is_space(c)      ( ((c)==' ') || ((c)=='\"') || ((c)==10) || ((c)==13) || ((c)==9) || ((c)==12) || ((c)==11) || ((c)=='\'') )
#define  is_realspace(c)  ( ((c)==' ')                || ((c)==10) || ((c)==13) || ((c)==9) || ((c)==12) || ((c)==11)                )
#define  is_taborspace(c) ( ((c)==' ')                                          || ((c)==9)                             )
#define  is_quote(c)      (               ((c)=='\"')                                                    || ((c)=='\'') )
#define  is_retorsep(c)   (                              ((c)==10) || ((c)==13) || ((c)==9)                                          )

/* Static definitions */

/* Log one line; a NULL severity discards it. */
HTS_UNUSED HTS_PRINTF_FUN(2, 0) static void proxytrack_vprint_log(
    const char *severity, const char *format, va_list args) {
  if (severity != NULL) {
    const int error = errno;
    FILE *const fp = stderr;

    fprintf(fp, " * %s: ", severity);
    (void) vfprintf(fp, format, args);
    fputs("\n", fp);
    fflush(fp);
    errno = error;
  }
}

HTS_UNUSED
HTS_PRINTF_FUN(2, 3)

static void proxytrack_print_log(const char *severity, const char *format,
                                 ...) {
  if (severity != NULL) {
    va_list args;

    va_start(args, format);
    proxytrack_vprint_log(severity, format, args);
    va_end(args);
  }
}

#define CRITICAL "critical"
#define WARNING "warning"
#define LOG "log"
#if defined(_DEBUG) || defined(DEBUG)
#define DEBUG "debug"
#else
#define DEBUG NULL
#endif

/* Header for generated pages */
#define PROXYTRACK_COMMENT_HEADER \
	"<!-- Generated by ProxyTrack " PROXYTRACK_VERSION " -->\r\n" \
	"<!-- This is an add-on for HTTrack " HTTRACK_VERSIONID " -->\r\n"

/* See IE "feature" (MSKB Q294807) */
#define DISABLE_IE_FRIENDLY_HTTP_ERROR_MESSAGES											\
	"<!-- Start Disable IE Friendly HTTP Error Messages -->\r\n"			\
	"<!-- _-._.--._._-._.--._._-._.--._._-._.--._._-._.--._. -->\r\n" \
	"<!-- _-._.--._._-._.--._._-._.--._._-._.--._._-._.--._. -->\r\n" \
	"<!-- _-._.--._._-._.--._._-._.--._._-._.--._._-._.--._. -->\r\n" \
	"<!-- _-._.--._._-._.--._._-._.--._._-._.--._._-._.--._. -->\r\n" \
	"<!-- _-._.--._._-._.--._._-._.--._._-._.--._._-._.--._. -->\r\n" \
	"<!-- _-._.--._._-._.--._._-._.--._._-._.--._._-._.--._. -->\r\n" \
	"<!-- _-._.--._._-._.--._._-._.--._._-._.--._._-._.--._. -->\r\n" \
	"<!-- _-._.--._._-._.--._._-._.--._._-._.--._._-._.--._. -->\r\n" \
	"<!-- _-._.--._._-._.--._._-._.--._._-._.--._._-._.--._. -->\r\n" \
	"<!-- _-._.--._._-._.--._._-._.--._._-._.--._._-._.--._. -->\r\n" \
	"<!-- End Disable IE Friendly HTTP Error Messages -->\r\n"

/* Same contract as hts_gethome(): proxytrack does not link libhttrack */
HTS_UNUSED static const char *gethomedir(void) {
  const char *home = getenv("HOME");

  /* An empty $HOME would resolve a relative path against the root */
  return strnotempty(home) ? home : ".";
}

HTS_UNUSED static int linput(FILE * fp, char *s, int max) {
  int c;
  int j = 0;

  do {
    c = fgetc(fp);
    if (c != EOF) {
      switch (c) {
      case 13:
        break;                  // sauter CR
      case 10:
        c = -1;
        break;
      case 0:
      case 9:
      case 12:
        break;                  // sauter ces caractères
      default:
        s[j++] = (char) c;
        break;
      }
    }
  } while((c != -1) && (c != EOF) && (j < (max - 1)));
  s[j] = '\0';
  return j;
}

HTS_UNUSED static int link_has_authority(const char *lien) {
  const char *a = lien;

  if (isalpha((const unsigned char) *a)) {
    // Skip scheme?
    while(isalpha((const unsigned char) *a))
      a++;
    if (*a == ':')
      a++;
    else
      return 0;
  }
  if (strncmp(a, "//", 2) == 0)
    return 1;
  return 0;
}

HTS_UNUSED static const char *jump_protocol(const char *source) {
  int p;

  // scheme
  // "Comparisons of scheme names MUST be case-insensitive" (RFC2616)
  if ((p = strfield(source, "http:")))
    source += p;
  else if ((p = strfield(source, "ftp:")))
    source += p;
  else if ((p = strfield(source, "https:")))
    source += p;
  else if ((p = strfield(source, "file:")))
    source += p;
  // net_path
  if (strncmp(source, "//", 2) == 0)
    source += 2;
  return source;
}

HTS_UNUSED static const char *strrchr_limit(const char *s, char c, const char *limit) {
  if (limit == NULL) {
    char *p = strrchr(s, c);

    return p ? (p + 1) : NULL;
  } else {
    char *a = NULL, *p;

    for(;;) {
      p = strchr((a) ? a : s, c);
      if ((p >= limit) || (p == NULL))
        return a;
      a = p + 1;
    }
  }
}

HTS_UNUSED static const char *jump_protocol_and_auth(const char *source) {
  const char *a, *trytofind;

  if (strcmp(source, "file://") == 0)
    return source;
  a = jump_protocol(source);
  trytofind = strrchr_limit(a, '@', strchr(a, '/'));
  return (trytofind != NULL) ? trytofind : a;
}

#ifndef min
#define min(a,b) ((a)>(b)?(b):(a))
#endif
#ifndef max
#define max(a,b) ((a)>(b)?(a):(b))
#endif
HTS_UNUSED static int linput_trim(FILE * fp, char *s, int max) {
  int rlen = 0;
  char *const ls = (char *) malloc(max + 1);

  s[0] = '\0';
  if (ls) {
    char *a;

    // lire ligne
    rlen = linput(fp, ls, max);
    if (rlen) {
      // sauter espaces et tabs en fin
      while((rlen > 0) && is_realspace(ls[max(rlen - 1, 0)]))
        ls[--rlen] = '\0';
      // sauter espaces en début
      a = ls;
      while((rlen > 0) && ((*a == ' ') || (*a == '\t'))) {
        a++;
        rlen--;
      }
      if (rlen > 0) {
        memcpy(s, a, rlen);     // can copy \0 chars
        s[rlen] = '\0';
      }
    }
    //
    free(ls);
  }
  return rlen;
}

#ifndef S_ISREG
#define S_ISREG(m) ((m) & _S_IFREG)
#endif
HTS_UNUSED static int fexist(char *s) {
  struct stat st;

  memset(&st, 0, sizeof(st));
  if (stat(s, &st) == 0) {
    if (S_ISREG(st.st_mode)) {
      return 1;
    }
  }
  return 0;
}

HTS_UNUSED static struct tm PT_GetTime(time_t t) {
  struct tm tmbuf;

  if (!hts_gmtime(t, &tmbuf)) {
    /* an all-zero tm has tm_mday == 0, which the ARC date field prints as a
       day of "00"; the epoch is the conventional "date unknown" */
    memset(&tmbuf, 0, sizeof(tmbuf));
    tmbuf.tm_year = 70;
    tmbuf.tm_mday = 1;
  }
  return tmbuf;
}
HTS_UNUSED static int set_filetime(const char *file, struct tm *tm_time) {
  struct utimbuf tim;

  tim.actime = tim.modtime = timegm(tm_time);
  return utime(file, &tim);
}
HTS_UNUSED static int set_filetime_time_t(const char *file, time_t t) {
  if (t != (time_t) 0 && t != (time_t) - 1) {
    struct tm tm = PT_GetTime(t);

    return set_filetime(file, &tm);
  }
  return -1;
}
HTS_UNUSED static int set_filetime_rfc822(const char *file, const char *date) {
  struct tm buffer;
  struct tm *tm_s = convert_time_rfc822(&buffer, date);

  if (tm_s) {
    return set_filetime(file, tm_s);
  } else
    return -1;
}

#endif
