/*
 * Dpi for FTP.
 *
 * This server checks the ftp-URL to be a directory (requires wget).
 * If true, it sends back an html representation of it, and if not
 * a dpip message (which is catched by dillo who redirects the ftp URL
 * to the downloads server).
 *
 * THIS IS A PROTOTYPE to illustrate how it is done. Feel free to polish!
 *
 * Copyright 2003 Jorge Arellano Cid <jcid@dillo.org>
 *
 * 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.
 *
 */

/*
 * TODO:
 * - Check dpi command syntax
 * - Handle simultaneous connections. 
 *   (note that if it becomes resident there's less forking).
 * - Send feedback about the FTP login process from wget's stderr.
 *   i.e. capture our child's stderr, process it, and report back.
 * - Add error handling
 * - etc
 *
 *   If ftp.dpi is implemented with a low level ftp library, it becomes
 *   possible to keep the connection alive, and thus make browsing of ftp
 *   directories faster (this avoids one login per page, and forks). Perhaps
 *   it's not worth, but can be done.
 *
 *   Currently, anything the user clicks is sent back to Dillo. This
 *   may be better handled by inserting file type detection here. If
 *   we detect a binary, the connection is aborted and a download request
 *   dpi tag is sent instead.
 *
 *   This ftp plugin profits from the fact that given no MIME type in
 *   the HTTP header, dillo tries to detect the filetype itself.
 */

#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <sys/wait.h>
#include <errno.h>
#include <sys/time.h>
#include <glib.h>

#define BUFLEN 256
#define TOUT 300

/*---------------------------------------------------------------------------*/

/*
 * Task: given a tag and an attribute name, return its value.
 *       (character stuffing is removed here)
 * Return value: the attribute value, or NULL if not present or malformed.
 * (copied from bookmarks.c)
 */          
char *Get_attr_value(char *tag, int tagsize, char *attrname)
{            
   char *p, *q, *ltag, quote, *start, *val = NULL;

   ltag = g_strndup(tag, tagsize);
   if ((p = strstr(ltag, attrname)) &&
       (p = strchr(p, '=')) &&
       (p = strpbrk(p, "'\"")) ) {
      quote = *p;                 
      start = ++p;
      while ((q = strchr(p, quote)) && q[1] == quote)
         p = q + 2;
      if (q) {
         val = g_strndup(start, q - start);
         for (p = q = val; (*q = *p); ++p, ++q)
            if ((*p == '"' || *p == '\'') && p[1] == p[0])
               ++p;
      }
   }         
   g_free(ltag);

   return val;
}


/*---------------------------------------------------------------------------*/

/*
 * Build a shell command using wget for this URL.
 */
gchar *make_wget_cmd(gchar *url)
{
   gchar *p, *wcmd = NULL;

   if ((p = strrchr(url, '/')) != NULL){
      /* if it does not end with a slash, append one */
      wcmd = 
      // g_strdup_printf("wget -O - %s%s 2>/dev/null", url, p[1] ? "/" : "");
         g_strdup_printf("wget -O - '%s' 2>/dev/null", url);
   }

   return wcmd;
}

/*
 * Send the stream char by char (useful for slow transfer test)
 * Return value: bytes written
 */
gint send_stream_1(FILE *in_stream, gchar *url)
{
   gint i, ch;

   for (i = 0; (ch = fgetc(in_stream)) != EOF; ++i) {
      if (i == 0) {
        printf("<dpi cmd='start_send_page' url='%s'>", url);
        fflush(stdout);
        printf("Content-type: text/html\n\n");
        fflush(stdout);
      }
      printf("%c", ch);
      g_printerr("%c", ch);
      fflush(stdout);
   }
   return i;
}

/*
 * Send the stream with default buffering (somewhat faster)
 * Return value: bytes written
 */
