Logo Search packages:      
Sourcecode: leafnode version File versions

miscutil.c

/*
libutil -- miscellaneous stuff

Written by Arnt Gulbrandsen <agulbra@troll.no> and copyright 1995
Troll Tech AS, Postboks 6133 Etterstad, 0602 Oslo, Norway, fax +47
22646949.
Modified by Cornelius Krasel <krasel@wpxx02.toxi.uni-wuerzburg.de>
and Randolf Skerka <Randolf.Skerka@gmx.de>.
Copyright of the modifications 1997.
Modified by Kent Robotti <robotti@erols.com>. Copyright of the
modifications 1998.
Modified by Markus Enzenberger <enz@cip.physik.uni-muenchen.de>.
Copyright of the modifications 1998.
Modified by Cornelius Krasel <krasel@wpxx02.toxi.uni-wuerzburg.de>.
Copyright of the modifications 1998, 1999.
Modified by Matthias Andree <matthias.andree@gmx.de>.
Copyright of the modifications 1999 - 2002.
Modified and copyright of the modifications 2002 by Ralf Wildenhues
<ralf.wildenhues@gmx.de>.

See file COPYING for restrictions on the use of this software.
*/

#include "leafnode.h"
#include "validatefqdn.h"
#include "strlcpy.h"
#include "mastring.h"
#include "ln_log.h"

#include <fcntl.h>
#include <sys/uio.h>
#include <sys/param.h>
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <stdlib.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>
#include <syslog.h>
#include <pwd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include "system.h"
#include <signal.h>
#include <stdarg.h>

static void whoami(void);

static void suicide(void) {
    /* just in case */
    fflush(stdout);
    fflush(stderr);
    raise(SIGKILL);
}

char fqdn[FQDNLEN + 1] = "";

static const mode_t default_umask = 0002;

/* xoverutil global vars */
struct xoverinfo *xoverinfo;
unsigned long xfirst, xlast;

static int
createmsgiddir(void) {
    mastr *dir = mastr_new(1024);
    mastr *mid = mastr_new(1024);
    DIR *d;
    int rc = 0;
    int havedir[1000] = {0};

    mastr_vcat(dir, spooldir, "/message.id", NULL);
    d = opendir(mastr_str(dir));
    if (d) {
      struct dirent *de;
      unsigned long u;
      const char *t;
      char *e;

      /* read directory - should be faster than stat */
      while(errno = 0, de = readdir(d)) {
          t = de->d_name;
          if (isdigit((unsigned char)*t)) {
            u = strtoul(t, &e, 10);
            if (e > t)
                havedir[u] = 1;
          }
      }

      if (errno)
          ln_log(LNLOG_SERR, LNLOG_CTOP, "error reading directory %s: %m",
                mastr_str(dir));

      closedir(d);

      /* create missing */
      for(u = 0; u < 1000; u++) {
          char b[4];

          snprintf(b, sizeof(b), "%03lu", u);
          mastr_clear(mid);
          if (!havedir[u]) {
            mastr_vcat(mid, spooldir, "/message.id/", b, NULL);
            if (mkdir(mastr_str(mid), 02755)) {
                ln_log(LNLOG_SERR, LNLOG_CTOP, "error creating directory %s: %m",
                      mastr_str(mid));
                break;
            }
          }
      }
    } else {
      ln_log(LNLOG_SERR, LNLOG_CTOP, "cannot read %s: %m", mastr_str(dir));
      rc = -1;
    }

    mastr_delete(mid);
    mastr_delete(dir);
    return rc;
}

static struct { const char* name; mode_t mode; } dirs[] = {
    {"", 04755 },
    {"interesting.groups", 02775 },
    {"leaf.node", 0755 },
    {"failed.postings", 02775 },
    {"message.id", 0755 },
    {"out.going", 0755 },
    {"temp.files", 0755 },
};

static const int dirs_count = sizeof(dirs)/sizeof(dirs[0]);

/*
 * initialize all global variables
 */
