#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <syslog.h>
#include <errno.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>

#include "tcp_listen.h"
#include "signal.h"
#include "ssl.h"

static char * port = NULL;
static int become_daemon = 0;
const char * pidfile = "/var/run/akpop3d.pid";
const char * ssl_certfile = "/etc/akpop3d/cert.pem";
const char * ssl_keyfile = "/etc/akpop3d/key.pem";
const char * authfile = NULL;
const char * local_mbox = NULL;
char * mailspool = "/var/mail/";
int use_pop3_allow_deny = 0;

int daemonize(void);
void pop3_session(int fd);
static void delete_pid(void);

static void sig_chld(int signo) {
  pid_t pid;
  int stat;
  while ((pid = waitpid(-1,&stat,WNOHANG)) > 0);
}

static void sig_term(int signo) {
  if (become_daemon) delete_pid();
  exit(0);
}

static void usage(char * argv0) {
  printf("Usage: %s [OPTIONS]\n", argv0);
  printf("Start a POP3 daemon.\n"
    "\n"
    "  -d             run as a daemon (in the background)\n"
    "  -f <pidfile>   store process ID in <pidfile> (must be a full path)\n"
    "  -s             use SSL for all connections\n"
    "  -c <certfile>  use <certfile> for SSL certificate [%s]\n"
    "  -k <keyfile>   use <keyfile> for SSL RSA key      [%s]\n"
    "  -p <port>      listen on <port>    [default: 110, or 995 if SSL]\n",
    ssl_certfile, ssl_keyfile
  );
  printf(
    "  -l <address>   listen on <address> [default: any]\n"
    "  -a <authfile>  use text file <authfile> for authentication\n"
    "  -m <spooldir>  use directory <spooldir> as mail spool\n"
    "  -D             use /etc/pop3.{allow,deny} files\n"
    "  -L <mbox>      use ~/<mbox> as mail spool\n"
    "\n"
    "Use `%s -v' to show the program version.\n",
    argv0
  );
  exit(EXIT_SUCCESS);
}

static void write_pid(void) {
  FILE * f = fopen(pidfile,"w+");
  if (f) {
    fprintf(f,"%u",getpid());
    fclose(f);
  }
  /* ignore errors */
}

static void delete_pid(void) {
  unlink(pidfile);
}

int main(int argc, char * argv[]) {
  int listenfd, connfd;
  pid_t childpid;
  struct stat sbuf;
  socklen_t addrlen;
  const char * optstring = "df:sp:c:k:l:a:m:hvDL:";
  char * listenhost = NULL;
  char * progname;
  int c;

  do {
    c = getopt(argc,argv,optstring);
    if (c < 0) continue;
    switch (c) {
      case 'd': become_daemon = 1; break;
      case 'f': pidfile = optarg; break;
      case 's':
#if HAVE_LIBSSL
        set_use_ssl(1);
        if (port==NULL)
          port = "995"; /* POP3 over SSL */
#else
        fprintf(stderr,"%s: %s\n", argv[0], "POP3 over SSL not supported");
        exit(EXIT_FAILURE);
#endif
        break;
      case 'p': port = optarg; break;
      case 'c': ssl_certfile = optarg; break;
      case 'k': ssl_keyfile = optarg; break;
      case 'l': listenhost = optarg; break;
      case 'a': authfile = optarg; break;
      case 'm': if (stat(optarg,&sbuf)!=0) {
                  fprintf(stderr,"%s: %s: %s\n",argv[0], optarg,strerror(errno));
                  exit(EXIT_FAILURE);
                }
                if (!S_ISDIR(sbuf.st_mode)) {
                  fprintf(stderr,"%s: %s is not a directory\n",argv[0],optarg);
                  exit(EXIT_FAILURE);
                }
                if (optarg[strlen(optarg)-1]!='/') {
                  int ms_len = strlen(optarg)+2;
                  mailspool = alloca(ms_len);
                  if (mailspool) {
                    snprintf(mailspool,ms_len,"%s/",optarg);
                  } else {
                    fprintf(stderr,"%s: unable to allocate memory on the stack\n",argv[0]);
                    exit(EXIT_FAILURE);
                  }
                } else {
                  mailspool = optarg; 
                }
                break;
      case 'D': use_pop3_allow_deny = 1; break;
      case 'L': local_mbox = optarg; break;
      case 'h': usage(argv[0]); exit(EXIT_SUCCESS);
      case 'v': printf("%s %s\n","akpop3d",VERSION); exit(EXIT_SUCCESS);
      default:
        fprintf(stderr,"%s: %s\n",argv[0],"unknown option - try '-h'");
        exit(EXIT_FAILURE);
    }
  } while (c != -1);

  if (port==NULL)
    port = "110";

  if (become_daemon) {
    daemonize();
    write_pid();
  }

  progname = strrchr(argv[0], '/');
  if (progname == NULL) {
    progname = argv[0];
  } else {
    progname++;
  }
  openlog(progname, LOG_PID, LOG_MAIL);

  listenfd = Tcp_listen(listenhost, port, &addrlen);

  Signal(SIGCHLD,sig_chld);

  Signal(SIGTERM,sig_term);
  Signal(SIGINT,sig_term);
  Signal(SIGHUP,sig_term);
  Signal(SIGQUIT,sig_term);

  for (;;) {
    if ((connfd = accept(listenfd,NULL,NULL)) < 0) {
      if (errno == EINTR) {
        continue;
      } else {
        syslog(LOG_ERR,"%s: %s","failed to accept connection",strerror(errno));
        continue;
      }
    }

    if ((childpid = fork()) == 0) {
      close(listenfd);
      pop3_session(connfd);
      exit(0);
    } else if (childpid == -1) {
      syslog(LOG_ERR,"%s: %s","fork failed (aborting)",strerror(errno));
    }
    close(connfd);
  }

  /* NOTREACHED */
  return 0;
}
