* Exim - an Internet mail transport agent *
*************************************************/
+/* Copyright (c) The Exim Maintainers 2020 - 2023 */
/* Copyright (c) University of Cambridge 1995 - 2018 */
-/* Copyright (c) The Exim Maintainers 2020 */
/* See the file NOTICE for conditions of use and distribution. */
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/* Miscellaneous string-handling functions. Some are not required for
utilities and tests, and are cut out by the COMPILE_UTILITY macro. */
maskptr NULL if no mask is permitted to follow
otherwise, points to an int where the offset of '/' is placed
if there is no / followed by trailing digits, *maskptr is set 0
+ errp NULL if no diagnostic information is required, and if the netmask
+ length should not be checked. Otherwise it is set pointing to a short
+ descriptive text.
Returns: 0 if the string is not a textual representation of an IP address
4 if it is an IPv4 address
6 if it is an IPv6 address
+
+The legacy string_is_ip_address() function follows below.
*/
int
-string_is_ip_address(const uschar *s, int *maskptr)
+string_is_ip_addressX(const uschar * ip_addr, int * maskptr, const uschar ** errp)
{
-int yield = 4;
+uschar * slash, * percent, * endp = NULL;
+long int mask = 0;
+const uschar * addr = NULL;
+int af;
+union { /* we do not need this, but inet_pton() needs a place for storage */
+ struct in_addr sa4;
+ struct in6_addr sa6;
+} sa;
+
+/* If there is a slash, but we didn't request a (optional) netmask,
+we return failure, as we do if the mask isn't a pure numerical value,
+or if it is negative. The actual length is checked later, once we know
+the address family. */
+if (slash = Ustrchr(ip_addr, '/'))
+ {
+ uschar * rest;
-/* If an optional mask is permitted, check for it. If found, pass back the
-offset. */
+ if (!maskptr)
+ {
+ if (errp) *errp = US"netmask found, but not requested";
+ return 0;
+ }
-if (maskptr)
- {
- const uschar *ss = s + Ustrlen(s);
- *maskptr = 0;
- if (s != ss && isdigit(*(--ss)))
+ mask = Ustrtol(slash+1, &rest, 10);
+ if (*rest || mask < 0)
{
- while (ss > s && isdigit(ss[-1])) ss--;
- if (ss > s && *(--ss) == '/') *maskptr = ss - s;
+ if (errp) *errp = US"netmask not numeric or <0";
+ return 0;
}
+
+ *maskptr = slash - ip_addr; /* offset of the slash */
+ endp = slash;
}
+else if (maskptr) *maskptr = 0; /* no slash found */
-/* A colon anywhere in the string => IPv6 address */
+/* The interface-ID suffix (%<id>) is optional (for IPv6). If it
+exists, we check it syntactically. Later, if we know the address
+family is IPv4, we might reject it.
+The interface-ID is mutually exclusive with the netmask, to the
+best of my knowledge. */
-if (Ustrchr(s, ':') != NULL)
+if (percent = Ustrchr(ip_addr, '%'))
{
- BOOL had_double_colon = FALSE;
- BOOL v4end = FALSE;
-
- yield = 6;
-
- /* An IPv6 address must start with hex digit or double colon. A single
- colon is invalid. */
-
- if (*s == ':' && *(++s) != ':') return 0;
-
- /* Now read up to 8 components consisting of up to 4 hex digits each. There
- may be one and only one appearance of double colon, which implies any number
- of binary zero bits. The number of preceding components is held in count. */
-
- for (int count = 0; count < 8; count++)
+ if (slash)
{
- /* If the end of the string is reached before reading 8 components, the
- address is valid provided a double colon has been read. This also applies
- if we hit the / that introduces a mask or the % that introduces the
- interface specifier (scope id) of a link-local address. */
-
- if (*s == 0 || *s == '%' || *s == '/') return had_double_colon ? yield : 0;
-
- /* If a component starts with an additional colon, we have hit a double
- colon. This is permitted to appear once only, and counts as at least
- one component. The final component may be of this form. */
-
- if (*s == ':')
- {
- if (had_double_colon) return 0;
- had_double_colon = TRUE;
- s++;
- continue;
- }
-
- /* If the remainder of the string contains a dot but no colons, we
- can expect a trailing IPv4 address. This is valid if either there has
- been no double-colon and this is the 7th component (with the IPv4 address
- being the 7th & 8th components), OR if there has been a double-colon
- and fewer than 6 components. */
-
- if (Ustrchr(s, ':') == NULL && Ustrchr(s, '.') != NULL)
+ if (errp) *errp = US"interface-ID and netmask are mutually exclusive";
+ return 0;
+ }
+ for (uschar *p = percent+1; *p; p++)
+ if (!isalnum(*p) && !ispunct(*p))
{
- if ((!had_double_colon && count != 6) ||
- (had_double_colon && count > 6)) return 0;
- v4end = TRUE;
- yield = 6;
- break;
+ if (errp) *errp = US"interface-ID must match [[:alnum:][:punct:]]";
+ return 0;
}
+ endp = percent;
+ }
- /* Check for at least one and not more than 4 hex digits for this
- component. */
-
- if (!isxdigit(*s++)) return 0;
- if (isxdigit(*s) && isxdigit(*(++s)) && isxdigit(*(++s))) s++;
-
- /* If the component is terminated by colon and there is more to
- follow, skip over the colon. If there is no more to follow the address is
- invalid. */
+/* inet_pton() can't parse netmasks and interface IDs, so work on a shortened copy
+allocated on the current stack */
- if (*s == ':' && *(++s) == 0) return 0;
+if (endp)
+ {
+ ptrdiff_t l = endp - ip_addr;
+ if (l > 255)
+ {
+ if (errp) *errp = US"rudiculous long ip address string";
+ return 0;
}
+ addr = string_copyn(ip_addr, l);
+ }
+else
+ addr = ip_addr;
- /* If about to handle a trailing IPv4 address, drop through. Otherwise
- all is well if we are at the end of the string or at the mask or at a percent
- sign, which introduces the interface specifier (scope id) of a link local
- address. */
-
- if (!v4end)
- return (*s == 0 || *s == '%' ||
- (*s == '/' && maskptr != NULL && *maskptr != 0))? yield : 0;
+af = Ustrchr(addr, ':') ? AF_INET6 : AF_INET;
+if (!inet_pton(af, CCS addr, &sa))
+ {
+ if (errp) *errp = af == AF_INET6 ? US"IP address string not parsable as IPv6"
+ : US"IP address string not parsable IPv4";
+ return 0;
}
-/* Test for IPv4 address, which may be the tail-end of an IPv6 address. */
+/* we do not check the values of the mask here, as
+this is done on the callers side (but I don't understand why), so
+actually I'd like to do it here, but it breaks at least testcase 0002 */
-for (int i = 0; i < 4; i++)
+switch (af)
{
- long n;
- uschar * end;
-
- if (i != 0 && *s++ != '.') return 0;
- n = strtol(CCS s, CSS &end, 10);
- if (n > 255 || n < 0 || end <= s || end > s+3) return 0;
- s = end;
+ case AF_INET6:
+ if (errp && mask > 128)
+ {
+ *errp = US"IPv6 netmask value must not be >128";
+ return 0;
+ }
+ return 6;
+ case AF_INET:
+ if (percent)
+ {
+ if (errp) *errp = US"IPv4 address string must not have an interface-ID";
+ return 0;
+ }
+ if (errp && mask > 32)
+ {
+ *errp = US"IPv4 netmask value must not be >32";
+ return 0;
+ }
+ return 4;
+ default:
+ if (errp) *errp = US"unknown address family (should not happen)";
+ return 0;
}
+}
+
-return !*s || (*s == '/' && maskptr && *maskptr != 0) ? yield : 0;
+int
+string_is_ip_address(const uschar * ip_addr, int * maskptr)
+{
+return string_is_ip_addressX(ip_addr, maskptr, 0);
}
+
#endif /* COMPILE_UTILITY */
*************************************************/
/* Convert a long integer into an ASCII base 62 string. For Cygwin the value of
-BASE_62 is actually 36. Always return exactly 6 characters plus zero, in a
-static area.
+BASE_62 is actually 36. Always return exactly 6 characters plus a NUL, in a
+static area. This is enough for a 32b input, for 62 (for 64b we would want 11+nul);
+but with 36 we lose half the input range of a 32b input.
Argument: a long integer
Returns: pointer to base 62 string
*/
uschar *
-string_base62(unsigned long int value)
+string_base62_32(unsigned long int value)
{
static uschar yield[7];
-uschar *p = yield + sizeof(yield) - 1;
+uschar * p = yield + sizeof(yield) - 1;
*p = 0;
while (p > yield)
{
- *(--p) = base62_chars[value % BASE_62];
+ *--p = base62_chars[value % BASE_62];
value /= BASE_62;
}
return yield;
}
+
+uschar *
+string_base62_64(unsigned long int value)
+{
+static uschar yield[12];
+uschar * p = yield + sizeof(yield) - 1;
+*p = '\0';
+while (p > yield)
+ if (value)
+ {
+ *--p = base62_chars[value % BASE_62];
+ value /= BASE_62;
+ }
+ else
+ *--p = '0';
+return yield;
+}
#endif /* COMPILE_UTILITY */
const uschar *t = s;
uschar *ss, *tt;
-while (*t != 0)
+while (*t)
{
int c = *t++;
if ( !mac_isprint(c)
/* Get a new block of store guaranteed big enough to hold the
expanded string. */
-tt = ss = store_get(length + nonprintcount * 3 + 1, is_tainted(s));
+tt = ss = store_get(length + nonprintcount * 3 + 1, s);
/* Copy everything, escaping non printers. */
if (!p) return s;
len = Ustrlen(s) + 1;
-ss = store_get(len, is_tainted(s));
+ss = store_get(len, s);
q = ss;
off = p - s;
*/
uschar *
-string_copy_function(const uschar *s)
+string_copy_function(const uschar * s)
{
-return string_copy_taint(s, is_tainted(s));
+return string_copy_taint(s, s);
}
-/* This function assumes that memcpy() is faster than strcpy().
-As above, but explicitly specifying the result taint status
+/* As above, but explicitly specifying the result taint status
*/
uschar *
-string_copy_taint_function(const uschar * s, BOOL tainted)
+string_copy_taint_function(const uschar * s, const void * proto_mem)
{
-int len = Ustrlen(s) + 1;
-uschar *ss = store_get(len, tainted);
-memcpy(ss, s, len);
-return ss;
+return string_copy_taint(s, proto_mem);
}
*/
uschar *
-string_copyn_function(const uschar *s, int n)
+string_copyn_function(const uschar * s, int n)
{
-uschar *ss = store_get(n + 1, is_tainted(s));
-Ustrncpy(ss, s, n);
-ss[n] = 0;
-return ss;
+return string_copyn(s, n);
}
#endif
*/
uschar *
-string_copy_malloc(const uschar *s)
+string_copy_malloc(const uschar * s)
{
int len = Ustrlen(s) + 1;
-uschar *ss = store_malloc(len);
+uschar * ss = store_malloc(len);
memcpy(ss, s, len);
return ss;
}
*/
uschar *
-string_split_message(uschar *msg)
+string_split_message(uschar * msg)
{
uschar *s, *ss;
-if (msg == NULL || Ustrlen(msg) <= 75) return msg;
+if (!msg || Ustrlen(msg) <= 75) return msg;
s = ss = msg = string_copy(msg);
for (;;)
{
int i = 0;
- while (i < 75 && *ss != 0 && *ss != '\n') ss++, i++;
- if (*ss == 0) break;
+ while (i < 75 && *ss && *ss != '\n') ss++, i++;
+ if (!*ss) break;
if (*ss == '\n')
s = ++ss;
else
{
- uschar *t = ss + 1;
- uschar *tt = NULL;
+ uschar * t = ss + 1;
+ uschar * tt = NULL;
while (--t > s + 35)
{
if (*t == ' ')
{
if (t[-1] == ':') { tt = t; break; }
- if (tt == NULL) tt = t;
+ if (!tt) tt = t;
}
}
- if (tt == NULL) /* Can't split behind - try ahead */
+ if (!tt) /* Can't split behind - try ahead */
{
t = ss + 1;
- while (*t != 0)
+ while (*t)
{
if (*t == ' ' || *t == '\n')
{ tt = t; break; }
}
}
- if (tt == NULL) break; /* Can't find anywhere to split */
+ if (!tt) break; /* Can't find anywhere to split */
*tt = '\n';
s = ss = tt+1;
}
*/
uschar *
-string_copy_dnsdomain(uschar *s)
+string_copy_dnsdomain(uschar * s)
{
-uschar *yield;
-uschar *ss = yield = store_get(Ustrlen(s) + 1, is_tainted(s));
+uschar * yield;
+uschar * ss = yield = store_get(Ustrlen(s) + 1, GET_TAINTED); /* always treat as tainted */
-while (*s != 0)
+while (*s)
{
if (*s != '\\')
*ss++ = *s++;
*ss++ = (s[1] - '0')*100 + (s[2] - '0')*10 + s[3] - '0';
s += 4;
}
- else if (*(++s) != 0)
+ else if (*++s)
*ss++ = *s++;
}
*/
uschar *
-string_dequote(const uschar **sptr)
+string_dequote(const uschar ** sptr)
{
-const uschar *s = *sptr;
-uschar *t, *yield;
+const uschar * s = * sptr;
+uschar * t, * yield;
/* First find the end of the string */
if (*s != '\"')
- while (*s != 0 && !isspace(*s)) s++;
+ while (*s && !isspace(*s)) s++;
else
{
s++;
/* Get enough store to copy into */
-t = yield = store_get(s - *sptr + 1, is_tainted(*sptr));
+t = yield = store_get(s - *sptr + 1, *sptr);
s = *sptr;
/* Do the copy */
if (*s != '\"')
- while (*s != 0 && !isspace(*s)) *t++ = *s++;
+ while (*s && !isspace(*s)) *t++ = *s++;
else
{
s++;
- while (*s != 0 && *s != '\"')
+ while (*s && *s != '\"')
{
*t++ = *s == '\\' ? string_interpret_escape(&s) : *s;
s++;
Arguments:
format a printf() format - deliberately char * rather than uschar *
because it will most usually be a literal string
+ func caller, for debug
+ line caller, for debug
... arguments for format
Returns: pointer to fresh piece of store containing sprintf'ed string
*/
uschar *
-string_sprintf_trc(const char *format, const uschar * func, unsigned line, ...)
+string_sprintf_trc(const char * format, const uschar * func, unsigned line, ...)
{
#ifdef COMPILE_UTILITY
uschar buffer[STRING_SPRINTF_BUFFER_SIZE];
*/
int
-strncmpic(const uschar *s, const uschar *t, int n)
+strncmpic(const uschar * s, const uschar * t, int n)
{
while (n--)
{
*/
int
-strcmpic(const uschar *s, const uschar *t)
+strcmpic(const uschar * s, const uschar * t)
{
-while (*s != 0)
+while (*s)
{
int c = tolower(*s++) - tolower(*t++);
if (c != 0) return c;
Returns: pointer to substring in string, or NULL if not found
*/
-uschar *
-strstric(uschar *s, uschar *t, BOOL space_follows)
+const uschar *
+strstric_c(const uschar * s, const uschar * t, BOOL space_follows)
{
-uschar *p = t;
-uschar *yield = NULL;
+const uschar * p = t;
+const uschar * yield = NULL;
int cl = tolower(*p);
int cu = toupper(*p);
{
if (*s == cl || *s == cu)
{
- if (yield == NULL) yield = s;
- if (*(++p) == 0)
+ if (!yield) yield = s;
+ if (!*++p)
{
if (!space_follows || s[1] == ' ' || s[1] == '\n' ) return yield;
yield = NULL;
cu = toupper(*p);
s++;
}
- else if (yield != NULL)
+ else if (yield)
{
yield = NULL;
p = t;
return NULL;
}
+uschar *
+strstric(uschar * s, uschar * t, BOOL space_follows)
+{
+return US strstric_c(s, t, space_follows);
+}
#ifdef COMPILE_UTILITY
separator a pointer to the separator character in an int (see above)
buffer where to put a copy of the next string in the list; or
NULL if the next string is returned in new memory
+ Note that if the list is tainted then a provided buffer must be
+ also (else we trap, with a message referencing the callsite).
+ If we do the allocation, taint is handled there.
buflen when buffer is not NULL, the size of buffer; otherwise ignored
+ func caller, for debug
+ line caller, for debug
+
Returns: pointer to buffer, containing the next substring,
or NULL if no more substrings
*/
uschar *
-string_nextinlist_trc(const uschar **listptr, int *separator, uschar *buffer, int buflen,
- const uschar * func, int line)
+string_nextinlist_trc(const uschar ** listptr, int * separator, uschar * buffer,
+ int buflen, const uschar * func, int line)
{
int sep = *separator;
-const uschar *s = *listptr;
+const uschar * s = *listptr;
BOOL sep_is_special;
if (!s) return NULL;
sep_is_special = iscntrl(sep);
/* Handle the case when a buffer is provided. */
+/*XXX need to also deal with qouted-requirements mismatch */
if (buffer)
{
s = ss;
if (!*s || *++s != sep || sep_is_special) break;
}
+
+ /* Trim trailing spaces from the returned string */
+
/* while (g->ptr > 0 && isspace(g->s[g->ptr-1])) g->ptr--; */
while ( g->ptr > 0 && isspace(g->s[g->ptr-1])
&& (g->ptr == 1 || g->s[g->ptr-2] != '\\') )
g->ptr--;
buffer = string_from_gstring(g);
- gstring_release_unused(g);
+ gstring_release_unused_trc(g, CCS func, line);
}
/* Update the current pointer and return the new string */
{
int p = g->ptr;
int oldsize = g->size;
-BOOL tainted = is_tainted(g->s);
/* Mostly, string_cat() is used to build small strings of a few hundred
characters at most. There are times, however, when the strings are very much
unsigned inc = oldsize < 4096 ? 127 : 1023;
+if (g->ptr < 0 || g->ptr > g->size || g->size >= INT_MAX/2)
+ log_write(0, LOG_MAIN|LOG_PANIC_DIE,
+ "internal error in gstring_grow (ptr %d size %d)", g->ptr, g->size);
+
if (count <= 0) return;
+
+if (count >= INT_MAX/2 - g->ptr)
+ log_write(0, LOG_MAIN|LOG_PANIC_DIE,
+ "internal error in gstring_grow (ptr %d count %d)", g->ptr, count);
+
g->size = (p + count + inc + 1) & ~inc; /* one for a NUL */
/* Try to extend an existing allocation. If the result of calling
was the last item on the dynamic memory stack. This is the case if it matches
store_last_get. */
-if (!store_extend(g->s, tainted, oldsize, g->size))
- g->s = store_newblock(g->s, tainted, g->size, p);
+if (!store_extend(g->s, oldsize, g->size))
+ g->s = store_newblock(g->s, g->size, p);
}
sometimes called to extract parts of other strings.
Arguments:
- string points to the start of the string that is being built, or NULL
- if this is a new string that has no contents yet
+ g growable-string that is being built, or NULL if not assigned yet
s points to characters to add
count count of characters to add; must not exceed the length of s, if s
is a C string.
-Returns: pointer to the start of the string, changed if copied for expansion.
+Returns: growable string, changed if copied for expansion.
Note that a NUL is not added, though space is left for one. This is
because string_cat() is often called multiple times to build up a
string - there's no point adding the NUL till the end.
+ NULL is a possible return.
*/
/* coverity[+alloc] */
gstring *
-string_catn(gstring * g, const uschar *s, int count)
+string_catn(gstring * g, const uschar * s, int count)
{
int p;
-BOOL srctaint = is_tainted(s);
+if (count < 0)
+ log_write(0, LOG_MAIN|LOG_PANIC_DIE,
+ "internal error in string_catn (count %d)", count);
+if (count == 0) return g;
+
+/*debug_printf("string_catn '%.*s'\n", count, s);*/
if (!g)
{
unsigned inc = count < 4096 ? 127 : 1023;
- unsigned size = ((count + inc) & ~inc) + 1;
- g = string_get_tainted(size, srctaint);
+ unsigned size = ((count + inc) & ~inc) + 1; /* round up requested count */
+ g = string_get_tainted(size, s);
+ }
+else if (!g->s) /* should not happen */
+ {
+ g->s = string_copyn(s, count);
+ g->ptr = count;
+ g->size = count; /*XXX suboptimal*/
+ return g;
+ }
+else if (is_incompatible(g->s, s))
+ {
+/* debug_printf("rebuf A\n"); */
+ gstring_rebuffer(g, s);
}
-else if (srctaint && !is_tainted(g->s))
- gstring_rebuffer(g);
+
+if (g->ptr < 0 || g->ptr > g->size)
+ log_write(0, LOG_MAIN|LOG_PANIC_DIE,
+ "internal error in string_catn (ptr %d size %d)", g->ptr, g->size);
p = g->ptr;
-if (p + count >= g->size)
+if (count >= g->size - p)
gstring_grow(g, count);
/* Because we always specify the exact number of characters to copy, we can
gstring *
-string_cat(gstring *string, const uschar *s)
+string_cat(gstring * g, const uschar * s)
{
-return string_catn(string, s, Ustrlen(s));
+return string_catn(g, s, Ustrlen(s));
}
It calls string_cat() to do the dirty work.
Arguments:
- string expanding-string that is being built, or NULL
- if this is a new string that has no contents yet
+ g growable-string that is being built, or NULL if not yet assigned
count the number of strings to append
... "count" uschar* arguments, which must be valid zero-terminated
C strings
-Returns: pointer to the start of the string, changed if copied for expansion.
+Returns: growable string, changed if copied for expansion.
The string is not zero-terminated - see string_cat() above.
*/
__inline__ gstring *
-string_append(gstring *string, int count, ...)
+string_append(gstring * g, int count, ...)
{
va_list ap;
va_start(ap, count);
while (count-- > 0)
{
- uschar *t = va_arg(ap, uschar *);
- string = string_cat(string, t);
+ uschar * t = va_arg(ap, uschar *);
+ g = string_cat(g, t);
}
va_end(ap);
-return string;
+return g;
}
#endif
string_format_trc(uschar * buffer, int buflen,
const uschar * func, unsigned line, const char * format, ...)
{
-gstring g = { .size = buflen, .ptr = 0, .s = buffer }, *gp;
+gstring g = { .size = buflen, .ptr = 0, .s = buffer }, * gp;
va_list ap;
va_start(ap, format);
gp = string_vformat_trc(&g, func, line, STRING_SPRINTF_BUFFER_SIZE,
will not be grown, and is usable in the original place after return.
The return value can be NULL to signify overflow.
+Field width: decimal digits, or *
+Precision: dot, followed by decimal digits or *
+Length modifiers: h L l ll z
+Conversion specifiers: n d o u x X p f e E g G % c s S T Y D M
+
Returns the possibly-new (if copy for growth or taint-handling was needed)
string, not nul-terminated.
*/
gstring *
string_vformat_trc(gstring * g, const uschar * func, unsigned line,
- unsigned size_limit, unsigned flags, const char *format, va_list ap)
+ unsigned size_limit, unsigned flags, const char * format, va_list ap)
{
enum ltypes { L_NORMAL=1, L_SHORT=2, L_LONG=3, L_LONGLONG=4, L_LONGDOUBLE=5, L_SIZE=6 };
int width, precision, off, lim, need;
const char * fp = format; /* Deliberately not unsigned */
-BOOL dest_tainted = FALSE;
string_datestamp_offset = -1; /* Datestamp not inserted */
string_datestamp_length = 0; /* Datestamp not inserted */
/* Ensure we have a string, to save on checking later */
if (!g) g = string_get(16);
-else if (!(flags & SVFMT_TAINT_NOCHK)) dest_tainted = is_tainted(g->s);
-if (!(flags & SVFMT_TAINT_NOCHK) && !dest_tainted && is_tainted(format))
+if (!(flags & SVFMT_TAINT_NOCHK) && is_incompatible(g->s, format))
{
#ifndef MACRO_PREDEF
if (!(flags & SVFMT_REBUFFER))
die_tainted(US"string_vformat", func, line);
#endif
- gstring_rebuffer(g);
- dest_tainted = TRUE;
+/* debug_printf("rebuf B\n"); */
+ gstring_rebuffer(g, format);
}
#endif /*!COMPILE_UTILITY*/
while (*fp)
{
int length = L_NORMAL;
- int *nptr;
+ int * nptr;
int slen;
const char *null = "NULL"; /* ) These variables */
const char *item_start, *s; /* ) are deliberately */
slen = string_datestamp_length;
goto INSERT_STRING;
+ case 'Y': /* gstring pointer */
+ {
+ gstring * zg = va_arg(ap, gstring *);
+ if (zg) { s = CS zg->s; slen = zg->ptr; }
+ else { s = null; slen = Ustrlen(s); }
+ goto INSERT_GSTRING;
+ }
+
case 's':
case 'S': /* Forces *lower* case */
case 'T': /* Forces *upper* case */
if (!s) s = null;
slen = Ustrlen(s);
- if (!(flags & SVFMT_TAINT_NOCHK) && !dest_tainted && is_tainted(s))
+ INSERT_GSTRING: /* Coome to from %Y above */
+
+ if (!(flags & SVFMT_TAINT_NOCHK) && is_incompatible(g->s, s))
if (flags & SVFMT_REBUFFER)
{
- gstring_rebuffer(g);
+/* debug_printf("%s %d: untainted workarea, tainted %%s :- rebuffer\n", __FUNCTION__, __LINE__); */
+ gstring_rebuffer(g, s);
gp = CS g->s + g->ptr;
- dest_tainted = TRUE;
}
#ifndef MACRO_PREDEF
else
Arguments:
format a text format string - deliberately not uschar *
+ func caller, for debug
+ line caller, for debug
... arguments for the format string
Returns: a message, in dynamic store
uschar *
string_open_failed_trc(const uschar * func, unsigned line,
- const char *format, ...)
+ const char * format, ...)
{
va_list ap;
gstring * g = string_get(1024);
uschar buffer[256];
printf("Testing is_ip_address\n");
+store_init();
while (fgets(CS buffer, sizeof(buffer), stdin) != NULL)
{
int llflag = 0;
int n = 0;
int count;
- int countset = 0;
+ BOOL countset = FASE;
uschar format[256];
uschar outbuf[256];
uschar *s;
else if (Ustrcmp(ss, "*") == 0)
{
args[n++] = (void *)(&count);
- countset = 1;
+ countset = TRUE;
}
else