/*@-globstate@*/
int
initvars(char *progname)
{
#ifndef TESTMODE
    struct passwd *pw;
#endif
    char s[SIZE_s+1];
    int i;
    char *t;

    active = NULL;
    xoverinfo = NULL;
    xfirst = 0;
    xlast = 0;

    /* config.c stuff does not have to be initialized */

    expire_base = NULL;
    servers = NULL;

    t = getenv("LN_DEBUG");
    if (t)
      debugmode = atoi(t);

    (void)umask(default_umask);
    if (strlen(spooldir) != strspn(spooldir, PORTFILENAMECSET "/")) {
      /* verrecke! */
      syslog(LOG_CRIT, "Fatal: spooldir contains illegal characters. "
             "Recompile leafnode with a proper spooldir setting.");
      suicide();
    }

#ifndef TESTMODE
    pw = getpwnam(NEWS_USER);
    if (!pw) {
      fprintf(stderr, "no such user: %s\n", NEWS_USER);
      return FALSE;
    }
#endif

    /* These directories should exist anyway */
    for (i = 0 ; i < dirs_count ; i++) {
      xsnprintf(s, SIZE_s, "%s/%s", spooldir, dirs[i].name);
      if ((mkdir(s, dirs[i].mode) && errno != EEXIST)
            || chmod(s, dirs[i].mode)
#ifndef TESTMODE
      || chown(s, pw->pw_uid, pw->pw_gid)
#endif
         ) {
          int e = errno;
          struct stat st;
          if (stat(s, &st)
#ifndef TESTMODE
                || st.st_uid != pw->pw_uid
#endif
             ) {
            fprintf(stderr, "Warning: cannot create %s with proper ownership: %s\nMake sure you run this program as user root or %s.\n", s, strerror(e),
                  NEWS_USER);
            syslog(LOG_WARNING, "Warning: cannot create %s with proper ownership: %s", s, strerror(e));
            suicide();
          }
      }
    }

    whoami();

#ifndef TESTMODE
    if (progname) {
#ifdef HAVE_SETGID
      setgid(pw->pw_gid);
#else
      setregid(pw->pw_gid, pw->pw_gid);
#endif

#ifdef HAVE_SETUID
      setuid(pw->pw_uid);
#else
      setreuid(pw->pw_uid, pw->pw_uid);
#endif
      if (getuid() != pw->pw_uid || getgid() != pw->pw_gid) {
          syslog(LOG_ERR, "%s: must be run as %s or root\n", progname, NEWS_USER);
          fprintf(stderr, "%s: must be run as %s or root\n", progname, NEWS_USER);
          endpwent();
          return FALSE;
      }
    }
#endif                        /* not TESTMODE */
    endpwent();

    if (chdir(spooldir) || (i = open(".", O_RDONLY)) < 0) {
      int e = errno;
      syslog(LOG_CRIT, "Fatal: cannot change to or open spooldir: %m");
      fprintf(stderr, "Fatal: cannot change or open spooldir: %s\n",
            strerror(e));
      suicide();
    }
    (void)close(i);

    /* create missing message.id directories */
    if (createmsgiddir())
      return FALSE;

    return TRUE;
}

/*@=globstate@*/

/*
 * check whether "groupname" is represented in interesting.groups without
 * touching the file
 */
int
isinteresting(const char *groupname)
{
    DIR *d;
    struct dirent *de;
    char s[SIZE_s+1];

    xsnprintf(s, SIZE_s, "%s/interesting.groups", spooldir);
    d = opendir(s);
    if (!d) {
      syslog(LOG_ERR, "Unable to open directory %s: %m", s);
      printf("Unable to open directory %s\n", s);
      return FALSE;
    }

    while ((de = readdir(d)) != NULL) {
      if (strcasecmp(de->d_name, groupname) == 0) {
          closedir(d);
          return TRUE;
      }
    }
    closedir(d);
    return FALSE;
}

void
overrun(void)
/* report buffer overrun */
{
    syslog(LOG_CRIT, "buffer size too small, cannot continue, aborting program");
    abort();
    kill(getpid(), SIGKILL);  /* really die! */
}

