Ejemplo n.º 1
0
static BOOL
read_nonrecipients_tree(tree_node **connect, FILE *f, uschar *buffer,
  int buffer_size)
{
tree_node *node;
int n = Ustrlen(buffer);
BOOL right = buffer[1] == 'Y';

if (n < 5) return FALSE;    /* malformed line */
buffer[n-1] = 0;            /* Remove \n */
node = store_get(sizeof(tree_node) + n - 3);
*connect = node;
Ustrcpy(node->name, buffer + 3);
node->data.ptr = NULL;

if (buffer[0] == 'Y')
  {
  if (Ufgets(buffer, buffer_size, f) == NULL ||
    !read_nonrecipients_tree(&node->left, f, buffer, buffer_size))
      return FALSE;
  }
else node->left = NULL;

if (right)
  {
  if (Ufgets(buffer, buffer_size, f) == NULL ||
    !read_nonrecipients_tree(&node->right, f, buffer, buffer_size))
      return FALSE;
  }
else node->right = NULL;

(void) count_below(*connect);
return TRUE;
}
Ejemplo n.º 2
0
Archivo: pvs.c Proyecto: jff/mathspad
void pvs_use_result(char *pvs_result, int forward)
{
  char *c, *h, bcount;
  PVSProof *prf;
  /* search formula that has to be proven */
  c=strstr(pvs_result, "|----");
  if (!c) { fprintf(stderr, ">> No |----\n");return; }
  h = strchr(c,'\n');
  if (h) c=h+1;
  while (!isspace(*c)) c++;
  while (isspace(*c)) c++;
  /* search for result variable ... */
  h=strstr(c," = Tresult");
  if (!h && !using_booleans) { fprintf(stderr, ">> No = Tresult\n");return;}
  else {
    /* ... and remove it. */ 
    if (h) *h=0;
  }
  h=c;
  /* Make sure the () match */
  bcount=0;
  while (*h) {
    if (*h=='(') bcount++;
    else if (*h==')') bcount--;
    h++;
  }
  while (bcount>0) { *h++=')'; bcount--;}
  *h='\0';
  /* parse the result */
  pvs_parse_string(c);
  /* result is on the stack */
  prf = toproof;
  insert_template_selection(prf->selection,
			    notation_with_number(equalhint_uid));
  /* make buffer with hint text */
  {
    Char buffer[1024];
    Char *bp;
    ProofStep *p;
    p=prf->step;
    bp=buffer;
    while (p) {
      if (p->steptype==KeywordStep &&
	  p->keyword) {
	if (bp!=buffer) { *bp++=','; *bp++=' '; }
	Ustrcpy(bp, p->keyword->str);
	bp=bp+p->keyword->len;
      }
      p=p->next;
    }
    insert_string_selection(prf->selection, buffer);
  }
  next_node_selection(prf->selection);
  if (!forward) {
    commute_selection(prf->selection);
  }
  /* insert_string_selection(prf->selection, translate("NoWay")); */
  insert_parse_result(prf->selection);
  redraw_selection(prf->selection);
}
Ejemplo n.º 3
0
Archivo: tree.c Proyecto: fanf2/exim
void
tree_add_duplicate(uschar *s, address_item *addr)
{
tree_node *node = store_get(sizeof(tree_node) + Ustrlen(s));
Ustrcpy(node->name, s);
node->data.ptr = addr;
if (!tree_insertnode(&tree_duplicates, node)) store_reset(node);
}
Ejemplo n.º 4
0
Archivo: tree.c Proyecto: fanf2/exim
void
tree_add_nonrecipient(uschar *s)
{
tree_node *node = store_get(sizeof(tree_node) + Ustrlen(s));
Ustrcpy(node->name, s);
node->data.ptr = NULL;
if (!tree_insertnode(&tree_nonrecipients, node)) store_reset(node);
}
Ejemplo n.º 5
0
Archivo: tree.c Proyecto: fanf2/exim
void
tree_add_unusable(host_item *h)
{
tree_node *node;
uschar s[256];
sprintf(CS s, "T:%.200s:%s", h->name, h->address);
node = store_get(sizeof(tree_node) + Ustrlen(s));
Ustrcpy(node->name, s);
node->data.val = h->why;
if (h->status == hstatus_unusable_expired) node->data.val += 256;
if (!tree_insertnode(&tree_unusable, node)) store_reset(node);
}
Ejemplo n.º 6
0
Archivo: string.c Proyecto: Exim/exim
uschar *
string_format_size(int size, uschar *buffer)
{
    if (size == 0) Ustrcpy(buffer, "     ");
    else if (size < 1024) sprintf(CS buffer, "%5d", size);
    else if (size < 10*1024)
        sprintf(CS buffer, "%4.1fK", (double)size / 1024.0);
    else if (size < 1024*1024)
        sprintf(CS buffer, "%4dK", (size + 512)/1024);
    else if (size < 10*1024*1024)
        sprintf(CS buffer, "%4.1fM", (double)size / (1024.0 * 1024.0));
    else
        sprintf(CS buffer, "%4dM", (size + 512 * 1024)/(1024*1024));
    return buffer;
}
Ejemplo n.º 7
0
uschar *bmi_get_alt_location(uschar *base64_verdict) {
  BmiError err;
  BmiErrorLocation err_loc;
  BmiErrorType err_type;
  BmiVerdict *verdict = NULL;
  uschar *rc = NULL;

  /* always deliver when there is no verdict */
  if (base64_verdict == NULL)
    return NULL;

  /* create verdict from base64 string */
  err = bmiCreateVerdictFromStr(CS base64_verdict, &verdict);
  if (bmiErrorIsFatal(err) == BMI_TRUE) {
    err_loc = bmiErrorGetLocation(err);
    err_type = bmiErrorGetType(err);
    log_write(0, LOG_PANIC,
               "bmi error [loc %d type %d]: bmiCreateVerdictFromStr() failed. [%s]", (int)err_loc, (int)err_type, base64_verdict);
    return NULL;
  };

  err = bmiVerdictError(verdict);
  if (bmiErrorIsFatal(err) == BMI_TRUE) {
    /* deliver normally due to error */
    rc = NULL;
  }
  else if (bmiVerdictDestinationIsDefault(verdict) == BMI_TRUE) {
    /* deliver normally */
    rc = NULL;
  }
  else if (bmiVerdictAccessDestination(verdict) == NULL) {
    /* do not deliver */
    rc = NULL;
  }
  else {
    /* deliver to alternate location */
    rc = store_get(strlen(bmiVerdictAccessDestination(verdict))+1);
    Ustrcpy(rc, bmiVerdictAccessDestination(verdict));
    rc[strlen(bmiVerdictAccessDestination(verdict))] = '\0';
  };

  bmiFreeVerdict(verdict);
  return rc;
}
Ejemplo n.º 8
0
Archivo: rda.c Proyecto: 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;
}
Ejemplo n.º 9
0
void
version_init(void)
{
int i = 0;
uschar today[20];

version_string = US"2.06";

Ustrcpy(today, __DATE__);
if (today[4] == ' ') i = 1;
today[3] = today[6] = '-';

version_date = (uschar *)malloc(32);
version_date[0] = 0;
Ustrncat(version_date, today+4+i, 3-i);
Ustrncat(version_date, today, 4);
Ustrncat(version_date, today+7, 4);
Ustrcat(version_date, " ");
Ustrcat(version_date, __TIME__);
}
Ejemplo n.º 10
0
Archivo: edit.c Proyecto: jff/mathspad
static void set_output_name(EDITINFO *einf)
{
    int i=0,l;
    if (einf->outputname) free(einf->outputname);
    i= Ustrlen(latexdir);
    l = i+Ustrlen(einf->filename)+15;
    einf->outputname = (Char*) malloc(sizeof(Char)*l);
    if (latexdir[i-1]=='/')
      concat_in(einf->outputname,latexdir,einf->filename);
    else {
      Ustrcpy(einf->outputname,latexdir);
      einf->outputname[i]='/';
      einf->outputname[i+1]=0;
      Ustrcat(einf->outputname, einf->filename);
    }
    if (output_mode==ASCII)
      Ustrncat(einf->outputname, translate(".asc"),l);
    else
      Ustrncat(einf->outputname, translate(".tex"),l);
}
Ejemplo n.º 11
0
open_db *
dbfn_open(uschar *name, int flags, open_db *dbblock, BOOL lof)
{
int rc, save_errno;
BOOL read_only = flags == O_RDONLY;
BOOL created = FALSE;
flock_t lock_data;
uschar buffer[256];

/* The first thing to do is to open a separate file on which to lock. This
ensures that Exim has exclusive use of the database before it even tries to
open it. Early versions tried to lock on the open database itself, but that
gave rise to mysterious problems from time to time - it was suspected that some
DB libraries "do things" on their open() calls which break the interlocking.
The lock file is never written to, but we open it for writing so we can get a
write lock if required. If it does not exist, we create it. This is done
separately so we know when we have done it, because when running as root we
need to change the ownership - see the bottom of this function. We also try to
make the directory as well, just in case. We won't be doing this many times
unnecessarily, because usually the lock file will be there. If the directory
exists, there is no error. */

sprintf(CS buffer, "%s/db/%s.lockfile", spool_directory, name);

if ((dbblock->lockfd = Uopen(buffer, O_RDWR, EXIMDB_LOCKFILE_MODE)) < 0)
  {
  created = TRUE;
  (void)directory_make(spool_directory, US"db", EXIMDB_DIRECTORY_MODE, TRUE);
  dbblock->lockfd = Uopen(buffer, O_RDWR|O_CREAT, EXIMDB_LOCKFILE_MODE);
  }

if (dbblock->lockfd < 0)
  {
  log_write(0, LOG_MAIN, "%s",
    string_open_failed(errno, "database lock file %s", buffer));
  errno = 0;      /* Indicates locking failure */
  return NULL;
  }

/* Now we must get a lock on the opened lock file; do this with a blocking
lock that times out. */

lock_data.l_type = read_only? F_RDLCK : F_WRLCK;
lock_data.l_whence = lock_data.l_start = lock_data.l_len = 0;

DEBUG(D_hints_lookup|D_retry|D_route|D_deliver)
  debug_printf("locking %s\n", buffer);

sigalrm_seen = FALSE;
alarm(EXIMDB_LOCK_TIMEOUT);
rc = fcntl(dbblock->lockfd, F_SETLKW, &lock_data);
alarm(0);

if (sigalrm_seen) errno = ETIMEDOUT;
if (rc < 0)
  {
  log_write(0, LOG_MAIN|LOG_PANIC, "Failed to get %s lock for %s: %s",
    read_only? "read" : "write", buffer,
    (errno == ETIMEDOUT)? "timed out" : strerror(errno));
  (void)close(dbblock->lockfd);
  errno = 0;       /* Indicates locking failure */
  return NULL;
  }

DEBUG(D_hints_lookup) debug_printf("locked %s\n", buffer);

/* At this point we have an opened and locked separate lock file, that is,
exclusive access to the database, so we can go ahead and open it. If we are
expected to create it, don't do so at first, again so that we can detect
whether we need to change its ownership (see comments about the lock file
above.) There have been regular reports of crashes while opening hints
databases - often this is caused by non-matching db.h and the library. To make
it easy to pin this down, there are now debug statements on either side of the
open call. */

sprintf(CS buffer, "%s/db/%s", spool_directory, name);
DEBUG(D_hints_lookup) debug_printf("EXIM_DBOPEN(%s)\n", buffer);
EXIM_DBOPEN(buffer, flags, EXIMDB_MODE, &(dbblock->dbptr));
DEBUG(D_hints_lookup) debug_printf("returned from EXIM_DBOPEN\n");

if (dbblock->dbptr == NULL && errno == ENOENT && flags == O_RDWR)
  {
  DEBUG(D_hints_lookup)
    debug_printf("%s appears not to exist: trying to create\n", buffer);
  created = TRUE;
  EXIM_DBOPEN(buffer, flags|O_CREAT, EXIMDB_MODE, &(dbblock->dbptr));
  DEBUG(D_hints_lookup) debug_printf("returned from EXIM_DBOPEN\n");
  }

save_errno = errno;

/* If we are running as root and this is the first access to the database, its
files will be owned by root. We want them to be owned by exim. We detect this
situation by noting above when we had to create the lock file or the database
itself. Because the different dbm libraries use different extensions for their
files, I don't know of any easier way of arranging this than scanning the
directory for files with the appropriate base name. At least this deals with
the lock file at the same time. Also, the directory will typically have only
half a dozen files, so the scan will be quick.

This code is placed here, before the test for successful opening, because there
was a case when a file was created, but the DBM library still returned NULL
because of some problem. It also sorts out the lock file if that was created
but creation of the database file failed. */

if (created && geteuid() == root_uid)
  {
  DIR *dd;
  struct dirent *ent;
  uschar *lastname = Ustrrchr(buffer, '/') + 1;
  int namelen = Ustrlen(name);

  *lastname = 0;
  dd = opendir(CS buffer);

  while ((ent = readdir(dd)) != NULL)
    {
    if (Ustrncmp(ent->d_name, name, namelen) == 0)
      {
      struct stat statbuf;
      Ustrcpy(lastname, ent->d_name);
      if (Ustat(buffer, &statbuf) >= 0 && statbuf.st_uid != exim_uid)
        {
        DEBUG(D_hints_lookup) debug_printf("ensuring %s is owned by exim\n", buffer);
        (void)Uchown(buffer, exim_uid, exim_gid);
        }
      }
    }

  closedir(dd);
  }

/* If the open has failed, return NULL, leaving errno set. If lof is TRUE,
log the event - also for debugging - but not if the file just doesn't exist. */

if (dbblock->dbptr == NULL)
  {
  if (save_errno != ENOENT)
    {
    if (lof)
      log_write(0, LOG_MAIN, "%s", string_open_failed(save_errno, "DB file %s",
        buffer));
    else
      DEBUG(D_hints_lookup)
        debug_printf("%s", CS string_open_failed(save_errno, "DB file %s\n",
          buffer));
    }
  (void)close(dbblock->lockfd);
  errno = save_errno;
  return NULL;
  }

DEBUG(D_hints_lookup)
  debug_printf("opened hints database %s: flags=%s\n", buffer,
    (flags == O_RDONLY)? "O_RDONLY" : (flags == O_RDWR)? "O_RDWR" :
    (flags == (O_RDWR|O_CREAT))? "O_RDWR|O_CREAT" : "??");

/* Pass back the block containing the opened database handle and the open fd
for the lock. */

return dbblock;
}
Ejemplo n.º 12
0
int
auth_cram_md5_server(auth_instance *ablock, uschar *data)
{
auth_cram_md5_options_block *ob =
  (auth_cram_md5_options_block *)(ablock->options_block);
uschar *challenge = string_sprintf("<%d.%ld@%s>", getpid(),
    (long int) time(NULL), primary_hostname);
uschar *clear, *secret;
uschar digest[16];
int i, rc, len;

/* If we are running in the test harness, always send the same challenge,
an example string taken from the RFC. */

if (running_in_test_harness)
  challenge = US"<*****@*****.**>";

/* No data should have been sent with the AUTH command */

if (*data != 0) return UNEXPECTED;

/* Send the challenge, read the return */

if ((rc = auth_get_data(&data, challenge, Ustrlen(challenge))) != OK) return rc;
if ((len = b64decode(data, &clear)) < 0) return BAD64;

/* The return consists of a user name, space-separated from the CRAM-MD5
digest, expressed in hex. Extract the user name and put it in $auth1 and $1.
The former is now the preferred variable; the latter is the original one. Then
check that the remaining length is 32. */

auth_vars[0] = expand_nstring[1] = clear;
while (*clear != 0 && !isspace(*clear)) clear++;
if (!isspace(*clear)) return FAIL;
*clear++ = 0;

expand_nlength[1] = clear - expand_nstring[1] - 1;
if (len - expand_nlength[1] - 1 != 32) return FAIL;
expand_nmax = 1;

/* Expand the server_secret string so that it can compute a value dependent on
the user name if necessary. */

debug_print_string(ablock->server_debug_string);    /* customized debugging */
secret = expand_string(ob->server_secret);

/* A forced fail implies failure of authentication - i.e. we have no secret for
the given name. */

if (secret == NULL)
  {
  if (expand_string_forcedfail) return FAIL;
  auth_defer_msg = expand_string_message;
  return DEFER;
  }

/* Compute the CRAM-MD5 digest that we should have received from the client. */

compute_cram_md5(secret, challenge, digest);

HDEBUG(D_auth)
  {
  uschar buff[64];
  debug_printf("CRAM-MD5: user name = %s\n", auth_vars[0]);
  debug_printf("          challenge = %s\n", challenge);
  debug_printf("          received  = %s\n", clear);
  Ustrcpy(buff,"          digest    = ");
  for (i = 0; i < 16; i++) sprintf(CS buff+22+2*i, "%02x", digest[i]);
  debug_printf("%.54s\n", buff);
  }

/* We now have to compare the digest, which is 16 bytes in binary, with the
data received, which is expressed in lower case hex. We checked above that
there were 32 characters of data left. */

for (i = 0; i < 16; i++)
  {
  int a = *clear++;
  int b = *clear++;
  if (((((a >= 'a')? a - 'a' + 10 : a - '0') << 4) +
        ((b >= 'a')? b - 'a' + 10 : b - '0')) != digest[i]) return FAIL;
  }

/* Expand server_condition as an authorization check */
return auth_check_serv_cond(ablock);
}
Ejemplo n.º 13
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;
}
Ejemplo n.º 14
0
Archivo: queue.c Proyecto: Exim/exim
static queue_filename *
queue_get_spool_list(int subdiroffset, uschar *subdirs, int *subcount,
                     BOOL randomize)
{
    int i;
    int flags = 0;
    int resetflags = -1;
    int subptr;
    queue_filename *yield = NULL;
    queue_filename *last = NULL;
    struct dirent *ent;
    DIR *dd;
    uschar buffer[256];
    queue_filename *root[LOG2_MAXNODES];

    /* When randomizing, the file names are added to the start or end of the list
    according to the bits of the flags variable. Get a collection of bits from the
    current time. Use the bottom 16 and just keep re-using them if necessary. When
    not randomizing, initialize the sublists for the bottom-up merge sort. */

    if (randomize)
        resetflags = time(NULL) & 0xFFFF;
    else
        for (i = 0; i < LOG2_MAXNODES; i++)
            root[i] = NULL;

    /* If processing the full queue, or just the top-level, start at the base
    directory, and initialize the first subdirectory name (as none). Otherwise,
    start at the sub-directory offset. */

    if (subdiroffset <= 0)
    {
        i = 0;
        subdirs[0] = 0;
        *subcount = 0;
    }
    else
        i = subdiroffset;

    /* Set up prototype for the directory name. */

    spool_pname_buf(buffer, sizeof(buffer));
    buffer[sizeof(buffer) - 3] = 0;
    subptr = Ustrlen(buffer);
    buffer[subptr+2] = 0;               /* terminator for lengthened name */

    /* This loop runs at least once, for the main or given directory, and then as
    many times as necessary to scan any subdirectories encountered in the main
    directory, if they are to be scanned at this time. */

    for (; i <= *subcount; i++)
    {
        int count = 0;
        int subdirchar = subdirs[i];      /* 0 for main directory */

        if (subdirchar != 0)
        {
            buffer[subptr] = '/';
            buffer[subptr+1] = subdirchar;
        }

        DEBUG(D_queue_run) debug_printf("looking in %s\n", buffer);
        if (!(dd = opendir(CS buffer)))
            continue;

        /* Now scan the directory. */

        while ((ent = readdir(dd)))
        {
            uschar *name = US ent->d_name;
            int len = Ustrlen(name);

            /* Count entries */

            count++;

            /* If we find a single alphameric sub-directory in the base directory,
            add it to the list for subsequent scans. */

            if (i == 0 && len == 1 && isalnum(*name))
            {
                *subcount = *subcount + 1;
                subdirs[*subcount] = *name;
                continue;
            }

            /* Otherwise, if it is a header spool file, add it to the list */

            if (len == SPOOL_NAME_LENGTH &&
                    Ustrcmp(name + SPOOL_NAME_LENGTH - 2, "-H") == 0)
            {
                queue_filename *next =
                    store_get(sizeof(queue_filename) + Ustrlen(name));
                Ustrcpy(next->text, name);
                next->dir_uschar = subdirchar;

                /* Handle the creation of a randomized list. The first item becomes both
                the top and bottom of the list. Subsequent items are inserted either at
                the top or the bottom, randomly. This is, I argue, faster than doing a
                sort by allocating a random number to each item, and it also saves having
                to store the number with each item. */

                if (randomize)
                    if (!yield)
                    {
                        next->next = NULL;
                        yield = last = next;
                    }
                    else
                    {
                        if (flags == 0)
                            flags = resetflags;
                        if ((flags & 1) == 0)
                        {
                            next->next = yield;
                            yield = next;
                        }
                        else
                        {
                            next->next = NULL;
                            last->next = next;
                            last = next;
                        }
                        flags = flags >> 1;
                    }

                /* Otherwise do a bottom-up merge sort based on the name. */

                else
                {
                    int j;
                    next->next = NULL;
                    for (j = 0; j < LOG2_MAXNODES; j++)
                        if (root[j])
                        {
                            next = merge_queue_lists(next, root[j]);
                            root[j] = (j == LOG2_MAXNODES - 1)? next : NULL;
                        }
                        else
                        {
                            root[j] = next;
                            break;
                        }
                }
            }
        }
Ejemplo n.º 15
0
Archivo: ldap.c Proyecto: 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;
}
Ejemplo n.º 16
0
Archivo: dns.c Proyecto: 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
}
Ejemplo n.º 17
0
void *
search_open(uschar *filename, int search_type, int modemask, uid_t *owners,
  gid_t *owngroups)
{
void *handle;
tree_node *t;
search_cache *c;
lookup_info *lk = lookup_list[search_type];
uschar keybuffer[256];
int old_pool = store_pool;

/* Change to the search store pool and remember our reset point */

store_pool = POOL_SEARCH;
if (search_reset_point == NULL) search_reset_point = store_get(0);

DEBUG(D_lookup) debug_printf("search_open: %s \"%s\"\n", lk->name,
  (filename == NULL)? US"NULL" : filename);

/* See if we already have this open for this type of search, and if so,
pass back the tree block as the handle. The key for the tree node is the search
type plus '0' concatenated with the file name. There may be entries in the tree
with closed files if a lot of files have been opened. */

sprintf(CS keybuffer, "%c%.254s", search_type + '0',
  (filename == NULL)? US"" : filename);

if ((t = tree_search(search_tree, keybuffer)) != NULL)
  {
  c = (search_cache *)(t->data.ptr);
  if (c->handle != NULL)
    {
    DEBUG(D_lookup) debug_printf("  cached open\n");
    store_pool = old_pool;
    return t;
    }
  DEBUG(D_lookup) debug_printf("  cached closed\n");
  }

/* Otherwise, we need to open the file or database - each search type has its
own code, which is now split off into separately compiled modules. Before doing
this, if the search type is one that uses real files, check on the number that
we are holding open in the cache. If the limit is reached, close the least
recently used one. */

if (lk->type == lookup_absfile && open_filecount >= lookup_open_max)
  {
  if (open_bot == NULL)
    log_write(0, LOG_MAIN|LOG_PANIC, "too many lookups open, but can't find "
      "one to close");
  else
    {
    search_cache *c = (search_cache *)(open_bot->data.ptr);
    DEBUG(D_lookup) debug_printf("Too many lookup files open\n  closing %s\n",
      open_bot->name);
    open_bot = c->up;
    if (open_bot != NULL)
      ((search_cache *)(open_bot->data.ptr))->down = NULL;
    else
      open_top = NULL;
    ((lookup_list[c->search_type])->close)(c->handle);
    c->handle = NULL;
    open_filecount--;
    }
  }

/* If opening is successful, call the file-checking function if there is one,
and if all is still well, enter the open database into the tree. */

handle = (lk->open)(filename, &search_error_message);
if (handle == NULL)
  {
  store_pool = old_pool;
  return NULL;
  }

if (lk->check != NULL &&
   !lk->check(handle, filename, modemask, owners, owngroups,
     &search_error_message))
  {
  lk->close(handle);
  store_pool = old_pool;
  return NULL;
  }

/* If this is a search type that uses real files, keep count. */

if (lk->type == lookup_absfile) open_filecount++;

/* If we found a previously opened entry in the tree, re-use it; otherwise
insert a new entry. On re-use, leave any cached lookup data and the lookup
count alone. */

if (t == NULL)
  {
  t = store_get(sizeof(tree_node) + Ustrlen(keybuffer));
  t->data.ptr = c = store_get(sizeof(search_cache));
  c->item_cache = NULL;
  Ustrcpy(t->name, keybuffer);
  tree_insertnode(&search_tree, t);
  }
else c = t->data.ptr;

c->handle = handle;
c->search_type = search_type;
c->up = c->down = NULL;

store_pool = old_pool;
return t;
}
Ejemplo n.º 18
0
int
spam(const uschar **listptr)
{
int sep = 0;
const uschar *list = *listptr;
uschar *user_name;
uschar user_name_buffer[128];
unsigned long mbox_size;
FILE *mbox_file;
int spamd_sock = -1;
uschar spamd_buffer[32600];
int i, j, offset, result;
uschar spamd_version[8];
uschar spamd_short_result[8];
uschar spamd_score_char;
double spamd_threshold, spamd_score, spamd_reject_score;
int spamd_report_offset;
uschar *p,*q;
int override = 0;
time_t start;
size_t read, wrote;
#ifndef NO_POLL_H
struct pollfd pollfd;
#else                               /* Patch posted by Erik ? for OS X */
struct timeval select_tv;         /* and applied by PH */
fd_set select_fd;
#endif
uschar *spamd_address_work;
spamd_address_container * sd;

/* stop compiler warning */
result = 0;

/* find the username from the option list */
if ((user_name = string_nextinlist(&list, &sep,
				   user_name_buffer,
				   sizeof(user_name_buffer))) == NULL)
  {
  /* no username given, this means no scanning should be done */
  return FAIL;
  }

/* if username is "0" or "false", do not scan */
if ( (Ustrcmp(user_name,"0") == 0) ||
     (strcmpic(user_name,US"false") == 0) )
  return FAIL;

/* if there is an additional option, check if it is "true" */
if (strcmpic(list,US"true") == 0)
  /* in that case, always return true later */
  override = 1;

/* expand spamd_address if needed */
if (*spamd_address == '$')
  {
  spamd_address_work = expand_string(spamd_address);
  if (spamd_address_work == NULL)
    {
    log_write(0, LOG_MAIN|LOG_PANIC,
      "%s spamd_address starts with $, but expansion failed: %s",
      loglabel, expand_string_message);
    return DEFER;
    }
  }
else
  spamd_address_work = spamd_address;

DEBUG(D_acl) debug_printf("spamd: addrlist '%s'\n", spamd_address_work);

/* check if previous spamd_address was expanded and has changed. dump cached results if so */
if (  spam_ok
   && prev_spamd_address_work != NULL
   && Ustrcmp(prev_spamd_address_work, spamd_address_work) != 0
   )
  spam_ok = 0;

/* if we scanned for this username last time, just return */
if (spam_ok && Ustrcmp(prev_user_name, user_name) == 0)
  return override ? OK : spam_rc;

/* make sure the eml mbox file is spooled up */
mbox_file = spool_mbox(&mbox_size, NULL);

if (mbox_file == NULL)
  {
  /* error while spooling */
  log_write(0, LOG_MAIN|LOG_PANIC,
	 "%s error while creating mbox spool file", loglabel);
  return DEFER;
  }

start = time(NULL);

  {
  int num_servers = 0;
  int current_server;
  uschar * address;
  const uschar * spamd_address_list_ptr = spamd_address_work;
  spamd_address_container * spamd_address_vector[32];

  /* Check how many spamd servers we have
     and register their addresses */
  sep = 0;				/* default colon-sep */
  while ((address = string_nextinlist(&spamd_address_list_ptr, &sep,
				      NULL, 0)) != NULL)
    {
    const uschar * sublist;
    int sublist_sep = -(int)' ';	/* default space-sep */
    unsigned args;
    uschar * s;

    DEBUG(D_acl) debug_printf("spamd: addr entry '%s'\n", address);
    sd = (spamd_address_container *)store_get(sizeof(spamd_address_container));

    for (sublist = address, args = 0, spamd_param_init(sd);
	 (s = string_nextinlist(&sublist, &sublist_sep, NULL, 0));
	 args++
	 )
      {
	DEBUG(D_acl) debug_printf("spamd:  addr parm '%s'\n", s);
	switch (args)
	{
	case 0:   sd->hostspec = s;
		  if (*s == '/') args++;	/* local; no port */
		  break;
	case 1:   sd->hostspec = string_sprintf("%s %s", sd->hostspec, s);
		  break;
	default:  spamd_param(s, sd);
		  break;
	}
      }
    if (args < 2)
      {
      log_write(0, LOG_MAIN,
	"%s warning - invalid spamd address: '%s'", loglabel, address);
      continue;
      }

    spamd_address_vector[num_servers] = sd;
    if (++num_servers > 31)
      break;
    }

  /* check if we have at least one server */
  if (!num_servers)
    {
    log_write(0, LOG_MAIN|LOG_PANIC,
       "%s no useable spamd server addresses in spamd_address configuration option.",
       loglabel);
    goto defer;
    }

  current_server = spamd_get_server(spamd_address_vector, num_servers);
  sd = spamd_address_vector[current_server];
  for(;;)
    {
    uschar * errstr;

    DEBUG(D_acl) debug_printf("spamd: trying server %s\n", sd->hostspec);

    for (;;)
      {
      if (  (spamd_sock = ip_streamsocket(sd->hostspec, &errstr, 5)) >= 0
         || sd->retry <= 0
	 )
	break;
      DEBUG(D_acl) debug_printf("spamd: server %s: retry conn\n", sd->hostspec);
      while (sd->retry > 0) sd->retry = sleep(sd->retry);
      }
    if (spamd_sock >= 0)
      break;

    log_write(0, LOG_MAIN, "%s spamd: %s", loglabel, errstr);
    sd->is_failed = TRUE;

    current_server = spamd_get_server(spamd_address_vector, num_servers);
    if (current_server < 0)
      {
      log_write(0, LOG_MAIN|LOG_PANIC, "%s all spamd servers failed", loglabel);
      goto defer;
      }
    sd = spamd_address_vector[current_server];
    }
  }

(void)fcntl(spamd_sock, F_SETFL, O_NONBLOCK);
/* now we are connected to spamd on spamd_sock */
if (sd->is_rspamd)
  {				/* rspamd variant */
  uschar *req_str;
  const uschar * helo;
  const uschar * fcrdns;
  const uschar * authid;

  req_str = string_sprintf("CHECK RSPAMC/1.3\r\nContent-length: %lu\r\n"
    "Queue-Id: %s\r\nFrom: <%s>\r\nRecipient-Number: %d\r\n",
    mbox_size, message_id, sender_address, recipients_count);
  for (i = 0; i < recipients_count; i ++)
    req_str = string_sprintf("%sRcpt: <%s>\r\n", req_str, recipients_list[i].address);
  if ((helo = expand_string(US"$sender_helo_name")) != NULL && *helo != '\0')
    req_str = string_sprintf("%sHelo: %s\r\n", req_str, helo);
  if ((fcrdns = expand_string(US"$sender_host_name")) != NULL && *fcrdns != '\0')
    req_str = string_sprintf("%sHostname: %s\r\n", req_str, fcrdns);
  if (sender_host_address != NULL)
    req_str = string_sprintf("%sIP: %s\r\n", req_str, sender_host_address);
  if ((authid = expand_string(US"$authenticated_id")) != NULL && *authid != '\0')
    req_str = string_sprintf("%sUser: %s\r\n", req_str, authid);
  req_str = string_sprintf("%s\r\n", req_str);
  wrote = send(spamd_sock, req_str, Ustrlen(req_str), 0);
  }
else
  {				/* spamassassin variant */
  (void)string_format(spamd_buffer,
	  sizeof(spamd_buffer),
	  "REPORT SPAMC/1.2\r\nUser: %s\r\nContent-length: %ld\r\n\r\n",
	  user_name,
	  mbox_size);
  /* send our request */
  wrote = send(spamd_sock, spamd_buffer, Ustrlen(spamd_buffer), 0);
  }

if (wrote == -1)
  {
  (void)close(spamd_sock);
  log_write(0, LOG_MAIN|LOG_PANIC,
       "%s spamd %s send failed: %s", loglabel, callout_address, strerror(errno));
  goto defer;
  }

/* now send the file */
/* spamd sometimes accepts conections but doesn't read data off
 * the connection.  We make the file descriptor non-blocking so
 * that the write will only write sufficient data without blocking
 * and we poll the desciptor to make sure that we can write without
 * blocking.  Short writes are gracefully handled and if the whole
 * trasaction takes too long it is aborted.
 * Note: poll() is not supported in OSX 10.2 and is reported to be
 *       broken in more recent versions (up to 10.4).
 */
#ifndef NO_POLL_H
pollfd.fd = spamd_sock;
pollfd.events = POLLOUT;
#endif
(void)fcntl(spamd_sock, F_SETFL, O_NONBLOCK);
do
  {
  read = fread(spamd_buffer,1,sizeof(spamd_buffer),mbox_file);
  if (read > 0)
    {
    offset = 0;
again:
#ifndef NO_POLL_H
    result = poll(&pollfd, 1, 1000);

/* Patch posted by Erik ? for OS X and applied by PH */
#else
    select_tv.tv_sec = 1;
    select_tv.tv_usec = 0;
    FD_ZERO(&select_fd);
    FD_SET(spamd_sock, &select_fd);
    result = select(spamd_sock+1, NULL, &select_fd, NULL, &select_tv);
#endif
/* End Erik's patch */

    if (result == -1 && errno == EINTR)
      goto again;
    else if (result < 1)
      {
      if (result == -1)
	log_write(0, LOG_MAIN|LOG_PANIC,
	  "%s %s on spamd %s socket", loglabel, callout_address, strerror(errno));
      else
	{
	if (time(NULL) - start < sd->timeout)
	  goto again;
	log_write(0, LOG_MAIN|LOG_PANIC,
	  "%s timed out writing spamd %s, socket", loglabel, callout_address);
	}
      (void)close(spamd_sock);
      goto defer;
      }

    wrote = send(spamd_sock,spamd_buffer + offset,read - offset,0);
    if (wrote == -1)
      {
      log_write(0, LOG_MAIN|LOG_PANIC,
	  "%s %s on spamd %s socket", loglabel, callout_address, strerror(errno));
      (void)close(spamd_sock);
      goto defer;
      }
    if (offset + wrote != read)
      {
      offset += wrote;
      goto again;
      }
    }
  }
while (!feof(mbox_file) && !ferror(mbox_file));

if (ferror(mbox_file))
  {
  log_write(0, LOG_MAIN|LOG_PANIC,
    "%s error reading spool file: %s", loglabel, strerror(errno));
  (void)close(spamd_sock);
  goto defer;
  }

(void)fclose(mbox_file);

/* we're done sending, close socket for writing */
shutdown(spamd_sock,SHUT_WR);

/* read spamd response using what's left of the timeout.  */
memset(spamd_buffer, 0, sizeof(spamd_buffer));
offset = 0;
while ((i = ip_recv(spamd_sock,
		   spamd_buffer + offset,
		   sizeof(spamd_buffer) - offset - 1,
		   sd->timeout - time(NULL) + start)) > 0)
  offset += i;
spamd_buffer[offset] = '\0';	/* guard byte */

/* error handling */
if (i <= 0 && errno != 0)
  {
  log_write(0, LOG_MAIN|LOG_PANIC,
       "%s error reading from spamd %s, socket: %s", loglabel, callout_address, strerror(errno));
  (void)close(spamd_sock);
  return DEFER;
  }

/* reading done */
(void)close(spamd_sock);

if (sd->is_rspamd)
  {				/* rspamd variant of reply */
  int r;
  if (  (r = sscanf(CS spamd_buffer,
	  "RSPAMD/%7s 0 EX_OK\r\nMetric: default; %7s %lf / %lf / %lf\r\n%n",
	  spamd_version, spamd_short_result, &spamd_score, &spamd_threshold,
	  &spamd_reject_score, &spamd_report_offset)) != 5
     || spamd_report_offset >= offset		/* verify within buffer */
     )
    {
    log_write(0, LOG_MAIN|LOG_PANIC,
	      "%s cannot parse spamd %s, output: %d", loglabel, callout_address, r);
    return DEFER;
    }
  /* now parse action */
  p = &spamd_buffer[spamd_report_offset];

  if (Ustrncmp(p, "Action: ", sizeof("Action: ") - 1) == 0)
    {
    p += sizeof("Action: ") - 1;
    q = &spam_action_buffer[0];
    while (*p && *p != '\r' && (q - spam_action_buffer) < sizeof(spam_action_buffer) - 1)
      *q++ = *p++;
    *q = '\0';
    }
  }
else
  {				/* spamassassin */
  /* dig in the spamd output and put the report in a multiline header,
  if requested */
  if (sscanf(CS spamd_buffer,
       "SPAMD/%7s 0 EX_OK\r\nContent-length: %*u\r\n\r\n%lf/%lf\r\n%n",
       spamd_version,&spamd_score,&spamd_threshold,&spamd_report_offset) != 3)
    {
      /* try to fall back to pre-2.50 spamd output */
      if (sscanf(CS spamd_buffer,
	   "SPAMD/%7s 0 EX_OK\r\nSpam: %*s ; %lf / %lf\r\n\r\n%n",
	   spamd_version,&spamd_score,&spamd_threshold,&spamd_report_offset) != 3)
	{
	log_write(0, LOG_MAIN|LOG_PANIC,
		  "%s cannot parse spamd %s output", loglabel, callout_address);
	return DEFER;
	}
    }

  Ustrcpy(spam_action_buffer,
    spamd_score >= spamd_threshold ? "reject" : "no action");
  }

/* Create report. Since this is a multiline string,
we must hack it into shape first */
p = &spamd_buffer[spamd_report_offset];
q = spam_report_buffer;
while (*p != '\0')
  {
  /* skip \r */
  if (*p == '\r')
    {
    p++;
    continue;
    }
  *q++ = *p;
  if (*p++ == '\n')
    {
    /* add an extra space after the newline to ensure
    that it is treated as a header continuation line */
    *q++ = ' ';
    }
  }
/* NULL-terminate */
*q-- = '\0';
/* cut off trailing leftovers */
while (*q <= ' ')
  *q-- = '\0';

spam_report = spam_report_buffer;
spam_action = spam_action_buffer;

/* create spam bar */
spamd_score_char = spamd_score > 0 ? '+' : '-';
j = abs((int)(spamd_score));
i = 0;
if (j != 0)
  while ((i < j) && (i <= MAX_SPAM_BAR_CHARS))
     spam_bar_buffer[i++] = spamd_score_char;
else
  {
  spam_bar_buffer[0] = '/';
  i = 1;
  }
spam_bar_buffer[i] = '\0';
spam_bar = spam_bar_buffer;

/* create "float" spam score */
(void)string_format(spam_score_buffer, sizeof(spam_score_buffer),
	"%.1f", spamd_score);
spam_score = spam_score_buffer;

/* create "int" spam score */
j = (int)((spamd_score + 0.001)*10);
(void)string_format(spam_score_int_buffer, sizeof(spam_score_int_buffer),
	"%d", j);
spam_score_int = spam_score_int_buffer;

/* compare threshold against score */
spam_rc = spamd_score >= spamd_threshold
  ? OK	/* spam as determined by user's threshold */
  : FAIL;	/* not spam */

/* remember expanded spamd_address if needed */
if (spamd_address_work != spamd_address)
  prev_spamd_address_work = string_copy(spamd_address_work);

/* remember user name and "been here" for it */
Ustrcpy(prev_user_name, user_name);
spam_ok = 1;

return override
  ? OK		/* always return OK, no matter what the score */
  : spam_rc;

defer:
  (void)fclose(mbox_file);
  return DEFER;
}
Ejemplo n.º 19
0
Archivo: em_log.c Proyecto: fanf2/exim
void read_log(void)
{
struct stat statdata;
uschar buffer[log_buffer_len];

/* If log is not yet open, skip all of this. */

if (LOG != NULL)
  {
  fseek(LOG, log_position, SEEK_SET);

  while (Ufgets(buffer, log_buffer_len, LOG) != NULL)
    {
    uschar *id;
    uschar *p = buffer;
    void *reset_point;
    int length = Ustrlen(buffer);
    int i;

    /* Skip totally blank lines (paranoia: there shouldn't be any) */

    while (*p == ' ' || *p == '\t') p++;
    if (*p == '\n') continue;

    /* We should now have a complete log entry in the buffer; check
    it for various regular expression matches and take appropriate
    action. Get the current store point so we can reset to it. */

    reset_point = store_get(0);

    /* First, update any stripchart data values, noting that the zeroth
    stripchart is the queue length, which is handled elsewhere, and the
    1st may the a size monitor. */

    for (i = stripchart_varstart; i < stripchart_number; i++)
      {
      if (pcre_exec(stripchart_regex[i], NULL, CS buffer, length, 0, PCRE_EOPT,
            NULL, 0) >= 0)
        stripchart_total[i]++;
      }

    /* Munge the log entry and display shortened form on one line.
    We omit the date and show only the time. Remove any time zone offset.
    Take note of the presence of [pid]. */

    if (pcre_exec(yyyymmdd_regex,NULL,CS buffer,length,0,PCRE_EOPT,NULL,0) >= 0)
      {
      int pidlength = 0;
      if ((buffer[20] == '+' || buffer[20] == '-') &&
          isdigit(buffer[21]) && buffer[25] == ' ')
        memmove(buffer + 20, buffer + 26, Ustrlen(buffer + 26) + 1);
      if (buffer[20] == '[')
        {
        while (Ustrchr("[]0123456789", buffer[20+pidlength++]) != NULL);
        }
      id = string_copyn(buffer + 20 + pidlength, MESSAGE_ID_LENGTH);
      show_log("%s", buffer+11);
      }
    else
      {
      id = US"";
      show_log("%s", buffer);
      }

    /* Deal with frozen and unfrozen messages */

    if (strstric(buffer, US"frozen", FALSE) != NULL)
      {
      queue_item *qq = find_queue(id, queue_noop, 0);
      if (qq != NULL)
        {
        if (strstric(buffer, US"unfrozen", FALSE) != NULL)
          qq->frozen = FALSE;
        else qq->frozen = TRUE;
        }
      }

    /* Notice defer messages, and add the destination if it
    isn't already on the list for this message, with a pointer
    to the parent if we can. */

    if ((p = Ustrstr(buffer, "==")) != NULL)
      {
      queue_item *qq = find_queue(id, queue_noop, 0);
      if (qq != NULL)
        {
        dest_item *d;
        uschar *q, *r;
        p += 2;
        while (isspace(*p)) p++;
        q = p;
        while (*p != 0 && !isspace(*p))
          {
          if (*p++ != '\"') continue;
          while (*p != 0)
            {
            if (*p == '\\') p += 2;
              else if (*p++ == '\"') break;
            }
          }
        *p++ = 0;
        if ((r = strstric(q, qualify_domain, FALSE)) != NULL &&
          *(--r) == '@') *r = 0;

        /* If we already have this destination, as tested case-insensitively,
        do not add it to the destinations list. */

        d = find_dest(qq, q, dest_add, TRUE);

        if (d->parent == NULL)
          {
          while (isspace(*p)) p++;
          if (*p == '<')
            {
            dest_item *dd;
            q = ++p;
            while (*p != 0 && *p != '>') p++;
            *p = 0;
            if ((p = strstric(q, qualify_domain, FALSE)) != NULL &&
              *(--p) == '@') *p = 0;
            dd = find_dest(qq, q, dest_noop, FALSE);
            if (dd != NULL && dd != d) d->parent = dd;
            }
          }
        }
      }

    store_reset(reset_point);
    }
  }


/* We have to detect when the log file is changed, and switch to the new file.
In practice, for non-datestamped files, this means that some deliveries might
go unrecorded, since they'll be written to the old file, but this usually
happens in the middle of the night, and I don't think the hassle of keeping
track of two log files is worth it.

First we check the datestamped name of the log file if necessary; if it is
different to the file we currently have open, go for the new file. As happens
in Exim itself, we leave in the following inode check, even when datestamping
because it does no harm and will cope should a file actually be renamed for
some reason.

The test for a changed log file is to look up the inode of the file by name and
compare it with the saved inode of the file we currently are processing. This
accords with the usual interpretation of POSIX and other Unix specs that imply
"one file, one inode". However, it appears that on some Digital systems, if an
open file is unlinked, a new file may be created with the same inode while the
old file remains in existence. This can happen if the old log file is renamed,
processed in some way, and then deleted. To work round this, also test for a
link count of zero on the currently open file. */

if (log_datestamping)
  {
  uschar log_file_wanted[256];
  string_format(log_file_wanted, sizeof(log_file_wanted), CS log_file);
  if (Ustrcmp(log_file_wanted, log_file_open) != 0)
    {
    if (LOG != NULL)
      {
      fclose(LOG);
      LOG = NULL;
      }
    Ustrcpy(log_file_open, log_file_wanted);
    }
  }

if (LOG == NULL ||
    (fstat(fileno(LOG), &statdata) == 0 && statdata.st_nlink == 0) ||
    (Ustat(log_file, &statdata) == 0 && log_inode != statdata.st_ino))
  {
  FILE *TEST;

  /* Experiment shows that sometimes you can't immediately open
  the new log file - presumably immediately after the old one
  is renamed and before the new one exists. Therefore do a
  trial open first to be sure. */

  if ((TEST = fopen(CS log_file_open, "r")) != NULL)
    {
    if (LOG != NULL) fclose(LOG);
    LOG = TEST;
    fstat(fileno(LOG), &statdata);
    log_inode = statdata.st_ino;
    }
  }

/* Save the position we have got to in the log. */

if (LOG != NULL) log_position = ftell(LOG);
}
Ejemplo n.º 20
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;
}
Ejemplo n.º 21
0
Archivo: pvs.c Proyecto: jff/mathspad
void pvs_parse_result(char *pvs_result)
{
  /* divide result in premise and target. Use natural language to combine
  ** the expressions.
  */
  char *c, *h;
  Char buffer[512];
  int pos,assump_count;
  Char *part;
  /* fprintf(stderr,"Result:>>>\n%s\n<<<\n", pvs_result); */
  assump_count=0; part=0; pos=0;
  c=pvs_result;
  while (c && strncmp("  |-----", c,8)) {
    /* fprintf(stderr,"Assumption %i, position %i\n", assump_count, c-pvs_result); */
    if (!assump_count) {
      part=translate("Under the assumption of ");
      Ustrcpy(buffer,part);
      pos=Ustrlen(part);
    } else {
      buffer[pos++]=',';
      buffer[pos++]=' ';
    }
    buffer[pos++]=MP_Expr;
    assump_count++;
    while (!isspace(*c)) c++;
    while (isspace(*c)) c++;
    h=c;
    c=strchr(c,'\n');
    while (c && isspace(c[1]) && c[3]!='|') {
      c=strchr(c+1, '\n');
    }
    if (!c) c=h;
    *c='\0';
    /* fprintf(stderr, ">>>\n%s\n<<<\n",h); */
    pvs_parse_string(h);
    *c='\n';
    c++;
  }
  if (assump_count>1) {
    /* replace last ", E" by " and E" */
    pos=pos-3;
    part=translate(" and ");
    Ustrcpy(buffer+pos, part);
    pos=pos+Ustrlen(part);
    buffer[pos++]=MP_Expr;
  }
  if (assump_count) {
    buffer[pos++]=',';
    buffer[pos++]=Newline;
  }
  part=translate("Prove that ");
  Ustrcpy(buffer+pos, part);
  if (assump_count) buffer[pos]=Utolower(buffer[pos]);
  pos=pos+Ustrlen(part);
  buffer[pos++]=MP_Expr;
  if (pvs_do_leibnitz) {
    buffer[pos++]=Newline;
    part=translate("Or: ");
    Ustrcpy(buffer+pos, part);
    pos=pos+Ustrlen(part);
    buffer[pos++]=MP_Expr;
  }
  buffer[pos++]='.';
  buffer[pos++]=Newline;
  buffer[pos]=0;
  h=strchr(c,'\n');
  if (h) c=h+1;
  /* fprintf(stderr, "To prove1:>>>\n%s\n<<<\n", c); */
  while (!isspace(*c)) c++;
  while (isspace(*c)) c++;
  /* fprintf(stderr, "To prove2:>>>\n%s\n<<<\n", c); */
  pvs_parse_string(c);
  if (pvs_do_leibnitz) {
    apply_leibnitz();
  }
  make_node(MP_Text, buffer, pos,0,0);
}
Ejemplo n.º 22
0
int
main(int argc, char **cargv)
{
open_db dbblock[8];
int max_db = sizeof(dbblock)/sizeof(open_db);
int current = -1;
int showtime = 0;
int i;
dbdata_wait *dbwait = NULL;
uschar **argv = USS cargv;
uschar buffer[256];
uschar structbuffer[1024];

if (argc != 2)
  {
  printf("Usage: test_dbfn directory\n");
  printf("The subdirectory called \"db\" in the given directory is used for\n");
  printf("the files used in this test program.\n");
  return 1;
  }

/* Initialize */

spool_directory = argv[1];
debug_selector = D_all - D_memory;
debug_file = stderr;
big_buffer = malloc(big_buffer_size);

for (i = 0; i < max_db; i++) dbblock[i].dbptr = NULL;

printf("\nExim's db functions tester: interface type is %s\n", EXIM_DBTYPE);
printf("DBM library: ");

#ifdef DB_VERSION_STRING
printf("Berkeley DB: %s\n", DB_VERSION_STRING);
#elif defined(BTREEVERSION) && defined(HASHVERSION)
  #ifdef USE_DB
  printf("probably Berkeley DB version 1.8x (native mode)\n");
  #else
  printf("probably Berkeley DB version 1.8x (compatibility mode)\n");
  #endif
#elif defined(_DBM_RDONLY) || defined(dbm_dirfno)
printf("probably ndbm\n");
#elif defined(USE_TDB)
printf("using tdb\n");
#else
  #ifdef USE_GDBM
  printf("probably GDBM (native mode)\n");
  #else
  printf("probably GDBM (compatibility mode)\n");
  #endif
#endif

/* Test the functions */

printf("\nTest the functions\n> ");

while (Ufgets(buffer, 256, stdin) != NULL)
  {
  int len = Ustrlen(buffer);
  int count = 1;
  clock_t start = 1;
  clock_t stop = 0;
  uschar *cmd = buffer;
  while (len > 0 && isspace((uschar)buffer[len-1])) len--;
  buffer[len] = 0;

  if (isdigit((uschar)*cmd))
    {
    count = Uatoi(cmd);
    while (isdigit((uschar)*cmd)) cmd++;
    while (isspace((uschar)*cmd)) cmd++;
    }

  if (Ustrncmp(cmd, "open", 4) == 0)
    {
    int i;
    open_db *odb;
    uschar *s = cmd + 4;
    while (isspace((uschar)*s)) s++;

    for (i = 0; i < max_db; i++)
      if (dbblock[i].dbptr == NULL) break;

    if (i >= max_db)
      {
      printf("Too many open databases\n> ");
      continue;
      }

    start = clock();
    odb = dbfn_open(s, O_RDWR, dbblock + i, TRUE);
    stop = clock();

    if (odb != NULL)
      {
      current = i;
      printf("opened %d\n", current);
      }
    /* Other error cases will have written messages */
    else if (errno == ENOENT)
      {
      printf("open failed: %s%s\n", strerror(errno),
        #ifdef USE_DB
        " (or other Berkeley DB error)"
        #else
        ""
        #endif
        );
      }
    }

  else if (Ustrncmp(cmd, "write", 5) == 0)
    {
    int rc = 0;
    uschar *key = cmd + 5;
    uschar *data;

    if (current < 0)
      {
      printf("No current database\n");
      continue;
      }

    while (isspace((uschar)*key)) key++;
    data = key;
    while (*data != 0 && !isspace((uschar)*data)) data++;
    *data++ = 0;
    while (isspace((uschar)*data)) data++;

    dbwait = (dbdata_wait *)(&structbuffer);
    Ustrcpy(dbwait->text, data);

    start = clock();
    while (count-- > 0)
      rc = dbfn_write(dbblock + current, key, dbwait,
        Ustrlen(data) + sizeof(dbdata_wait));
    stop = clock();
    if (rc != 0) printf("Failed: %s\n", strerror(errno));
    }

  else if (Ustrncmp(cmd, "read", 4) == 0)
    {
    uschar *key = cmd + 4;
    if (current < 0)
      {
      printf("No current database\n");
      continue;
      }
    while (isspace((uschar)*key)) key++;
    start = clock();
    while (count-- > 0)
      dbwait = (dbdata_wait *)dbfn_read_with_length(dbblock+ current, key, NULL);
    stop = clock();
    printf("%s\n", (dbwait == NULL)? "<not found>" : CS dbwait->text);
    }

  else if (Ustrncmp(cmd, "delete", 6) == 0)
    {
    uschar *key = cmd + 6;
    if (current < 0)
      {
      printf("No current database\n");
      continue;
      }
    while (isspace((uschar)*key)) key++;
    dbfn_delete(dbblock + current, key);
    }

  else if (Ustrncmp(cmd, "scan", 4) == 0)
    {
    EXIM_CURSOR *cursor;
    BOOL startflag = TRUE;
    uschar *key;
    uschar keybuffer[256];
    if (current < 0)
      {
      printf("No current database\n");
      continue;
      }
    start = clock();
    while ((key = dbfn_scan(dbblock + current, startflag, &cursor)) != NULL)
      {
      startflag = FALSE;
      Ustrcpy(keybuffer, key);
      dbwait = (dbdata_wait *)dbfn_read_with_length(dbblock + current,
        keybuffer, NULL);
      printf("%s: %s\n", keybuffer, dbwait->text);
      }
    stop = clock();
    printf("End of scan\n");
    }

  else if (Ustrncmp(cmd, "close", 5) == 0)
    {
    uschar *s = cmd + 5;
    while (isspace((uschar)*s)) s++;
    i = Uatoi(s);
    if (i >= max_db || dbblock[i].dbptr == NULL) printf("Not open\n"); else
      {
      start = clock();
      dbfn_close(dbblock + i);
      stop = clock();
      dbblock[i].dbptr = NULL;
      if (i == current) current = -1;
      }
    }

  else if (Ustrncmp(cmd, "file", 4) == 0)
    {
    uschar *s = cmd + 4;
    while (isspace((uschar)*s)) s++;
    i = Uatoi(s);
    if (i >= max_db || dbblock[i].dbptr == NULL) printf("Not open\n");
      else current = i;
    }

  else if (Ustrncmp(cmd, "time", 4) == 0)
    {
    showtime = ~showtime;
    printf("Timing %s\n", showtime? "on" : "off");
    }

  else if (Ustrcmp(cmd, "q") == 0 || Ustrncmp(cmd, "quit", 4) == 0) break;

  else if (Ustrncmp(cmd, "help", 4) == 0)
    {
    printf("close  [<number>]              close file [<number>]\n");
    printf("delete <key>                   remove record from current file\n");
    printf("file   <number>                make file <number> current\n");
    printf("open   <name>                  open db file\n");
    printf("q[uit]                         exit program\n");
    printf("read   <key>                   read record from current file\n");
    printf("scan                           scan current file\n");
    printf("time                           time display on/off\n");
    printf("write  <key> <rest-of-line>    write record to current file\n");
    }

  else printf("Eh?\n");

  if (showtime && stop >= start)
    printf("start=%d stop=%d difference=%d\n", (int)start, (int)stop,
     (int)(stop - start));

  printf("> ");
  }

for (i = 0; i < max_db; i++)
  {
  if (dbblock[i].dbptr != NULL)
    {
    printf("\nClosing %d", i);
    dbfn_close(dbblock + i);
    }
  }

printf("\n");
return 0;
}
Ejemplo n.º 23
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;
}
Ejemplo n.º 24
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;
}
Ejemplo n.º 25
0
Archivo: pvs.c Proyecto: jff/mathspad
int pvs_parse_prove(unsigned char *buffer, unsigned int *len)
{
  char *pos, *startat;
  if (!toproof || !curstep) {
    /* end of proof, expect a 'really want to quit?' prompt */
    if (strstr(buffer,"want to quit?")) {
      edit_string_to_proces(translate("y\n"), translate("PVS-shell"));
    }
    return 0;
  }
  startat=(char*)buffer;
  while (startat && *startat) {
    switch (prove_pos) {
    case InRubbish:
      pos = strstr(startat, "Q.E.D.");
      if (pos) {
	Char *pvsres;
	Char txt[10240];
	pvsres = translate("PVS Results");
	string_to_window(pvsres, LocaletoUstr((unsigned char*)toproof->lemma));
	string_to_window(pvsres, translate(" is correct according to PVS.\n"));
	curstep = curstep->next;
	txt[0]=0;
	while (curstep) {
	  if (curstep->steptype == KeywordStep) {
	    string_to_window(pvsres, translate("Warning: keyword \""));
	    string_to_window(pvsres, curstep->keyword->str);
	    string_to_window(pvsres, translate("\" is not needed\n"));
	    Ustrcat(txt, translate("Warning: keyword \""));
	    Ustrcat(txt, curstep->keyword->str);
	    Ustrcat(txt, translate("\" is not needed.\n"));
	  }
	  curstep=curstep->next;
	}
	if (txt[0]) {
	  int i=Ustrlen(txt);
	  make_node(MP_Text, txt, i, 0,0);
	  txt[0]=PhNum2Char(MP_Text,1);
	  make_node(MP_Op, txt, 1, warning_template,0);
	} else {
	  make_node(MP_Op, txt, 0, good_template,0);
	}
	finish_current_proof(0);
	startat=0;
	break;
      }
      pos = strstr(startat, "this yields ");
      if (pos && strstr(pos, "subgoals")) {
	Char *pvsres;
	Char *split_error;
	Char txt[1024];
	pvsres = translate("PVS Results");
	split_error =
	  translate(": PVS split the proof into subgoals. Unable to proceed.");
	string_to_window(pvsres, LocaletoUstr((unsigned char*)toproof->lemma));
	string_to_window(pvsres, split_error);
	string_to_window(pvsres, LocaletoUstr("\n"));
	message2(MP_ERROR, LocaletoUstr(toproof->lemma), split_error);
	Ustrcpy(txt, split_error);
	make_node(MP_Text, txt, Ustrlen(txt),0,0);
	txt[0]=PhNum2Char(MP_Text,1);
	make_node(MP_Op, txt, 1, wrong_template, 0);
	finish_current_proof(1);
	startat=0;
	break;
      }
      pos = strstr(startat, "want to quit?");
      if (pos) {
	edit_string_to_proces(translate("y\n"), translate("PVS-shell"));
	startat=0;
	/* no matter what happened, the proof is finished */
	break;
      }
      pos = strstr(startat, toproof->lemma);
      if (pos) {
	pos=pos+strlen(toproof->lemma);
	if (*pos!='.') {
	  /* perhaps could use strchr(pos,'\n') here. */
	  while (*pos && *pos!=':') pos++;
	  if (*pos==':') pos++;
	  while (*pos==' ' || *pos=='\t') pos++;
	  while (*pos=='\n') pos++;
	  step_clear_result(curstep);
	  prove_pos = InExpr;
	  startat = pos;
	  break;
	}
      }
      pos = strstr(startat, "Rule? ");
      if (pos) {
	if (!pos[6]) {
	  /* !pos[6] indicates that there is no more input yet. */
	  if (curstep->next) {
	    ProofStep *h=curstep;
	    /* send next step */
	    curstep=curstep->next;
	    if (curstep->pvsinput) {
	      edit_string_to_proces(LocaletoUstr(curstep->pvsinput),
				    translate("PVS-shell"));
	    }
	    if (curstep->keyword) {
	      edit_string_to_proces(LocaletoUstr(curstep->keyword->step),
				    translate("PVS-shell"));
	    }
	    if (curstep->use_result) {
	      PVSProof *prf;
	      pvs_use_result(h->result, curstep->use_result-1);
	      curstep=0;
	      prf=toproof;
	      toproof=toproof->next;
	      destruct_selection(prf->selection);
	      /* free prf */
	    }
	  } else {
	    Char *pvsres;
	    Char *insuf_error;
	    pvsres= translate("PVS Results");
	    insuf_error= translate(": Insufficient information to prove the selected hint.");
	    message2(MP_ERROR, LocaletoUstr(toproof->lemma), insuf_error);
	    string_to_window(pvsres, LocaletoUstr(toproof->lemma));
	    string_to_window(pvsres, insuf_error);
	    string_to_window(pvsres, LocaletoUstr("\n"));
	    string_to_window(pvsres, translate("The proof attempt ends with the following PVS expression:\n"));
	    /* add parse - diff - analyse routine here */
	    if (1)
	    {
	      Char txt[2];
	      pvs_parse_result(curstep->result);
	      txt[0]=PhNum2Char(MP_Text,1);
	      make_node(MP_Op, txt, 1, wrong_template,0);
	      /* structure_to_window(pvsres); */
	    } else {
	      string_to_window(pvsres, LocaletoUstr(curstep->result));
	    }
	    string_to_window(pvsres, LocaletoUstr("\n"));
	    finish_current_proof(1);
	  }
	}
	startat=pos+6;
	break;
      }
      pos = strstr(startat, ";;; GC:");
      if (pos) {
	message(MP_MESSAGE, translate("PVS collects garbage. One moment please."));
	startat = pos+7;
	break;
      }
      pos = strstr(startat, ";;; Finished GC");
      if (pos) {
	message(MP_MESSAGE, translate("PVS finished collecting garbage. Sorry for the inconvenience."));
	startat = pos+15;
	break;
      }
      /* nothing usefull found. Just skip everything. */
      startat=0;
      break;
    case InExpr:
      /* search for the PVS prompt */
      if (after_newline && startat[0]=='\n') {
	int stepreslen;
	stepreslen = strlen(curstep->result);
	curstep->result[stepreslen-1]=0;
	startat++;
	after_newline=0;
	prove_pos=InRubbish;
      } else {
	pos = strstr(startat, "\n\n");
	if (pos) {
	  *pos=0;
	  step_add_to_result(curstep,startat);
	  after_newline=0;
	  startat=pos+2;
	  prove_pos=InRubbish;
	} else {
	  step_add_to_result(curstep,startat);
	  if (startat[strlen(startat)-1]=='\n') {
	    after_newline=1;
	  }
	  startat = 0;
	}
      }
      break;
    default:
      fprintf(stderr, "Bad case in pvs_parse_prove.\n");
      startat=0;
      break;
    }
  }
  *len=0;
  return 0;
}
Ejemplo n.º 26
0
Archivo: em_menu.c Proyecto: Exim/exim
static void ActOnMessage(uschar *id, uschar *action, uschar *address_arg)
{
int pid;
int pipe_fd[2];
int delivery = Ustrcmp(action + Ustrlen(action) - 2, "-M") == 0;
uschar *quote = US"";
uschar *at = US"";
uschar *qualify = US"";
uschar buffer[256];
queue_item *qq;
Widget text = NULL;

/* If the address arg is not empty and does not contain @ and there is a
qualify domain, qualify it. (But don't qualify '<>'.)*/

if (address_arg[0] != 0)
  {
  quote = US"\'";
  if (Ustrchr(address_arg, '@') == NULL &&
      Ustrcmp(address_arg, "<>") != 0 &&
      qualify_domain != NULL &&
      qualify_domain[0] != 0)
    {
    at = US"@";
    qualify = qualify_domain;
    }
  }
sprintf(CS buffer, "%s %s %s %s %s %s%s%s%s%s", exim_path,
  (alternate_config == NULL)? US"" : US"-C",
  (alternate_config == NULL)? US"" : alternate_config,
  action, id, quote, address_arg, at, qualify, quote);

/* If we know we are going to need the window, create it now. */

if (action_output || delivery)
  {
  text = text_create(id, text_depth);
  text_showf(text, "%s\n", buffer);
  }

/* Create the pipe for output. Remember, on most systems pipe[0] is
for reading and pipe[1] is for writing! Solaris, with its two-way
pipes is a trap! */

if (pipe(pipe_fd) != 0)
  {
  if (text == NULL)
    {
    text = text_create(id, text_depth);
    text_showf(text, "%s\n", buffer);
    }
  text_show(text, US"*** Failed to create pipe ***\n");
  return;
  }

if (  fcntl(pipe_fd[0], F_SETFL, O_NONBLOCK)
   || fcntl(pipe_fd[1], F_SETFL, O_NONBLOCK))
  {
  perror("set nonblocking on pipe");
  exit(1);
  }

/* Delivering a message can take some time, and we want to show the
output as it goes along. This requires subprocesses and is coded below. For
other commands, we can assume an immediate response, and so need not waste
resources with subprocesses. If action_output is FALSE, don't show the
output at all. */

if (!delivery)
  {
  int count, rc;
  int save_stdout = dup(1);
  int save_stderr = dup(2);

  close(1);
  close(2);

  dup2(pipe_fd[1], 1);
  dup2(pipe_fd[1], 2);
  close(pipe_fd[1]);

  rc = system(CS buffer);

  close(1);
  close(2);

  if (action_output || rc != 0)
    {
    if (text == NULL)
      {
      text = text_create(id, text_depth);
      text_showf(text, "%s\n", buffer);
      }
    while ((count = read(pipe_fd[0], buffer, 254)) > 0)
      {
      buffer[count] = 0;
      text_show(text, buffer);
      }
    }

  close(pipe_fd[0]);

  dup2(save_stdout, 1);
  dup2(save_stderr, 2);
  close(save_stdout);
  close(save_stderr);

  /* If action was to change the sender, and it succeeded, we have to
  update the in-store data. */

  if (rc == 0 && Ustrcmp(action + Ustrlen(action) - 4, "-Mes") == 0)
    {
    queue_item *q = find_queue(id, queue_noop, 0);
    if (q)
      {
      if (q->sender) store_free(q->sender);
      q->sender = store_malloc(Ustrlen(address_arg) + 1);
      Ustrcpy(q->sender, address_arg);
      }
    }

  /* If configured, cause a display update and return */

  if (action_queue_update) tick_queue_accumulator = 999999;
  return;
  }

/* Message is to be delivered. Ensure that it is marked unfrozen,
because nothing will get written to the log to show that this has
happened. (Other freezing/unfreezings get logged and picked up from
there.) */

qq = find_queue(id, queue_noop, 0);
if (qq != NULL) qq->frozen = FALSE;

/* New, asynchronous code runs in a subprocess for commands that
will take some time. The main process does not wait. There is a
SIGCHLD handler in the main program that cleans up any terminating
sub processes. */

if ((pid = fork()) == 0)
  {
  close(1);
  close(2);

  dup2(pipe_fd[1], 1);
  dup2(pipe_fd[1], 2);
  close(pipe_fd[1]);

  system(CS buffer);

  close(1);
  close(2);
  close(pipe_fd[0]);
  _exit(0);
  }

/* Main process - set up an item for the main ticker to watch. */

if (pid < 0) text_showf(text, "Failed to fork: %s\n", strerror(errno)); else
  {
  pipe_item *p = (pipe_item *)store_malloc(sizeof(pipe_item));

  if (p == NULL)
    {
    text_show(text, US"Run out of store\n");
    return;
    }

  p->widget = text;
  p->fd = pipe_fd[0];

  p->next = pipe_chain;
  pipe_chain = p;

  close(pipe_fd[1]);
  }
}