Exemplo n.º 1
0
static int
matcher(pcre_list * re_list_head, uschar * linebuffer, int len)
{
pcre_list * ri;

for(ri = re_list_head; ri; ri = ri->next)
  {
  int ovec[3*(REGEX_VARS+1)];
  int n, nn;

  /* try matcher on the line */
  n = pcre_exec(ri->re, NULL, CS linebuffer, len, 0, 0, ovec, nelem(ovec));
  if (n > 0)
    {
    Ustrncpy(regex_match_string_buffer, ri->pcre_text,
	      sizeof(regex_match_string_buffer)-1);
    regex_match_string = regex_match_string_buffer;

    for (nn = 1; nn < n; nn++)
      regex_vars[nn-1] =
	string_copyn(linebuffer + ovec[nn*2], ovec[nn*2+1] - ovec[nn*2]);

    return OK;
    }
  }
return FAIL;
}
Exemplo n.º 2
0
Arquivo: string.c Projeto: Exim/exim
uschar *
string_copyn(const uschar *s, int n)
{
    uschar *ss = store_get(n + 1);
    Ustrncpy(ss, s, n);
    ss[n] = 0;
    return ss;
}
Exemplo n.º 3
0
Arquivo: em_menu.c Projeto: Exim/exim
static void markdelAction(Widget w, XtPointer client_data, XtPointer call_data)
{
w = w;      /* Keep picky compilers happy */
call_data = call_data;
Ustrncpy(actioned_message, client_data, 24);
actioned_message[23] = '\0';
action_required = US"-Mmd";
dialog_ref_widget = menushell;
create_dialog(US"Recipient address to mark delivered?", US"");
}
Exemplo n.º 4
0
Arquivo: em_menu.c Projeto: Exim/exim
static void editsenderAction(Widget w, XtPointer client_data,
  XtPointer call_data)
{
queue_item *q;
uschar *sender;
w = w;      /* Keep picky compilers happy */
call_data = call_data;
Ustrncpy(actioned_message, client_data, 24);
actioned_message[23] = '\0';
q = find_queue(actioned_message, queue_noop, 0);
sender = !q ? US"" : q->sender[0] == 0 ? US"<>" : q->sender;
action_required = US"-Mes";
dialog_ref_widget = menushell;
create_dialog(US"New sender address?", sender);
}
Exemplo n.º 5
0
Arquivo: rda.c Projeto: toddr/exim
static int
rda_exists(uschar *filename, uschar **error)
{
int rc, saved_errno;
uschar *slash;
struct stat statbuf;

if ((rc = Ustat(filename, &statbuf)) >= 0) return FILE_EXIST;
saved_errno = errno;

Ustrncpy(big_buffer, filename, big_buffer_size - 3);
sigalrm_seen = FALSE;

if (saved_errno == ENOENT)
  {
  slash = Ustrrchr(big_buffer, '/');
  Ustrcpy(slash+1, ".");

  alarm(30);
  rc = Ustat(big_buffer, &statbuf);
  if (rc != 0 && errno == EACCES && !sigalrm_seen)
    {
    *slash = 0;
    rc = Ustat(big_buffer, &statbuf);
    }
  saved_errno = errno;
  alarm(0);

  DEBUG(D_route) debug_printf("stat(%s)=%d\n", big_buffer, rc);
  }

if (sigalrm_seen || rc != 0)
  {
  *error = string_sprintf("failed to stat %s (%s)", big_buffer,
    sigalrm_seen? "timeout" : strerror(saved_errno));
  return FILE_EXIST_UNCLEAR;
  }

*error = string_sprintf("%s does not exist", filename);
DEBUG(D_route) debug_printf("%s\n", *error);
return FILE_NOT_EXIST;
}
Exemplo n.º 6
0
static Uchar *color_print(int lowvalue, Attribute *attr, RedirectFunc *func)
{
    int val;
    Uchar *v;
    Uchar *cb;

    val=lowvalue&0xff;
    switch (lowvalue&0x300) {
    case 0x000:  v=translate("Red"); break;
    case 0x100:  v=translate("Green"); break;
    case 0x200:  v=translate("Blue"); break;
    case 0x300:  v=translate("ColorCube"); break;
    default: v=translate(""); break;
    }
    Ustrncpy(colorbuf, v, 240);
    cb=colorbuf;
    while (*cb) cb++;
    *cb++=':';
    *cb++=valtohex[(val>>4)&0xf];
    *cb++=valtohex[val&0xf];
    return colorbuf;
}
Exemplo n.º 7
0
Arquivo: srs.c Projeto: ArthasZRZ/exim
srs_result eximsrs_db_insert(srs_t *srs, char *data, uint data_len, char *result, uint result_len)
{
  uschar *res;
  uschar buf[64];

  if(srs_db_forward == NULL)
    return SRS_RESULT_DBERROR;

  srs_db_address = string_copyn(data, data_len);
  if(srs_generate_unique_id(srs, srs_db_address, buf, 64) & SRS_RESULT_FAIL)
    return SRS_RESULT_DBERROR;

  srs_db_key = string_copyn(buf, 16);

  if((res = expand_string(srs_db_forward)) == NULL)
    return SRS_RESULT_DBERROR;

  if(result_len < 17)
    return SRS_RESULT_DBERROR;

  Ustrncpy(result, srs_db_key, result_len);

  return SRS_RESULT_OK;
}
Exemplo n.º 8
0
/*****************************************************
*  Export/import a certificate, binary/printable
*****************************************************/
int
tls_export_cert(uschar * buf, size_t buflen, void * cert)
{
    size_t sz = buflen;
    void * reset_point = store_get(0);
    int fail;
    const uschar * cp;

    if ((fail = gnutls_x509_crt_export((gnutls_x509_crt_t)cert,
                                       GNUTLS_X509_FMT_PEM, buf, &sz)))
    {
        log_write(0, LOG_MAIN, "TLS error in certificate export: %s",
                  gnutls_strerror(fail));
        return 1;
    }
    if ((cp = string_printing(buf)) != buf)
    {
        Ustrncpy(buf, cp, buflen);
        if (buf[buflen-1])
            fail = 1;
    }
    store_reset(reset_point);
    return fail;
}
Exemplo n.º 9
0
Arquivo: dns.c Projeto: loganaden/exim
static int
fakens_search(const uschar *domain, int type, uschar *answerptr, int size)
{
int len = Ustrlen(domain);
int asize = size;                  /* Locally modified */
uschar *endname;
uschar name[256];
uschar utilname[256];
uschar *aptr = answerptr;          /* Locally modified */
struct stat statbuf;

/* Remove terminating dot. */

if (domain[len - 1] == '.') len--;
Ustrncpy(name, domain, len);
name[len] = 0;
endname = name + len;

/* Look for the fakens utility, and if it exists, call it. */

(void)string_format(utilname, sizeof(utilname), "%s/bin/fakens",
  config_main_directory);

if (stat(CS utilname, &statbuf) >= 0)
  {
  pid_t pid;
  int infd, outfd, rc;
  uschar *argv[5];

  DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) using fakens\n", name, dns_text_type(type));

  argv[0] = utilname;
  argv[1] = config_main_directory;
  argv[2] = name;
  argv[3] = dns_text_type(type);
  argv[4] = NULL;

  pid = child_open(argv, NULL, 0000, &infd, &outfd, FALSE);
  if (pid < 0)
    log_write(0, LOG_MAIN|LOG_PANIC_DIE, "failed to run fakens: %s",
      strerror(errno));

  len = 0;
  rc = -1;
  while (asize > 0 && (rc = read(outfd, aptr, asize)) > 0)
    {
    len += rc;
    aptr += rc;       /* Don't modify the actual arguments, because they */
    asize -= rc;      /* may need to be passed on to res_search(). */
    }

  /* If we ran out of output buffer before exhasting the return,
  carry on reading and counting it. */

  if (asize == 0)
    while ((rc = read(outfd, name, sizeof(name))) > 0)
      len += rc;

  if (rc < 0)
    log_write(0, LOG_MAIN|LOG_PANIC_DIE, "read from fakens failed: %s",
      strerror(errno));

  switch(child_close(pid, 0))
    {
    case 0: return len;
    case 1: h_errno = HOST_NOT_FOUND; return -1;
    case 2: h_errno = TRY_AGAIN; return -1;
    default:
    case 3: h_errno = NO_RECOVERY; return -1;
    case 4: h_errno = NO_DATA; return -1;
    case 5: /* Pass on to res_search() */
    DEBUG(D_dns) debug_printf("fakens returned PASS_ON\n");
    }
  }
else
  {
  DEBUG(D_dns) debug_printf("fakens (%s) not found\n", utilname);
  }

/* fakens utility not found, or it returned "pass on" */

DEBUG(D_dns) debug_printf("passing %s on to res_search()\n", domain);

return res_search(CS domain, C_IN, type, answerptr, size);
}
Exemplo n.º 10
0
Arquivo: dns.c Projeto: loganaden/exim
void
dns_build_reverse(const uschar *string, uschar *buffer)
{
const uschar *p = string + Ustrlen(string);
uschar *pp = buffer;

/* Handle IPv4 address */

#if HAVE_IPV6
if (Ustrchr(string, ':') == NULL)
#endif
  {
  int i;
  for (i = 0; i < 4; i++)
    {
    const uschar *ppp = p;
    while (ppp > string && ppp[-1] != '.') ppp--;
    Ustrncpy(pp, ppp, p - ppp);
    pp += p - ppp;
    *pp++ = '.';
    p = ppp - 1;
    }
  Ustrcpy(pp, "in-addr.arpa");
  }

/* Handle IPv6 address; convert to binary so as to fill out any
abbreviation in the textual form. */

#if HAVE_IPV6
else
  {
  int i;
  int v6[4];
  (void)host_aton(string, v6);

  /* The original specification for IPv6 reverse lookup was to invert each
  nibble, and look in the ip6.int domain. The domain was subsequently
  changed to ip6.arpa. */

  for (i = 3; i >= 0; i--)
    {
    int j;
    for (j = 0; j < 32; j += 4)
      {
      sprintf(CS pp, "%x.", (v6[i] >> j) & 15);
      pp += 2;
      }
    }
  Ustrcpy(pp, "ip6.arpa.");

  /* Another way of doing IPv6 reverse lookups was proposed in conjunction
  with A6 records. However, it fell out of favour when they did. The
  alternative was to construct a binary key, and look in ip6.arpa. I tried
  to make this code do that, but I could not make it work on Solaris 8. The
  resolver seems to lose the initial backslash somehow. However, now that
  this style of reverse lookup has been dropped, it doesn't matter. These
  lines are left here purely for historical interest. */

  /**************************************************
  Ustrcpy(pp, "\\[x");
  pp += 3;

  for (i = 0; i < 4; i++)
    {
    sprintf(pp, "%08X", v6[i]);
    pp += 8;
    }
  Ustrcpy(pp, "].ip6.arpa.");
  **************************************************/

  }
#endif
}
Exemplo n.º 11
0
Arquivo: dns.c Projeto: Chaohua/exim
static int
fakens_search(const uschar *domain, int type, uschar *answerptr, int size)
{
int len = Ustrlen(domain);
int asize = size;                  /* Locally modified */
uschar *endname;
uschar name[256];
uschar utilname[256];
uschar *aptr = answerptr;          /* Locally modified */
struct stat statbuf;

/* Remove terminating dot. */

if (domain[len - 1] == '.') len--;
Ustrncpy(name, domain, len);
name[len] = 0;
endname = name + len;

/* This code, for forcing TRY_AGAIN and NO_RECOVERY, is here so that it works
for the old test suite that uses a real nameserver. When the old test suite is
eventually abandoned, this code could be moved into the fakens utility. */

if (len >= 14 && Ustrcmp(endname - 14, "test.again.dns") == 0)
  {
  int delay = Uatoi(name);  /* digits at the start of the name */
  DEBUG(D_dns) debug_printf("Return from DNS lookup of %s (%s) faked for testing\n",
    name, dns_text_type(type));
  if (delay > 0)
    {
    DEBUG(D_dns) debug_printf("delaying %d seconds\n", delay);
    sleep(delay);
    }
  h_errno = TRY_AGAIN;
  return -1;
  }

if (len >= 13 && Ustrcmp(endname - 13, "test.fail.dns") == 0)
  {
  DEBUG(D_dns) debug_printf("Return from DNS lookup of %s (%s) faked for testing\n",
    name, dns_text_type(type));
  h_errno = NO_RECOVERY;
  return -1;
  }

/* Look for the fakens utility, and if it exists, call it. */

(void)string_format(utilname, sizeof(utilname), "%s/../bin/fakens",
  spool_directory);

if (stat(CS utilname, &statbuf) >= 0)
  {
  pid_t pid;
  int infd, outfd, rc;
  uschar *argv[5];

  DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) using fakens\n",
    name, dns_text_type(type));

  argv[0] = utilname;
  argv[1] = spool_directory;
  argv[2] = name;
  argv[3] = dns_text_type(type);
  argv[4] = NULL;

  pid = child_open(argv, NULL, 0000, &infd, &outfd, FALSE);
  if (pid < 0)
    log_write(0, LOG_MAIN|LOG_PANIC_DIE, "failed to run fakens: %s",
      strerror(errno));

  len = 0;
  rc = -1;
  while (asize > 0 && (rc = read(outfd, aptr, asize)) > 0)
    {
    len += rc;
    aptr += rc;       /* Don't modify the actual arguments, because they */
    asize -= rc;      /* may need to be passed on to res_search(). */
    }

  if (rc < 0)
    log_write(0, LOG_MAIN|LOG_PANIC_DIE, "read from fakens failed: %s",
      strerror(errno));

  switch(child_close(pid, 0))
    {
    case 0: return len;
    case 1: h_errno = HOST_NOT_FOUND; return -1;
    case 2: h_errno = TRY_AGAIN; return -1;
    default:
    case 3: h_errno = NO_RECOVERY; return -1;
    case 4: h_errno = NO_DATA; return -1;
    case 5: /* Pass on to res_search() */
    DEBUG(D_dns) debug_printf("fakens returned PASS_ON\n");
    }
  }

/* fakens utility not found, or it returned "pass on" */

DEBUG(D_dns) debug_printf("passing %s on to res_search()\n", domain);

