xref: /freebsd/contrib/sendmail/libsm/util.c (revision fed1ca4b719c56c930f2259d80663cd34be812bb)
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 **
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 
34 char *
35 str2prt(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 
139 char *
140 quote_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 
222 int
223 dequote_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