/* no good but this server isn't going to be scalable so shut up */
const char *
lookup(const char *msgid)
{
    static char *name = NULL;
    static size_t namelen = 0;
    unsigned int r;
    size_t i;

    if (msgid == LOOKUP_FREE) {
      namelen = 0;
      if (name)
          free(name);
      return NULL;
    }

    if (!msgid || !*msgid)
      return NULL;

    i = strlen(msgid) + strlen(spooldir) + 30;

    if (!name) {
      name = (char *)critmalloc(i, "lookup");
      namelen = i;
    } else if (i > namelen) {
      free(name);
      name = (char *)critmalloc(i, "lookup");
      namelen = i;
    }

    strcpy(name, spooldir);   /* RATS: ignore */
    strcat(name, "/message.id/000/");     /* RATS: ignore */
    i = strlen(name);
    strcat(name, msgid);      /* RATS: ignore */

    r = 0;
    do {
      if (name[i] == '/')
          name[i] = '@';
      else if (name[i] == '>')
          name[i + 1] = '\0';
      r += (int)(name[i]);
      r += ++i;
    } while (name[i]);

    i = strlen(spooldir) + 14;      /* to the last digit */
    r = (r % 999) + 1;
    name[i--] = '0' + (char)(r % 10);
    r /= 10;
    name[i--] = '0' + (char)(r % 10);
    r /= 10;
    name[i] = '0' + (char)(r);
    return name;
}

#define LM_SIZE 65536

static int
makedir(char *d)
{
    char *p;
    char *q;

    if (verbose > 3)
      printf("makedir(%s)\n", d);
    if (!d || *d != '/' || chdir("/"))
      return 0;
    q = d;
    do {
      *q = '/';
      p = q;
      q = strchr(++p, '/');
      if (q)
          *q = '\0';
      if (!chdir(p))
          continue;           /* ok, I do use it sometimes :) */
      if (errno == ENOENT)
          if (mkdir(p, 0775)) {
            syslog(LOG_ERR, "mkdir %s: %m", d);
            exit(1);
          }
      if (chdir(p)) {
          syslog(LOG_ERR, "chdir %s: %m", d);
          exit(1);
      }
    } while (q);
    return 1;
}

/* prefix numeric group name components with a minus */
static int migrate(const char *name) {
    char *p = critstrdup(name, "dogroup"), *q, *t = NULL;

    /* shortcut: don't call into chdir() excessively */
    for(q = strtok(p, "."); q; q = strtok(NULL, ".")) {
      if (strspn(q, "0123456789") == strlen(q)) break;
    }
    if (!q) {
      free(p);
      return 0;
    }

    if (chdir(spooldir)) goto barf;

    for(q = strtok(p, "."); q; q = strtok(NULL, ".")) {
      t = critmalloc(strlen(q)+2, "dogroup");
      t[0] = '-';
      strcpy(t+1, q);
      if (strspn(q, "0123456789") == strlen(q)) {
          struct stat st;
          if (0 == chdir(t)) {
            free(t);
            continue;
          }
          if (0 == stat(q, &st) && S_ISDIR(st.st_mode)) {
            if (rename(q, t)) {
                ln_log(LNLOG_SERR, LNLOG_CTOP, "cannot rename %s to %s: %m",
                      q, t);
                goto barf;
            }
            if (0 == chdir(t)) {
                free(t);
                continue;
            }
          }
          goto barf;
      }
      if (chdir(q)) {
          goto barf;
      }
      free(t);
    }
    free(p);
    return 0;
barf:
    free(p);
    free(t);
    return -1;
}

/* chdir to the directory of the argument if it's a valid group */
int
chdirgroup(const char *group, int creatdir)
{
    char *p;
    const char *c;

    if (group && *group) {
      int dots = 0;
      char *nam, *q;
      mastr *d = mastr_new(1024);

      migrate(group);
      mastr_vcat(d, spooldir, "/", group, NULL);
      p = mastr_modifyable_str(d) + strlen(spooldir) + 1;
      while (*p) {
          if (*p == '.') {
            *p = '/';
          } else
            *p = tolower((unsigned char)*p);
          p++;
      }
      for (c = mastr_str(d);*c;c++) {
          if (*c == '/' && c[1] && strspn(c+1, "0123456789") == strcspn(c+1, "/"))
            dots++;
      }
      nam = critmalloc(mastr_len(d) + dots + 1, "chdirgroup");
      for (c = mastr_str(d), q=nam;*c;c++) {
          *(q++) = *c;
          if (*c == '/' && c[1] && strspn(c+1, "0123456789") == strcspn(c+1, "/"))
            *(q++) = '-';
      }
      *q = 0;
      mastr_delete(d);

      if (!chdir(nam)) {
          free(nam);
          return 1;           /* chdir successful */
      }
      if (creatdir) {
          int r = makedir(nam);
          free(nam);
          return r;
      }
      free(nam);
    }
    return 0;
}

