xref: /freebsd/contrib/sendmail/libsm/util.c (revision e0c4386e)
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 
13 SM_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 **		(except for some special chars, see below)
25 **
26 **	Parameters:
27 **		s -- string to convert [A]
28 **
29 **	Returns:
30 **		converted string [S][U]
31 **		This is a static local buffer, string must be copied
32 **		before this function is called again!
33 */
34 
35 char *
36 str2prt(s)
37 	char *s;
38 {
39 	int l;
40 	char c, *h;
41 	bool ok;
42 	static int len = 0;
43 	static char *buf = NULL;
44 
45 #if _FFR_LOGASIS >= 1
46 #define BADCHAR(ch)	((unsigned char)(ch) <= 31)
47 #else
48 #define BADCHAR(ch)	(!(isascii(ch) && isprint(ch)))
49 #endif
50 
51 	if (s == NULL)
52 		return NULL;
53 	ok = true;
54 	for (h = s, l = 1; *h != '\0'; h++, l++)
55 	{
56 		if (*h == '\\')
57 		{
58 			++l;
59 			ok = false;
60 		}
61 		else if (BADCHAR(*h))
62 		{
63 			l += 3;
64 			ok = false;
65 		}
66 	}
67 	if (ok)
68 		return s;
69 	if (l > len)
70 	{
71 		char *nbuf = sm_pmalloc_x(l);
72 
73 		if (buf != NULL)
74 			sm_free(buf);
75 		len = l;
76 		buf = nbuf;
77 	}
78 	for (h = buf; *s != '\0' && l > 0; s++, l--)
79 	{
80 		c = *s;
81 		if (c != '\\' && !BADCHAR(c))
82 		{
83 			*h++ = c;
84 		}
85 		else
86 		{
87 			*h++ = '\\';
88 			--l;
89 			switch (c)
90 			{
91 			  case '\\':
92 				*h++ = '\\';
93 				break;
94 			  case '\t':
95 				*h++ = 't';
96 				break;
97 			  case '\n':
98 				*h++ = 'n';
99 				break;
100 			  case '\r':
101 				*h++ = 'r';
102 				break;
103 			  default:
104 				SM_ASSERT(l >= 2);
105 				(void) sm_snprintf(h, l, "%03o",
106 					(unsigned int)((unsigned char) c));
107 
108 				/*
109 				**  XXX since l is unsigned this may wrap
110 				**  around if the calculation is screwed up...
111 				*/
112 
113 				l -= 2;
114 				h += 3;
115 				break;
116 			}
117 		}
118 	}
119 	*h = '\0';
120 	buf[len - 1] = '\0';
121 	return buf;
122 }
123 
124 /*
125 **  QUOTE_INTERNAL_CHARS -- do quoting of internal characters
126 **
127 **	Necessary to make sure that we don't have metacharacters such
128 **	as the internal versions of "$*" or "$&" in a string.
129 **	The input and output pointers can be the same.
130 **
131 **	Parameters:
132 **		ibp -- a pointer to the string to translate [x]
133 **		obp -- a pointer to an output buffer [i][m:A]
134 **		bsp -- pointer to the length of the output buffer
135 **		rpool -- rpool for allocations
136 **
137 **	Returns:
138 **		A possibly new obp (if the buffer needed to grow);
139 **		if it is different, *bsp will updated to the size of
140 **		the new buffer and the caller is responsible for
141 **		freeing the memory.
142 */
143 
144 #define SM_MM_QUOTE(ch) (((ch) & 0377) == METAQUOTE || (((ch) & 0340) == 0200))
145 
146 char *
147 #if SM_HEAP_CHECK > 2
148 quote_internal_chars_tagged
149 #else
150 quote_internal_chars
151 #endif
152 	(ibp, obp, bsp, rpool
153 #if SM_HEAP_CHECK > 2
154 	, tag, line, group
155 #endif
156 	)
157 	char *ibp;
158 	char *obp;
159 	int *bsp;
160 	SM_RPOOL_T *rpool;
161 #if SM_HEAP_CHECK > 2
162 	char *tag;
163 	int line;
164 	int group;
165 #else
166 # define tag  "quote_internal_chars"
167 # define line 1
168 # define group 1
169 #endif
170 {
171 	char *ip, *op;
172 	int bufused, olen;
173 	bool buffer_same, needs_quoting;
174 
175 	if (NULL == ibp)
176 		return NULL;
177 	buffer_same = ibp == obp;
178 	SM_REQUIRE(NULL != bsp);
179 	if (NULL == obp)
180 		*bsp = 0;
181 	needs_quoting = false;
182 
183 	/* determine length of output string (starts at 1 for trailing '\0') */
184 	for (ip = ibp, olen = 1; *ip != '\0'; ip++, olen++)
185 	{
186 		if (SM_MM_QUOTE(*ip))
187 		{
188 			olen++;
189 			needs_quoting = true;
190 		}
191 	}
192 
193 	/* is the output buffer big enough? */
194 	if (olen > *bsp)
195 	{
196 		obp = sm_rpool_malloc_tagged_x(rpool, olen, tag, line, group);
197 		buffer_same = false;
198 		*bsp = olen;
199 	}
200 
201 	/*
202 	**  shortcut: no change needed?
203 	**  Note: we don't check this first as some bozo may use the same
204 	**  buffers but restrict the size of the output buffer to less
205 	**  than the length of the input buffer in which case we need to
206 	**  allocate a new buffer.
207 	*/
208 
209 	if (!needs_quoting)
210 	{
211 		if (!buffer_same)
212 		{
213 			bufused = sm_strlcpy(obp, ibp, *bsp);
214 			SM_ASSERT(bufused <= olen);
215 		}
216 		return obp;
217 	}
218 
219 	if (buffer_same)
220 	{
221 		obp = sm_malloc_tagged_x(olen, tag, line + 1, group);
222 		buffer_same = false;
223 		*bsp = olen;
224 	}
225 
226 	for (ip = ibp, op = obp, bufused = 0; *ip != '\0'; ip++)
227 	{
228 		if (SM_MM_QUOTE(*ip))
229 		{
230 			SM_ASSERT(bufused < olen);
231 			op[bufused++] = METAQUOTE;
232 		}
233 		SM_ASSERT(bufused < olen);
234 		op[bufused++] = *ip;
235 	}
236 	op[bufused] = '\0';
237 	return obp;
238 }
239 #if SM_HEAP_CHECK <= 2
240 # undef tag
241 # undef line
242 # undef group
243 #endif
244 
245 /*
246 **  DEQUOTE_INTERNAL_CHARS -- undo the effect of quote_internal_chars
247 **
248 **	Parameters:
249 **		ibp -- a pointer to the string to be translated. [i]
250 **		obp -- a pointer to the output buffer. [x]
251 **			Can be the same as ibp.
252 **		obs -- the size of the output buffer.
253 **
254 **	Returns:
255 **		number of character added to obp
256 */
257 
258 int
259 dequote_internal_chars(ibp, obp, obs)
260 	char *ibp;
261 	char *obp;
262 	int obs;
263 {
264 	char *ip, *op;
265 	int len;
266 	bool quoted;
267 
268 	quoted = false;
269 	len = 0;
270 	for (ip = ibp, op = obp; *ip != '\0'; ip++)
271 	{
272 		if ((*ip & 0377) == METAQUOTE && !quoted)
273 		{
274 			quoted = true;
275 			continue;
276 		}
277 		if (op < &obp[obs - 1])
278 		{
279 			*op++ = *ip;
280 			++len;
281 		}
282 		quoted = false;
283 	}
284 	*op = '\0';
285 	return len;
286 }
287