gint send_stream_2(FILE *in_stream, gchar *url)
{
   gint i, ch;

   for (i = 0; (ch = fgetc(in_stream)) != EOF; ++i) {
      if (i == 0) {
        printf("<dpi cmd='start_send_page' url='%s'>", url);
        printf("Content-type: text/html\n\n");
      }
      printf("%c", ch);
      g_printerr("%c", ch);
   }
   return i;
}

/*
 * Send the stream with coarse buffering (fastest)
 * Return value: 1: data sent,  0: nothing sent
 */
gint send_stream_3(FILE *in_stream, gchar *url)
{
   gint header = 0;
   gchar buf[4096];
   size_t n;

   while ((n = fread (buf, 1, 4096, in_stream)) > 0) {
      //g_printerr("ftp.dpi::fread: read n = %d bytes\n", n);
      if (n > 0 && !header) {
         printf("<dpi cmd='start_send_page' url='%s'>", url);
         //printf("Content-type: text/html\n\n");
         printf("\n\n");
         header = 1;
      }
      fwrite(buf, 1, n, stdout);
      //fwrite(buf, 1, n, stderr);
   }

// g_printerr("ftp.dpi::fread: read n = %d bytes\n", n);

   return header;
}


/*
 * Fork, exec command, get its output and redirect to stdout.
 * Return: Number of bytes transfered.
 */
gint try_ftp_transfer(gchar *wget_cmd, gchar *url)
{
   FILE *in_stream;
   gint nb, ret;

   if ((in_stream = popen(wget_cmd, "r")) == NULL) {
      perror("popen");
      exit (EXIT_FAILURE);
   }
   /* Read/Write */
   nb = send_stream_3(in_stream, url);

   if ((ret = pclose(in_stream)) != 0)
      g_printerr("ftp.dpi::pclose: [%d] %s\n", ret, g_strerror(errno));

   return nb;
}

/*
 *
 */
int main(void)
{
   FILE *F_stdin;
   gchar *dpip_tag = NULL, *cmd = NULL, *url = NULL, *url2 = NULL,
         *wget_cmd = NULL, *wget_cmd2 = NULL;
   gint nb, rd_len;
   gchar buf[4096], *p;

   /* wget may need to write a temporary file... */
   chdir("/tmp");

   /* Read the dpi command from STDIN */
   F_stdin = fdopen (STDIN_FILENO, "r");
   rd_len = read(STDIN_FILENO, buf, 4096);
   dpip_tag = g_strndup(buf, rd_len);
   fclose(F_stdin);
// close(STDIN_FILENO); /* Necessary? */
// g_printerr("[%s]\n", dpip_tag);

   cmd = Get_attr_value(dpip_tag, strlen(dpip_tag), "cmd");
   url = Get_attr_value(dpip_tag, strlen(dpip_tag), "url");
   if (!cmd || !url) {
      g_printerr("ftp.dpi:: Error, cmd=%s, url=%s\n", cmd, url);
      exit (EXIT_FAILURE);
   }

   /* make command string */
   wget_cmd = make_wget_cmd(url);
   g_printerr("ftp.dpi::[%s]\n", wget_cmd);

   if ((nb = try_ftp_transfer(wget_cmd, url)) == 0) {
      /* Transfer failed, the requested file may not exist or be a symlnk
       * to a directory. Try again... */
      if ((p = strrchr(url, '/')) && p[1]) {
         url2 = g_strconcat(url, "/", NULL);
         wget_cmd2 = make_wget_cmd(url2);
         nb = try_ftp_transfer(wget_cmd2, url);
      }
   }

   if (nb == 0) {
      /* The transfer failed, let dillo know... */
      printf("<dpi cmd='answer' to_cmd='open_url' msg='not a directory'>");
      fflush(stdout);
   }

   g_free(cmd);
   g_free(url);
   g_free(url2);
   g_free(dpip_tag);
   g_free(wget_cmd);
   g_free(wget_cmd2);
   return 0;
}

