xref: /illumos-gate/usr/src/cmd/sendmail/libsm/util.c (revision 058561cb)
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 
15 SM_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 
36 char *
37 str2prt(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 
141 char *
142 quote_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 
224 int
225 dequote_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