/* get the fully qualified domain name of this box into fqdn */
static void
whoami(void)
{
    struct hostent *he;
    int debugqual = 0;
    char *x;

    if ((x = getenv("LN_DEBUG_QUALIFICATION")) != NULL
      && *x)
      debugqual = 1;

    if (!gethostname(fqdn, sizeof(fqdn)) && (he = gethostbyname(fqdn)) != NULL) {
      xstrlcpy(fqdn, he->h_name, sizeof(fqdn));
      if (debugqual) syslog(LOG_DEBUG, "canonical hostname: %s", fqdn);
      if (!is_validfqdn(fqdn)) {
          char **alias;
          alias = he->h_aliases;
          while (alias && *alias) {
            if (debugqual) {
                syslog(LOG_DEBUG, "alias for my hostname: %s", *alias);
            }
            if (is_validfqdn(*alias)) {
                xstrlcpy(fqdn, *alias, sizeof(fqdn));
                break;
            } else {
                alias++;
            }
          }
      }
      endhostent();
    }
}

/*
 * prepend string "newentry" to stringlist "list".
 */
void
prependtolist(struct stringlist **list, /*@unique@*/ const char *newentry)
{

    struct stringlist *ptr;

    ptr = (struct stringlist *)critmalloc(sizeof(struct stringlist) +
                                strlen(newentry),
                                "Allocating space in stringlist");
    strcpy(ptr->string, newentry);  /* RATS: ignore */
    ptr->next = *list;
    *list = ptr;
}

/*
 * find a string in a stringlist
 * return pointer to string if found, NULL otherwise
 */
char *
findinlist(struct stringlist *haystack, char *needle)
{
    struct stringlist *a;

    a = haystack;
    while (a && a->string) {
      if (strncmp(needle, a->string, strlen(needle)) == 0)
          return a->string;
      a = a->next;
    }
    return NULL;
}

/*
 * find a string in a stringlist
 * return pointer to string if found, NULL otherwise
 */
struct stringlist **
lfindinlist(struct stringlist **haystack, char *needle, size_t len)
{
    struct stringlist **a;

    a = haystack;
    while (a && *a && (*a)->string) {
      if (strncmp(needle, (*a)->string, len) == 0)
          return a;
      a = &(*a)->next;
    }
    return NULL;
}

void replaceinlist(struct stringlist **haystack, char *needle, size_t len)
{
    struct stringlist **f = lfindinlist(haystack, needle, len);
    struct stringlist *n;
    if (!f) prependtolist(haystack, needle);
    else {
      n = (*f)->next;
      free(*f);
      *f = (struct stringlist *)critmalloc(sizeof(struct stringlist) + 
                  strlen(needle), "Allocating space in stringlist");
      strcpy((*f)->string, needle); /* RATS: ignore */
      (*f)->next = n;
    }
}

/*
 * free a list
 */
void
freelist( /*@only@*/ struct stringlist *list)
{
    struct stringlist *a;

    while (list) {
      a = list->next;
      free(list);
      list = a;
    }
}

/* next few routines implement a mapping from message-id to article
   number, and clearing the entire space */

struct msgidtree {
    struct msgidtree *left;
    struct msgidtree *right;
    char msgid[1]; /* RATS: ignore */
};

static struct msgidtree *head;      /* starts as NULL */

static int
comparemsgid(const char *id1, const char *id2)
{
    int c;

    /* comparing only by msgid is uncool because the tree becomes
       very unbalanced */
    c = strcmp(strchr(id1, '@'), strchr(id1, '@'));
    if (!c)
      c = strcmp(id1, id2);
    return c;
}

