/* poppoll.c
   Copyright (C) 1995-1996 Eberhard Mattes

This file is part of GNU Emacs.

GNU Emacs 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, or (at your option)
any later version.

GNU Emacs 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 GNU Emacs; see the file COPYING.  If not, write to
the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.  */


#define INCL_DOSSEMAPHORES
#include <os2.h>
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <io.h>
#include <share.h>
#include <signal.h>
#include <time.h>
#include <sys/time.h>
#include <errno.h>
#include <getopt.h>
#include "mailutil.h"
#include "poputil.h"

static char opt_beep;
static char opt_from;
static char opt_number;
static char opt_subject;
static char opt_verbose;
static int interval = 5 * 60;
static int retries = 15;
static char sem_name[] = "/sem32/poppoll/now";


static void block_signals (void)
{
  sigset_t set;

  sigemptyset (&set);
  sigaddset (&set, SIGINT);
  sigprocmask (SIG_BLOCK, &set, NULL);
}


static void unblock_signals (void)
{
  sigset_t set;

  sigemptyset (&set);
  sigaddset (&set, SIGINT);
  sigprocmask (SIG_UNBLOCK, &set, NULL);
}


static char *cmd (FILE *f, const char *fmt, ...)
{
  va_list arg_ptr;
  char *answer;
  int rc;

  va_start (arg_ptr, fmt);
  rc = pop_vcmd (f, &answer, fmt, arg_ptr);
  va_end (arg_ptr);
  if (rc == EX_POPERR)
    {
      fprintf (stderr, "%s\n", answer);
      pop_abort (f);
      return (NULL);
    }
  else if (rc != EX_OK)
    {
      perror ("pop_vcmd()");
      pop_abort (f);
      return (NULL);
    }
  return (answer);
}


static char *fetch_header (const char *s)
{
  while (*s == ' ' || *s == '\t')
    ++s;
  return (strdup (s));
}


static void poll (FILE *inp, char *user, char *cookie, char *output_fname)
{
  int rc, i, msg_count, msg_size;
  char in_header;
  char *pass, *p, *hdr_subject, *hdr_from;
  FILE *out;
  long file_size;
  time_t t;

  rc = pop_answer (inp, &p);
  if (rc == EX_POPERR)
    {
      fprintf (stderr, "%s\n", p);
      pop_abort (inp);
      return;
    }
  else if (rc != EX_OK)
    {
      perror ("pop_answer()");
      return;
    }

  if (cmd (inp, "USER %s", user) == NULL) return;

  pass = pop_getpass (cookie);
  if (pass == NULL)
    {
      pop_abort (inp); return;
    }
  if (cmd (inp, "PASS %s", pass) == NULL) return;
  memset (pass, 0, POP_GETPASS_LEN);

  p = cmd (inp, "STAT");
  if (p == NULL) return;
  if (sscanf (p, "+OK %d %d", &msg_count, &msg_size) != 2)
    {
      fprintf (stderr, "Cannot parse answer of STAT command\n");
      pop_abort (inp); return;
    }

  if (opt_number)
    printf ("%d new message%s\n", msg_count, (msg_count == 1 ? "" : "s"));

  if (msg_count == 0)
    {
      cmd (inp, "QUIT");
      return;
    }

  out = NULL;
  for (i = 0; i <= retries; ++i)
    {
      out = _fsopen (output_fname, "a", SH_DENYRW);
      if (out != NULL || errno != EACCES)
        break;
      sleep (1);
    }
  if (out == NULL)
    {
      perror (output_fname);
      pop_abort (inp); return;
    }
  file_size = filelength (fileno (out));
  if (file_size == -1)
    {
      perror ("filelength()");
      pop_abort (inp); return;
    }

  rc = EX_EOF;
  for (i = 1; i <= msg_count; ++i)
    {
      if (cmd (inp, "RETR %d", i) == NULL)
        {
          ftruncate (fileno (out), file_size);
          fclose (out);
          return;
        }
      time (&t);
      fprintf (out, "From dummy@dummy  %s", ctime (&t));
      hdr_from = hdr_subject = NULL; in_header = 1;
      while ((rc = read_line (inp, 0, &p)) == EX_OK)
        {
          if (strcmp (p, ".") == 0)
            break;
          if (*p == '.')
            ++p;
          if (*p == 0)
            in_header = 0;
          if (in_header)
            {
              if (opt_from && strncmp (p, "From:", 5) == 0)
                hdr_from = fetch_header (p + 5);
              else if (opt_subject && strncmp (p, "Subject:", 8) == 0)
                hdr_subject = fetch_header (p + 8);
            }
          else if (strncmp (p, "From", 4) == 0)
            fputc ('>', out);
          fputs (p, out);
          fputc ('\n', out);
        }
      fputc ('\n', out);

      if ((hdr_from != NULL || hdr_subject != NULL)
          && i == 1 && file_size == 0)
        fputc ('\n', stdout);

      if (hdr_from != NULL && hdr_subject != NULL)
        printf ("%-38.38s | %-38.38s\n", hdr_from, hdr_subject);
      else if (hdr_from != NULL)
        printf ("%.79s\n", hdr_from);
      else if (hdr_subject != NULL)
        printf ("%.79s\n", hdr_subject);
      if (hdr_from != NULL) free (hdr_from);
      if (hdr_subject != NULL) free (hdr_subject);
    }
  if (rc != EX_OK && rc != EX_EOF)
    {
      perror ("read_line()");
      ftruncate (fileno (out), file_size);
      fclose (out);
      pop_abort (inp); return;
    }

  if (fflush (out) != 0)
    {
      perror (output_fname);
      ftruncate (fileno (out), file_size);
      fclose (out);
      pop_abort (inp); return;
    }
  if (fclose (out) != 0)
    {
      pop_abort (inp); return;
    }

  if (msg_count > 0 && opt_beep)
    fputc (0x07, stdout);

  for (i = 1; i <= msg_count; ++i)
    cmd (inp, "DELE %d", i);

  cmd (inp, "QUIT");
}


