#include "config.h"
#include <pop_pass.h>
#include <sieve_lock.h>

#include "acap_commands.h"
#include "util.h"
#include "log.h"

#include <errno.h>
#include <time.h>
#include <unistd.h>
#include <pwd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>

/* DEBUG */
#include <stdio.h>

static CommandResult (*next_func)(char*);
static char home_path[MAX_PATH_LEN+1];

void
acapCommandsInit()
{
  acap_state = NonAuth;
}

CommandResult
acapStore(char *tag, StoreStruct *data)
{
  FILE *attrib_file;
  char path[MAX_PATH_LEN+1];
  char *attrib;
  LockStruct lock;

  if (strcmp(data->attribute, "account.sieve.script")) {
    sprintf(command_result, "%s NO \"Unsupported attribute\"\r\n", tag);
    return CommandDone;
  }
  attrib = SIEVE_SCRIPT_FILE;

  strcpy(path, home_path);
  strncat(path, attrib, sizeof(path));
  path[MAX_PATH_LEN] = 0;

  lock = ObtainLock(home_path, LOCK_FILE);
  if (!lock.result) {
    sprintf(command_result, "%s NO \"Error obtaining file lock\"\r\n", tag);
    return CommandDone;
  }

  attrib_file = fopen(path, "wb");
  if (!attrib_file) {
    sprintf(command_result, "%s NO \"Error open file for writing\"\r\n", tag);
    return CommandDone;
  }

  if (!fwrite(data->value, strlen(data->value), 1, attrib_file)) {
    fclose(attrib_file);
    chmod(path, 0600);
    sprintf(command_result, "%s NO \"Error writing attribute\"\r\n", tag);
    return CommandDone;
  }

  fclose(attrib_file);
  chmod(path, 0600);
  ReleaseLock(lock);

  sprintf(command_result, "%s OK \"STORE completed\"\r\n", tag);
  return CommandDone;
}

static int search_pos;
static SearchStruct *ss;

CommandResult
acapSearchMore(char *tag)
{
  struct tm *modtime;
  time_t clock;
  FILE *attrib_file;
  int ch;
  int buf_loc;
  char path[MAX_PATH_LEN+1];
  char value[MAX_VALUE_LEN+1];
  LockStruct lock;

  if (search_pos==0) {
    next_func = acapSearchMore;
    search_pos++;
    command_result[0] = 0;

    if (!strcmp(ss->attribute, "account.sieve.script")) {
      strcpy(path, home_path);
      strncat(path, SIEVE_SCRIPT_FILE, sizeof(path));
      path[MAX_PATH_LEN] = 0;
      
      lock = ObtainLock(home_path, LOCK_FILE);
      if (!lock.result) {
	sprintf(command_result, "%s NO \"Could not obtain file lock\"\r\n",
		tag);
	return CommandDone;
      }

      if (!(attrib_file = fopen(path, "rb"))) {
	ReleaseLock(lock);
	return CommandMore;
      }

      buf_loc = 0;
      while ((ch = getc(attrib_file))!=EOF) {
	if (buf_loc>=MAX_VALUE_LEN)
	  break;
	value[buf_loc++] = ch;
      }
      value[buf_loc] = 0;
      fclose(attrib_file);
      ReleaseLock(lock);

      sprintf(command_result, "%s ENTRY \"\" {%d}\r\n",
	      tag, strlen(value));
      strncat(command_result, value,
	      sizeof(command_result)-strlen(command_result)-1);
      path[MAX_PATH_LEN] = 0;
      strncat(command_result, "\r\n",
	      sizeof(command_result)-strlen(command_result)-1);
      path[MAX_PATH_LEN] = 0;
    
    }
    return CommandMore;
  }
  else if (search_pos==1) {
    clock = time(NULL);
    modtime = gmtime(&clock);
    sprintf(command_result, "%s MODTIME \"%04d%02d%02d%02d%02d%02d00\"\r\n",
	    tag, 1900+modtime->tm_year, modtime->tm_mon+1,
	    modtime->tm_mday, modtime->tm_hour, modtime->tm_min,
	    modtime->tm_sec);
    next_func = acapSearchMore;
    search_pos++;
    return CommandMore;
  }
  else {
    sprintf(command_result, "%s OK \"SEARCH completed\"\r\n", tag);
    return CommandDone;
  }
}

CommandResult
acapSearch(char *tag, SearchStruct *data)
{
  search_pos = 0;
  ss = data;

  return acapSearchMore(tag);
}

static int login_attempt = 0;

CommandResult
acapAuth(char *tag, AuthStruct *data)
{
  struct passwd *pw;
  char *pass_ptr;

  strToUpper(data->type);

  if (strcmp("PLAIN", data->type)) {
    sprintf(command_result,
	    "%s NO \"Authentication method not supported\"\r\n", tag);
    return CommandDone;
  }

  if (++login_attempt>MAX_LOGINS) {
    sprintf(command_result, "%s NO \"Too many login attempts\"\r\n", tag);
    return CommandDone;
  }

  for (pass_ptr=data->param; *pass_ptr&&*pass_ptr!=' '; pass_ptr++);
  if (*pass_ptr==' ') {
    *(pass_ptr++) = 0;
  }

  if (!pop_pass(data->param, pass_ptr)) {
    acap_state = Auth;
    lprintf(LOG_INFO, "User %s logged in", data->param);

    pw = getpwnam(data->param);
    if (pw) {
      if (strlen(pw->pw_dir)<MAX_PATH_LEN&&
	  pw->pw_dir[strlen(pw->pw_dir)-1]!='/') {
	sprintf(home_path, "%s/", pw->pw_dir);
      }
      else if (strlen(pw->pw_dir)<=MAX_PATH_LEN) {
	sprintf(home_path, "%s", pw->pw_dir);
      }
      else {
	sprintf(home_path, "/");
      }
      
      /* set uid */
      if(setuid(pw->pw_uid)) {
	lprintf(LOG_ERR, "Unable to set UID for logged in user");
	exit(-1);
      }
    }
    else {
      /* unable to obtain password entry */
      lprintf(LOG_ERR, "Unable to obtain password entry");
      exit(-1);
    }

    sprintf(command_result, "%s OK \"Logged in\"\r\n", tag);
    return CommandDone;
  }

  lprintf(LOG_INFO, "Login failed with username \"%s\"", data->param);
  sprintf(command_result, "%s NO \"Authentication failed\"\r\n", tag);
  return CommandDone;
}

CommandResult
commandMore(char *tag)
{
  return (*next_func)(tag);
}