return res_search(CS domain, C_IN, type, answerptr, size);
}
Exemplo n.º 12
0
Arquivo: regex.c Projeto: fanf2/exim
int regex(uschar **listptr) {
  int sep = 0;
  uschar *list = *listptr;
  uschar *regex_string;
  uschar regex_string_buffer[1024];
  unsigned long mbox_size;
  FILE *mbox_file;
  pcre *re;
  pcre_list *re_list_head = NULL;
  pcre_list *re_list_item;
  const char *pcre_error;
  int pcre_erroffset;
  uschar *linebuffer;
  long f_pos = 0;

  /* reset expansion variable */
  regex_match_string = NULL;

  if (mime_stream == NULL) {
    /* We are in the DATA ACL */
    mbox_file = spool_mbox(&mbox_size, NULL);
    if (mbox_file == NULL) {
      /* error while spooling */
      log_write(0, LOG_MAIN|LOG_PANIC,
             "regex acl condition: error while creating mbox spool file");
      return DEFER;
    };
  }
  else {
    f_pos = ftell(mime_stream);
    mbox_file = mime_stream;
  };

  /* precompile our regexes */
  while ((regex_string = string_nextinlist(&list, &sep,
                                           regex_string_buffer,
                                           sizeof(regex_string_buffer))) != NULL) {

    /* parse option */
    if ( (strcmpic(regex_string,US"false") == 0) ||
         (Ustrcmp(regex_string,"0") == 0) ) {
      /* explicitly no matching */
      continue;
    };

    /* compile our regular expression */
    re = pcre_compile( CS regex_string,
                       0,
                       &pcre_error,
                       &pcre_erroffset,
                       NULL );

    if (re == NULL) {
      log_write(0, LOG_MAIN,
           "regex acl condition warning - error in regex '%s': %s at offset %d, skipped.", regex_string, pcre_error, pcre_erroffset);
      continue;
    }
    else {
      re_list_item = store_get(sizeof(pcre_list));
      re_list_item->re = re;
      re_list_item->pcre_text = string_copy(regex_string);
      re_list_item->next = re_list_head;
      re_list_head = re_list_item;
    };
  };

  /* no regexes -> nothing to do */
  if (re_list_head == NULL) {
    return FAIL;
  };

  /* match each line against all regexes */
  linebuffer = store_get(32767);
  while (fgets(CS linebuffer, 32767, mbox_file) != NULL) {
    if ( (mime_stream != NULL) && (mime_current_boundary != NULL) ) {
      /* check boundary */
      if (Ustrncmp(linebuffer,"--",2) == 0) {
        if (Ustrncmp((linebuffer+2),mime_current_boundary,Ustrlen(mime_current_boundary)) == 0)
          /* found boundary */
          break;
      };
    };
    re_list_item = re_list_head;
    do {
      /* try matcher on the line */
      if (pcre_exec(re_list_item->re, NULL, CS linebuffer,
      (int)Ustrlen(linebuffer), 0, 0, NULL, 0) >= 0) {
        Ustrncpy(regex_match_string_buffer, re_list_item->pcre_text, 1023);
        regex_match_string = regex_match_string_buffer;
        if (mime_stream == NULL)
          (void)fclose(mbox_file);
        else {
          clearerr(mime_stream);
          fseek(mime_stream,f_pos,SEEK_SET);
        };
        return OK;
      };
      re_list_item = re_list_item->next;
    } while (re_list_item != NULL);
  };

  if (mime_stream == NULL)
    (void)fclose(mbox_file);
  else {
    clearerr(mime_stream);
    fseek(mime_stream,f_pos,SEEK_SET);
  };

  /* no matches ... */
  return FAIL;
}
Exemplo n.º 13
0
Arquivo: regex.c Projeto: fanf2/exim
int mime_regex(uschar **listptr) {
  int sep = 0;
  uschar *list = *listptr;
  uschar *regex_string;
  uschar regex_string_buffer[1024];
  pcre *re;
  pcre_list *re_list_head = NULL;
  pcre_list *re_list_item;
  const char *pcre_error;
  int pcre_erroffset;
  FILE *f;
  uschar *mime_subject = NULL;
  int mime_subject_len = 0;

  /* reset expansion variable */
  regex_match_string = NULL;

  /* precompile our regexes */
  while ((regex_string = string_nextinlist(&list, &sep,
                                           regex_string_buffer,
                                           sizeof(regex_string_buffer))) != NULL) {

    /* parse option */
    if ( (strcmpic(regex_string,US"false") == 0) ||
         (Ustrcmp(regex_string,"0") == 0) ) {
      /* explicitly no matching */
      continue;
    };

    /* compile our regular expression */
    re = pcre_compile( CS regex_string,
                       0,
                       &pcre_error,
                       &pcre_erroffset,
                       NULL );

    if (re == NULL) {
      log_write(0, LOG_MAIN,
           "regex acl condition warning - error in regex '%s': %s at offset %d, skipped.", regex_string, pcre_error, pcre_erroffset);
      continue;
    }
    else {
      re_list_item = store_get(sizeof(pcre_list));
      re_list_item->re = re;
      re_list_item->pcre_text = string_copy(regex_string);
      re_list_item->next = re_list_head;
      re_list_head = re_list_item;
    };
  };

  /* no regexes -> nothing to do */
  if (re_list_head == NULL) {
    return FAIL;
  };

  /* check if the file is already decoded */
  if (mime_decoded_filename == NULL) {
    uschar *empty = US"";
    /* no, decode it first */
    mime_decode(&empty);
    if (mime_decoded_filename == NULL) {
      /* decoding failed */
      log_write(0, LOG_MAIN,
           "mime_regex acl condition warning - could not decode MIME part to file.");
      return DEFER;
    };
  };


  /* open file */
  f = fopen(CS mime_decoded_filename, "rb");
  if (f == NULL) {
    /* open failed */
    log_write(0, LOG_MAIN,
         "mime_regex acl condition warning - can't open '%s' for reading.", mime_decoded_filename);
    return DEFER;
  };

  /* get 32k memory */
  mime_subject = (uschar *)store_get(32767);

  /* read max 32k chars from file */
  mime_subject_len = fread(mime_subject, 1, 32766, f);

  re_list_item = re_list_head;
  do {
    /* try matcher on the mmapped file */
    debug_printf("Matching '%s'\n", re_list_item->pcre_text);
    if (pcre_exec(re_list_item->re, NULL, CS mime_subject,
                  mime_subject_len, 0, 0, NULL, 0) >= 0) {
      Ustrncpy(regex_match_string_buffer, re_list_item->pcre_text, 1023);
      regex_match_string = regex_match_string_buffer;
      (void)fclose(f);
      return OK;
    };
    re_list_item = re_list_item->next;
  } while (re_list_item != NULL);

  (void)fclose(f);

  /* no matches ... */
  return FAIL;
}
Exemplo n.º 14
0
uschar *
search_find(void *handle, uschar *filename, uschar *keystring, int partial,
  const uschar *affix, int affixlen, int starflags, int *expand_setup)
{
tree_node *t = (tree_node *)handle;
BOOL set_null_wild = FALSE;
uschar *yield;

DEBUG(D_lookup)
  {
  if (partial < 0) affixlen = 99;   /* So that "NULL" prints */
  debug_printf("search_find: file=\"%s\"\n  key=\"%s\" "
    "partial=%d affix=%.*s starflags=%x\n",
    (filename == NULL)? US"NULL" : filename,
    keystring, partial, affixlen, affix, starflags);
  }

/* Arrange to put this database at the top of the LRU chain if it is a type
that opens real files. */

if (open_top != (tree_node *)handle &&
    lookup_list[t->name[0]-'0']->type == lookup_absfile)
  {
  search_cache *c = (search_cache *)(t->data.ptr);
  tree_node *up = c->up;
  tree_node *down = c->down;

  /* Cut it out of the list. A newly opened file will a NULL up pointer.
  Otherwise there will be a non-NULL up pointer, since we checked above that
  this block isn't already at the top of the list. */

  if (up != NULL)
    {
    ((search_cache *)(up->data.ptr))->down = down;
    if (down != NULL)
      ((search_cache *)(down->data.ptr))->up = up;
    else open_bot = up;
    }

  /* Now put it at the head of the list. */

  c->up = NULL;
  c->down = open_top;
  if (open_top == NULL) open_bot = t; else
    ((search_cache *)(open_top->data.ptr))->up = t;
  open_top = t;
  }

DEBUG(D_lookup)
  {
  tree_node *t = open_top;
  debug_printf("LRU list:\n");
  while (t != NULL)
    {
    search_cache *c = (search_cache *)(t->data.ptr);
    debug_printf("  %s\n", t->name);
    if (t == open_bot) debug_printf("  End\n");
    t = c->down;
    }
  }

/* First of all, try to match the key string verbatim. If matched a complete
entry but could have been partial, flag to set up variables. */

yield = internal_search_find(handle, filename, keystring);
if (search_find_defer) return NULL;
if (yield != NULL) { if (partial >= 0) set_null_wild = TRUE; }

/* Not matched a complete entry; handle partial lookups, but only if the full
search didn't defer. Don't use string_sprintf() to construct the initial key,
just in case the original key is too long for the string_sprintf() buffer (it
*has* happened!). The case of a zero-length affix has to be treated specially.
*/

else if (partial >= 0)
  {
  int len = Ustrlen(keystring);
  uschar *keystring2;

  /* Try with the affix on the front, except for a zero-length affix */

  if (affixlen == 0) keystring2 = keystring; else
    {
    keystring2 = store_get(len + affixlen + 1);
    Ustrncpy(keystring2, affix, affixlen);
    Ustrcpy(keystring2 + affixlen, keystring);
    DEBUG(D_lookup) debug_printf("trying partial match %s\n", keystring2);
    yield = internal_search_find(handle, filename, keystring2);
    if (search_find_defer) return NULL;
    }

  /* The key in its entirety did not match a wild entry; try chopping off
  leading components. */

  if (yield == NULL)
    {
    int dotcount = 0;
    uschar *keystring3 = keystring2 + affixlen;
    uschar *s = keystring3;
    while (*s != 0) if (*s++ == '.') dotcount++;

    while (dotcount-- >= partial)
      {
      while (*keystring3 != 0 && *keystring3 != '.') keystring3++;

      /* If we get right to the end of the string (which will be the last time
      through this loop), we've failed if the affix is null. Otherwise do one
      last lookup for the affix itself, but if it is longer than 1 character,
      remove the last character if it is ".". */

      if (*keystring3 == 0)
        {
        if (affixlen < 1) break;
        if (affixlen > 1 && affix[affixlen-1] == '.') affixlen--;
        Ustrncpy(keystring2, affix, affixlen);
        keystring2[affixlen] = 0;
        keystring3 = keystring2;
        }
      else
        {
        keystring3 -= affixlen - 1;
        if (affixlen > 0) Ustrncpy(keystring3, affix, affixlen);
        }

      DEBUG(D_lookup) debug_printf("trying partial match %s\n", keystring3);
      yield = internal_search_find(handle, filename, keystring3);
      if (search_find_defer) return NULL;
      if (yield != NULL)
        {
        /* First variable is the wild part; second is the fixed part. Take care
        to get it right when keystring3 is just "*". */

        if (expand_setup != NULL && *expand_setup >= 0)
          {
          int fixedlength = Ustrlen(keystring3) - affixlen;
          int wildlength = Ustrlen(keystring) - fixedlength - 1;
          *expand_setup += 1;
          expand_nstring[*expand_setup] = keystring;
          expand_nlength[*expand_setup] = wildlength;
          *expand_setup += 1;
          expand_nstring[*expand_setup] = keystring + wildlength + 1;
          expand_nlength[*expand_setup] = (fixedlength < 0)? 0 : fixedlength;
          }
        break;
        }
      keystring3 += affixlen;
      }
    }

  else set_null_wild = TRUE; /* Matched a wild entry without any wild part */
  }

/* If nothing has been matched, but the option to look for "*@" is set, try
replacing everthing to the left of @ by *. After a match, the wild part
is set to the string to the left of the @. */

if (yield == NULL && (starflags & SEARCH_STARAT) != 0)
  {
  uschar *atat = Ustrrchr(keystring, '@');
  if (atat != NULL && atat > keystring)
    {
    int savechar;
    savechar = *(--atat);
    *atat = '*';

    DEBUG(D_lookup) debug_printf("trying default match %s\n", atat);
    yield = internal_search_find(handle, filename, atat);
    *atat = savechar;
    if (search_find_defer) return NULL;

    if (yield != NULL && expand_setup != NULL && *expand_setup >= 0)
      {
      *expand_setup += 1;
      expand_nstring[*expand_setup] = keystring;
      expand_nlength[*expand_setup] = atat - keystring + 1;
      *expand_setup += 1;
      expand_nstring[*expand_setup] = keystring;
      expand_nlength[*expand_setup] = 0;
      }
    }
  }

/* If we still haven't matched anything, and the option to look for "*" is set,
try that. If we do match, the first variable (the wild part) is the whole key,
and the second is empty. */

if (yield == NULL && (starflags & (SEARCH_STAR|SEARCH_STARAT)) != 0)
  {
  DEBUG(D_lookup) debug_printf("trying to match *\n");
  yield = internal_search_find(handle, filename, US"*");
  if (yield != NULL && expand_setup != NULL && *expand_setup >= 0)
    {
    *expand_setup += 1;
    expand_nstring[*expand_setup] = keystring;
    expand_nlength[*expand_setup] = Ustrlen(keystring);
    *expand_setup += 1;
    expand_nstring[*expand_setup] = keystring;
    expand_nlength[*expand_setup] = 0;
    }
  }

/* If this was a potentially partial lookup, and we matched either a
complete non-wild domain entry, or we matched a wild-carded entry without
chopping off any of the domain components, set up the expansion variables
(if required) so that the first one is empty, and the second one is the
fixed part of the domain. The set_null_wild flag is set only when yield is not
NULL. */

if (set_null_wild && expand_setup != NULL && *expand_setup >= 0)
  {
  *expand_setup += 1;
  expand_nstring[*expand_setup] = keystring;
  expand_nlength[*expand_setup] = 0;
  *expand_setup += 1;
  expand_nstring[*expand_setup] = keystring;
  expand_nlength[*expand_setup] = Ustrlen(keystring);
  }

return yield;
}
Exemplo n.º 15
0
Arquivo: ldap.c Projeto: fanf2/exim
static int
perform_ldap_search(uschar *ldap_url, uschar *server, int s_port, int search_type,
  uschar **res, uschar **errmsg, BOOL *defer_break, uschar *user, uschar *password,
  int sizelimit, int timelimit, int tcplimit, int dereference, void *referrals)
{
LDAPURLDesc     *ludp = NULL;
LDAPMessage     *result = NULL;
BerElement      *ber;
LDAP_CONNECTION *lcp;

struct timeval timeout;
struct timeval *timeoutptr = NULL;

uschar *attr;
uschar **attrp;
uschar *data = NULL;
uschar *dn = NULL;
uschar *host;
uschar **values;
uschar **firstval;
uschar porttext[16];

uschar *error1 = NULL;   /* string representation of errcode (static) */
uschar *error2 = NULL;   /* error message from the server */
uschar *matched = NULL;  /* partially matched DN */

int    attr_count = 0;
int    error_yield = DEFER;
int    msgid;
int    rc, ldap_rc, ldap_parse_rc;
int    port;
int    ptr = 0;
int    rescount = 0;
int    size = 0;
BOOL   attribute_found = FALSE;
BOOL   ldapi = FALSE;

DEBUG(D_lookup)
  debug_printf("perform_ldap_search: ldap%s URL = \"%s\" server=%s port=%d "
    "sizelimit=%d timelimit=%d tcplimit=%d\n",
    (search_type == SEARCH_LDAP_MULTIPLE)? "m" :
    (search_type == SEARCH_LDAP_DN)? "dn" :
    (search_type == SEARCH_LDAP_AUTH)? "auth" : "",
    ldap_url, server, s_port, sizelimit, timelimit, tcplimit);

/* Check if LDAP thinks the URL is a valid LDAP URL. We assume that if the LDAP
library that is in use doesn't recognize, say, "ldapi", it will barf here. */

if (!ldap_is_ldap_url(CS ldap_url))
  {
  *errmsg = string_sprintf("ldap_is_ldap_url: not an LDAP url \"%s\"\n",
    ldap_url);
  goto RETURN_ERROR_BREAK;
  }

/* Parse the URL */

if ((rc = ldap_url_parse(CS ldap_url, &ludp)) != 0)
  {
  *errmsg = string_sprintf("ldap_url_parse: (error %d) parsing \"%s\"\n", rc,
    ldap_url);
  goto RETURN_ERROR_BREAK;
  }

/* If the host name is empty, take it from the separate argument, if one is
given. OpenLDAP 2.0.6 sets an unset hostname to "" rather than empty, but
expects NULL later in ldap_init() to mean "default", annoyingly. In OpenLDAP
2.0.11 this has changed (it uses NULL). */

if ((ludp->lud_host == NULL || ludp->lud_host[0] == 0) && server != NULL)
  {
  host = server;
  port = s_port;
  }
else
  {
  host = US ludp->lud_host;
  if (host != NULL && host[0] == 0) host = NULL;
  port = ludp->lud_port;
  }

DEBUG(D_lookup) debug_printf("after ldap_url_parse: host=%s port=%d\n",
  host, port);

if (port == 0) port = LDAP_PORT;      /* Default if none given */
sprintf(CS porttext, ":%d", port);    /* For messages */

/* If the "host name" is actually a path, we are going to connect using a Unix
socket, regardless of whether "ldapi" was actually specified or not. This means
that a Unix socket can be declared in eldap_default_servers, and "traditional"
LDAP queries using just "ldap" can be used ("ldaps" is similarly overridden).
The path may start with "/" or it may already be escaped as "%2F" if it was
actually declared that way in eldap_default_servers. (I did it that way the
first time.) If the host name is not a path, the use of "ldapi" causes an
error, except in the default case. (But lud_scheme doesn't seem to exist in
older libraries.) */

if (host != NULL)
  {
  if ((host[0] == '/' || Ustrncmp(host, "%2F", 3) == 0))
    {
    ldapi = TRUE;
    porttext[0] = 0;    /* Remove port from messages */
    }

  #if defined LDAP_LIB_OPENLDAP2
  else if (strncmp(ludp->lud_scheme, "ldapi", 5) == 0)
    {
    *errmsg = string_sprintf("ldapi requires an absolute path (\"%s\" given)",
      host);
    goto RETURN_ERROR;
    }
  #endif
  }

/* Count the attributes; we need this later to tell us how to format results */

for (attrp = USS ludp->lud_attrs; attrp != NULL && *attrp != NULL; attrp++)
  attr_count++;

/* See if we can find a cached connection to this host. The port is not
relevant for ldapi. The host name pointer is set to NULL if no host was given
(implying the library default), rather than to the empty string. Note that in
this case, there is no difference between ldap and ldapi. */

for (lcp = ldap_connections; lcp != NULL; lcp = lcp->next)
  {
  if ((host == NULL) != (lcp->host == NULL) ||
      (host != NULL && strcmpic(lcp->host, host) != 0))
    continue;
  if (ldapi || port == lcp->port) break;
  }

/* Use this network timeout in any requests. */

if (tcplimit > 0)
  {
  timeout.tv_sec = tcplimit;
  timeout.tv_usec = 0;
  timeoutptr = &timeout;
  }

/* If no cached connection found, we must open a connection to the server. If
the server name is actually an absolute path, we set ldapi=TRUE above. This
requests connection via a Unix socket. However, as far as I know, only OpenLDAP
supports the use of sockets, and the use of ldap_initialize(). */

if (lcp == NULL)
  {
  LDAP *ld;


  /* --------------------------- OpenLDAP ------------------------ */

  /* There seems to be a preference under OpenLDAP for ldap_initialize()
  instead of ldap_init(), though I have as yet been unable to find
  documentation that says this. (OpenLDAP documentation is sparse to
  non-existent). So we handle OpenLDAP differently here. Also, support for
  ldapi seems to be OpenLDAP-only at present. */

  #ifdef LDAP_LIB_OPENLDAP2

  /* We now need an empty string for the default host. Get some store in which
  to build a URL for ldap_initialize(). In the ldapi case, it can't be bigger
  than (9 + 3*Ustrlen(shost)), whereas in the other cases it can't be bigger
  than the host name + "ldaps:///" plus : and a port number, say 20 + the
  length of the host name. What we get should accommodate both, easily. */

  uschar *shost = (host == NULL)? US"" : host;
  uschar *init_url = store_get(20 + 3 * Ustrlen(shost));
  uschar *init_ptr;

  /* Handle connection via Unix socket ("ldapi"). We build a basic LDAP URI to
  contain the path name, with slashes escaped as %2F. */

  if (ldapi)
    {
    int ch;
    init_ptr = init_url + 8;
    Ustrcpy(init_url, "ldapi://");
    while ((ch = *shost++) != 0)
      {
      if (ch == '/')
        {
        Ustrncpy(init_ptr, "%2F", 3);
        init_ptr += 3;
        }
      else *init_ptr++ = ch;
      }
    *init_ptr = 0;
    }

  /* This is not an ldapi call. Just build a URI with the protocol type, host
  name, and port. */

  else
    {
    init_ptr = Ustrchr(ldap_url, '/');
    Ustrncpy(init_url, ldap_url, init_ptr - ldap_url);
    init_ptr = init_url + (init_ptr - ldap_url);
    sprintf(CS init_ptr, "//%s:%d/", shost, port);
    }

  /* Call ldap_initialize() and check the result */

  DEBUG(D_lookup) debug_printf("ldap_initialize with URL %s\n", init_url);
  rc = ldap_initialize(&ld, CS init_url);
  if (rc != LDAP_SUCCESS)
    {
    *errmsg = string_sprintf("ldap_initialize: (error %d) URL \"%s\"\n",
      rc, init_url);
    goto RETURN_ERROR;
    }
  store_reset(init_url);   /* Might as well save memory when we can */


  /* ------------------------- Not OpenLDAP ---------------------- */

  /* For libraries other than OpenLDAP, use ldap_init(). */

  #else   /* LDAP_LIB_OPENLDAP2 */
  ld = ldap_init(CS host, port);
  #endif  /* LDAP_LIB_OPENLDAP2 */

  /* -------------------------------------------------------------- */


  /* Handle failure to initialize */

  if (ld == NULL)
    {
    *errmsg = string_sprintf("failed to initialize for LDAP server %s%s - %s",
      host, porttext, strerror(errno));
    goto RETURN_ERROR;
    }

  /* Set the TCP connect time limit if available. This is something that is
  in Netscape SDK v4.1; I don't know about other libraries. */

  #ifdef LDAP_X_OPT_CONNECT_TIMEOUT
  if (tcplimit > 0)
    {
    int timeout1000 = tcplimit*1000;
    ldap_set_option(ld, LDAP_X_OPT_CONNECT_TIMEOUT, (void *)&timeout1000);
    }
  else
    {
    int notimeout = LDAP_X_IO_TIMEOUT_NO_TIMEOUT;
    ldap_set_option(ld, LDAP_X_OPT_CONNECT_TIMEOUT, (void *)&notimeout);
    }
  #endif

  /* Set the TCP connect timeout. This works with OpenLDAP 2.2.14. */

  #ifdef LDAP_OPT_NETWORK_TIMEOUT
  if (tcplimit > 0)
    ldap_set_option(ld, LDAP_OPT_NETWORK_TIMEOUT, (void *)timeoutptr);
  #endif

  /* I could not get TLS to work until I set the version to 3. That version
  seems to be the default nowadays. The RFC is dated 1997, so I would hope
  that all the LDAP libraries support it. Therefore, if eldap_version hasn't
  been set, go for v3 if we can. */

  if (eldap_version < 0)
    {
    #ifdef LDAP_VERSION3
    eldap_version = LDAP_VERSION3;
    #else
    eldap_version = 2;
    #endif
    }

  #ifdef LDAP_OPT_PROTOCOL_VERSION
  ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, (void *)&eldap_version);
  #endif

  DEBUG(D_lookup) debug_printf("initialized for LDAP (v%d) server %s%s\n",
    eldap_version, host, porttext);

  /* If not using ldapi and TLS is available, set appropriate TLS options: hard
  for "ldaps" and soft otherwise. */

  #ifdef LDAP_OPT_X_TLS
  if (!ldapi)
    {
    int tls_option;
    if (strncmp(ludp->lud_scheme, "ldaps", 5) == 0)
      {
      tls_option = LDAP_OPT_X_TLS_HARD;
      DEBUG(D_lookup) debug_printf("LDAP_OPT_X_TLS_HARD set\n");
      }
    else
      {
      tls_option = LDAP_OPT_X_TLS_TRY;
      DEBUG(D_lookup) debug_printf("LDAP_OPT_X_TLS_TRY set\n");
      }
    ldap_set_option(ld, LDAP_OPT_X_TLS, (void *)&tls_option);
    }
  #endif  /* LDAP_OPT_X_TLS */

  #ifdef LDAP_OPT_X_TLS_CACERTFILE
  if (eldap_ca_cert_file != NULL)
    {
    ldap_set_option(ld, LDAP_OPT_X_TLS_CACERTFILE, eldap_ca_cert_file);
    }
  #endif
  #ifdef LDAP_OPT_X_TLS_CACERTDIR
  if (eldap_ca_cert_dir != NULL)
    {
    ldap_set_option(ld, LDAP_OPT_X_TLS_CACERTDIR, eldap_ca_cert_dir);
    }
  #endif
  #ifdef LDAP_OPT_X_TLS_CERTFILE
  if (eldap_cert_file != NULL)
    {
    ldap_set_option(ld, LDAP_OPT_X_TLS_CERTFILE, eldap_cert_file);
    }
  #endif
  #ifdef LDAP_OPT_X_TLS_KEYFILE
  if (eldap_cert_key != NULL)
    {
    ldap_set_option(ld, LDAP_OPT_X_TLS_KEYFILE, eldap_cert_key);
    }
  #endif
  #ifdef LDAP_OPT_X_TLS_CIPHER_SUITE
  if (eldap_cipher_suite != NULL)
    {
    ldap_set_option(ld, LDAP_OPT_X_TLS_CIPHER_SUITE, eldap_cipher_suite);
    }
  #endif
  #ifdef LDAP_OPT_X_TLS_REQUIRE_CERT
  if (eldap_require_cert != NULL)
    {
    int cert_option = LDAP_OPT_X_TLS_NEVER;
    if (Ustrcmp(eldap_require_cert, "hard") == 0)
      {
      cert_option = LDAP_OPT_X_TLS_HARD;
      }
    else if (Ustrcmp(eldap_require_cert, "demand") == 0)
      {
      cert_option = LDAP_OPT_X_TLS_DEMAND;
      }
    else if (Ustrcmp(eldap_require_cert, "allow") == 0)
      {
      cert_option = LDAP_OPT_X_TLS_ALLOW;
      }
    else if (Ustrcmp(eldap_require_cert, "try") == 0)
      {
      cert_option = LDAP_OPT_X_TLS_TRY;
      }
    ldap_set_option(ld, LDAP_OPT_X_TLS_REQUIRE_CERT, &cert_option);
    }
  #endif

  /* Now add this connection to the chain of cached connections */

  lcp = store_get(sizeof(LDAP_CONNECTION));
  lcp->host = (host == NULL)? NULL : string_copy(host);
  lcp->bound = FALSE;
  lcp->user = NULL;
  lcp->password = NULL;
  lcp->port = port;
  lcp->ld = ld;
  lcp->next = ldap_connections;
  ldap_connections = lcp;
  }

