/*
 *  $Id: dsread.c,v 1.5 1997/10/19 20:00:02 bert Exp $
 *
 *  Discuss interface code
 *  (part of diswww, a Discuss->WWW gateway)
 */

#ifndef lint
static char rcsid_dsread[] = "$Id: dsread.c,v 1.5 1997/10/19 20:00:02 bert Exp $";
#endif

#include "proto.h"  /* the copyright notice is included in this file, too. */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
#include <sys/file.h>
#include <errno.h>
#include <malloc.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <discuss/discuss.h>
#include "extern.h"
#include "status.h"

extern tfile http_tfile();

extern char *sys_errlist[];
extern int sys_nerr;

int bsize;
char *buffer;

/* formats an error for a meeting.
 *    code -- error return code
 *    mtg --  name_blk for meeting
 *    info -- returned information from dsc_get_mtg_info
 */
void ds_error_mtg(int code, name_blk *mtg, mtg_info *info)
{
  if (code == MTG_MOVED) {
    char *url, *path;

    /* if meeting has moved, the new location is returned in the long_name */
    /* field of the mtg_info record.  Don't you love Discuss internals?  =) */

    path = info->location;
    if (!strncmp("/usr/spool/discuss", path, 18)) path += 18;

    url = (char*)malloc(strlen(SRV_DISWWW)+
			strlen(info->long_name)+strlen(path)+3);
    CHECK_MALLOC(url);

    /* SRV_DISWWW must be the start for a full URL, http://host:port */
    sprintf(url, "%s/%s%s/", SRV_DISWWW, info->long_name, path);
    send_moved_notice(&conn, S_moved, url);
  } else {
    if ((code == NEW_VERSION)||(code == INCONSISTENT)||(code == NO_WRITE)
	||(code == NO_MTGS_FILE)||(code == BAD_MTGS_FILE)
	||(code == CANT_WRITE_TEMP)||(code == NO_SUPPORT))
      send_error_notice(&conn, S_internal_error, E_mtg_error_fmt,
			error_message(code), mtg->hostname, mtg->pathname);
    else if ((code == NO_ACCESS)||(code == NO_PRINC)||(code == NO_SUCH_USER)
	     ||(code == CANT_ATTEND))
      send_error_notice(&conn, S_forbidden, E_mtg_error_fmt,
			error_message(code), mtg->hostname, mtg->pathname);
    else
      send_error_notice(&conn, S_not_found, E_mtg_error_fmt,
			error_message(code), mtg->hostname, mtg->pathname);
  }
}

/* formats an error for a particular transaction.
 *    code -- error return code
 *    mtg --  name_blk for meeting
 *    trn --  transaction number
 */
void ds_error_trn(int code, name_blk *mtg, int trn)
{
  if ((code == NEW_VERSION)||(code == INCONSISTENT)||(code == NO_WRITE)
      ||(code == NO_MTGS_FILE)||(code == BAD_MTGS_FILE)
      ||(code == CANT_WRITE_TEMP)||(code == NO_SUPPORT))
    send_error_notice(&conn, S_internal_error, E_trn_error_fmt,
		      error_message(code), trn, mtg->hostname, mtg->pathname);
  else if ((code == NO_ACCESS)||(code == NO_PRINC)||(code == NO_SUCH_USER)
	   ||(code == CANT_ATTEND))
    send_error_notice(&conn, S_forbidden, E_trn_error_fmt,
		      error_message(code), trn, mtg->hostname, mtg->pathname);
  else
    send_error_notice(&conn, S_not_found, E_trn_error_fmt,
		      error_message(code), trn, mtg->hostname, mtg->pathname);
}

/*
 * get transaction and send the nicely formatted text out to the socket
 *    host --      hostname of the Discuss server (from URL)
 *    path --      full pathname to the meeting (from URL)
 *    trans_num -- the transaction to retrieve
 *    out --       the file descriptor of the socket
 */
