/*****************************************************************************

  POPular -- A POP3 server and proxy for large mail systems

  $Id: libpdm_db4.c,v 1.1 2003/12/01 13:30:55 sqrt Exp $

  http://www.remote.org/jochen/mail/popular/

******************************************************************************

  Copyright (C) 1999-2003  Jochen Topf <jochen@remote.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.

  This program 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 this program; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA

*****************************************************************************/

#include <db4/db.h>
#include <popular.h>


static struct pdm_mvar *mv;

struct pdm_ctx_db4 {
  char dirname[MAXBUF];
};


/*****************************************************************************
 
  pdm_init()
 
  Currently the filename is only saved in the init function and the file
  will be opened every time an authentication is done. This might change
  in a future version.
 
*****************************************************************************/
char *
pdm_init(int argc, char *argv[], struct pdm_mvar *mvar, void **pdmctx)
{
  struct pdm_ctx_db4 *ctx;
  mv = mvar;

  PDM_DEBUG3("pdm_init", "start with argc=%d id='%s' name='%s'", argc, argv[0], argv[1]);
  
  if (argc != 3) {
    PDM_DEBUG0("pdm_init", "argc != 1");
    return("not enough or too many arguments");
  }

  ctx = malloc(sizeof(struct pdm_ctx_db4));
  if (! ctx) {
    PDM_DEBUG0("pdm_init", "out of memory");
    return("out of memory");
  }
  strlcpy(ctx->dirname, argv[2], sizeof(ctx->dirname));

  *pdmctx = ctx;
  PDM_DEBUG1("pdm_init", "module initialized (dirname='%s')", ctx->dirname);
  return NULL;
}


/*****************************************************************************
 
  pdm_close()

*****************************************************************************/
int
pdm_close(void *pdmctx)
{
  free(pdmctx);
  return 0;
}


/*****************************************************************************
 
  pdm_args()

*****************************************************************************/
char *
pdm_args(void *pdmctx)
{
  struct pdm_ctx_db4 *ctx = pdmctx;
  return ctx->dirname;
}


/*****************************************************************************
 
  pdm_reload()

  We don't have to do anything in this module. Just return true.

*****************************************************************************/
int
pdm_reload(void *pdmctx)
{
  return 1;
}


/*****************************************************************************
 
  pdm_check()

*****************************************************************************/
pdm_result_t
pdm_check(void *pdmctx, const struct pdm_request *pdmr, struct pdm_data *pdmd)
{
  struct pdm_ctx_db4 *ctx = pdmctx;
  DBT key, data;
  DB *db;
  int result;
  char *pw, *ho, *li;
  char namebuf[80];
  char resbuf[1024];
  int dberrno;
  int len;

  PDM_DEBUG1("pdm_check", "started ns='%s'", pdmr->namespace);

  /* translate user name to lowercase */
  {
    char *p;
    for (p=pdmr->user; *p; p++) *p = tolower(*p);
  }

  /* Name of dbm file */
  snprintf(namebuf, sizeof(namebuf), "%s/%s.db", ctx->dirname, pdmr->namespace);

  /* Create database handle */
  dberrno = db_create(&db, NULL, 0);
  if (dberrno) {
    PDM_DEBUG2("pdm_check", "db_create failed errno=%d errstr='%s'", dberrno, strerror(dberrno));
    return pdmError;
  }

  /* Open dbm file */
  PDM_DEBUG1("pdm_check", "opening db file '%s'", namebuf);
  dberrno = db->open(db, namebuf, NULL, DB_HASH, DB_RDONLY, 0);

  if (dberrno) {
    PDM_DEBUG3("pdm_check", "dbopen_error errno=%d errstr='%s' file='%s'", dberrno, strerror(dberrno), namebuf);
    return pdmError;
  }

  PDM_DEBUG1("pdm_check", "db file '%s' opened", namebuf);
  memset(&key, 0, sizeof(key));
  memset(&data, 0, sizeof(data));
  key.data = pdmr->user;
  key.size = strlen(pdmr->user);

  /* Get data from Berkeley DB */ 
  PDM_DEBUG0("pdm_check", "getting data from db");
  result = (db->get)(db, NULL, &key, &data, 0);
  if (result == DB_NOTFOUND) {
    (void) db->close(db, 0);
    PDM_DEBUG0("pdm_check", "user not found");
    return pdmUnknown;
  } else if (result != 0) {
    PDM_DEBUG2("pdm_check", "dbget_error errno=%d errmsg='%s'", result, strerror(result));
    (void) db->close(db, 0);
    return pdmError;
  }

  PDM_DEBUG0("pdm_check", "got data from db");
  memcpy(resbuf, data.data, data.size < sizeof(resbuf) ? data.size : sizeof(resbuf));
  resbuf[data.size] = 0;

  pw = strtok(resbuf, ":");     /* password */
  ho = strtok(NULL, ":");       /* server number */
  li = strtok(NULL, ":");       /* mailbox file */
  if (!ho || !li) {
    PDM_DEBUG0("pdm_check", "format error");
    (void) db->close(db, 0);
    return pdmError;
  }

  len = strlcpy(pdmd->backend, ho, MAXLEN_ID);
  if (len >= MAXLEN_ID) return pdmError;

  len = strlcpy(pdmd->user, li, MAXLEN_USERNAME);
  if (len >= MAXLEN_USERNAME) return pdmError;

  PDM_DEBUG2("pdm_check", "got user/mailbox='%s' backend='%s'", pdmd->user, pdmd->backend);

  /* Check password */
  if (!strcmp(pw, crypt(pdmr->pass, pw))) {
    (void) db->close(db, 0);
    PDM_DEBUG0("pdm_check", "password match, returning");
    len = strlcpy(pdmd->pass, pdmr->pass, MAXLEN_PASSWORD);
    if (len >= MAXLEN_PASSWORD) return pdmError;
    return pdmAccept;
  }

  (void) db->close(db, 0);

  PDM_DEBUG0("pdm_check", "password mismatch, returning");
  pdmd->reason = pdmFailPassword;
  return pdmFail;
}


/** THE END *****************************************************************/


syntax highlighted by Code2HTML, v. 0.9.1