/* Found cached connection */

else
  {
  DEBUG(D_lookup)
    debug_printf("re-using cached connection to LDAP server %s%s\n",
      host, porttext);
  }

/* Bind with the user/password supplied, or an anonymous bind if these values
are NULL, unless a cached connection is already bound with the same values. */

if (!lcp->bound ||
    (lcp->user == NULL && user != NULL) ||
    (lcp->user != NULL && user == NULL) ||
    (lcp->user != NULL && user != NULL && Ustrcmp(lcp->user, user) != 0) ||
    (lcp->password == NULL && password != NULL) ||
    (lcp->password != NULL && password == NULL) ||
    (lcp->password != NULL && password != NULL &&
      Ustrcmp(lcp->password, password) != 0))
  {
  DEBUG(D_lookup) debug_printf("%sbinding with user=%s password=%s\n",
    (lcp->bound)? "re-" : "", user, password);
#ifdef LDAP_OPT_X_TLS
  /* The Oracle LDAP libraries (LDAP_LIB_TYPE=SOLARIS) don't support this: */
  if (eldap_start_tls)
    {
    ldap_start_tls_s(lcp->ld, NULL, NULL);
    }
#endif
  if ((msgid = ldap_bind(lcp->ld, CS user, CS password, LDAP_AUTH_SIMPLE))
       == -1)
    {
    *errmsg = string_sprintf("failed to bind the LDAP connection to server "
      "%s%s - ldap_bind() returned -1", host, porttext);
    goto RETURN_ERROR;
    }

  if ((rc = ldap_result( lcp->ld, msgid, 1, timeoutptr, &result )) <= 0)
    {
    *errmsg = string_sprintf("failed to bind the LDAP connection to server "
      "%s%s - LDAP error: %s", host, porttext,
      rc == -1 ? "result retrieval failed" : "timeout" );
    result = NULL;
    goto RETURN_ERROR;
    }

  rc = ldap_result2error( lcp->ld, result, 0 );

  /* Invalid credentials when just checking credentials returns FAIL. This
  stops any further servers being tried. */

  if (search_type == SEARCH_LDAP_AUTH && rc == LDAP_INVALID_CREDENTIALS)
    {
    DEBUG(D_lookup)
      debug_printf("Invalid credentials: ldapauth returns FAIL\n");
    error_yield = FAIL;
    goto RETURN_ERROR_NOMSG;
    }

  /* Otherwise we have a problem that doesn't stop further servers from being
  tried. */

  if (rc != LDAP_SUCCESS)
    {
    *errmsg = string_sprintf("failed to bind the LDAP connection to server "
      "%s%s - LDAP error %d: %s", host, porttext, rc, ldap_err2string(rc));
    goto RETURN_ERROR;
    }

  /* Successful bind */

  lcp->bound = TRUE;
  lcp->user = (user == NULL)? NULL : string_copy(user);
  lcp->password = (password == NULL)? NULL : string_copy(password);

  ldap_msgfree(result);
  result = NULL;
  }

/* If we are just checking credentials, return OK. */

if (search_type == SEARCH_LDAP_AUTH)
  {
  DEBUG(D_lookup) debug_printf("Bind succeeded: ldapauth returns OK\n");
  goto RETURN_OK;
  }

/* Before doing the search, set the time and size limits (if given). Here again
the different implementations of LDAP have chosen to do things differently. */

#if defined(LDAP_OPT_SIZELIMIT)
ldap_set_option(lcp->ld, LDAP_OPT_SIZELIMIT, (void *)&sizelimit);
ldap_set_option(lcp->ld, LDAP_OPT_TIMELIMIT, (void *)&timelimit);
#else
lcp->ld->ld_sizelimit = sizelimit;
lcp->ld->ld_timelimit = timelimit;
#endif

/* Similarly for dereferencing aliases. Don't know if this is possible on
an LDAP library without LDAP_OPT_DEREF. */

#if defined(LDAP_OPT_DEREF)
ldap_set_option(lcp->ld, LDAP_OPT_DEREF, (void *)&dereference);
#endif

/* Similarly for the referral setting; should the library follow referrals that
the LDAP server returns? The conditional is just in case someone uses a library
without it. */

#if defined(LDAP_OPT_REFERRALS)
ldap_set_option(lcp->ld, LDAP_OPT_REFERRALS, referrals);
#endif

/* Start the search on the server. */

DEBUG(D_lookup) debug_printf("Start search\n");

msgid = ldap_search(lcp->ld, ludp->lud_dn, ludp->lud_scope, ludp->lud_filter,
  ludp->lud_attrs, 0);

if (msgid == -1)
  {
  #if defined LDAP_LIB_SOLARIS || defined LDAP_LIB_OPENLDAP2
  int err;
  ldap_get_option(lcp->ld, LDAP_OPT_ERROR_NUMBER, &err);
  *errmsg = string_sprintf("ldap_search failed: %d, %s", err,
    ldap_err2string(err));

  #else
  *errmsg = string_sprintf("ldap_search failed");
  #endif

  goto RETURN_ERROR;
  }

/* Loop to pick up results as they come in, setting a timeout if one was
given. */

while ((rc = ldap_result(lcp->ld, msgid, 0, timeoutptr, &result)) ==
        LDAP_RES_SEARCH_ENTRY)
  {
  LDAPMessage  *e;

  DEBUG(D_lookup) debug_printf("ldap_result loop\n");

  for(e = ldap_first_entry(lcp->ld, result);
      e != NULL;
      e = ldap_next_entry(lcp->ld, e))
    {
    uschar *new_dn;
    BOOL insert_space = FALSE;

    DEBUG(D_lookup) debug_printf("LDAP entry loop\n");

    rescount++;   /* Count results */

    /* Results for multiple entries values are separated by newlines. */

    if (data != NULL) data = string_cat(data, &size, &ptr, US"\n", 1);

    /* Get the DN from the last result. */

    new_dn = US ldap_get_dn(lcp->ld, e);
    if (new_dn != NULL)
      {
      if (dn != NULL)
        {
        #if defined LDAP_LIB_NETSCAPE || defined LDAP_LIB_OPENLDAP2
        ldap_memfree(dn);
        #else   /* OPENLDAP 1, UMich, Solaris */
        free(dn);
        #endif
        }
      /* Save for later */
      dn = new_dn;
      }

    /* If the data we want is actually the DN rather than any attribute values,
    (an "ldapdn" search) add it to the data string. If there are multiple
    entries, the DNs will be concatenated, but we test for this case below, as
    for SEARCH_LDAP_SINGLE, and give an error. */

    if (search_type == SEARCH_LDAP_DN)   /* Do not amalgamate these into one */
      {                                  /* condition, because of the else */
      if (new_dn != NULL)                /* below, that's for the first only */
        {
        data = string_cat(data, &size, &ptr, new_dn, Ustrlen(new_dn));
        data[ptr] = 0;
        attribute_found = TRUE;
        }
      }

    /* Otherwise, loop through the entry, grabbing attribute values. If there's
    only one attribute being retrieved, no attribute name is given, and the
    result is not quoted. Multiple values are separated by (comma, space).
    If more than one attribute is being retrieved, the data is given as a
    sequence of name=value pairs, with the value always in quotes. If there are
    multiple values, they are given within the quotes, comma separated. */

    else for (attr = US ldap_first_attribute(lcp->ld, e, &ber);
              attr != NULL;
              attr = US ldap_next_attribute(lcp->ld, e, ber))
      {
      if (attr[0] != 0)
        {
        /* Get array of values for this attribute. */

        if ((firstval = values = USS ldap_get_values(lcp->ld, e, CS attr))
             != NULL)
          {
          if (attr_count != 1)
            {
            if (insert_space)
              data = string_cat(data, &size, &ptr, US" ", 1);
            else
              insert_space = TRUE;
            data = string_cat(data, &size, &ptr, attr, Ustrlen(attr));
            data = string_cat(data, &size, &ptr, US"=\"", 2);
            }

          while (*values != NULL)
            {
            uschar *value = *values;
            int len = Ustrlen(value);

            DEBUG(D_lookup) debug_printf("LDAP attr loop %s:%s\n", attr, value);

            if (values != firstval)
              data = string_cat(data, &size, &ptr, US", ", 2);

            /* For multiple attributes, the data is in quotes. We must escape
            internal quotes, backslashes, newlines. */

            if (attr_count != 1)
              {
              int j;
              for (j = 0; j < len; j++)
                {
                if (value[j] == '\n')
                  data = string_cat(data, &size, &ptr, US"\\n", 2);
                else
                  {
                  if (value[j] == '\"' || value[j] == '\\')
                    data = string_cat(data, &size, &ptr, US"\\", 1);
                  data = string_cat(data, &size, &ptr, value+j, 1);
                  }
                }
              }

            /* For single attributes, copy the value verbatim */

            else data = string_cat(data, &size, &ptr, value, len);

            /* Move on to the next value */

            values++;
            attribute_found = TRUE;
            }

          /* Closing quote at the end of the data for a named attribute. */

          if (attr_count != 1)
            data = string_cat(data, &size, &ptr, US"\"", 1);

          /* Free the values */

          ldap_value_free(CSS firstval);
          }
        }

      #if defined LDAP_LIB_NETSCAPE || defined LDAP_LIB_OPENLDAP2

      /* Netscape and OpenLDAP2 LDAP's attrs are dynamically allocated and need
      to be freed. UMich LDAP stores them in static storage and does not require
      this. */

      ldap_memfree(attr);
      #endif
      }        /* End "for" loop for extracting attributes from an entry */
    }          /* End "for" loop for extracting entries from a result */

  /* Free the result */

  ldap_msgfree(result);
  result = NULL;
  }            /* End "while" loop for multiple results */

/* Terminate the dynamic string that we have built and reclaim unused store */

if (data != NULL)
  {
  data[ptr] = 0;
  store_reset(data + ptr + 1);
  }

/* Copy the last dn into eldap_dn */

if (dn != NULL)
  {
  eldap_dn = string_copy(dn);
  #if defined LDAP_LIB_NETSCAPE || defined LDAP_LIB_OPENLDAP2
  ldap_memfree(dn);
  #else   /* OPENLDAP 1, UMich, Solaris */
  free(dn);
  #endif
  }

DEBUG(D_lookup) debug_printf("search ended by ldap_result yielding %d\n",rc);