void dsread(char* host, char* path, int trans_num, int out)
{
  name_blk meetings;
  mtg_info meeting_info;
  int result;
  tfile tf;
  trn_info2 trn_info;

  initialize_dsc_error_table();

  meetings.hostname = host;
  meetings.pathname = path;
  meetings.user_id = "";
  meetings.aliases = NULL;
  meetings.status = 0;
  meetings.date_attended = 0;
  meetings.last = 0;

  tf = http_tfile(out);   /* Wrap socket in a tfile.  Gross, but it works. */

  dsc_get_mtg_info(&meetings,&meeting_info,&result);
  if (result != 0) {
    ds_error_mtg(result, &meetings, &meeting_info);
    return;
  }

  dsc_get_trn_info2(&meetings,trans_num,&trn_info,&result);
  if (result != 0) {
    ds_error_trn(result, &meetings, trans_num);
    return;
  }

  /* If we're here, we've succeeded.  (It's possible we'll run into
   * some internal error on the Discuss server and blow up, but it's
   * not likely.)  Send a success response to HTTP/1.0+ clients. */
  if (conn.ver)
    send_header(&conn, S_ok, T_html);

  fd_print(out, "<HEAD><TITLE>discuss@%s: [%d] in \"%s\"</TITLE>\r\n",
	   meetings.hostname, trans_num, meeting_info.long_name);
  fd_print(out, "<H1>[%d] in %s</H1></HEAD>\r\n",
	   trans_num, meeting_info.long_name);

      /***  print out help and back-out stuff  ***/

  /* fd_print(out, "<A HREF=\"%d\">%d</A><P>\r\n",); */

  fd_print(out,
	   "<A HREF=\"%s/\"><IMG SRC=\"%s/i-d.gif\" ALT=\"root\"></A>\r\n",
	   SRV_SELF, SRV_SELF);

  fd_print(out,
   "<A HREF=\"?%d\"><IMG SRC=\"%s/i-back.gif\" ALT=\"meeting\"></A>\r\n",
	   trans_num, SRV_SELF);

  fd_print(out,
   "<A HREF=\"%s/help.html\"><IMG SRC=\"%s/i-help.gif\" ALT=\"help\"></A>\r\n",
	   SRV_SELF, SRV_SELF);

      /***  print out prev/next/pref/nref/... pointers  ***/

  if (meeting_info.first && (meeting_info.first != trans_num))
    fd_print(out,
           "<A HREF=\"%d\"><IMG SRC=\"%s/i-first.gif\" ALT=\"first\"></A>\r\n",
	     meeting_info.first, SRV_SELF);
  else fd_print(out, "<IMG SRC=\"%s/n-first.gif\" ALT=\"\">\r\n", SRV_SELF);

  if (trn_info.fref && (trn_info.fref != trans_num))
    fd_print(out,
   "<A HREF=\"%d\"><IMG SRC=\"%s/i-fref.gif\" ALT=\"first in chain\"></A>\r\n",
	     trn_info.fref, SRV_SELF);
  else fd_print(out, "<IMG SRC=\"%s/n-fref.gif\" ALT=\"\">\r\n", SRV_SELF);

  if (trn_info.pref)
    fd_print(out,
"<A HREF=\"%d\"><IMG SRC=\"%s/i-pref.gif\" ALT=\"previous in chain\"></A>\r\n",
	     trn_info.pref, SRV_SELF);
  else fd_print(out, "<IMG SRC=\"%s/n-pref.gif\" ALT=\"\">\r\n", SRV_SELF);

  if (trn_info.prev)
    fd_print(out,
         "<A HREF=\"%d\"><IMG SRC=\"%s/i-prev.gif\" ALT=\"previous\"></A>\r\n",
	     trn_info.prev, SRV_SELF);
  else fd_print(out, "<IMG SRC=\"%s/n-prev.gif\" ALT=\"\">\r\n", SRV_SELF);

  if (trn_info.next)
    fd_print(out,
             "<A HREF=\"%d\"><IMG SRC=\"%s/i-next.gif\" ALT=\"next\"></A>\r\n",
	     trn_info.next, SRV_SELF);
  else fd_print(out, "<IMG SRC=\"%s/n-next.gif\" ALT=\"\">\r\n", SRV_SELF);

  if (trn_info.nref)
    fd_print(out,
    "<A HREF=\"%d\"><IMG SRC=\"%s/i-nref.gif\" ALT=\"next in chain\"></A>\r\n",
	     trn_info.nref, SRV_SELF);
  else fd_print(out, "<IMG SRC=\"%s/n-nref.gif\" ALT=\"\">\r\n", SRV_SELF);

  if (trn_info.lref && (trn_info.lref != trans_num))
    fd_print(out,
    "<A HREF=\"%d\"><IMG SRC=\"%s/i-lref.gif\" ALT=\"last in chain\"></A>\r\n",
	     trn_info.lref, SRV_SELF);
  else fd_print(out, "<IMG SRC=\"%s/n-lref.gif\" ALT=\"\">\r\n", SRV_SELF);

  if (meeting_info.last && (meeting_info.last != trans_num))
    fd_print(out,
           "<A HREF=\"%d\"><IMG SRC=\"%s/i-last.gif\" ALT=\"last\"></A>\r\n",
	     meeting_info.last, SRV_SELF);
  else fd_print(out, "<IMG SRC=\"%s/n-last.gif\" ALT=\"\">\r\n", SRV_SELF);

      /***  print out the transaction  ***/

  if (!(trn_info.flags & TRN_FDELETED)) {
    char *tdate;
#ifdef FIX_CTIME
    char *datep, *dateq, *datee;
#endif

    fd_write(out, "<HR><H2>");
    fd_write_quoted(out, trn_info.subject);
    fd_write(out, "</H2>\r\n<H3>");

    fd_write_quoted(out, trn_info.author);
    fd_write(out, " (");

#ifndef FIX_CTIME
    tdate = ctime(&trn_info.date_entered);
    fd_write (out, tdate);
#else
    /* This is just a local hack.  It switches time and year from ctime(). */
    /* I should stop unparsing output of ctime() and write some real code. */

    /* If I used this code as a reference for a programming job, please */
    /* disregard everything between here and #else. =) */

    tdate = ctime(&trn_info.date_entered);
    datep = strpbrk(tdate, " \t\r\n"); /* day of week */
    while ((*datep==' ') || (*datep=='\t')) datep++;
    datep = strpbrk(datep, " \t\r\n"); /* month */
    while ((*datep==' ') || (*datep=='\t')) datep++;
    datep = strpbrk(datep, " \t\r\n"); /* date */
    if (datep && (*datep)) { (*datep) = '\0'; datep++; }

    if (tdate) fd_write(out, tdate);
    fd_write(out, " ");

    while ((*datep==' ') || (*datep=='\t')) datep++;
    dateq = strpbrk(datep, " \t\r\n"); /* time */
    if (dateq && (*dateq)) { (*dateq) = '\0'; dateq++; }
    while ((*dateq==' ') || (*dateq=='\t')) dateq++;
    datee = strpbrk(dateq, " \t\r\n"); /* time */
    if (datee && (*datee)) (*datee) = '\0';

    if (dateq) fd_write(out, dateq);
    fd_write(out, "   ");
    if (datep) fd_write(out, datep);
#endif
    fd_write(out, ")</H3>\r\n");
    free(tdate);

    fd_write(out, "<PRE>\r\n");
    dsc_get_trn(&meetings,trans_num,tf,&result);
    fd_write(out, "</PRE>\r\n");
    if (result != 0)
      /* if we get an error now, it's too late for the HTTP error code... */
      fd_print(out,"error getting transaction %s[%d]: %s\r\n",
	       (char *) (rindex(meetings.pathname,'/')+1), trans_num,
	       error_message(result));
  }
}

