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
quote_internal_chars_tagged(ibp,obp,bsp,rpool,tag,line,group)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
dequote_internal_chars(ibp,obp,obs)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