if (rc == 0)
  {
  *errmsg = US"ldap_result timed out";
  goto RETURN_ERROR;
  }

/* A return code of -1 seems to mean "ldap_result failed internally or couldn't
provide you with a message". Other error states seem to exist where
ldap_result() didn't give us any message from the server at all, leaving result
set to NULL. Apparently, "the error parameters of the LDAP session handle will
be set accordingly". That's the best we can do to retrieve an error status; we
can't use functions like ldap_result2error because they parse a message from
the server, which we didn't get.

Annoyingly, the different implementations of LDAP have gone for different
methods of handling error codes and generating error messages. */

if (rc == -1 || result == NULL)
  {
  int err;
  DEBUG(D_lookup) debug_printf("ldap_result failed\n");

  #if defined LDAP_LIB_SOLARIS || defined LDAP_LIB_OPENLDAP2
    ldap_get_option(lcp->ld, LDAP_OPT_ERROR_NUMBER, &err);
    *errmsg = string_sprintf("ldap_result failed: %d, %s",
      err, ldap_err2string(err));

  #elif defined LDAP_LIB_NETSCAPE
    /* Dubious (surely 'matched' is spurious here?) */
    (void)ldap_get_lderrno(lcp->ld, &matched, &error1);
    *errmsg = string_sprintf("ldap_result failed: %s (%s)", error1, matched);

  #else                             /* UMich LDAP aka OpenLDAP 1.x */
    *errmsg = string_sprintf("ldap_result failed: %d, %s",
      lcp->ld->ld_errno, ldap_err2string(lcp->ld->ld_errno));
  #endif

  goto RETURN_ERROR;
  }

/* A return code that isn't -1 doesn't necessarily mean there were no problems
with the search. The message must be an LDAP_RES_SEARCH_RESULT or
LDAP_RES_SEARCH_REFERENCE or else it's something we can't handle. Some versions
of LDAP do not define LDAP_RES_SEARCH_REFERENCE (LDAP v1 is one, it seems). So
we don't provide that functionality when we can't. :-) */

if (rc != LDAP_RES_SEARCH_RESULT
#ifdef LDAP_RES_SEARCH_REFERENCE
    && rc != LDAP_RES_SEARCH_REFERENCE
#endif
   )
  {
  *errmsg = string_sprintf("ldap_result returned unexpected code %d", rc);
  goto RETURN_ERROR;
  }

/* We have a result message from the server. This doesn't yet mean all is well.
We need to parse the message to find out exactly what's happened. */

#if defined LDAP_LIB_SOLARIS || defined LDAP_LIB_OPENLDAP2
  ldap_rc = rc;
  ldap_parse_rc = ldap_parse_result(lcp->ld, result, &rc, CSS &matched,
    CSS &error2, NULL, NULL, 0);
  DEBUG(D_lookup) debug_printf("ldap_parse_result: %d\n", ldap_parse_rc);
  if (ldap_parse_rc < 0 &&
      (ldap_parse_rc != LDAP_NO_RESULTS_RETURNED
      #ifdef LDAP_RES_SEARCH_REFERENCE
      || ldap_rc != LDAP_RES_SEARCH_REFERENCE
      #endif
     ))
    {
    *errmsg = string_sprintf("ldap_parse_result failed %d", ldap_parse_rc);
    goto RETURN_ERROR;
    }
  error1 = US ldap_err2string(rc);

#elif defined LDAP_LIB_NETSCAPE
  /* Dubious (it doesn't reference 'result' at all!) */
  rc = ldap_get_lderrno(lcp->ld, &matched, &error1);

#else                             /* UMich LDAP aka OpenLDAP 1.x */
  rc = ldap_result2error(lcp->ld, result, 0);
  error1 = ldap_err2string(rc);
  error2 = lcp->ld->ld_error;
  matched = lcp->ld->ld_matched;
#endif

/* Process the status as follows:

  (1) If we get LDAP_SIZELIMIT_EXCEEDED, just carry on, to return the
      truncated result list.

  (2) If we get LDAP_RES_SEARCH_REFERENCE, also just carry on. This was a
      submitted patch that is reported to "do the right thing" with Solaris
      LDAP libraries. (The problem it addresses apparently does not occur with
      Open LDAP.)

  (3) The range of errors defined by LDAP_NAME_ERROR generally mean "that
      object does not, or cannot, exist in the database". For those cases we
      fail the lookup.

  (4) All other non-successes here are treated as some kind of problem with
      the lookup, so return DEFER (which is the default in error_yield).
*/

DEBUG(D_lookup) debug_printf("ldap_parse_result yielded %d: %s\n",
  rc, ldap_err2string(rc));

if (rc != LDAP_SUCCESS && rc != LDAP_SIZELIMIT_EXCEEDED
    #ifdef LDAP_RES_SEARCH_REFERENCE
    && rc != LDAP_RES_SEARCH_REFERENCE
    #endif
    )
  {
  *errmsg = string_sprintf("LDAP search failed - error %d: %s%s%s%s%s",
    rc,
    (error1 != NULL)?                       error1  : US"",
    (error2 != NULL && error2[0] != 0)?     US"/"   : US"",
    (error2 != NULL)?                       error2  : US"",
    (matched != NULL && matched[0] != 0)?   US"/"   : US"",
    (matched != NULL)?                      matched : US"");

  #if defined LDAP_NAME_ERROR
  if (LDAP_NAME_ERROR(rc))
  #elif defined NAME_ERROR    /* OPENLDAP1 calls it this */
  if (NAME_ERROR(rc))
  #else
  if (rc == LDAP_NO_SUCH_OBJECT)
  #endif

    {
    DEBUG(D_lookup) debug_printf("lookup failure forced\n");
    error_yield = FAIL;
    }
  goto RETURN_ERROR;
  }

/* The search succeeded. Check if we have too many results */

if (search_type != SEARCH_LDAP_MULTIPLE && rescount > 1)
  {
  *errmsg = string_sprintf("LDAP search: more than one entry (%d) was returned "
    "(filter not specific enough?)", rescount);
  goto RETURN_ERROR_BREAK;
  }

/* Check if we have too few (zero) entries */

if (rescount < 1)
  {
  *errmsg = string_sprintf("LDAP search: no results");
  error_yield = FAIL;
  goto RETURN_ERROR_BREAK;
  }

/* If an entry was found, but it had no attributes, we behave as if no entries
were found, that is, the lookup failed. */

if (!attribute_found)
  {
  *errmsg = US"LDAP search: found no attributes";
  error_yield = FAIL;
  goto RETURN_ERROR;
  }

/* Otherwise, it's all worked */

DEBUG(D_lookup) debug_printf("LDAP search: returning: %s\n", data);
*res = data;

RETURN_OK:
if (result != NULL) ldap_msgfree(result);
ldap_free_urldesc(ludp);
return OK;

/* Error returns */

RETURN_ERROR_BREAK:
*defer_break = TRUE;

RETURN_ERROR:
DEBUG(D_lookup) debug_printf("%s\n", *errmsg);

RETURN_ERROR_NOMSG:
if (result != NULL) ldap_msgfree(result);
if (ludp != NULL) ldap_free_urldesc(ludp);

#if defined LDAP_LIB_OPENLDAP2
  if (error2 != NULL)  ldap_memfree(error2);
  if (matched != NULL) ldap_memfree(matched);
#endif

