1/*
2 * Copyright (c) 2006 Proofpoint, Inc. and its suppliers.
3 *	All rights reserved.
4 *
5 * By using this file, you agree to the terms and conditions set
6 * forth in the LICENSE file which can be found at the top level of
7 * the sendmail distribution.
8 *
9 */
10
11#include <sm/gen.h>
12
13SM_RCSID("@(#)$Id: util.c,v 1.10 2013-11-22 20:51:44 ca Exp $")
14#include <sm/setjmp.h>
15#include <sm/conf.h>
16#include <sm/assert.h>
17#include <sm/heap.h>
18#include <sm/string.h>
19#include <sm/sendmail.h>
20#include <ctype.h>
21
22/*
23**  STR2PRT -- convert "unprintable" characters in a string to \oct
24**
25**	Parameters:
26**		s -- string to convert
27**
28**	Returns:
29**		converted string.
30**		This is a static local buffer, string must be copied
31**		before this function is called again!
32*/
33
34char *
35str2prt(s)
36	char *s;
37{
38	int l;
39	char c, *h;
40	bool ok;
41	static int len = 0;
42	static char *buf = NULL;
43
44	if (s == NULL)
45		return NULL;
46	ok = true;
47	for (h = s, l = 1; *h != '\0'; h++, l++)
48	{
49		if (*h == '\\')
50		{
51			++l;
52			ok = false;
53		}
54		else if (!(isascii(*h) && isprint(*h)))
55		{
56			l += 3;
57			ok = false;
58		}
59	}
60	if (ok)
61		return s;
62	if (l > len)
63	{
64		char *nbuf = sm_pmalloc_x(l);
65
66		if (buf != NULL)
67			sm_free(buf);
68		len = l;
69		buf = nbuf;
70	}
71	for (h = buf; *s != '\0' && l > 0; s++, l--)
72	{
73		c = *s;
74		if (isascii(c) && isprint(c) && c != '\\')
75		{
76			*h++ = c;
77		}
78		else
79		{
80			*h++ = '\\';
81			--l;
82			switch (c)
83			{
84			  case '\\':
85				*h++ = '\\';
86				break;
87			  case '\t':
88				*h++ = 't';
89				break;
90			  case '\n':
91				*h++ = 'n';
92				break;
93			  case '\r':
94				*h++ = 'r';
95				break;
96			  default:
97				SM_ASSERT(l >= 2);
98				(void) sm_snprintf(h, l, "%03o",
99					(unsigned int)((unsigned char) c));
100
101				/*
102				**  XXX since l is unsigned this may
103				**  wrap around if the calculation is screwed
104				**  up...
105				*/
106
107				l -= 2;
108				h += 3;
109				break;
110			}
111		}
112	}
113	*h = '\0';
114	buf[len - 1] = '\0';
115	return buf;
116}
117
118/*
119**  QUOTE_INTERNAL_CHARS -- do quoting of internal characters
120**
121**	Necessary to make sure that we don't have metacharacters such
122**	as the internal versions of "$*" or "$&" in a string.
123**	The input and output pointers can be the same.
124**
125**	Parameters:
126**		ibp -- a pointer to the string to translate
127**		obp -- a pointer to an output buffer
128**		bsp -- pointer to the length of the output buffer
129**
130**	Returns:
131**		A possibly new bp (if the buffer needed to grow); if
132**		it is different, *bsp will updated to the size of
133**		the new buffer and the caller is responsible for
134**		freeing the memory.
135*/
136
137#define SM_MM_QUOTE(ch) (((ch) & 0377) == METAQUOTE || (((ch) & 0340) == 0200))
138
139char *
140quote_internal_chars(ibp, obp, bsp)
141	char *ibp;
142	char *obp;
143	int *bsp;
144{
145	char *ip, *op;
146	int bufused, olen;
147	bool buffer_same, needs_quoting;
148
149	buffer_same = ibp == obp;
150	needs_quoting = false;
151
152	/* determine length of output string (starts at 1 for trailing '\0') */
153	for (ip = ibp, olen = 1; *ip != '\0'; ip++, olen++)
154	{
155		if (SM_MM_QUOTE(*ip))
156		{
157			olen++;
158			needs_quoting = true;
159		}
160	}
161
162	/* is the output buffer big enough? */
163	if (olen > *bsp)
164	{
165		obp = sm_malloc_x(olen);
166		buffer_same = false;
167		*bsp = olen;
168	}
169
170	/*
171	**  shortcut: no change needed?
172	**  Note: we don't check this first as some bozo may use the same
173	**  buffers but restrict the size of the output buffer to less
174	**  than the length of the input buffer in which case we need to
175	**  allocate a new buffer.
176	*/
177
178	if (!needs_quoting)
179	{
180		if (!buffer_same)
181		{
182			bufused = sm_strlcpy(obp, ibp, *bsp);
183			SM_ASSERT(bufused <= olen);
184		}
185		return obp;
186	}
187
188	if (buffer_same)
189	{
190		obp = sm_malloc_x(olen);
191		buffer_same = false;
192		*bsp = olen;
193	}
194
195	for (ip = ibp, op = obp, bufused = 0; *ip != '\0'; ip++)
196	{
197		if (SM_MM_QUOTE(*ip))
198		{
199			SM_ASSERT(bufused < olen);
200			op[bufused++] = METAQUOTE;
201		}
202		SM_ASSERT(bufused < olen);
203		op[bufused++] = *ip;
204	}
205	op[bufused] = '\0';
206	return obp;
207}
208
209/*
210**  DEQUOTE_INTERNAL_CHARS -- undo the effect of quote_internal_chars
211**
212**	Parameters:
213**		ibp -- a pointer to the string to be translated.
214**		obp -- a pointer to the output buffer.  Can be the
215**			same as ibp.
216**		obs -- the size of the output buffer.
217**
218**	Returns:
219**		number of character added to obp
220*/
221
222int
223dequote_internal_chars(ibp, obp, obs)
224	char *ibp;
225	char *obp;
226	int obs;
227{
228	char *ip, *op;
229	int len;
230	bool quoted;
231
232	quoted = false;
233	len = 0;
234	for (ip = ibp, op = obp; *ip != '\0'; ip++)
235	{
236		if ((*ip & 0377) == METAQUOTE && !quoted)
237		{
238			quoted = true;
239			continue;
240		}
241		if (op < &obp[obs - 1])
242		{
243			*op++ = *ip;
244			++len;
245		}
246		quoted = false;
247	}
248	*op = '\0';
249	return len;
250}
251