void
insertmsgid( /*@unique@*/ const char *msgid)
{
    struct msgidtree **a;
    int c;

    if (strchr(msgid, '@') == 0)
      return;

    a = &head;
    while (a) {
      if (*a) {
          c = comparemsgid((*a)->msgid, msgid);
          if (c < 0)
            a = &((*a)->left);
          else if (c > 0)
            a = &((*a)->right);
          else {
            return;
          }
      } else {
          *a = (struct msgidtree *)
            critmalloc(sizeof(struct msgidtree) + strlen(msgid),
                     "Building expiry database");
          (*a)->left = NULL;
          (*a)->right = NULL;
          strcpy((*a)->msgid, msgid);     /* RATS: ignore */
          return;
      }
    }
}

/* returns 0 if not found, 1 otherwise */
int
findmsgid(const char *msgid)
{
    struct msgidtree *a;
    int c;

    /* domain part differs more than local-part, so try it first */

    if (NULL == strchr(msgid, '@'))
      return 0;

    a = head;
    while (a) {
      c = comparemsgid(a->msgid, msgid);
      if (c < 0)
          a = a->left;
      else if (c > 0)
          a = a->right;
      else
          return 1;
    }
    return 0;
}

static void
begone( /*@null@*//*@only@*/ struct msgidtree *m)
{
    if (m) {
      begone(m->right);
      begone(m->left);
      free((char *)m);
    }
}

void
clearidtree(void)
{
    begone(head);
    head = NULL;
}

static int
xtraverseidtree(struct msgidtree *m, tmihook h)
{
    int e = 0;
    if (!m) return 0;
    e |= xtraverseidtree(m->left, h);
    e |= h(m->msgid);
    e |= xtraverseidtree(m->right, h);
    return e;
}

int
traverseidtree(tmihook h) {
    return xtraverseidtree(head, h);
}

/*@dependent@*/ const char *
rfctime(void)
{
    static char date[128]; /* RATS: ignore */
    const char *months[] = { "Jan", "Feb", "Mar", "Apr",
      "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
    };
    const char *days[] = { "Sun", "Mon", "Tue", "Wed",
      "Thu", "Fri", "Sat"
    };
    time_t now;
    struct tm gm;

    now = time(NULL);
#ifdef HAVE_STRUCT_TM_TM_GMTOFF
    {
      char sign;
      long off;

      gm = *(localtime(&now));
      /* fiddle a bit to make sure we don't get negative a%b results:
       * make sure operands are non-negative */
      off = gm.tm_gmtoff/60;
      sign = off < 0 ? '-' : '+';
      off = labs(off);
      xsnprintf(date, sizeof(date), "%3s, %d %3s %4d %02d:%02d:%02d %c%02ld%02ld",
            days[gm.tm_wday], gm.tm_mday, months[gm.tm_mon],
            gm.tm_year + 1900, gm.tm_hour, gm.tm_min, gm.tm_sec,
            sign, off / 60, off % 60);
    }
#else
    gm = *(gmtime(&now));
    xsnprintf(date, sizeof(date), "%3s, %d %3s %4d %02d:%02d:%02d -0000",
            days[gm.tm_wday], gm.tm_mday, months[gm.tm_mon],
            gm.tm_year + 1900, gm.tm_hour, gm.tm_min, gm.tm_sec);
#endif

    return (date);
}

int
ngmatch(const char *pattern, const char *str)
{
    return !wildmat(str, pattern);
}

int
xsnprintf(char *str, size_t n, const char *format, ...)
{
    int r;
    va_list ap;

    va_start(ap, format);
    r = vsnprintf(str, n, format, ap);
    va_end(ap);

    if ((size_t) r >= n || r < 0)
      overrun();
    return r;
}

size_t xstrlcpy(char *dst, const char *src, size_t size) 
{
    size_t s;
    s = strlcpy(dst, src, size);
    if (s >= size)
      overrun();
    return s;
}

Generated by  Doxygen 1.6.0   Back to index