/*
 * This is part of DCD, Dave's CD Player.
 * (C) 1998-2001 David E. Smith <dave@technopagan.org>
 * Released under GNU GPL v2. (See 'COPYING' for details.)
 *
 * Much of this code was shamelessly stolen from a sockets example
 * cooked up by LordVader@DALnet. His code worked; if this doesn't, it's 
 * MY fault, not his. :-) Per his request, here's some more headers...
 */ 

/*
 * Webdump program by Jesse Estum, a.k.a LordVader on DALnet.  darkdarth@yahoo.com
 * Re-distributable under the terms of the GNU General Public Licence version of your choice.
 * This header MUST remain in re-distributed versions.
 */


#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <unistd.h>
#include <string.h>
#include <netdb.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include "dcd.h"
#include "libcdplay.h"
#include "cdindex.h"
#include "version.h"

#define NAMESIZE 80
#define NO_TRACKNAME "Trackname unavailable"
#define REMOTE_SITE "www.cdindex.org"
#define REMOTE_PORT 80
#define NET_BUF_SZ 1024

static char trackname[100][NAMESIZE];
static int cdi_flag;
static int net_flag;
static int cdi_check_matches (FILE *fd);
static int noinfo_disp = 0;

int cdi_status (void) {
  return cdi_flag;
}

static char * fname (void) {
  char *home = getenv("HOME");
  char *ret = malloc(NET_BUF_SZ);
  memset (ret, '\0', sizeof(ret));
  snprintf (ret, NET_BUF_SZ, "%s/%s/%s", home, CDINDEX_HOME, cd_discid());
  return ret;
}

static int cdi_dircheck (void) {
  int staterr, dmode;
  char dirname[NET_BUF_SZ];
  struct stat statbuf;
  #ifdef DEBUG
    fprintf (stderr, "Entering cdi_dircheck()\n");
  #endif
  snprintf(dirname, NET_BUF_SZ, "%s/%s", getenv("HOME"), CDINDEX_HOME);
  #if DEBUG > 1
    fprintf (stderr, "Dirname is %s\n", dirname);
  #endif
  staterr = stat(dirname, &statbuf);
  if (staterr != 0) {
    #ifdef DEBUG
      fprintf (stderr, "staterr is %i, errno is %i (%s)\n",
               staterr, errno, strerror(errno));
    #endif
    return -1;
  }
  dmode = statbuf.st_mode;
  #if DEBUG > 1
    fprintf (stderr, "stage two of dircheck, mode %i\n", dmode);
  #endif
  if ((S_ISDIR((mode_t)dmode))) {
    #ifdef DEBUG
      fprintf (stderr, "S_ISDIR choked out by Tazz\n");
    #endif
    return 0;
  }
  #ifdef DEBUG
    fprintf (stderr, "leaving cdi_dircheck successfully with -1.\n");
  #endif
  return -1;
}

void cdi_noinit (void) {
  cdi_flag = CDI_NONE;
  #ifdef DEBUG
    fprintf (stderr, "Welcome to cdi_noinit().\n");
    fprintf (stderr, "Unable to open CD Index file for this disc. Track names unavailable.\n");
  #endif
}

int cdi_init (int net_toggle) {
  int i, s, fflag = 0;
  int ft = cd_first_track();
  int lt = cd_last_track();
  int dc;
  char trknm[NAMESIZE]; /* XXX bounded, UNSAFE! */
  char cdi_filename[NAMESIZE];
  FILE *fd;
  char *tn;
  cdi_flag = CDI_NONE;

  #ifdef DEBUG
    fprintf (stderr, "Entering cdi_init()\n");
  #endif

  net_flag = net_toggle; /* wheee! */
  snprintf (cdi_filename, NAMESIZE, "%s", fname());

  dc = cdi_dircheck();
  if (dc != 0) {
    cdi_noinit();
    return -1;
  }

  /* There we go. Now maybe the debian people will leave me alone. */

  #if DEBUG > 1
    fprintf (stderr, "fopen()\n");
  #endif
  fd = fopen(cdi_filename, "r");

  if (NULL == fd) {
    #ifdef DEBUG
      fprintf (stderr, "Hm, fd is NULL. Creepy, neh?\n");
    #endif
    fflag = cdi_get_info_net();
    if (fflag == FALSE) {
      cdi_noinit();
      return -1;
    }
    fd = fopen (fname(), "r");
  }

  /* if we get here, we know we have a file there of some sort */

  #if DEBUG > 1
    fprintf (stderr, "We're all the way up to clearerr().\n");
  #endif
  clearerr (fd);
  i = cdi_check_matches (fd);
  if (i != 1) {
    #ifdef DEBUG
      fprintf (stderr, "cdi_check_matches returned %i\n", i);
    #endif
    cdi_noinit();
    return -1;
  }
  fclose(fd);
  fd = fopen (fname(), "r");
  clearerr (fd);

  for ( i=0; i<lt; i++) {
    tn = malloc(NAMESIZE);
    memset (tn, '\n', sizeof(tn));
    snprintf (tn, NAMESIZE, "%s\n", NO_TRACKNAME);
    strncpy (trackname[i], tn, NAMESIZE);
  }

  /* wheee. we've set up our backup. now let's have fun. */

  #ifdef DEBUG
    fprintf (stderr, "cdi_init: entering trackscan loop\n");
  #endif
  while ((!feof(fd)) && (!ferror(fd))) {
    i = 0;
    #ifdef DEBUG
      fprintf (stderr, "Scanning: ");
    #endif
    s = fscanf (fd, "Track%i: ", &i);
    if ((s > 0) && (i >= ft) && (i <= lt)) {
      fgets (trknm, NAMESIZE, fd);
      strncpy (trackname[i-1], trknm, NAMESIZE);
      #ifdef DEBUG
        fprintf (stderr, "%i (%s)\n", i, trknm);
      #endif
    if (feof(fd) || (EOF == s)) break; /* eeeeek! */
    } /* track-name matching loop */
    else {
      fgets(trknm, NAMESIZE, fd); /* clear to newline */
      #ifdef DEBUG
        fprintf (stderr, "failed\n");
      #endif
    }
  }
  #ifdef DEBUG
    fprintf (stderr, "cdi_init: leaving the loop\n");
  #endif 

  fclose (fd);
  cdi_flag = CDI_OK;
  #ifdef DEBUG
    fprintf (stderr, "Leaving cdi_init()\n");
  #endif
  return 0;
}

