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