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

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

  $Id: pdm.c,v 1.9 2001/04/08 18:23:57 sqrt Exp $

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

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

  Copyright (C) 1999-2001  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

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

#if HAVE_CONFIG_H
# include <config.h>
#endif

#include "popular.h"


extern int debug;

extern struct pdm_module *first_pdm_module;

struct pdm_mvar mvar;


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

  pdm_attach()

  dir  - the directory where the module can be found
  argc - number of arguments in argv
  argv - arguments
	 argv[0] - id (symbolic name) for this instance of the module
         argv[1] - name of module without the prefix 'pdm_' and suffix '.so'
	 argv[2] - first argument etc.

*****************************************************************************/
struct pdm_module *
pdm_attach(const char *dir, int argc, char *argv[])
{
  char soname[MAXLEN_SONAME];
  void *lib;
  struct pdm_module *module;
  char *errtext;
  int n;

  strlcpy(soname, dir,        sizeof(soname));
  strlcat(soname, "/libpdm_", sizeof(soname));
  strlcat(soname, argv[1],    sizeof(soname));
  strlcat(soname, ".so",      sizeof(soname));

  lib = dlopen(soname, RTLD_NOW);
  if (! lib) {
    DEBUG2(DG_AUTH, "pdm_attach", "could not open database module pdm_%s.so: %s", argv[1], dlerror());
    return NULL;
  }

  module = malloc(sizeof(struct pdm_module));
  if (! module) {
    DEBUG0(DG_AUTH, "pdm_attach", "out of memory");
    return NULL;
  }

  strlcpy(module->id,   argv[0], sizeof(module->id));
  strlcpy(module->name, argv[1], sizeof(module->name));
  module->next   = NULL;
  module->lib    = lib;
  module->init   = dlsym(lib, "pdm_init");
  module->reload = dlsym(lib, "pdm_reload");
  module->close  = dlsym(lib, "pdm_close");
  module->args   = dlsym(lib, "pdm_args");
  module->check  = dlsym(lib, "pdm_check");
  module->auth   = dlsym(lib, "pdm_auth");

  if (! module->init) {
    DEBUG1(DG_AUTH, "pdm_attach", "pdm_init() missing in pdm_%s.so module", argv[1]);
    goto error;
  }
  if (! module->close) {
    DEBUG1(DG_AUTH, "pdm_attach", "pdm_close() missing in pdm_%s.so module", argv[1]);
    goto error;
  }
  if (! module->check && ! module->auth) {
    DEBUG1(DG_AUTH, "pdm_attach", "pdm_check() and pdm_auth() missing in pdm_%s.so module", argv[1]);
    goto error;
  }

  DEBUG1(DG_AUTH, "pdm_attach", "calling pdm_init() with argc=%d", argc);
  for (n=0; n<argc; n++) {
    DEBUG2(DG_AUTH, "pdm_attach", "calling pdm_init() with argv[%d]=%s", n, argv[n]);
  }

  /* these pointers are needed by the module for debug logging */
  mvar.debug = &debug;
  mvar.xlog_printf = xlog_printf;
  errtext = (*(module->init))(argc, argv, &mvar, &(module->ctx));
  if (errtext) {
    DEBUG2(DG_AUTH, "pdm_attach", "init of pdm_%s.so module failed: %s", argv[1], errtext);
    goto error;
  }

  return module;

error:
  free(module);
  dlclose(lib);
  return NULL;
}


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

  pdm_detach()

*****************************************************************************/
int
pdm_detach(struct pdm_module *m)
{
  int r = m->close(m->ctx);
  dlclose(m->lib);
  free(m);
  return r;
}


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

  pdm_get_args()

*****************************************************************************/
char *
pdm_get_args(struct pdm_module *m)
{
  if (m->args) return (*(m->args))(m->ctx);
  else         return "[unknown args]";
}


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

  pdm_call_reload()

*****************************************************************************/
int
pdm_call_reload(struct pdm_module *m)
{
  if (m->reload) return (*(m->reload))(m->ctx);
  /* XLOG-DOC:ADM:004c:pdm_reload_failed
   * The reloading of a pdm module failed because this module doesn't
   * support the pdm_reload() function. */
  xlog_printf(xlog_adm, 0x004c, "pdm_reload_failed");
  return 0;
}


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

  pmd_auth_user()

*****************************************************************************/
pdm_result_t
pdm_auth_user(const struct pdm_request *pr, struct pdm_data *pd)
{
  struct pdm_module *e;
  pdm_result_t result;

  DEBUG0(DG_AUTH, "pdm_auth_user", "start");

  memset(pd, 0, sizeof(struct pdm_data));

  for (e = first_pdm_module; e; e = e->next) {
    if (! e->check) continue;
    DEBUG1(DG_AUTH, "pdm_auth_user", "Trying module '%s' (check)", e->id);
    result = (*(e->check))(e->ctx, pr, pd);
    switch (result) {
      case pdmAccept:
        DEBUG0(DG_AUTH, "pdm_auth_user", "User accepted");
        return pdmAccept;
      case pdmFail:
	DEBUG3(DG_AUTH, "pdm_auth_user", "User exists, but auth failed (backend='%s' user='%s' pass='%s')", pd->backend, pd->user, pd->pass);
	goto try_auth_only;
      case pdmError:
	/* XLOG-DOC:SOS:004d:pdm_error
	 * A module returned an internal error while checking a user. This
	 * means that the module is confused, maybe the underlain database
	 * is corrupted. Check for log messages from the module itself to
	 * find out more. */
	xlog_printf(xlog_sos, 0x004d, "pdm_error module='%s'", e->id);
	break;
      case pdmUnknown:
	break;
    }
  }
  DEBUG0(DG_AUTH, "pdm_auth_user", "No module found the user");
  return pdmUnknown;

try_auth_only:
  for (e = first_pdm_module; e; e = e->next) {
    if (! e->auth) continue;
    DEBUG1(DG_AUTH, "pdm_auth_user", "Trying module '%s' (auth)", e->id);
    result = (*(e->auth))(e->ctx, pr, pd);
    switch (result) {
      case pdmAccept:
        DEBUG0(DG_AUTH, "pdm_auth_user", "User accepted");
        return pdmAccept;
      case pdmError:
	/* XLOG-DOC:SOS:004e:pdm_error
	 * A module returned an internal error while checking a user. This
	 * means that the module is confused, maybe the underlain database
	 * is corrupted. Check for log messages from the module itself to
	 * find out more. */
	xlog_printf(xlog_sos, 0x004e, "pdm_error module='%s'", e->id);
	break;
      case pdmFail:
	break;
      case pdmUnknown:
	break;
    }
  }
  DEBUG0(DG_AUTH, "pdm_auth_user", "User exists, but couldn't be authenticated");
  return pdmFail;
}


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


syntax highlighted by Code2HTML, v. 0.9.1