char* cdi_trackname (int trknum) {
  char *ret = malloc(NAMESIZE);
  #ifdef DEBUG
    fprintf (stderr, "Calling cdi_trackname(%i)\n", trknum);
  #endif
  memset (ret, '\n', sizeof(ret));
  snprintf (ret, NAMESIZE, "%s", trackname[trknum-1]);
  return ret;
}

int cdi_get_info_net (void) {
  int sockfd;
  struct sockaddr_in server_addr;
  struct hostent *h;
  char *l_msg, *l_buffer;
  extern int h_errno;
  FILE *outfile;

  #ifdef DEBUG
    fprintf (stderr, "Entering cdi_get_info_net()\n");
  #endif

  if (net_flag == FALSE) return FALSE;

  l_msg = (char *)malloc (NET_BUF_SZ);
  l_buffer = (char *)malloc (NET_BUF_SZ);

  if ((l_msg == NULL) || (l_buffer == NULL)) {
    #ifdef DEBUG
      fprintf (stderr, "Unable to malloc, bailing\n");
    #endif
    return 1;
  }
  memset (l_buffer, '\0', NET_BUF_SZ);

  /* setup for net stuffz */

  #ifdef DEBUG
    fprintf (stderr, "Entering network setup phase.");
  #endif

  h = gethostbyname (REMOTE_SITE);

  if (h == NULL) {
    #ifdef DEBUG
      fprintf (stderr, "\nh is null, probably a DNS error\n");
    #endif
    return FALSE;
  }

  #ifdef DEBUG
    fprintf (stderr, ".");
  #endif

  sockfd = socket(AF_INET, SOCK_STREAM, 0);
  server_addr.sin_family = AF_INET;
  server_addr.sin_port = htons(REMOTE_PORT);
  #ifdef DEBUG
    fprintf (stderr, ".");
  #endif
  server_addr.sin_addr = *((struct in_addr *)h->h_addr_list[0]);
  memset(server_addr.sin_zero, '\0', sizeof(server_addr.sin_zero));

  #ifdef DEBUG
    fprintf (stderr, ". done.\n");
  #endif

  if (connect(sockfd, (struct sockaddr *)&server_addr,
      sizeof(struct sockaddr)) == -1)  return FALSE;
  
  sprintf (l_msg,
          "GET /cgi-bin/cdi/get.pl?id=%s HTTP/1.0\nUser-Agent: %s\nHost: %s\n\n",
          cd_discid(), VERSION, REMOTE_SITE ); 

  #ifdef DEBUG
    fprintf (stderr, "Request formed:\n%s", l_msg);
  #endif

  outfile = fopen (fname(), "w");
  if (NULL == outfile) {
    #ifdef DEBUG
      fprintf (stderr, "Unable to open new file, bailing\n");
    #endif
    return 1;
  }

  send(sockfd, l_msg, strlen(l_msg), 0);
  while (recv(sockfd, l_buffer, NET_BUF_SZ, 0) == NET_BUF_SZ) {
    fputs(l_buffer, outfile);
    memset (l_buffer, '\0', NET_BUF_SZ);
  }
  fputs(l_buffer, outfile);
  
  /* cleanup */
  #if DEBUG > 1
    fprintf (stderr, "Cleaning up...\n");
  #endif
  fclose(outfile);
  close(sockfd);
  #if 0
    free (l_buffer);
    free (l_msg);
  #endif

  #ifdef DEBUG
    fprintf (stderr, "Successfully exiting net_get()\n");
  #endif

  return TRUE;
}

static int cdi_check_matches (FILE *fd) {
  int i; int s; char trknm[100];
  while ((!feof(fd)) && (!ferror(fd))) {
    s = fscanf (fd, "NumMatches: %i", &i);
    #ifdef DEBUG
      fprintf (stderr, "Scanning, found %i alleged matches\n", i);
    #endif
    if (s > 0) switch (i) { /* the latter is a sanity check */
      case 0:
              #ifdef UNKNOWN_CD_WARN
                #if DEBUG > 1
                  fprintf (stderr, "About to print no-info message (%i)\n", noinfo_disp);
                #endif
                if (noinfo_disp == 0) {
                  printf ("CD Index doesn't know about this CD. Track names unavailable.\n");
                  printf ("If you want to avoid this error in the future, consider submitting\n");
                  printf ("information about this CD to the CD Index by using this URL:\n");
                  printf ("%s\n", cd_subid());
                }
                noinfo_disp++;
                /* end blatant bug-concealing measures */
              #endif
              unlink(fname()); /* don't care about errors */
              break;
      case 1: break; /* this is the "good" case */
      default: printf ("CD Index claims multiple matches for this CD.\n");
               printf ("This is, of course, impossible.\n");
               printf ("Please send email to dave@technopagan.org about this.\n");
               printf ("In the meantime, track names are unavailable. Sorry.\n");
               break;
    } /* switch */
    else fgets(trknm, NAMESIZE, fd); /* clear to newline */
  } /* while */    
  return i;
} /* func */