static void init_and_loop (char *user_host, char *output_fname)
{
  char *user, *host, *cookie;
  char sem_ok;
  int rc, incr;
  FILE *inp;
  struct timeval last, now;
  long elapsed;
  HEV hev;
  ULONG post_count, sem_rc;

  signal (SIGPIPE, SIG_IGN);

  rc = pop_user_host (user_host, &user, &host);
  if (rc == EX_MAILHOST)
    {
      fputs ("MAILHOST not set\n", stderr);
      exit (1);
    }
  else if (rc != EX_OK)
    {
      perror ("pop_user_host()");
      exit (1);
    }

  cookie = pop_cookie (user, host);

  hev = 0;
  sem_rc = DosOpenEventSem (sem_name, &hev);
  if (sem_rc != 0)
    sem_rc = DosCreateEventSem (sem_name, &hev, DC_SEM_SHARED, FALSE);

  if (sem_rc == 0)
    sem_ok = 1;
  else
    {
      fprintf (stderr, "Cannot create semaphore (error code %lu)\n", sem_rc);
      sem_ok = 0;
    }

  gettimeofday (&last, NULL);

  for (;;)
    {
      if (sem_ok)
        DosResetEventSem (hev, &post_count);
      if (opt_verbose)
        printf ("Polling...\n");
      rc = pop_open (host, 110, &inp);
      if (rc == EX_OK)
        {
          block_signals ();
          poll (inp, user, cookie, output_fname);
          unblock_signals ();
          fclose (inp);
        }
      if (interval == 0)
        break;
      gettimeofday (&now, NULL);
      incr = interval;
      if (now.tv_sec >= last.tv_sec)
        {
          elapsed = now.tv_sec - last.tv_sec;
          while (incr < elapsed)
            incr += interval;
          if (incr > elapsed)
            {
              if (opt_verbose)
                printf ("Sleeping for %ld seconds...\n", incr - elapsed);
              if (sem_ok)
                {
                  sem_rc = DosWaitEventSem (hev, (incr - elapsed) * 1000);
                  if (sem_rc == 0)
                    incr = 0;
                }
              else
                sleep (incr - elapsed);
            }
        }
      last.tv_sec += incr;
    }
}


static void usage (void)
{
  fputs ("Usage: poppoll [<options>] <user>[@<host>] <output_file>\n"
         "       poppoll now\n\n"
         "Options:\n"
         "  -b         Beep when new mail is available\n"
         "  -f         Display From header\n"
         "  -i time    Set interval (default: 5m = 300s)\n"
         "  -n         Display number of messages\n"
         "  -r number  Set number of retries to open the output file (default: 15)\n"
         "  -s         Display Subject header\n"
         "  -v         Display verbose status information\n", stderr);
  exit (1);
}


static void set_interval (const char *s)
{
  char *e;
  long n;

  if (strcmp (s, "once") == 0)
    interval = 0;
  else
    {
      errno = 0;
      n = strtol (s, &e, 10);
      if (n < 1 || errno != 0)
        usage ();
      if (*e == 'h')
        interval = (int)n * 60 * 60;
      else if (*e == 'm')
        interval = (int)n * 60;
      else if (*e == 's')
        interval = (int)n;
      else
        usage ();
    }
}


static void set_retries (const char *s)
{
  char *e;
  long n;

  errno = 0;
  n = strtol (s, &e, 10);
  if (n < 0 || n > 30 || errno != 0)
    usage ();
  retries = (int)n;
}


static void poll_now (void)
{
  ULONG rc;
  HEV hev;

  hev = 0;
  rc = DosOpenEventSem (sem_name, &hev);
  if (rc != 0)
    {
      fprintf (stderr, "poppoll is not running (error code %lu)\n", rc);
      exit (1);
    }
  DosPostEventSem (hev);
  DosCloseEventSem (hev);
  exit (0);
}


int main (int argc, char *argv[])
{
  int c;

  if (argc == 2 && strcmp (argv[1], "now") == 0)
    poll_now ();

  while ((c = getopt (argc, argv, "bfi:nr:sv")) != -1)
    switch (c)
      {
      case 'b':
        opt_beep = 1;
        break;
      case 'f':
        opt_from = 1;
        break;
      case 'i':
        set_interval (optarg);
        break;
      case 'n':
        opt_number = 1;
        break;
      case 'r':
        set_retries (optarg);
        break;
      case 's':
        opt_subject = 1;
        break;
      case 'v':
        opt_verbose = 1;
        break;
      default:
        usage ();
    }
  if (argc - optind != 2)
    usage ();

  init_and_loop (argv[optind+0], argv[optind+1]);
  return (0);
}