/*
 * format a header listing for a part of a discuss meeting.
 *    host --      hostname of the Discuss server (from URL)
 *    path --      full pathname to the meeting (from URL)
 *    lo --        the  (or <=0 if unspecified)
 *    hi --        the last transaction to include (or <=0 if unspecified)
 *    lo --        first transaction to include (0 if no query, -1 if left out)
 *    hi --        first transaction to include (0 if no query, -1 if left out,
 *                 or -2 if there was no "-" in query)
 *    num_tr --    number of trns to list if lo or hi is unspecified
 *    out --       the file descriptor of the socket
 */
void dslist(char* host, char* path, int lo, int hi, int num_tr, int out)
{
  name_blk meetings;
  mtg_info meeting_info;
  int result;
  trn_info2 trn_info;
  int tr;

  initialize_dsc_error_table();

  meetings.hostname = host;
  meetings.pathname = path;
  meetings.user_id = "";
  meetings.aliases = NULL;
  meetings.status = 0;
  meetings.date_attended = 0;
  meetings.last = 0;

  --num_tr;

  dsc_get_mtg_info(&meetings,&meeting_info,&result);
  if (result != 0) {
    ds_error_mtg(result, &meetings, &meeting_info);
    return;
  }

  if (lo > 0) {
    if (lo < meeting_info.lowest)  lo = meeting_info.lowest;
    if (lo > meeting_info.highest) lo = meeting_info.highest;
    if (hi <= -2) {
      hi = lo + num_tr;
    } else if (hi <= 0) {
      /* only for queries of the form, '?number-' */
      hi = meeting_info.highest;
    }
    if (hi > meeting_info.highest) hi = meeting_info.highest;
  } else {
    hi = meeting_info.highest;
    lo = (hi - num_tr);
    if (lo < meeting_info.lowest)  lo = meeting_info.lowest;
  }

  /* If we're here, we've succeeded.  (It's possible we'll run into
   * some internal error on the Discuss server and blow up, but it's
   * not likely.)  Send a success response to HTTP/1.0+ clients. */
  if (conn.ver)
    send_header(&conn, S_ok, T_html);

  fd_print(out, "<HEAD><TITLE>discuss@%s: %d-%d in \"%s\"</TITLE>\r\n",
	   meetings.hostname, lo, hi, meeting_info.long_name);
  fd_print(out, "<H1>%s</H1></HEAD>\r\n", meeting_info.long_name);

  fd_print(out,
	   "<A HREF=\"%s/\"><IMG SRC=\"%s/i-d.gif\" ALT=\"root\"></A>\r\n",
	   SRV_SELF, SRV_SELF);

  fd_print(out,
   "<A HREF=\"%s/help.html\"><IMG SRC=\"%s/i-help.gif\" ALT=\"help\"></A>\r\n",
	   SRV_SELF, SRV_SELF);

  if (lo > meeting_info.lowest) {
    tr = lo - num_tr - 1;
    if (tr < meeting_info.lowest) tr = meeting_info.lowest;
    fd_print(out,
	     "<A HREF=\"?%d-%d\"><IMG SRC=\"%s/i-up.gif\" ALT=\"up\"></A>\r\n",
	     tr, lo-1, SRV_SELF);
  } else fd_print(out, "<IMG SRC=\"%s/n-up.gif\" ALT=\"\">\r\n", SRV_SELF);

  if (hi < meeting_info.highest) {
    tr = hi + num_tr + 1;
    if (tr > meeting_info.highest) tr = meeting_info.highest;
    fd_print(out,
	 "<A HREF=\"?%d-%d\"><IMG SRC=\"%s/i-down.gif\" ALT=\"down\"></A>\r\n",
	     hi+1, tr, SRV_SELF);
  } else fd_print(out, "<IMG SRC=\"%s/n-down.gif\" ALT=\"\">\r\n", SRV_SELF);

  fd_write(out, "<PRE>\r\n");
  for (tr = lo; tr <= hi; tr++) {
    dsc_get_trn_info2(&meetings,tr,&trn_info,&result);
    if (result != 0) {
      if (result != DELETED_TRN)
	fd_print(out, "[%04d]          %s.\r\n", error_message(result));
#ifndef IGNORE_DELETED_TRNS
      else
	fd_print(out, "[%04d]          &lt;deleted transaction&gt;\r\n", tr);
#endif
    } else {
      fd_print(out, "<A HREF=\"%d\">[%04d]</A> ", tr, tr);
      fd_print_quoted(out, "%-25s %s\r\n", trn_info.author, trn_info.subject);
    }
  }
  fd_write(out, "</PRE>\r\n");
}
