find_header(uschar *name, BOOL exists_only, int *newsize, BOOL want_raw,
  uschar *charset)
{
BOOL found = name == NULL;
int comma = 0;
int len = found? 0 : Ustrlen(name);
int i;
uschar *yield = NULL;
uschar *ptr = NULL;

/* Loop for two passes - saves code repetition */

for (i = 0; i < 2; i++)
  {
  int size = 0;
  header_line *h;

  for (h = header_list; size < header_insert_maxlen && h != NULL; h = h->next)
    {
    if (h->type != htype_old && h->text != NULL)  /* NULL => Received: placeholder */
      {
      if (name == NULL || (len <= h->slen && strncmpic(name, h->text, len) == 0))
        {
        int ilen;
        uschar *t;

        if (exists_only) return US"1";      /* don't need actual string */
        found = TRUE;
        t = h->text + len;                  /* text to insert */
        if (!want_raw)                      /* unless wanted raw, */
          while (isspace(*t)) t++;          /* remove leading white space */
        ilen = h->slen - (t - h->text);     /* length to insert */

        /* Unless wanted raw, remove trailing whitespace, including the
        newline. */

        if (!want_raw)
          while (ilen > 0 && isspace(t[ilen-1])) ilen--;

        /* Set comma = 1 if handling a single header and it's one of those
        that contains an address list, except when asked for raw headers. Only
        need to do this once. */

        if (!want_raw && name != NULL && comma == 0 &&
            Ustrchr("BCFRST", h->type) != NULL)
          comma = 1;

        /* First pass - compute total store needed; second pass - compute
        total store used, including this header. */

        size += ilen + comma + 1;  /* +1 for the newline */

        /* Second pass - concatentate the data, up to a maximum. Note that
        the loop stops when size hits the limit. */

        if (i != 0)
          {
          if (size > header_insert_maxlen)
            {
            ilen -= size - header_insert_maxlen - 1;
            comma = 0;
            }
          Ustrncpy(ptr, t, ilen);
          ptr += ilen;

          /* For a non-raw header, put in the comma if needed, then add
          back the newline we removed above, provided there was some text in
          the header. */

          if (!want_raw && ilen > 0)
            {
            if (comma != 0) *ptr++ = ',';
            *ptr++ = '\n';
            }
          }
        }
      }
    }

  /* At end of first pass, return NULL if no header found. Then truncate size
  if necessary, and get the buffer to hold the data, returning the buffer size.
  */

  if (i == 0)
    {
    if (!found) return NULL;
    if (size > header_insert_maxlen) size = header_insert_maxlen;
    *newsize = size + 1;
    ptr = yield = store_get(*newsize);
    }
  }

/* That's all we do for raw header expansion. */

if (want_raw)
  {
  *ptr = 0;
  }

/* Otherwise, remove a final newline and a redundant added comma. Then we do
RFC 2047 decoding, translating the charset if requested. The rfc2047_decode2()
function can return an error with decoded data if the charset translation
fails. If decoding fails, it returns NULL. */

else
  {
  uschar *decoded, *error;
  if (ptr > yield && ptr[-1] == '\n') ptr--;
  if (ptr > yield && comma != 0 && ptr[-1] == ',') ptr--;
  *ptr = 0;
  decoded = rfc2047_decode2(yield, check_rfc2047_length, charset, '?', NULL,
    newsize, &error);
  if (error != NULL)
    {
    DEBUG(D_any) debug_printf("*** error in RFC 2047 decoding: %s\n"
      "    input was: %s\n", error, yield);
    }
  if (decoded != NULL) yield = decoded;
  }

return yield;
}

find_header(uschar *name, BOOL exists_only, int *newsize, BOOL want_raw,
  uschar *charset)
{
BOOL found = name == NULL;
int comma = 0;
int len = found? 0 : Ustrlen(name);
int i;
uschar *yield = NULL;
uschar *ptr = NULL;

/* Loop for two passes - saves code repetition */

for (i = 0; i < 2; i++)
  {
  int size = 0;
  header_line *h;

  for (h = header_list; size < header_insert_maxlen && h != NULL; h = h->next)
    {
    if (h->type != htype_old && h->text != NULL)  /* NULL => Received: placeholder */
      {
      if (name == NULL || (len <= h->slen && strncmpic(name, h->text, len) == 0))
        {
        int ilen;
        uschar *t;

        if (exists_only) return US"1";      /* don't need actual string */
        found = TRUE;
        t = h->text + len;                  /* text to insert */
        if (!want_raw)                      /* unless wanted raw, */
          while (isspace(*t)) t++;          /* remove leading white space */
        ilen = h->slen - (t - h->text);     /* length to insert */

        /* Unless wanted raw, remove trailing whitespace, including the
        newline. */

        if (!want_raw)
          while (ilen > 0 && isspace(t[ilen-1])) ilen--;

        /* Set comma = 1 if handling a single header and it's one of those
        that contains an address list, except when asked for raw headers. Only
        need to do this once. */

        if (!want_raw && name != NULL && comma == 0 &&
            Ustrchr("BCFRST", h->type) != NULL)
          comma = 1;

        /* First pass - compute total store needed; second pass - compute
        total store used, including this header. */

        size += ilen + comma + 1;  /* +1 for the newline */

        /* Second pass - concatentate the data, up to a maximum. Note that
        the loop stops when size hits the limit. */

        if (i != 0)
          {
          if (size > header_insert_maxlen)
            {
            ilen -= size - header_insert_maxlen - 1;
            comma = 0;
            }
          Ustrncpy(ptr, t, ilen);
          ptr += ilen;

          /* For a non-raw header, put in the comma if needed, then add
          back the newline we removed above, provided there was some text in
          the header. */

          if (!want_raw && ilen > 0)
            {
            if (comma != 0) *ptr++ = ',';
            *ptr++ = '\n';
            }
          }
        }
      }
    }

  /* At end of first pass, return NULL if no header found. Then truncate size
  if necessary, and get the buffer to hold the data, returning the buffer size.
  */

  if (i == 0)
    {
    if (!found) return NULL;
    if (size > header_insert_maxlen) size = header_insert_maxlen;
    *newsize = size + 1;
    ptr = yield = store_get(*newsize);
    }
  }

/* That's all we do for raw header expansion. */

if (want_raw)
  {
  *ptr = 0;
  }

/* Otherwise, remove a final newline and a redundant added comma. Then we do
RFC 2047 decoding, translating the charset if requested. The rfc2047_decode2()
function can return an error with decoded data if the charset translation
fails. If decoding fails, it returns NULL. */

else
  {
  uschar *decoded, *error;
  if (ptr > yield && ptr[-1] == '\n') ptr--;
  if (ptr > yield && comma != 0 && ptr[-1] == ',') ptr--;
  *ptr = 0;
  decoded = rfc2047_decode2(yield, check_rfc2047_length, charset, '?', NULL,
    newsize, &error);
  if (error != NULL)
    {
    DEBUG(D_any) debug_printf("*** error in RFC 2047 decoding: %s\n"
      "    input was: %s\n", error, yield);
    }
  if (decoded != NULL) yield = decoded;
  }

return yield;
}