return error_yield;
}
Exemplo n.º 16
0
int main(int argc, char **cargv)
{
int dbdata_type;
uschar **argv = USS cargv;
uschar buffer[256];
uschar name[256];
void *reset_point = store_get(0);

name[0] = 0;  /* No name set */

/* Sort out the database type, verify what we are working on and then process
user requests */

dbdata_type = check_args(argc, argv, US"fixdb", US"");
printf("Modifying Exim hints database %s/db/%s\n", argv[1], argv[2]);

for(;;)
  {
  open_db dbblock;
  open_db *dbm;
  void *record;
  dbdata_retry *retry;
  dbdata_wait *wait;
  dbdata_callout_cache *callout;
  dbdata_ratelimit *ratelimit;
  int i, oldlength;
  uschar *t;
  uschar field[256], value[256];

  store_reset(reset_point);

  printf("> ");
  if (Ufgets(buffer, 256, stdin) == NULL) break;

  buffer[Ustrlen(buffer)-1] = 0;
  field[0] = value[0] = 0;

  /* If the buffer contains just one digit, or just consists of "d", use the
  previous name for an update. */

  if ((isdigit((uschar)buffer[0]) && (buffer[1] == ' ' || buffer[1] == '\0'))
       || Ustrcmp(buffer, "d") == 0)
    {
    if (name[0] == 0)
      {
      printf("No previous record name is set\n");
      continue;
      }
    (void)sscanf(CS buffer, "%s %s", field, value);
    }
  else
    {
    name[0] = 0;
    (void)sscanf(CS buffer, "%s %s %s", name, field, value);
    }

  /* Handle an update request */

  if (field[0] != 0)
    {
    int verify = 1;
    dbm = dbfn_open(argv[1], argv[2], O_RDWR, &dbblock);
    if (dbm == NULL) continue;

    if (Ustrcmp(field, "d") == 0)
      {
      if (value[0] != 0) printf("unexpected value after \"d\"\n");
        else printf("%s\n", (dbfn_delete(dbm, name) < 0)?
          "not found" : "deleted");
      dbfn_close(dbm);
      continue;
      }

    else if (isdigit((uschar)field[0]))
      {
      int fieldno = Uatoi(field);
      if (value[0] == 0)
        {
        printf("value missing\n");
        dbfn_close(dbm);
        continue;
        }
      else
        {
        record = dbfn_read_with_length(dbm, name, &oldlength);
        if (record == NULL) printf("not found\n"); else
          {
          time_t tt;
          int length = 0;     /* Stops compiler warning */

          switch(dbdata_type)
            {
            case type_retry:
            retry = (dbdata_retry *)record;
            length = sizeof(dbdata_retry) + Ustrlen(retry->text);

            switch(fieldno)
              {
              case 0:
              retry->basic_errno = Uatoi(value);
              break;

              case 1:
              retry->more_errno = Uatoi(value);
              break;

              case 2:
              if ((tt = read_time(value)) > 0) retry->first_failed = tt;
                else printf("bad time value\n");
              break;

              case 3:
              if ((tt = read_time(value)) > 0) retry->last_try = tt;
                else printf("bad time value\n");
              break;

              case 4:
              if ((tt = read_time(value)) > 0) retry->next_try = tt;
                else printf("bad time value\n");
              break;

              case 5:
              if (Ustrcmp(value, "yes") == 0) retry->expired = TRUE;
              else if (Ustrcmp(value, "no") == 0) retry->expired = FALSE;
              else printf("\"yes\" or \"no\" expected=n");
              break;

              default:
              printf("unknown field number\n");
              verify = 0;
              break;
              }
            break;

            case type_wait:
            printf("Can't change contents of wait database record\n");
            break;

            case type_misc:
            printf("Can't change contents of misc database record\n");
            break;

            case type_callout:
            callout = (dbdata_callout_cache *)record;
            length = sizeof(dbdata_callout_cache);
            switch(fieldno)
              {
              case 0:
              callout->result = Uatoi(value);
              break;

              case 1:
              callout->postmaster_result = Uatoi(value);
              break;

              case 2:
              callout->random_result = Uatoi(value);
              break;

              default:
              printf("unknown field number\n");
              verify = 0;
              break;
              }
            break;

            case type_ratelimit:
            ratelimit = (dbdata_ratelimit *)record;
            length = sizeof(dbdata_ratelimit);
            switch(fieldno)
              {
              case 0:
              if ((tt = read_time(value)) > 0) ratelimit->time_stamp = tt;
                else printf("bad time value\n");
              break;

              case 1:
              ratelimit->time_usec = Uatoi(value);
              break;

              case 2:
              ratelimit->rate = Ustrtod(value, NULL);
              break;

              default:
              printf("unknown field number\n");
              verify = 0;
              break;
              }
            break;
            }

          dbfn_write(dbm, name, record, length);
          }
        }
      }

    else
      {
      printf("field number or d expected\n");
      verify = 0;
      }

    dbfn_close(dbm);
    if (!verify) continue;
    }

  /* The "name" q causes an exit */

  else if (Ustrcmp(name, "q") == 0) return 0;

  /* Handle a read request, or verify after an update. */

  dbm = dbfn_open(argv[1], argv[2], O_RDONLY, &dbblock);
  if (dbm == NULL) continue;

  record = dbfn_read_with_length(dbm, name, &oldlength);
  if (record == NULL)
    {
    printf("record %s not found\n", name);
    name[0] = 0;
    }
  else
    {
    int count_bad = 0;
    printf("%s\n", CS print_time(((dbdata_generic *)record)->time_stamp));
    switch(dbdata_type)
      {
      case type_retry:
      retry = (dbdata_retry *)record;
      printf("0 error number: %d %s\n", retry->basic_errno, retry->text);
      printf("1 extra data:   %d\n", retry->more_errno);
      printf("2 first failed: %s\n", print_time(retry->first_failed));
      printf("3 last try:     %s\n", print_time(retry->last_try));
      printf("4 next try:     %s\n", print_time(retry->next_try));
      printf("5 expired:      %s\n", (retry->expired)? "yes" : "no");
      break;

      case type_wait:
      wait = (dbdata_wait *)record;
      t = wait->text;
      printf("Sequence: %d\n", wait->sequence);
      if (wait->count > WAIT_NAME_MAX)
        {
        printf("**** Data corrupted: count=%d=0x%x max=%d ****\n", wait->count,
          wait->count, WAIT_NAME_MAX);
        wait->count = WAIT_NAME_MAX;
        count_bad = 1;
        }
      for (i = 1; i <= wait->count; i++)
        {
        Ustrncpy(value, t, MESSAGE_ID_LENGTH);
        value[MESSAGE_ID_LENGTH] = 0;
        if (count_bad && value[0] == 0) break;
        if (Ustrlen(value) != MESSAGE_ID_LENGTH ||
            Ustrspn(value, "0123456789"
                          "abcdefghijklmnopqrstuvwxyz"
                          "ABCDEFGHIJKLMNOPQRSTUVWXYZ-") != MESSAGE_ID_LENGTH)
          {
          int j;
          printf("\n**** Data corrupted: bad character in message id ****\n");
          for (j = 0; j < MESSAGE_ID_LENGTH; j++)
            printf("%02x ", value[j]);
          printf("\n");
          break;
          }
        printf("%s ", value);
        t += MESSAGE_ID_LENGTH;
        }
      printf("\n");
      break;

      case type_misc:
      break;

      case type_callout:
      callout = (dbdata_callout_cache *)record;
      printf("0 callout:    %s (%d)\n", print_cache(callout->result),
          callout->result);
      if (oldlength > sizeof(dbdata_callout_cache_address))
        {
        printf("1 postmaster: %s (%d)\n", print_cache(callout->postmaster_result),
            callout->postmaster_result);
        printf("2 random:     %s (%d)\n", print_cache(callout->random_result),
            callout->random_result);
        }
      break;

      case type_ratelimit:
      ratelimit = (dbdata_ratelimit *)record;
      printf("0 time stamp:  %s\n", print_time(ratelimit->time_stamp));
      printf("1 fract. time: .%06d\n", ratelimit->time_usec);
      printf("2 sender rate: % .3f\n", ratelimit->rate);
      break;
      }
    }

  /* The database is closed after each request */

  dbfn_close(dbm);
  }

printf("\n");
return 0;
}
Exemplo n.º 17
0
int
main(int argc, char **cargv)
{
int dbdata_type = 0;
int yield = 0;
open_db dbblock;
open_db *dbm;
EXIM_CURSOR *cursor;
uschar **argv = USS cargv;
uschar *key;
uschar keybuffer[1024];

/* Check the arguments, and open the database */

dbdata_type = check_args(argc, argv, US"dumpdb", US"");
dbm = dbfn_open(argv[1], argv[2], O_RDONLY, &dbblock);
if (dbm == NULL) exit(1);

/* Scan the file, formatting the information for each entry. Note
that data is returned in a malloc'ed block, in order that it be
correctly aligned. */

key = dbfn_scan(dbm, TRUE, &cursor);
while (key != NULL)
  {
  dbdata_retry *retry;
  dbdata_wait *wait;
  dbdata_callout_cache *callout;
  dbdata_ratelimit *ratelimit;
  int count_bad = 0;
  int i, length;
  uschar *t;
  uschar name[MESSAGE_ID_LENGTH + 1];
  void *value;

  /* Keep a copy of the key separate, as in some DBM's the pointer is into data
  which might change. */

  if (Ustrlen(key) > sizeof(keybuffer) - 1)
    {
    printf("**** Overlong key encountered: %s\n", key);
    return 1;
    }
  Ustrcpy(keybuffer, key);
  value = dbfn_read_with_length(dbm, keybuffer, &length);

  if (value == NULL)
    fprintf(stderr, "**** Entry \"%s\" was in the key scan, but the record "
                    "was not found in the file - something is wrong!\n",
      CS keybuffer);
  else
    {
    /* Note: don't use print_time more than once in one statement, since
    it uses a single buffer. */

    switch(dbdata_type)
      {
      case type_retry:
      retry = (dbdata_retry *)value;
      printf("  %s %d %d %s\n%s  ", keybuffer, retry->basic_errno,
        retry->more_errno, retry->text,
        print_time(retry->first_failed));
      printf("%s  ", print_time(retry->last_try));
      printf("%s %s\n", print_time(retry->next_try),
        (retry->expired)? "*" : "");
      break;

      case type_wait:
      wait = (dbdata_wait *)value;
      printf("%s ", keybuffer);
      t = wait->text;
      name[MESSAGE_ID_LENGTH] = 0;

      if (wait->count > WAIT_NAME_MAX)
        {
        fprintf(stderr,
          "**** Data for %s corrupted\n  count=%d=0x%x max=%d\n",
          CS keybuffer, wait->count, wait->count, WAIT_NAME_MAX);
        wait->count = WAIT_NAME_MAX;
        yield = count_bad = 1;
        }
      for (i = 1; i <= wait->count; i++)
        {
        Ustrncpy(name, t, MESSAGE_ID_LENGTH);
        if (count_bad && name[0] == 0) break;
        if (Ustrlen(name) != MESSAGE_ID_LENGTH ||
            Ustrspn(name, "0123456789"
                          "abcdefghijklmnopqrstuvwxyz"
                          "ABCDEFGHIJKLMNOPQRSTUVWXYZ-") != MESSAGE_ID_LENGTH)
          {
          int j;
          fprintf(stderr,
            "**** Data for %s corrupted: bad character in message id\n",
            CS keybuffer);
          for (j = 0; j < MESSAGE_ID_LENGTH; j++)
            fprintf(stderr, "%02x ", name[j]);
          fprintf(stderr, "\n");
          yield = 1;
          break;
          }
        printf("%s ", name);
        t += MESSAGE_ID_LENGTH;
        }
      printf("\n");
      break;

      case type_misc:
      printf("%s %s\n", print_time(((dbdata_generic *)value)->time_stamp),
        keybuffer);
      break;

      case type_callout:
      callout = (dbdata_callout_cache *)value;

      /* New-style address record */

      if (length == sizeof(dbdata_callout_cache_address))
        {
        printf("%s %s callout=%s\n",
          print_time(((dbdata_generic *)value)->time_stamp),
          keybuffer,
          print_cache(callout->result));
        }

      /* New-style domain record */

      else if (length == sizeof(dbdata_callout_cache))
        {
        printf("%s %s callout=%s postmaster=%s",
          print_time(((dbdata_generic *)value)->time_stamp),
          keybuffer,
          print_cache(callout->result),
          print_cache(callout->postmaster_result));
        if (callout->postmaster_result != ccache_unknown)
          printf(" (%s)", print_time(callout->postmaster_stamp));
        printf(" random=%s", print_cache(callout->random_result));
        if (callout->random_result != ccache_unknown)
          printf(" (%s)", print_time(callout->random_stamp));
        printf("\n");
        }

      /* Old-style domain record, without separate timestamps. This code can
      eventually be thrown away, say in 5 years' time (it's now Feb 2003). */

      else
        {
        printf("%s %s callout=%s postmaster=%s random=%s\n",
          print_time(((dbdata_generic *)value)->time_stamp),
          keybuffer,
          print_cache(callout->result),
          print_cache(callout->postmaster_result),
          print_cache(callout->random_result));
        }

      break;

      case type_ratelimit:
      ratelimit = (dbdata_ratelimit *)value;

      printf("%s.%06d rate: %10.3f key: %s\n",
        print_time(ratelimit->time_stamp), ratelimit->time_usec,
        ratelimit->rate, keybuffer);

      break;
      }
    store_reset(value);
    }
  key = dbfn_scan(dbm, FALSE, &cursor);
  }

dbfn_close(dbm);
return yield;
}
Exemplo n.º 18
0
int main(int argc, char **cargv)
{
struct stat statbuf;
int maxkeep = 30 * 24 * 60 * 60;
int dbdata_type, i, oldest, path_len;
key_item *keychain = NULL;
void *reset_point;
open_db dbblock;
open_db *dbm;
EXIM_CURSOR *cursor;
uschar **argv = USS cargv;
uschar buffer[256];
uschar *key;

/* Scan the options */

for (i = 1; i < argc; i++)
  {
  if (argv[i][0] != '-') break;
  if (Ustrcmp(argv[i], "-f") == 0) continue;
  if (Ustrcmp(argv[i], "-t") == 0)
    {
    uschar *s;
    s = argv[++i];
    maxkeep = 0;
    while (*s != 0)
      {
      int value, count;
      if (!isdigit(*s)) usage(US"tidydb", US" [-t <time>]");
      (void)sscanf(CS s, "%d%n", &value, &count);
      s += count;
      switch (*s)
        {
        case 'w': value *= 7;
        case 'd': value *= 24;
        case 'h': value *= 60;
        case 'm': value *= 60;
        case 's': s++;
        break;
        default: usage(US"tidydb", US" [-t <time>]");
        }
      maxkeep += value;
      }
    }
  else usage(US"tidydb", US" [-t <time>]");
  }

/* Adjust argument values and process arguments */

argc -= --i;
argv += i;

dbdata_type = check_args(argc, argv, US"tidydb", US" [-t <time>]");

/* Compute the oldest keep time, verify what we are doing, and open the
database */

oldest = time(NULL) - maxkeep;
printf("Tidying Exim hints database %s/db/%s\n", argv[1], argv[2]);

dbm = dbfn_open(argv[1], argv[2], O_RDWR, &dbblock);
if (dbm == NULL) exit(1);

/* Prepare for building file names */

sprintf(CS buffer, "%s/input/", argv[1]);
path_len = Ustrlen(buffer);


/* It appears, by experiment, that it is a bad idea to make changes
to the file while scanning it. Pity the man page doesn't warn you about that.
Therefore, we scan and build a list of all the keys. Then we use that to
read the records and possibly update them. */

key = dbfn_scan(dbm, TRUE, &cursor);
while (key != NULL)
  {
  key_item *k = store_get(sizeof(key_item) + Ustrlen(key));
  k->next = keychain;
  keychain = k;
  Ustrcpy(k->key, key);
  key = dbfn_scan(dbm, FALSE, &cursor);
  }

/* Now scan the collected keys and operate on the records, resetting
the store each time round. */

reset_point = store_get(0);

while (keychain != NULL)
  {
  dbdata_generic *value;

  store_reset(reset_point);
  key = keychain->key;
  keychain = keychain->next;
  value = dbfn_read_with_length(dbm, key, NULL);

  /* A continuation record may have been deleted or renamed already, so
  non-existence is not serious. */

  if (value == NULL) continue;

  /* Delete if too old */

  if (value->time_stamp < oldest)
    {
    printf("deleted %s (too old)\n", key);
    dbfn_delete(dbm, key);
    continue;
    }

  /* Do database-specific tidying for wait databases, and message-
  specific tidying for the retry database. */

  if (dbdata_type == type_wait)
    {
    dbdata_wait *wait = (dbdata_wait *)value;
    BOOL update = FALSE;

    /* Leave corrupt records alone */

    if (wait->count > WAIT_NAME_MAX)
      {
      printf("**** Data for %s corrupted\n  count=%d=0x%x max=%d\n",
        key, wait->count, wait->count, WAIT_NAME_MAX);
      continue;
      }

    /* Loop for renamed continuation records. For each message id,
    check to see if the message exists, and if not, remove its entry
    from the record. Because of the possibility of split input directories,
    we must look in both possible places for a -D file. */

    for (;;)
      {
      int offset;
      int length = wait->count * MESSAGE_ID_LENGTH;

      for (offset = length - MESSAGE_ID_LENGTH;
           offset >= 0; offset -= MESSAGE_ID_LENGTH)
        {
        Ustrncpy(buffer+path_len, wait->text + offset, MESSAGE_ID_LENGTH);
        sprintf(CS(buffer+path_len + MESSAGE_ID_LENGTH), "-D");

        if (Ustat(buffer, &statbuf) != 0)
          {
          buffer[path_len] = wait->text[offset+5];
          buffer[path_len+1] = '/';
          Ustrncpy(buffer+path_len+2, wait->text + offset, MESSAGE_ID_LENGTH);
          sprintf(CS(buffer+path_len+2 + MESSAGE_ID_LENGTH), "-D");

          if (Ustat(buffer, &statbuf) != 0)
            {
            int left = length - offset - MESSAGE_ID_LENGTH;
            if (left > 0) Ustrncpy(wait->text + offset,
              wait->text + offset + MESSAGE_ID_LENGTH, left);
            wait->count--;
            length -= MESSAGE_ID_LENGTH;
            update = TRUE;
            }
          }
        }

      /* If record is empty and the main record, either delete it or rename
      the next continuation, repeating if that is also empty. */

      if (wait->count == 0 && Ustrchr(key, ':') == NULL)
        {
        while (wait->count == 0 && wait->sequence > 0)
          {
          uschar newkey[256];
          dbdata_generic *newvalue;
          sprintf(CS newkey, "%s:%d", key, wait->sequence - 1);
          newvalue = dbfn_read_with_length(dbm, newkey, NULL);
          if (newvalue != NULL)
            {
            value = newvalue;
            wait = (dbdata_wait *)newvalue;
            dbfn_delete(dbm, newkey);
            printf("renamed %s\n", newkey);
            update = TRUE;
            }
          else wait->sequence--;
          }

        /* If we have ended up with an empty main record, delete it
        and break the loop. Otherwise the new record will be scanned. */

        if (wait->count == 0 && wait->sequence == 0)
          {
          dbfn_delete(dbm, key);
          printf("deleted %s (empty)\n", key);
          update = FALSE;
          break;
          }
        }

      /* If not an empty main record, break the loop */

      else break;
      }

    /* Re-write the record if required */

    if (update)
      {
      printf("updated %s\n", key);
      dbfn_write(dbm, key, wait, sizeof(dbdata_wait) +
        wait->count * MESSAGE_ID_LENGTH);
      }
    }

  /* If a retry record's key ends with a message-id, check that that message
  still exists; if not, remove this record. */

  else if (dbdata_type == type_retry)
    {
    uschar *id;
    int len = Ustrlen(key);

    if (len < MESSAGE_ID_LENGTH + 1) continue;
    id = key + len - MESSAGE_ID_LENGTH - 1;
    if (*id++ != ':') continue;

    for (i = 0; i < MESSAGE_ID_LENGTH; i++)
      {
      if (i == 6 || i == 13)
        { if (id[i] != '-') break; }
      else
        { if (!isalnum(id[i])) break; }
      }
    if (i < MESSAGE_ID_LENGTH) continue;

    Ustrncpy(buffer + path_len, id, MESSAGE_ID_LENGTH);
    sprintf(CS(buffer + path_len + MESSAGE_ID_LENGTH), "-D");

    if (Ustat(buffer, &statbuf) != 0)
      {
      sprintf(CS(buffer + path_len), "%c/%s-D", id[5], id);
      if (Ustat(buffer, &statbuf) != 0)
        {
        dbfn_delete(dbm, key);
        printf("deleted %s (no message)\n", key);
        }
      }
    }
  }

dbfn_close(dbm);
printf("Tidying complete\n");
return 0;
}
Exemplo n.º 19
0
int
spool_read_header(uschar *name, BOOL read_headers, BOOL subdir_set)
{
FILE *f = NULL;
int n;
int rcount = 0;
long int uid, gid;
BOOL inheader = FALSE;
uschar *p;

/* Reset all the global variables to their default values. However, there is
one exception. DO NOT change the default value of dont_deliver, because it may
be forced by an external setting. */

acl_var_c = acl_var_m = NULL;
authenticated_id = NULL;
authenticated_sender = NULL;
allow_unqualified_recipient = FALSE;
allow_unqualified_sender = FALSE;
body_linecount = 0;
body_zerocount = 0;
deliver_firsttime = FALSE;
deliver_freeze = FALSE;
deliver_frozen_at = 0;
deliver_manual_thaw = FALSE;
/* dont_deliver must NOT be reset */
header_list = header_last = NULL;
host_lookup_deferred = FALSE;
host_lookup_failed = FALSE;
interface_address = NULL;
interface_port = 0;
local_error_message = FALSE;
local_scan_data = NULL;
max_received_linelength = 0;
message_linecount = 0;
received_protocol = NULL;
received_count = 0;
recipients_list = NULL;
sender_address = NULL;
sender_fullhost = NULL;
sender_helo_name = NULL;
sender_host_address = NULL;
sender_host_name = NULL;
sender_host_port = 0;
sender_host_authenticated = NULL;
sender_ident = NULL;
sender_local = FALSE;
sender_set_untrusted = FALSE;
smtp_active_hostname = primary_hostname;
tree_nonrecipients = NULL;

#ifdef EXPERIMENTAL_BRIGHTMAIL
bmi_run = 0;
bmi_verdicts = NULL;
#endif

#ifndef DISABLE_DKIM
dkim_signers = NULL;
dkim_disable_verify = FALSE;
dkim_collect_input = FALSE;
#endif

#ifdef SUPPORT_TLS
tls_in.certificate_verified = FALSE;
# ifdef EXPERIMENTAL_DANE
tls_in.dane_verified = FALSE;
# endif
tls_in.cipher = NULL;
# ifndef COMPILE_UTILITY	/* tls support fns not built in */
tls_free_cert(&tls_in.ourcert);
tls_free_cert(&tls_in.peercert);
# endif
tls_in.peerdn = NULL;
tls_in.sni = NULL;
tls_in.ocsp = OCSP_NOT_REQ;
#endif

#ifdef WITH_CONTENT_SCAN
spam_bar = NULL;
spam_score = NULL;
spam_score_int = NULL;
#endif

#if defined(SUPPORT_I18N) && !defined(COMPILE_UTILITY)
message_smtputf8 = FALSE;
message_utf8_downconvert = 0;
#endif

dsn_ret = 0;
dsn_envid = NULL;

/* Generate the full name and open the file. If message_subdir is already
set, just look in the given directory. Otherwise, look in both the split
and unsplit directories, as for the data file above. */

for (n = 0; n < 2; n++)
  {
  if (!subdir_set)
    message_subdir[0] = (split_spool_directory == (n == 0))? name[5] : 0;
  sprintf(CS big_buffer, "%s/input/%s/%s", spool_directory, message_subdir,
    name);
  f = Ufopen(big_buffer, "rb");
  if (f != NULL) break;
  if (n != 0 || subdir_set || errno != ENOENT) return spool_read_notopen;
  }

errno = 0;

#ifndef COMPILE_UTILITY
DEBUG(D_deliver) debug_printf("reading spool file %s\n", name);
#endif  /* COMPILE_UTILITY */

/* The first line of a spool file contains the message id followed by -H (i.e.
the file name), in order to make the file self-identifying. */

if (Ufgets(big_buffer, big_buffer_size, f) == NULL) goto SPOOL_READ_ERROR;
if (Ustrlen(big_buffer) != MESSAGE_ID_LENGTH + 3 ||
    Ustrncmp(big_buffer, name, MESSAGE_ID_LENGTH + 2) != 0)
  goto SPOOL_FORMAT_ERROR;

/* The next three lines in the header file are in a fixed format. The first
contains the login, uid, and gid of the user who caused the file to be written.
There are known cases where a negative gid is used, so we allow for both
negative uids and gids. The second contains the mail address of the message's
sender, enclosed in <>. The third contains the time the message was received,
and the number of warning messages for delivery delays that have been sent. */

if (Ufgets(big_buffer, big_buffer_size, f) == NULL) goto SPOOL_READ_ERROR;

p = big_buffer + Ustrlen(big_buffer);
while (p > big_buffer && isspace(p[-1])) p--;
*p = 0;
if (!isdigit(p[-1])) goto SPOOL_FORMAT_ERROR;
while (p > big_buffer && (isdigit(p[-1]) || '-' == p[-1])) p--;
gid = Uatoi(p);
if (p <= big_buffer || *(--p) != ' ') goto SPOOL_FORMAT_ERROR;
*p = 0;
if (!isdigit(p[-1])) goto SPOOL_FORMAT_ERROR;
while (p > big_buffer && (isdigit(p[-1]) || '-' == p[-1])) p--;
uid = Uatoi(p);
if (p <= big_buffer || *(--p) != ' ') goto SPOOL_FORMAT_ERROR;
*p = 0;

originator_login = string_copy(big_buffer);
originator_uid = (uid_t)uid;
originator_gid = (gid_t)gid;

/* envelope from */
if (Ufgets(big_buffer, big_buffer_size, f) == NULL) goto SPOOL_READ_ERROR;
n = Ustrlen(big_buffer);
if (n < 3 || big_buffer[0] != '<' || big_buffer[n-2] != '>')
  goto SPOOL_FORMAT_ERROR;

sender_address = store_get(n-2);
Ustrncpy(sender_address, big_buffer+1, n-3);
sender_address[n-3] = 0;

/* time */
if (Ufgets(big_buffer, big_buffer_size, f) == NULL) goto SPOOL_READ_ERROR;
if (sscanf(CS big_buffer, "%d %d", &received_time, &warning_count) != 2)
  goto SPOOL_FORMAT_ERROR;

message_age = time(NULL) - received_time;

#ifndef COMPILE_UTILITY
DEBUG(D_deliver) debug_printf("user=%s uid=%ld gid=%ld sender=%s\n",
  originator_login, (long int)originator_uid, (long int)originator_gid,
  sender_address);
#endif  /* COMPILE_UTILITY */

/* Now there may be a number of optional lines, each starting with "-". If you
add a new setting here, make sure you set the default above.

Because there are now quite a number of different possibilities, we use a
switch on the first character to avoid too many failing tests. Thanks to Nico
Erfurth for the patch that implemented this. I have made it even more efficient
by not re-scanning the first two characters.

To allow new versions of Exim that add additional flags to interwork with older
versions that do not understand them, just ignore any lines starting with "-"
that we don't recognize. Otherwise it wouldn't be possible to back off a new
version that left new-style flags written on the spool. */

p = big_buffer + 2;
for (;;)
  {
  int len;
  if (Ufgets(big_buffer, big_buffer_size, f) == NULL) goto SPOOL_READ_ERROR;
  if (big_buffer[0] != '-') break;
  while (  (len = Ustrlen(big_buffer)) == big_buffer_size-1
	&& big_buffer[len-1] != '\n'
	)
    {	/* buffer not big enough for line; certs make this possible */
    uschar * buf;
    if (big_buffer_size >= BIG_BUFFER_SIZE*4) goto SPOOL_READ_ERROR;
    buf = store_get_perm(big_buffer_size *= 2);
    memcpy(buf, big_buffer, --len);
    big_buffer = buf;
    if (Ufgets(big_buffer+len, big_buffer_size-len, f) == NULL)
      goto SPOOL_READ_ERROR;
    }
  big_buffer[len-1] = 0;

  switch(big_buffer[1])
    {
    case 'a':

    /* Nowadays we use "-aclc" and "-aclm" for the different types of ACL
    variable, because Exim allows any number of them, with arbitrary names.
    The line in the spool file is "-acl[cm] <name> <length>". The name excludes
    the c or m. */

    if (Ustrncmp(p, "clc ", 4) == 0 ||
        Ustrncmp(p, "clm ", 4) == 0)
      {
      uschar *name, *endptr;
      int count;
      tree_node *node;
      endptr = Ustrchr(big_buffer + 6, ' ');
      if (endptr == NULL) goto SPOOL_FORMAT_ERROR;
      name = string_sprintf("%c%.*s", big_buffer[4], endptr - big_buffer - 6,
        big_buffer + 6);
      if (sscanf(CS endptr, " %d", &count) != 1) goto SPOOL_FORMAT_ERROR;
      node = acl_var_create(name);
      node->data.ptr = store_get(count + 1);
      if (fread(node->data.ptr, 1, count+1, f) < count) goto SPOOL_READ_ERROR;
      ((uschar*)node->data.ptr)[count] = 0;
      }

    else if (Ustrcmp(p, "llow_unqualified_recipient") == 0)
      allow_unqualified_recipient = TRUE;
    else if (Ustrcmp(p, "llow_unqualified_sender") == 0)
      allow_unqualified_sender = TRUE;

    else if (Ustrncmp(p, "uth_id", 6) == 0)
      authenticated_id = string_copy(big_buffer + 9);
    else if (Ustrncmp(p, "uth_sender", 10) == 0)
      authenticated_sender = string_copy(big_buffer + 13);
    else if (Ustrncmp(p, "ctive_hostname", 14) == 0)
      smtp_active_hostname = string_copy(big_buffer + 17);

    /* For long-term backward compatibility, we recognize "-acl", which was
    used before the number of ACL variables changed from 10 to 20. This was
    before the subsequent change to an arbitrary number of named variables.
    This code is retained so that upgrades from very old versions can still
    handle old-format spool files. The value given after "-acl" is a number
    that is 0-9 for connection variables, and 10-19 for message variables. */

    else if (Ustrncmp(p, "cl ", 3) == 0)
      {
      int index, count;
      uschar name[20];   /* Need plenty of space for %d format */
      tree_node *node;
      if (  sscanf(CS big_buffer + 5, "%d %d", &index, &count) != 2
	 || index >= 20
         )
        goto SPOOL_FORMAT_ERROR;
      if (index < 10)
        (void) string_format(name, sizeof(name), "%c%d", 'c', index);
      else
        (void) string_format(name, sizeof(name), "%c%d", 'm', index - 10);
      node = acl_var_create(name);
      node->data.ptr = store_get(count + 1);
      if (fread(node->data.ptr, 1, count+1, f) < count) goto SPOOL_READ_ERROR;
      ((uschar*)node->data.ptr)[count] = 0;
      }
    break;

    case 'b':
    if (Ustrncmp(p, "ody_linecount", 13) == 0)
      body_linecount = Uatoi(big_buffer + 15);
    else if (Ustrncmp(p, "ody_zerocount", 13) == 0)
      body_zerocount = Uatoi(big_buffer + 15);
#ifdef EXPERIMENTAL_BRIGHTMAIL
    else if (Ustrncmp(p, "mi_verdicts ", 12) == 0)
      bmi_verdicts = string_copy(big_buffer + 14);
#endif
    break;

    case 'd':
    if (Ustrcmp(p, "eliver_firsttime") == 0)
      deliver_firsttime = TRUE;
    /* Check if the dsn flags have been set in the header file */
    else if (Ustrncmp(p, "sn_ret", 6) == 0)
      dsn_ret= atoi(CS big_buffer + 8);
    else if (Ustrncmp(p, "sn_envid", 8) == 0)
      dsn_envid = string_copy(big_buffer + 11);
    break;

    case 'f':
    if (Ustrncmp(p, "rozen", 5) == 0)
      {
      deliver_freeze = TRUE;
      if (sscanf(CS big_buffer+7, TIME_T_FMT, &deliver_frozen_at) != 1)
	goto SPOOL_READ_ERROR;
      }
    break;

    case 'h':
    if (Ustrcmp(p, "ost_lookup_deferred") == 0)
      host_lookup_deferred = TRUE;
    else if (Ustrcmp(p, "ost_lookup_failed") == 0)
      host_lookup_failed = TRUE;
    else if (Ustrncmp(p, "ost_auth", 8) == 0)
      sender_host_authenticated = string_copy(big_buffer + 11);
    else if (Ustrncmp(p, "ost_name", 8) == 0)
      sender_host_name = string_copy(big_buffer + 11);
    else if (Ustrncmp(p, "elo_name", 8) == 0)
      sender_helo_name = string_copy(big_buffer + 11);

    /* We now record the port number after the address, separated by a
    dot. For compatibility during upgrading, do nothing if there
    isn't a value (it gets left at zero). */

    else if (Ustrncmp(p, "ost_address", 11) == 0)
      {
      sender_host_port = host_address_extract_port(big_buffer + 14);
      sender_host_address = string_copy(big_buffer + 14);
      }
    break;

    case 'i':
    if (Ustrncmp(p, "nterface_address", 16) == 0)
      {
      interface_port = host_address_extract_port(big_buffer + 19);
      interface_address = string_copy(big_buffer + 19);
      }
    else if (Ustrncmp(p, "dent", 4) == 0)
      sender_ident = string_copy(big_buffer + 7);
    break;

    case 'l':
    if (Ustrcmp(p, "ocal") == 0) sender_local = TRUE;
    else if (Ustrcmp(big_buffer, "-localerror") == 0)
      local_error_message = TRUE;
    else if (Ustrncmp(p, "ocal_scan ", 10) == 0)
      local_scan_data = string_copy(big_buffer + 12);
    break;

    case 'm':
    if (Ustrcmp(p, "anual_thaw") == 0) deliver_manual_thaw = TRUE;
    else if (Ustrncmp(p, "ax_received_linelength", 22) == 0)
      max_received_linelength = Uatoi(big_buffer + 24);
    break;

    case 'N':
    if (*p == 0) dont_deliver = TRUE;   /* -N */
    break;

    case 'r':
    if (Ustrncmp(p, "eceived_protocol", 16) == 0)
      received_protocol = string_copy(big_buffer + 19);
    break;

    case 's':
    if (Ustrncmp(p, "ender_set_untrusted", 19) == 0)
      sender_set_untrusted = TRUE;
#ifdef WITH_CONTENT_SCAN
    else if (Ustrncmp(p, "pam_bar ", 8) == 0)
      spam_bar = string_copy(big_buffer + 10);
    else if (Ustrncmp(p, "pam_score ", 10) == 0)
      spam_score = string_copy(big_buffer + 12);
    else if (Ustrncmp(p, "pam_score_int ", 14) == 0)
      spam_score_int = string_copy(big_buffer + 16);
#endif
#if defined(SUPPORT_I18N) && !defined(COMPILE_UTILITY)
    else if (Ustrncmp(p, "mtputf8", 7) == 0)
      message_smtputf8 = TRUE;
#endif
    break;

#ifdef SUPPORT_TLS
    case 't':
    if (Ustrncmp(p, "ls_certificate_verified", 23) == 0)
      tls_in.certificate_verified = TRUE;
    else if (Ustrncmp(p, "ls_cipher", 9) == 0)
      tls_in.cipher = string_copy(big_buffer + 12);
# ifndef COMPILE_UTILITY	/* tls support fns not built in */
    else if (Ustrncmp(p, "ls_ourcert", 10) == 0)
      (void) tls_import_cert(big_buffer + 13, &tls_in.ourcert);
    else if (Ustrncmp(p, "ls_peercert", 11) == 0)
      (void) tls_import_cert(big_buffer + 14, &tls_in.peercert);
# endif
    else if (Ustrncmp(p, "ls_peerdn", 9) == 0)
      tls_in.peerdn = string_unprinting(string_copy(big_buffer + 12));
    else if (Ustrncmp(p, "ls_sni", 6) == 0)
      tls_in.sni = string_unprinting(string_copy(big_buffer + 9));
    else if (Ustrncmp(p, "ls_ocsp", 7) == 0)
      tls_in.ocsp = big_buffer[10] - '0';
    break;
#endif

#if defined(SUPPORT_I18N) && !defined(COMPILE_UTILITY)
    case 'u':
    if (Ustrncmp(p, "tf8_downcvt", 11) == 0)
      message_utf8_downconvert = 1;
    else if (Ustrncmp(p, "tf8_optdowncvt", 15) == 0)
      message_utf8_downconvert = -1;
    break;
#endif

    default:    /* Present because some compilers complain if all */
    break;      /* possibilities are not covered. */
    }
  }

/* Build sender_fullhost if required */

#ifndef COMPILE_UTILITY
host_build_sender_fullhost();
#endif  /* COMPILE_UTILITY */

#ifndef COMPILE_UTILITY
DEBUG(D_deliver)
  debug_printf("sender_local=%d ident=%s\n", sender_local,
    (sender_ident == NULL)? US"unset" : sender_ident);
#endif  /* COMPILE_UTILITY */

/* We now have the tree of addresses NOT to deliver to, or a line
containing "XX", indicating no tree. */

if (Ustrncmp(big_buffer, "XX\n", 3) != 0 &&
  !read_nonrecipients_tree(&tree_nonrecipients, f, big_buffer, big_buffer_size))
    goto SPOOL_FORMAT_ERROR;

#ifndef COMPILE_UTILITY
DEBUG(D_deliver)
  {
  debug_printf("Non-recipients:\n");
  debug_print_tree(tree_nonrecipients);
  }
#endif  /* COMPILE_UTILITY */

/* After reading the tree, the next line has not yet been read into the
buffer. It contains the count of recipients which follow on separate lines. */

if (Ufgets(big_buffer, big_buffer_size, f) == NULL) goto SPOOL_READ_ERROR;
if (sscanf(CS big_buffer, "%d", &rcount) != 1) goto SPOOL_FORMAT_ERROR;

#ifndef COMPILE_UTILITY
DEBUG(D_deliver) debug_printf("recipients_count=%d\n", rcount);
#endif  /* COMPILE_UTILITY */

recipients_list_max = rcount;
recipients_list = store_get(rcount * sizeof(recipient_item));

for (recipients_count = 0; recipients_count < rcount; recipients_count++)
  {
  int nn;
  int pno = -1;
  int dsn_flags = 0;
  uschar *orcpt = NULL;
  uschar *errors_to = NULL;
  uschar *p;

  if (Ufgets(big_buffer, big_buffer_size, f) == NULL) goto SPOOL_READ_ERROR;
  nn = Ustrlen(big_buffer);
  if (nn < 2) goto SPOOL_FORMAT_ERROR;

  /* Remove the newline; this terminates the address if there is no additional
  data on the line. */

  p = big_buffer + nn - 1;
  *p-- = 0;

  /* Look back from the end of the line for digits and special terminators.
  Since an address must end with a domain, we can tell that extra data is
  present by the presence of the terminator, which is always some character
  that cannot exist in a domain. (If I'd thought of the need for additional
  data early on, I'd have put it at the start, with the address at the end. As
  it is, we have to operate backwards. Addresses are permitted to contain
  spaces, you see.)

  This code has to cope with various versions of this data that have evolved
  over time. In all cases, the line might just contain an address, with no
  additional data. Otherwise, the possibilities are as follows:

  Exim 3 type:       <address><space><digits>,<digits>,<digits>

    The second set of digits is the parent number for one_time addresses. The
    other values were remnants of earlier experiments that were abandoned.

  Exim 4 first type: <address><space><digits>

    The digits are the parent number for one_time addresses.

  Exim 4 new type:   <address><space><data>#<type bits>

    The type bits indicate what the contents of the data are.

    Bit 01 indicates that, reading from right to left, the data
      ends with <errors_to address><space><len>,<pno> where pno is
      the parent number for one_time addresses, and len is the length
      of the errors_to address (zero meaning none).

    Bit 02 indicates that, again reading from right to left, the data continues
     with orcpt len(orcpt),dsn_flags
   */

  while (isdigit(*p)) p--;

  /* Handle Exim 3 spool files */

  if (*p == ',')
    {
    int dummy;
    while (isdigit(*(--p)) || *p == ',');
    if (*p == ' ')
      {
      *p++ = 0;
      (void)sscanf(CS p, "%d,%d", &dummy, &pno);
      }
    }

  /* Handle early Exim 4 spool files */

  else if (*p == ' ')
    {
    *p++ = 0;
    (void)sscanf(CS p, "%d", &pno);
    }

  /* Handle current format Exim 4 spool files */

  else if (*p == '#')
    {
    int flags;

#if !defined (COMPILE_UTILITY)
    DEBUG(D_deliver) debug_printf("**** SPOOL_IN - Exim 4 standard format spoolfile\n");
#endif

    (void)sscanf(CS p+1, "%d", &flags);

    if ((flags & 0x01) != 0)      /* one_time data exists */
      {
      int len;
      while (isdigit(*(--p)) || *p == ',' || *p == '-');
      (void)sscanf(CS p+1, "%d,%d", &len, &pno);
      *p = 0;
      if (len > 0)
        {
        p -= len;
        errors_to = string_copy(p);
        }
      }

    *(--p) = 0;   /* Terminate address */
    if ((flags & 0x02) != 0)      /* one_time data exists */
      {
      int len;
      while (isdigit(*(--p)) || *p == ',' || *p == '-');
      (void)sscanf(CS p+1, "%d,%d", &len, &dsn_flags);
      *p = 0;
      if (len > 0)
        {
        p -= len;
        orcpt = string_copy(p);
        }
      }

    *(--p) = 0;   /* Terminate address */
    }
#if !defined(COMPILE_UTILITY)
  else
    { DEBUG(D_deliver) debug_printf("**** SPOOL_IN - No additional fields\n"); }

  if ((orcpt != NULL) || (dsn_flags != 0))
    {
    DEBUG(D_deliver) debug_printf("**** SPOOL_IN - address: |%s| orcpt: |%s| dsn_flags: %d\n",
      big_buffer, orcpt, dsn_flags);
    }
  if (errors_to != NULL)
    {
    DEBUG(D_deliver) debug_printf("**** SPOOL_IN - address: |%s| errorsto: |%s|\n",
      big_buffer, errors_to);
    }
#endif

  recipients_list[recipients_count].address = string_copy(big_buffer);
  recipients_list[recipients_count].pno = pno;
  recipients_list[recipients_count].errors_to = errors_to;
  recipients_list[recipients_count].orcpt = orcpt;
  recipients_list[recipients_count].dsn_flags = dsn_flags;
  }

/* The remainder of the spool header file contains the headers for the message,
separated off from the previous data by a blank line. Each header is preceded
by a count of its length and either a certain letter (for various identified
headers), space (for a miscellaneous live header) or an asterisk (for a header
that has been rewritten). Count the Received: headers. We read the headers
always, in order to check on the format of the file, but only create a header
list if requested to do so. */

inheader = TRUE;
if (Ufgets(big_buffer, big_buffer_size, f) == NULL) goto SPOOL_READ_ERROR;
if (big_buffer[0] != '\n') goto SPOOL_FORMAT_ERROR;

while ((n = fgetc(f)) != EOF)
  {
  header_line *h;
  uschar flag[4];
  int i;

  if (!isdigit(n)) goto SPOOL_FORMAT_ERROR;
  if(ungetc(n, f) == EOF  ||  fscanf(f, "%d%c ", &n, flag) == EOF)
    goto SPOOL_READ_ERROR;
  if (flag[0] != '*') message_size += n;  /* Omit non-transmitted headers */

  if (read_headers)
    {
    h = store_get(sizeof(header_line));
    h->next = NULL;
    h->type = flag[0];
    h->slen = n;
    h->text = store_get(n+1);

    if (h->type == htype_received) received_count++;

    if (header_list == NULL) header_list = h;
      else header_last->next = h;
    header_last = h;

    for (i = 0; i < n; i++)
      {
      int c = fgetc(f);
      if (c == 0 || c == EOF) goto SPOOL_FORMAT_ERROR;
      if (c == '\n' && h->type != htype_old) message_linecount++;
      h->text[i] = c;
      }
    h->text[i] = 0;
    }

  /* Not requiring header data, just skip through the bytes */

  else for (i = 0; i < n; i++)
    {
    int c = fgetc(f);
    if (c == 0 || c == EOF) goto SPOOL_FORMAT_ERROR;
    }
  }

/* We have successfully read the data in the header file. Update the message
line count by adding the body linecount to the header linecount. Close the file
and give a positive response. */

#ifndef COMPILE_UTILITY
DEBUG(D_deliver) debug_printf("body_linecount=%d message_linecount=%d\n",
  body_linecount, message_linecount);
#endif  /* COMPILE_UTILITY */

message_linecount += body_linecount;

fclose(f);
return spool_read_OK;


/* There was an error reading the spool or there was missing data,
or there was a format error. A "read error" with no errno means an
unexpected EOF, which we treat as a format error. */

SPOOL_READ_ERROR:
if (errno != 0)
  {
  n = errno;

#ifndef COMPILE_UTILITY
  DEBUG(D_any) debug_printf("Error while reading spool file %s\n", name);
#endif  /* COMPILE_UTILITY */

  fclose(f);
  errno = n;
  return inheader? spool_read_hdrerror : spool_read_enverror;
  }

SPOOL_FORMAT_ERROR:

#ifndef COMPILE_UTILITY
DEBUG(D_any) debug_printf("Format error in spool file %s\n", name);
#endif  /* COMPILE_UTILITY */

fclose(f);
errno = ERRNO_SPOOLFORMAT;
return inheader? spool_read_hdrerror : spool_read_enverror;
}
Exemplo n.º 20
0
int dcc_process(uschar **listptr) {
  int sep = 0;
  uschar *list = *listptr;
  FILE *data_file;
  uschar *dcc_daemon_ip = US"";
  uschar *dcc_default_ip_option = US"127.0.0.1";
  uschar *dcc_ip_option = US"";
  uschar *dcc_helo_option = US"localhost";
  uschar *dcc_reject_message = US"Rejected by DCC";
  uschar *xtra_hdrs = NULL;

  /* from local_scan */
  int i, j, k, c, retval, sockfd, resp, line;
  unsigned int portnr;
  struct sockaddr_un  serv_addr;
  struct sockaddr_in  serv_addr_in;
  struct hostent *ipaddress;
  uschar sockpath[128];
  uschar sockip[40], client_ip[40];
  uschar opts[128];
  uschar rcpt[128], from[128];
  uschar sendbuf[4096];
  uschar recvbuf[4096];
  uschar dcc_return_text[1024];
  uschar mbox_path[1024];
  uschar message_subdir[2];
  struct header_line *dcchdr;
  uschar *dcc_acl_options;
  uschar dcc_acl_options_buffer[10];
  uschar dcc_xtra_hdrs[1024];

  /* grep 1st option */
  if ((dcc_acl_options = string_nextinlist(&list, &sep,
                                           dcc_acl_options_buffer,
                                           sizeof(dcc_acl_options_buffer))) != NULL)
  {
    /* parse 1st option */
    if ( (strcmpic(dcc_acl_options,US"false") == 0) ||
         (Ustrcmp(dcc_acl_options,"0") == 0) ) {
      /* explicitly no matching */
      return FAIL;
    };

    /* special cases (match anything except empty) */
    if ( (strcmpic(dcc_acl_options,US"true") == 0) ||
         (Ustrcmp(dcc_acl_options,"*") == 0) ||
         (Ustrcmp(dcc_acl_options,"1") == 0) ) {
      dcc_acl_options = dcc_acl_options;
    };
  }
  else {
    /* empty means "don't match anything" */
    return FAIL;
  };

  sep = 0;

  /* if we scanned this message last time, just return */
  if ( dcc_ok )
      return dcc_rc;

  /* open the spooled body */
  message_subdir[1] = '\0';
  for (i = 0; i < 2; i++) {
    message_subdir[0] = (split_spool_directory == (i == 0))? message_id[5] : 0;
    sprintf(CS mbox_path, "%s/input/%s/%s-D", spool_directory, message_subdir, message_id);
    data_file = Ufopen(mbox_path,"rb");
    if (data_file != NULL)
      break;
  };

  if (data_file == NULL) {
    /* error while spooling */
    log_write(0, LOG_MAIN|LOG_PANIC,
           "dcc acl condition: error while opening spool file");
    return DEFER;
  };

  /* Initialize the variables */

  bzero(sockip,sizeof(sockip));
  if (dccifd_address) {
    if (dccifd_address[0] == '/')
      Ustrncpy(sockpath, dccifd_address, sizeof(sockpath));
    else
      if( sscanf(CS dccifd_address, "%s %u", sockip, &portnr) != 2) {
        log_write(0, LOG_MAIN,
          "dcc acl condition: warning - invalid dccifd address: '%s'", dccifd_address);
        (void)fclose(data_file);
        return DEFER;
      }
  }

  /* opts is what we send as dccifd options - see man dccifd */
  /* We don't support any other option than 'header' so just copy that */
  bzero(opts,sizeof(opts));
  Ustrncpy(opts, "header", sizeof(opts)-1);
  Ustrncpy(client_ip, dcc_ip_option, sizeof(client_ip)-1);
  /* If the dcc_client_ip is not provided use the
   * sender_host_address or 127.0.0.1 if it is NULL */
  DEBUG(D_acl)
    debug_printf("my_ip_option = %s - client_ip = %s - sender_host_address = %s\n", dcc_ip_option, client_ip, sender_host_address);
  if(!(Ustrcmp(client_ip, ""))){
    /* Do we have a sender_host_address or is it NULL? */
    if(sender_host_address){
      Ustrncpy(client_ip, sender_host_address, sizeof(client_ip)-1);
    } else {
      /* sender_host_address is NULL which means it comes from localhost */
      Ustrncpy(client_ip, dcc_default_ip_option, sizeof(client_ip)-1);
    }
  }
  DEBUG(D_acl)
    debug_printf("Client IP: %s\n", client_ip);
  Ustrncpy(sockip, dcc_daemon_ip, sizeof(sockip)-1);
  /* strncat(opts, my_request, strlen(my_request)); */
  Ustrcat(opts, "\n");
  Ustrncat(opts, client_ip, sizeof(opts)-Ustrlen(opts)-1);
  Ustrncat(opts, "\nHELO ", sizeof(opts)-Ustrlen(opts)-1);
  Ustrncat(opts, dcc_helo_option, sizeof(opts)-Ustrlen(opts)-2);
  Ustrcat(opts, "\n");

  /* initialize the other variables */
  dcchdr = header_list;
  /* we set the default return value to DEFER */
  retval = DEFER;

  bzero(sendbuf,sizeof(sendbuf));
  bzero(dcc_header_str,sizeof(dcc_header_str));
  bzero(rcpt,sizeof(rcpt));
  bzero(from,sizeof(from));

  /* send a null return path as "<>". */
  if (Ustrlen(sender_address) > 0)
    Ustrncpy(from, sender_address, sizeof(from));
  else
    Ustrncpy(from, "<>", sizeof(from));
  Ustrncat(from, "\n", sizeof(from)-Ustrlen(from)-1);

  /**************************************
   * Now creating the socket connection *
   **************************************/

  /* If there is a dcc_daemon_ip, we use a tcp socket, otherwise a UNIX socket */
  if(Ustrcmp(sockip, "")){
    ipaddress = gethostbyname((char *)sockip);
    bzero((char *) &serv_addr_in, sizeof(serv_addr_in));
    serv_addr_in.sin_family = AF_INET;
    bcopy((char *)ipaddress->h_addr, (char *)&serv_addr_in.sin_addr.s_addr, ipaddress->h_length);
    serv_addr_in.sin_port = htons(portnr);
    if ((sockfd = socket(AF_INET, SOCK_STREAM,0)) < 0){
      DEBUG(D_acl)
        debug_printf("Creating socket failed: %s\n", strerror(errno));
      log_write(0,LOG_REJECT,"Creating socket failed: %s\n", strerror(errno));
      /* if we cannot create the socket, defer the mail */
      (void)fclose(data_file);
      return retval;
    }
    /* Now connecting the socket (INET) */
    if (connect(sockfd, (struct sockaddr *)&serv_addr_in, sizeof(serv_addr_in)) < 0){
      DEBUG(D_acl)
        debug_printf("Connecting socket failed: %s\n", strerror(errno));
      log_write(0,LOG_REJECT,"Connecting socket failed: %s\n", strerror(errno));
      /* if we cannot contact the socket, defer the mail */
      (void)fclose(data_file);
      return retval;
    }
  } else {
    /* connecting to the dccifd UNIX socket */
    bzero((char *)&serv_addr,sizeof(serv_addr));
    serv_addr.sun_family = AF_UNIX;
    Ustrcpy(serv_addr.sun_path, sockpath);
    if ((sockfd = socket(AF_UNIX, SOCK_STREAM,0)) < 0){
      DEBUG(D_acl)
        debug_printf("Creating socket failed: %s\n", strerror(errno));
      log_write(0,LOG_REJECT,"Creating socket failed: %s\n", strerror(errno));
      /* if we cannot create the socket, defer the mail */
      (void)fclose(data_file);
      return retval;
    }
    /* Now connecting the socket (UNIX) */
    if (connect(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0){
      DEBUG(D_acl)
                            debug_printf("Connecting socket failed: %s\n", strerror(errno));
      log_write(0,LOG_REJECT,"Connecting socket failed: %s\n", strerror(errno));
      /* if we cannot contact the socket, defer the mail */
      (void)fclose(data_file);
      return retval;
    }
  }
  /* the socket is open, now send the options to dccifd*/
  DEBUG(D_acl)
    debug_printf("\n---------------------------\nSocket opened; now sending input\n-----------------\n");
  /* First, fill in the input buffer */
  Ustrncpy(sendbuf, opts, sizeof(sendbuf));
  Ustrncat(sendbuf, from, sizeof(sendbuf)-Ustrlen(sendbuf)-1);

  DEBUG(D_acl)
  {
    debug_printf("opts = %s\nsender = %s\nrcpt count = %d\n", opts, from, recipients_count);
    debug_printf("Sending options:\n****************************\n");
  }

  /* let's send each of the recipients to dccifd */
  for (i = 0; i < recipients_count; i++){
    DEBUG(D_acl)
      debug_printf("recipient = %s\n",recipients_list[i].address);
    if(Ustrlen(sendbuf) + Ustrlen(recipients_list[i].address) > sizeof(sendbuf))
    {
      DEBUG(D_acl)
        debug_printf("Writing buffer: %s\n", sendbuf);
      flushbuffer(sockfd, sendbuf);
      bzero(sendbuf, sizeof(sendbuf));
    }
    Ustrncat(sendbuf, recipients_list[i].address, sizeof(sendbuf)-Ustrlen(sendbuf)-1);
    Ustrncat(sendbuf, "\r\n", sizeof(sendbuf)-Ustrlen(sendbuf)-1);
  }
  /* send a blank line between options and message */
  Ustrncat(sendbuf, "\n", sizeof(sendbuf)-Ustrlen(sendbuf)-1);
  /* Now we send the input buffer */
  DEBUG(D_acl)
    debug_printf("%s\n****************************\n", sendbuf);
  flushbuffer(sockfd, sendbuf);

  /* now send the message */
  /* Clear the input buffer */
  bzero(sendbuf, sizeof(sendbuf));
  /* First send the headers */
  /* Now send the headers */
  DEBUG(D_acl)
    debug_printf("Sending headers:\n****************************\n");
  Ustrncpy(sendbuf, dcchdr->text, sizeof(sendbuf)-2);
  while((dcchdr=dcchdr->next)) {
    if(dcchdr->slen > sizeof(sendbuf)-2) {
      /* The size of the header is bigger than the size of
       * the input buffer, so split it up in smaller parts. */
       flushbuffer(sockfd, sendbuf);
       bzero(sendbuf, sizeof(sendbuf));
       j = 0;
       while(j < dcchdr->slen)
       {
        for(i = 0; i < sizeof(sendbuf)-2; i++) {
          sendbuf[i] = dcchdr->text[j];
          j++;
        }
        flushbuffer(sockfd, sendbuf);
        bzero(sendbuf, sizeof(sendbuf));
       }
    } else if(Ustrlen(sendbuf) + dcchdr->slen > sizeof(sendbuf)-2) {
      flushbuffer(sockfd, sendbuf);
      bzero(sendbuf, sizeof(sendbuf));
      Ustrncpy(sendbuf, dcchdr->text, sizeof(sendbuf)-2);
    } else {
      Ustrncat(sendbuf, dcchdr->text, sizeof(sendbuf)-Ustrlen(sendbuf)-2);
    }
  }

  /* a blank line seperates header from body */
  Ustrncat(sendbuf, "\n", sizeof(sendbuf)-Ustrlen(sendbuf)-1);
  flushbuffer(sockfd, sendbuf);
  DEBUG(D_acl)
    debug_printf("\n****************************\n%s", sendbuf);

  /* Clear the input buffer */
  bzero(sendbuf, sizeof(sendbuf));

  /* now send the body */
  DEBUG(D_acl)
    debug_printf("Writing body:\n****************************\n");
  (void)fseek(data_file, SPOOL_DATA_START_OFFSET, SEEK_SET);
  while((fread(sendbuf, 1, sizeof(sendbuf)-1, data_file)) > 0) {
    flushbuffer(sockfd, sendbuf);
    bzero(sendbuf, sizeof(sendbuf));
  }
  DEBUG(D_acl)
    debug_printf("\n****************************\n");

  /* shutdown() the socket */
  if(shutdown(sockfd, 1) < 0){
    DEBUG(D_acl)
      debug_printf("Couldn't shutdown socket: %s\n", strerror(errno));
    log_write(0,LOG_MAIN,"Couldn't shutdown socket: %s\n", strerror(errno));
    /* If there is a problem with the shutdown()
     * defer the mail. */
    (void)fclose(data_file);
    return retval;
  }
  DEBUG(D_acl)
    debug_printf("\n-------------------------\nInput sent.\n-------------------------\n");

    /********************************
   * receiving output from dccifd *
   ********************************/
  DEBUG(D_acl)
    debug_printf("\n-------------------------------------\nNow receiving output from server\n-----------------------------------\n");

  /******************************************************************
   * We should get 3 lines:                                         *
   * 1/ First line is overall result: either 'A' for Accept,        *
   *    'R' for Reject, 'S' for accept Some recipients or           *
   *    'T' for a Temporary error.                                  *
   * 2/ Second line contains the list of Accepted/Rejected          *
   *    recipients in the form AARRA (A = accepted, R = rejected).  *
   * 3/ Third line contains the X-DCC header.                       *
   ******************************************************************/

  line = 1;    /* we start at the first line of the output */
  j = 0;       /* will be used as index for the recipients list */
  k = 0;       /* initializing the index of the X-DCC header: dcc_header_str[k] */

  /* Let's read from the socket until there's nothing left to read */
  bzero(recvbuf, sizeof(recvbuf));
  while((resp = read(sockfd, recvbuf, sizeof(recvbuf)-1)) > 0) {
    /* How much did we get from the socket */
    c = Ustrlen(recvbuf) + 1;
    DEBUG(D_acl)
      debug_printf("Length of the output buffer is: %d\nOutput buffer is:\n------------\n%s\n-----------\n", c, recvbuf);

    /* Now let's read each character and see what we've got */
    for(i = 0; i < c; i++) {
      /* First check if we reached the end of the line and
       * then increment the line counter */
      if(recvbuf[i] == '\n') {
        line++;
      }
      else {
        /* The first character of the first line is the
         * overall response. If there's another character
         * on that line it is not correct. */
        if(line == 1) {
          if(i == 0) {
            /* Now get the value and set the
             * return value accordingly */
            if(recvbuf[i] == 'A') {
              DEBUG(D_acl)
                debug_printf("Overall result = A\treturning OK\n");
              Ustrcpy(dcc_return_text, "Mail accepted by DCC");
              dcc_result = US"A";
              retval = OK;
            }
            else if(recvbuf[i] == 'R') {
              DEBUG(D_acl)
                debug_printf("Overall result = R\treturning FAIL\n");
              dcc_result = US"R";
              retval = FAIL;
              if(sender_host_name) {
                log_write(0, LOG_MAIN, "H=%s [%s] F=<%s>: rejected by DCC", sender_host_name, sender_host_address, sender_address);
              }
              else {
                log_write(0, LOG_MAIN, "H=[%s] F=<%s>: rejected by DCC", sender_host_address, sender_address);
              }
              Ustrncpy(dcc_return_text, dcc_reject_message, Ustrlen(dcc_reject_message) + 1);
            }
            else if(recvbuf[i] == 'S') {
              DEBUG(D_acl)
                debug_printf("Overall result  = S\treturning OK\n");
              Ustrcpy(dcc_return_text, "Not all recipients accepted by DCC");
              /* Since we're in an ACL we want a global result
               * so we accept for all */
              dcc_result = US"A";
              retval = OK;
            }
            else if(recvbuf[i] == 'G') {
              DEBUG(D_acl)
                debug_printf("Overall result  = G\treturning FAIL\n");
              Ustrcpy(dcc_return_text, "Greylisted by DCC");
              dcc_result = US"G";
              retval = FAIL;
            }
            else if(recvbuf[i] == 'T') {
              DEBUG(D_acl)
                debug_printf("Overall result = T\treturning DEFER\n");
              retval = DEFER;
              log_write(0,LOG_MAIN,"Temporary error with DCC: %s\n", recvbuf);
              Ustrcpy(dcc_return_text, "Temporary error with DCC");
              dcc_result = US"T";
            }
            else {
              DEBUG(D_acl)
                debug_printf("Overall result = something else\treturning DEFER\n");
              retval = DEFER;
              log_write(0,LOG_MAIN,"Unknown DCC response: %s\n", recvbuf);
              Ustrcpy(dcc_return_text, "Unknown DCC response");
              dcc_result = US"T";
            }
          }
          else {
          /* We're on the first line but not on the first character,
           * there must be something wrong. */
            DEBUG(D_acl)
              debug_printf("Line = %d but i = %d != 0  character is %c - This is wrong!\n", line, i, recvbuf[i]);
              log_write(0,LOG_MAIN,"Wrong header from DCC, output is %s\n", recvbuf);
          }
        }
        else if(line == 2) {
          /* On the second line we get a list of
           * answers for each recipient. We don't care about
           * it because we're in an acl and take the
           * global result. */
        }
        else if(line > 2) {
          /* The third and following lines are the X-DCC header,
           * so we store it in dcc_header_str. */
          /* check if we don't get more than we can handle */
          if(k < sizeof(dcc_header_str)) { 
            dcc_header_str[k] = recvbuf[i];
            k++;
          }
          else {
            DEBUG(D_acl)
              debug_printf("We got more output than we can store in the X-DCC header. Truncating at 120 characters.\n");
          }
        }
        else {
          /* Wrong line number. There must be a problem with the output. */
          DEBUG(D_acl)
            debug_printf("Wrong line number in output. Line number is %d\n", line);
        }
      }
    }
    /* we reinitialize the output buffer before we read again */
    bzero(recvbuf,sizeof(recvbuf));
  }
  /* We have read everything from the socket */

  /* We need to terminate the X-DCC header with a '\n' character. This needs to be k-1
   * since dcc_header_str[k] contains '\0'. */
  dcc_header_str[k-1] = '\n';

  /* Now let's sum up what we've got. */
  DEBUG(D_acl)
    debug_printf("\n--------------------------\nOverall result = %d\nX-DCC header: %sReturn message: %s\ndcc_result: %s\n", retval, dcc_header_str, dcc_return_text, dcc_result);

  /* We only add the X-DCC header if it starts with X-DCC */
  if(!(Ustrncmp(dcc_header_str, "X-DCC", 5))){
    dcc_header = dcc_header_str;
    if(dcc_direct_add_header) {
      header_add(' ' , "%s", dcc_header_str);
  /* since the MIME ACL already writes the .eml file to disk without DCC Header we've to erase it */
      unspool_mbox();
    }
  }
  else {
    DEBUG(D_acl)
      debug_printf("Wrong format of the X-DCC header: %s\n", dcc_header_str);
  }

  /* check if we should add additional headers passed in acl_m_dcc_add_header */
  if(dcc_direct_add_header) {
    if (((xtra_hdrs = expand_string(US"$acl_m_dcc_add_header")) != NULL) && (xtra_hdrs[0] != '\0')) {
      Ustrncpy(dcc_xtra_hdrs, xtra_hdrs, sizeof(dcc_xtra_hdrs) - 2);
      if (dcc_xtra_hdrs[Ustrlen(dcc_xtra_hdrs)-1] != '\n')
        Ustrcat(dcc_xtra_hdrs, "\n");
      header_add(' ', "%s", dcc_xtra_hdrs);
      DEBUG(D_acl)
        debug_printf("adding additional headers in $acl_m_dcc_add_header: %s", dcc_xtra_hdrs);
    }
  }

  dcc_ok = 1;
  /* Now return to exim main process */
  DEBUG(D_acl)
    debug_printf("Before returning to exim main process:\nreturn_text = %s - retval = %d\ndcc_result = %s\n", dcc_return_text, retval, dcc_result);

  (void)fclose(data_file);
  dcc_rc = retval;
  return dcc_rc;
}
Exemplo n.º 21
0
Arquivo: em_menu.c Projeto: Exim/exim
void menu_create(Widget w, XEvent *event, String *actargs, Cardinal *count)
{
int line;
int i;
uschar *s;
XawTextPosition p;
Widget src, menu_line, item_1, item_2, item_3, item_4,
  item_5, item_6, item_7, item_8, item_9, item_10, item_11,
  item_12, item_13;
XtTranslations menu_trans = XtParseTranslationTable(
  "<EnterWindow>:   highlight()\n\
   <LeaveWindow>:   unhighlight()\n\
   <BtnMotion>:     highlight()\n\
   <BtnUp>:         MenuPopdown()notify()unhighlight()\n\
  ");

actargs = actargs;   /* Keep picky compilers happy */
count = count;

/* Get the sink and source and the current text pointer */

queue_get_arg[0].value = (XtArgVal)(&queue_text_sink);
queue_get_arg[1].value = (XtArgVal)(&src);
queue_get_arg[2].value = (XtArgVal)(&s);
XtGetValues(w, queue_get_arg, 3);

/* Find the line number of the pointer in the window, and the
character offset of the top lefthand of the window. */

line = (event->xbutton).y / XawTextSinkMaxHeight(queue_text_sink, 1);
p = XawTextTopPosition(w);

/* Find the start of the line on which the button was clicked. */

i = line;
while (i-- > 0)
  {
  while (s[p] != 0 && s[p++] != '\n');
  }

/* Now pointing either at 0 or 1st uschar after \n, or very 1st uschar.
If 0, the click was beyond the end of the data; just set up a dummy
menu. (Not easy to ignore as several actions are specified for the
mouse click and it expects this one to set up a menu.) If on a
continuation line, move back to the main line. */

if (s[p] == 0)
  {
  menushell_arg[0].value = (XtArgVal)"No message selected";
  menushell = XtCreatePopupShell("menu", simpleMenuWidgetClass,
    queue_widget, menushell_arg, XtNumber(menushell_arg));
  XtAddCallback(menushell, "popdownCallback", popdownAction, NULL);
  xs_SetValues(menushell, 2,
    "cursor",       XCreateFontCursor(X_display, XC_arrow),
    "translations", menu_trans);

  /* To keep the widgets in XFree86 happy, we have to create at least one menu
  item, it seems. (Openwindows doesn't mind a menu with no items.) Otherwise
  there's a complaint about a zero width menu, and a crash. */

  menu_line = XtCreateManagedWidget("line", smeLineObjectClass, menushell,
    NULL, 0);

  item_99_arg[0].value = (XtArgVal)menu_line;
  (void)XtCreateManagedWidget("item99", smeBSBObjectClass, menushell,
    item_99_arg, XtNumber(item_99_arg));

  highlighted_x = -1;
  return;
  }

while (p > 0 && s[p+11] == ' ')
  {
  line--;
  p--;
  while (p > 0 && s[p-1] != '\n') p--;
  }

/* Now pointing at first character of a main line. */

Ustrncpy(message_id, s+p+11, MESSAGE_ID_LENGTH);
message_id[MESSAGE_ID_LENGTH] = 0;

/* Highlight the line being menued, and save its parameters so that it
can be de-highlighted at popdown. */

highlighted_start = highlighted_end = p;
while (s[highlighted_end] != '\n') highlighted_end++;
highlighted_x = 17;
highlighted_y = line * XawTextSinkMaxHeight(queue_text_sink, 1) + 2;

XawTextSinkDisplayText(queue_text_sink,
  highlighted_x, highlighted_y,
  highlighted_start, highlighted_end, 1);

/* Create the popup shell and the other widgets that comprise the menu.
Set the translations and pointer shape, and add the callback pointers. */

menushell_arg[0].value = (XtArgVal)message_id;
menushell = XtCreatePopupShell("menu", simpleMenuWidgetClass,
  queue_widget, menushell_arg, XtNumber(menushell_arg));
XtAddCallback(menushell, "popdownCallback", popdownAction, NULL);

xs_SetValues(menushell, 2,
  "cursor",       XCreateFontCursor(X_display, XC_arrow),
  "translations", menu_trans);

menu_line = XtCreateManagedWidget("line", smeLineObjectClass, menushell,
  NULL, 0);

item_1_arg[0].value = (XtArgVal)menu_line;
item_1 = XtCreateManagedWidget("item1", smeBSBObjectClass, menushell,
  item_1_arg, XtNumber(item_1_arg));
XtAddCallback(item_1, "callback",  msglogAction, (XtPointer)message_id);

item_2_arg[0].value = (XtArgVal)item_1;
item_2 = XtCreateManagedWidget("item2", smeBSBObjectClass, menushell,
  item_2_arg, XtNumber(item_2_arg));
XtAddCallback(item_2, "callback",  headersAction, (XtPointer)message_id);

item_3_arg[0].value = (XtArgVal)item_2;
item_3 = XtCreateManagedWidget("item3", smeBSBObjectClass, menushell,
  item_3_arg, XtNumber(item_3_arg));
XtAddCallback(item_3, "callback",  bodyAction, (XtPointer)message_id);

item_4_arg[0].value = (XtArgVal)item_3;
item_4 = XtCreateManagedWidget("item4", smeBSBObjectClass, menushell,
  item_4_arg, XtNumber(item_4_arg));
XtAddCallback(item_4, "callback",  deliverAction, (XtPointer)message_id);

item_5_arg[0].value = (XtArgVal)item_4;
item_5 = XtCreateManagedWidget("item5", smeBSBObjectClass, menushell,
  item_5_arg, XtNumber(item_5_arg));
XtAddCallback(item_5, "callback",  freezeAction, (XtPointer)message_id);

item_6_arg[0].value = (XtArgVal)item_5;
item_6 = XtCreateManagedWidget("item6", smeBSBObjectClass, menushell,
  item_6_arg, XtNumber(item_6_arg));
XtAddCallback(item_6, "callback",  thawAction, (XtPointer)message_id);

item_7_arg[0].value = (XtArgVal)item_6;
item_7 = XtCreateManagedWidget("item7", smeBSBObjectClass, menushell,
  item_7_arg, XtNumber(item_7_arg));
XtAddCallback(item_7, "callback",  giveupAction, (XtPointer)message_id);

item_8_arg[0].value = (XtArgVal)item_7;
item_8 = XtCreateManagedWidget("item8", smeBSBObjectClass, menushell,
  item_8_arg, XtNumber(item_8_arg));
XtAddCallback(item_8, "callback",  removeAction, (XtPointer)message_id);

item_9_arg[0].value = (XtArgVal)item_8;
item_9 = XtCreateManagedWidget("item9", smeBSBObjectClass, menushell,
  item_9_arg, XtNumber(item_9_arg));

item_10_arg[0].value = (XtArgVal)item_9;
item_10 = XtCreateManagedWidget("item10", smeBSBObjectClass, menushell,
  item_10_arg, XtNumber(item_10_arg));
XtAddCallback(item_10, "callback",  addrecipAction, (XtPointer)message_id);

item_11_arg[0].value = (XtArgVal)item_10;
item_11 = XtCreateManagedWidget("item11", smeBSBObjectClass, menushell,
  item_11_arg, XtNumber(item_11_arg));
XtAddCallback(item_11, "callback",  markdelAction, (XtPointer)message_id);

item_12_arg[0].value = (XtArgVal)item_11;
item_12 = XtCreateManagedWidget("item12", smeBSBObjectClass, menushell,
  item_12_arg, XtNumber(item_12_arg));
XtAddCallback(item_12, "callback",  markalldelAction, (XtPointer)message_id);

item_13_arg[0].value = (XtArgVal)item_12;
item_13 = XtCreateManagedWidget("item13", smeBSBObjectClass, menushell,
  item_13_arg, XtNumber(item_13_arg));
XtAddCallback(item_13, "callback",  editsenderAction, (XtPointer)message_id);

/* Arrange that the menu pops up with the first item selected. */

xs_SetValues(menushell, 1, "popupOnEntry", item_1);

/* Flag that the menu is up to suppress queue updates. */

menu_is_up = TRUE;
}
Exemplo n.º 22
0
Arquivo: pipe.c Projeto: akissa/exim
static BOOL
set_up_shell_command(const uschar ***argvptr, uschar *cmd,
  BOOL expand_arguments, int expand_fail, address_item *addr, uschar *tname)
{
const uschar **argv;

*argvptr = argv = store_get((4)*sizeof(uschar *));

argv[0] = US"/bin/sh";
argv[1] = US"-c";

/* We have to take special action to handle the special "variable" called
$pipe_addresses, which is not recognized by the normal expansion function. */

DEBUG(D_transport)
  debug_printf("shell pipe command before expansion:\n  %s\n", cmd);

if (expand_arguments)
  {
  uschar *s = cmd;
  uschar *p = Ustrstr(cmd, "pipe_addresses");

  if (p != NULL && (
         (p > cmd && p[-1] == '$') ||
         (p > cmd + 1 && p[-2] == '$' && p[-1] == '{' && p[14] == '}')))
    {
    address_item *ad;
    uschar *q = p + 14;
    int size = Ustrlen(cmd) + 64;
    int offset;

    if (p[-1] == '{') { q++; p--; }

    s = store_get(size);
    offset = p - cmd - 1;
    Ustrncpy(s, cmd, offset);

    for (ad = addr; ad != NULL; ad = ad->next)
      {
      if (ad != addr) string_cat(s, &size, &offset, US" ", 1);
      string_cat(s, &size, &offset, ad->address, Ustrlen(ad->address));
      }

    string_cat(s, &size, &offset, q, Ustrlen(q));
    s[offset] = 0;
    }

  /* Allow $recipients in the expansion iff it comes from a system filter */

  enable_dollar_recipients = addr != NULL &&
    addr->parent != NULL &&
    Ustrcmp(addr->parent->address, "system-filter") == 0;
  argv[2] = expand_string(s);
  enable_dollar_recipients = FALSE;

  if (argv[2] == NULL)
    {
    addr->transport_return = search_find_defer? DEFER : expand_fail;
    addr->message = string_sprintf("Expansion of command \"%s\" "
      "in %s transport failed: %s",
      cmd, tname, expand_string_message);
    return FALSE;
    }

  DEBUG(D_transport)
    debug_printf("shell pipe command after expansion:\n  %s\n", argv[2]);
  }
else argv[2] = cmd;

argv[3] = (uschar *)0;
return TRUE;
}
Exemplo n.º 23
0
Arquivo: ldap.c Projeto: fanf2/exim
static uschar *
eldap_quote(uschar *s, uschar *opt)
{
register int c;
int count = 0;
int len = 0;
BOOL dn = FALSE;
uschar *t = s;
uschar *quoted;

/* Test for a DN quotation. */

if (opt != NULL)
  {
  if (Ustrcmp(opt, "dn") != 0) return NULL;    /* No others recognized */
  dn = TRUE;
  }

/* Compute how much extra store we need for the string. This doesn't have to be
exact as long as it isn't an underestimate. The worst case is the addition of 5
extra bytes for a single character. This occurs for certain characters in DNs,
where, for example, < turns into %5C%3C. For simplicity, we just add 5 for each
possibly escaped character. The really fast way would be just to test for
non-alphanumerics, but it is probably better to spot a few others that are
never escaped, because if there are no specials at all, we can avoid copying
the string. */

while ((c = *t++) != 0)
  {
  len++;
  if (!isalnum(c) && Ustrchr(ALWAYS_LITERAL, c) == NULL) count += 5;
  }
if (count == 0) return s;

/* Get sufficient store to hold the quoted string */

t = quoted = store_get(len + count + 1);

/* Handle plain quote_ldap */

if (!dn)
  {
  while ((c = *s++) != 0)
    {
    if (!isalnum(c))
      {
      if (Ustrchr(LDAP_QUOTE, c) != NULL)
        {
        sprintf(CS t, "%%5C%02X", c);        /* e.g. * => %5C2A */
        t += 5;
        continue;
        }
      if (Ustrchr(URL_NONQUOTE, c) == NULL)  /* e.g. ] => %5D */
        {
        sprintf(CS t, "%%%02X", c);
        t += 3;
        continue;
        }
      }
    *t++ = c;                                /* unquoted character */
    }
  }

/* Handle quote_ldap_dn */

else
  {
  uschar *ss = s + len;

  /* Find the last char before any trailing spaces */

  while (ss > s && ss[-1] == ' ') ss--;

  /* Quote leading spaces and sharps */

  for (; s < ss; s++)
    {
    if (*s != ' ' && *s != '#') break;
    sprintf(CS t, "%%5C%%%02X", *s);
    t += 6;
    }

  /* Handle the rest of the string, up to the trailing spaces */

  while (s < ss)
    {
    c = *s++;
    if (!isalnum(c))
      {
      if (Ustrchr(LDAP_DN_QUOTE, c) != NULL)
        {
        Ustrncpy(t, "%5C", 3);               /* insert \ where needed */
        t += 3;                              /* fall through to check URL */
        }
      if (Ustrchr(URL_NONQUOTE, c) == NULL)  /* e.g. ] => %5D */
        {
        sprintf(CS t, "%%%02X", c);
        t += 3;
        continue;
        }
      }
    *t++ = c;    /* unquoted character, or non-URL quoted after %5C */
    }

  /* Handle the trailing spaces */

  while (*ss++ != 0)
    {
    Ustrncpy(t, "%5C%20", 6);
    t += 6;
    }
  }

/* Terminate the new string and return */

*t = 0;
return quoted;
}