--- /dev/null
+/*
+** Copyright 2012 Double Precision, Inc. See COPYING for
+** distribution information.
+*/
+
+#if HAVE_CONFIG_H
+#include "courier_auth_config.h"
+#endif
+#include "auth.h"
+#include "courierauthdebug.h"
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+static const char *configfilename=0;
+static char *configauth=0;
+static size_t configauth_size=0;
+
+#define err courier_auth_err
+
+const char *authgetconfig(const char *filename, const char *env)
+{
+ size_t i;
+ char *p=0;
+ int l=strlen(env);
+
+ if (configfilename && strcmp(filename, configfilename))
+ {
+ if (configauth)
+ free(configauth);
+ configauth=0;
+ configauth_size=0;
+ }
+
+ configfilename=filename;
+
+ if (!configauth)
+ {
+ FILE *f=fopen(filename, "r");
+ struct stat buf;
+
+ if (!f) return (0);
+ if (fstat(fileno(f), &buf) ||
+ (configauth=malloc(buf.st_size+2)) == 0)
+ {
+ fclose(f);
+ return (0);
+ }
+ if (fread(configauth, buf.st_size, 1, f) != 1)
+ {
+ free(configauth);
+ configauth=0;
+ fclose(f);
+ return (0);
+ }
+ configauth[configauth_size=buf.st_size]=0;
+
+ for (i=0; i<configauth_size; i++)
+ if (configauth[i] == '\n')
+ { /* siefca@pld.org.pl */
+ if (!i || configauth[i-1] != '\\')
+ {
+ configauth[i]='\0';
+ }
+ else
+ {
+ configauth[i]=configauth[i-1]= ' ';
+ }
+ }
+ fclose(f);
+ }
+
+ for (i=0; i<configauth_size; )
+ {
+ p=configauth+i;
+ if (memcmp(p, env, l) == 0 &&
+ isspace((int)(unsigned char)p[l]))
+ {
+ p += l;
+ while (*p && *p != '\n' &&
+ isspace((int)(unsigned char)*p))
+ ++p;
+ break;
+ }
+
+ while (i < configauth_size)
+ if (configauth[i++] == 0) break;
+ }
+
+ if (i < configauth_size)
+ return (p);
+ return (0);
+}
+
+/* siefca@pld.org.pl */
+#define MAX_SUBSTITUTION_LEN 32
+#define SV_BEGIN_MARK "$("
+#define SV_END_MARK ")"
+#define SV_BEGIN_LEN ((sizeof(SV_BEGIN_MARK))-1)
+#define SV_END_LEN ((sizeof(SV_END_MARK))-1)
+
+/* siefca@pld.org.pl */
+struct var_data {
+ const char *name;
+ const char *value;
+ const size_t size;
+ size_t value_length;
+ } ;
+
+/* siefca@pld.org.pl */
+typedef int (*parsefunc)(const char *, size_t, void *);
+
+/* siefca@pld.org.pl */
+static struct var_data *get_variable (const char *begin, size_t len,
+ struct var_data *vdt)
+{
+struct var_data *vdp;
+
+ if (!begin || !vdt) /* should never happend */
+ {
+ err("get_variable: critical error while "
+ "parsing substitution variable");
+ return NULL;
+ }
+ if (len < 1)
+ {
+ err("get_variable: unknown empty substitution "
+ "variable - aborting");
+ return NULL;
+ }
+ if (len > MAX_SUBSTITUTION_LEN)
+ {
+ err("get_variable: variable name too long "
+ "while parsing substitution. "
+ "name begins with "
+ SV_BEGIN_MARK
+ "%.*s...", MAX_SUBSTITUTION_LEN, begin);
+ return NULL;
+ }
+
+ for (vdp=vdt; vdp->name; vdp++)
+ if (vdp->size == len+1 &&
+ !strncmp(begin, vdp->name, len))
+ {
+ if (!vdp->value)
+ vdp->value = "";
+ if (!vdp->value_length) /* length cache */
+ vdp->value_length = strlen (vdp->value);
+ return vdp;
+ }
+
+ err("get_variable: unknown substitution variable "
+ SV_BEGIN_MARK
+ "%.*s"
+ SV_END_MARK
+ , (int)len, begin);
+
+ return NULL;
+}
+
+/* siefca@pld.org.pl */
+static int ParsePlugin_counter (const char *p, size_t length, void *vp)
+{
+ if (!p || !vp || length < 0)
+ {
+ err("get_variable: bad arguments while counting "
+ "query string");
+ return -1;
+ }
+
+ *((size_t *)vp) += length;
+
+ return 0;
+}
+
+/* siefca@pld.org.pl */
+static int ParsePlugin_builder (const char *p, size_t length, void *vp)
+{
+char **strptr = (char **) vp;
+
+ if (!p || !vp || length < 0)
+ {
+ err("get_variable: bad arguments while building "
+ "query string");
+ return -1;
+ }
+
+ if (!length) return 0;
+ memcpy ((void *) *strptr, (void *) p, length);
+ *strptr += length;
+
+ return 0;
+}
+
+/* siefca@pld.org.pl */
+static int parse_core (const char *source, struct var_data *vdt,
+ parsefunc outfn, void *result)
+{
+size_t v_size = 0,
+ t_size = 0;
+const char *p, *q, *e,
+ *v_begin, *v_end,
+ *t_begin, *t_end;
+struct var_data *v_ptr;
+
+ if (!source)
+ source = "";
+ if (!result)
+ {
+ err("auth_parse: no memory allocated for result "
+ "while parser core was invoked");
+ return -1;
+ }
+ if (!vdt)
+ {
+ err("auth_parse: no substitution table found "
+ "while parser core was invoked");
+ return -1;
+ }
+
+ q = source;
+ while ( (p=strstr(q, SV_BEGIN_MARK)) )
+ {
+ e = strstr (p, SV_END_MARK);
+ if (!e)
+ {
+ err("auth_parse: syntax error in "
+ "substitution "
+ "- no closing symbol found! "
+ "bad variable begins with:"
+ "%.*s...", MAX_SUBSTITUTION_LEN, p);
+ return -1;
+ }
+
+ /*
+ **
+ ** __________sometext$(variable_name)_________
+ ** | | | |
+ ** t_begin' t_end' `v_begin `v_end
+ **
+ */
+
+ v_begin = p+SV_BEGIN_LEN; /* variable field ptr */
+ v_end = e-SV_END_LEN; /* variable field last character */
+ v_size = v_end-v_begin+1;/* variable field length */
+
+ t_begin = q; /* text field ptr */
+ t_end = p-1; /* text field last character */
+ t_size = t_end-t_begin+1;/* text field length */
+
+ /* work on text */
+ if ( (outfn (t_begin, t_size, result)) == -1 )
+ return -1;
+
+ /* work on variable */
+ v_ptr = get_variable (v_begin, v_size, vdt);
+ if (!v_ptr) return -1;
+
+ if ( (outfn (v_ptr->value, v_ptr->value_length, result)) == -1 )
+ return -1;
+
+ q = e + 1;
+ }
+
+ /* work on last part of text if any */
+ if (*q != '\0')
+ if ( (outfn (q, strlen(q), result)) == -1 )
+ return -1;
+
+ return 0;
+}
+
+/* siefca@pld.org.pl */
+static char *parse_string (const char *source, struct var_data *vdt)
+{
+struct var_data *vdp = NULL;
+char *output_buf = NULL,
+ *pass_buf = NULL;
+size_t buf_size = 2;
+
+ if (source == NULL || *source == '\0' ||
+ vdt == NULL || vdt[0].name == NULL)
+ {
+ err("auth_parse: source clause is empty "
+ "- this is critical error");
+ return NULL;
+ }
+
+ /* zero var_data length cache - important! */
+ for (vdp=vdt; vdp->name; vdp++)
+ vdp->value_length = 0;
+
+
+ /* phase 1 - count and validate string */
+ if ( (parse_core (source, vdt, &ParsePlugin_counter, &buf_size)) != 0)
+ return NULL;
+
+ /* phase 2 - allocate memory */
+ output_buf = malloc (buf_size);
+ if (!output_buf)
+ {
+ perror ("malloc");
+ return NULL;
+ }
+ pass_buf = output_buf;
+
+ /* phase 3 - build the output string */
+ if ( (parse_core (source, vdt, &ParsePlugin_builder, &pass_buf)) != 0)
+ {
+ free (output_buf);
+ return NULL;
+ }
+ *pass_buf = '\0';
+
+ return output_buf;
+}
+
+static char *local_part_escaped(const char *username,
+ char *(*escape_func)(const char *, size_t))
+{
+ const char *p=strchr(username, '@');
+ size_t n=p ? p-username:strlen(username);
+
+ return escape_func(username, n);
+}
+
+static char *domain_part_escaped(const char *username,
+ const char *defdomain,
+ char *(*escape_func)(const char *, size_t))
+{
+ const char *p=strchr(username, '@');
+ size_t n;
+
+ if (p)
+ ++p;
+ else
+ p=defdomain;
+
+ n=strlen(p);
+
+ return escape_func(p, n);
+}
+
+static int local_and_domain_part_escaped(char *(*escape_func)(const char *, size_t),
+ const char *username,
+ const char *defdomain,
+ char **local_ret,
+ char **domain_ret)
+{
+ if ((*local_ret=local_part_escaped(username, escape_func)) == NULL)
+ return 0;
+
+ if ((*domain_ret=domain_part_escaped(username, defdomain,
+ escape_func)) == NULL)
+ {
+ free(*local_ret);
+ return 0;
+ }
+
+ return 1;
+}
+
+/* siefca@pld.org.pl */
+char *auth_parse_select_clause (char *(*escape_func)(const char *, size_t),
+ const char *clause, const char *username,
+ const char *defdomain,
+ const char *service)
+{
+ char *str;
+
+ static struct var_data vd[]={
+ {"local_part", NULL, sizeof("local_part"), 0},
+ {"domain", NULL, sizeof("domain"), 0},
+ {"service", NULL, sizeof("service"), 0},
+ {NULL, NULL, 0, 0}};
+
+ char *l_part;
+ char *d_part;
+
+ if (clause == NULL || *clause == '\0' ||
+ !username || *username == '\0')
+ return NULL;
+
+ if (!local_and_domain_part_escaped(escape_func,
+ username, defdomain,
+ &l_part, &d_part))
+ return NULL;
+
+ vd[0].value=l_part;
+ vd[1].value=d_part;
+ vd[2].value = service;
+
+ str=parse_string (clause, vd);
+ free(l_part);
+ free(d_part);
+ return str;
+}
+
+/* siefca@pld.org.pl */
+char *auth_parse_chpass_clause (char *(*escape_func)(const char *, size_t),
+ const char *clause, const char *username,
+ const char *defdomain, const char *newpass,
+ const char *newpass_crypt)
+{
+ char *str;
+
+ static struct var_data vd[]={
+ {"local_part", NULL, sizeof("local_part"), 0},
+ {"domain", NULL, sizeof("domain"), 0},
+ {"newpass", NULL, sizeof("newpass"), 0},
+ {"newpass_crypt", NULL, sizeof("newpass_crypt"), 0},
+ {NULL, NULL, 0, 0}};
+ char *l_part;
+ char *d_part;
+
+ if (clause == NULL || *clause == '\0' ||
+ !username || *username == '\0' ||
+ !newpass || *newpass == '\0' ||
+ !newpass_crypt || *newpass_crypt == '\0') return NULL;
+
+ if (!local_and_domain_part_escaped(escape_func,
+ username, defdomain,
+ &l_part, &d_part))
+ return NULL;
+
+ vd[0].value=l_part;
+ vd[1].value=d_part;
+ vd[2].value = newpass;
+ vd[3].value = newpass_crypt;
+
+ if (!vd[0].value || !vd[1].value ||
+ !vd[2].value || !vd[3].value)
+ {
+ free(l_part);
+ free(d_part);
+ return NULL;
+ }
+
+ str=parse_string (clause, vd);
+ free(l_part);
+ free(d_part);
+ return str;
+}