1/*
2 * Copyright (c) 2006, 2023 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#include <sm/gen.h>
11SM_IDSTR(id, "@(#)$Id: t-qic.c,v 1.10 2013-11-22 20:51:43 ca Exp $")
12
13#include <stdio.h>
14#include <sm/sendmail.h>
15#include <sm/assert.h>
16#include <sm/heap.h>
17#include <sm/string.h>
18#include <sm/test.h>
19
20extern bool SmTestVerbose;
21
22void
23show_diff(s1, s2)
24	const char *s1;
25	const char *s2;
26{
27	int i;
28
29	for (i = 0; s1[i] != '\0' && s2[i] != '\0'; i++)
30	{
31		if (s1[i] != s2[i])
32		{
33			fprintf(stderr, "i=%d, s1[]=%u, s2[]=%u\n",
34				i, (unsigned char) s1[i],
35				(unsigned char) s2[i]);
36			return;
37		}
38	}
39	if (s1[i] != s2[i])
40	{
41		fprintf(stderr, "i=%d, s1[]=%u, s2[]=%u\n",
42			i, (unsigned char) s1[i], (unsigned char) s2[i]);
43	}
44}
45
46char *quote_unquote __P((char *, char *, int, int, int));
47
48char *
49quote_unquote(in, out, outlen, exp, mode)
50	char *in;
51	char *out;
52	int outlen;
53	int exp;
54	int mode;
55{
56	char *obp, *bp;
57	char line_back[1024];
58	char line_in[1024];
59	int cmp;
60
61	sm_strlcpy(line_in, in, sizeof(line_in));
62	obp = quote_internal_chars(in, out, &outlen, NULL);
63	bp = str2prt(line_in);
64	if (0 == mode)
65		dequote_internal_chars(obp, line_back, sizeof(line_back));
66	else if (1 == mode)
67		dequote_internal_chars(obp, line_back, strlen(obp));
68	else if (2 == mode)
69		dequote_internal_chars(obp, line_back, strlen(obp) + 1);
70	cmp = strcmp(line_in, line_back);
71	SM_TEST(exp == cmp);
72	if (cmp != exp && !SmTestVerbose)
73	{
74		fprintf(stderr, "in: %s\n", bp);
75		bp = str2prt(line_back);
76		fprintf(stderr, "out:%s\n", bp);
77		fprintf(stderr, "cmp=%d\n", cmp);
78		show_diff(in, line_back);
79	}
80	if (SmTestVerbose)
81	{
82		fprintf(stderr, "%s -> ", bp);
83		bp = str2prt(obp);
84		fprintf(stderr, "%s\n", bp);
85		fprintf(stderr, "cmp=%d\n", cmp);
86	}
87	return obp;
88}
89
90struct sm_qic_S
91{
92	char		*qic_in;
93	char		*qic_out;
94	int		 qic_exp;
95};
96
97typedef struct sm_qic_S sm_qic_T;
98
99
100int
101main(argc, argv)
102	int argc;
103	char *argv[];
104{
105	char line_in[1024], line[256], line_out[32], *obp;
106	int i, los, cmp, mode;
107	sm_qic_T inout[] = {
108		  { "", "",	0 }
109		, { "\t", "\t",	0 }
110		, { "\tuser", "\tuser",	0 }
111		, { "abcdef", "abcdef",	0 }
112		, { "01234567890123456789", "01234567890123456789",	0 }
113		, { "\\", "\\",	0 }
114		, { "\\A", "\\A",	0 }
115		, { "01234567890123456789\001", "01234567890123456789\001",
116			0 }
117		, { "012345\2067890123456789", "012345\377\2067890123456789",
118			0 }
119		, { "\377", "\377\377",	0 }
120		, { "\240", "\240",	0 }
121		, { "\220", "\377\220",	0 }
122		, { "\240\220", "\240\377\220",	0 }
123		, { "\377\377", "\377\377\377\377",	0 }
124		, { "\377a\377b", "\377\377a\377\377b",	0 }
125		, { "\376a\377b", "\376a\377\377b",	0 }
126		, { "\200\201\202\203\204\205\206\207\210\211\212\213\214\215\216\217\220\221\222\223\224\225\226\227\230\231\232\233\234\235\236\237\240",
127		    "\377\200\377\201\377\202\377\203\377\204\377\205\377\206\377\207\377\210\377\211\377\212\377\213\377\214\377\215\377\216\377\217\377\220\377\221\377\222\377\223\377\224\377\225\377\226\377\227\377\230\377\231\377\232\377\233\377\234\377\235\377\236\377\237\240",
128			0 }
129		, { NULL, NULL,	0 }
130	};
131
132	sm_test_begin(argc, argv, "test meta quoting");
133	mode = 0;
134	if (argc > 1)
135		mode = atoi(argv[1]);
136	for (i = 0; i < sizeof(line_out); i++)
137		line_out[i] = '\0';
138	for (i = 0; i < sizeof(line_in); i++)
139		line_in[i] = '\0';
140	for (i = 0; i < sizeof(line_in) / 2; i++)
141	{
142		char ch;
143
144		ch = 0200 + i;
145		if ('\0' == ch)
146			ch = '0';
147		line_in[i] = ch;
148	}
149	los = sizeof(line_out) / 2;
150	obp = quote_unquote(line_in, line_out, los, 0, mode);
151	if (obp != line_out)
152		SM_FREE(obp);
153
154	for (i = 0; i < sizeof(line_in); i++)
155		line_in[i] = '\0';
156	for (i = 0; i < sizeof(line_in) / 2; i++)
157	{
158		char ch;
159
160		ch = 0200 + i;
161		if ('\0' == ch)
162			ch = '0';
163		line_in[i] = ch;
164	}
165	los = sizeof(line_in);
166	obp = quote_unquote(line_in, line_in, los, 0, mode);
167	if (obp != line_in)
168		SM_FREE(obp);
169
170	for (i = 0; inout[i].qic_in != NULL; i++)
171	{
172		los = sizeof(line_out) / 2;
173		obp = quote_unquote(inout[i].qic_in, line_out, los,
174				inout[i].qic_exp, mode);
175		cmp = strcmp(inout[i].qic_out, obp);
176		SM_TEST(inout[i].qic_exp == cmp);
177		if (inout[i].qic_exp != cmp && !SmTestVerbose)
178		{
179			char *bp;
180
181			bp = str2prt(obp);
182			fprintf(stderr, "got: %s\n", bp);
183			bp = str2prt(inout[i].qic_out);
184			fprintf(stderr, "exp:%s\n", bp);
185			fprintf(stderr, "cmp=%d\n", cmp);
186			show_diff(inout[i].qic_in, inout[i].qic_out);
187		}
188		if (obp != line_out)
189			SM_FREE(obp);
190	}
191
192	/* use same buffer for in and out */
193	for (i = 0; inout[i].qic_in != NULL; i++)
194	{
195		bool same;
196
197		same = strcmp(inout[i].qic_in, inout[i].qic_out) == 0;
198		los = sm_strlcpy(line, inout[i].qic_in, sizeof(line));
199		SM_TEST(los + 1 < sizeof(line));
200		++los;
201		obp = quote_unquote(line, line, los, inout[i].qic_exp, mode);
202		cmp = strcmp(inout[i].qic_out, obp);
203		SM_TEST(inout[i].qic_exp == cmp);
204		if (inout[i].qic_exp != cmp && !SmTestVerbose)
205		{
206			char *bp;
207
208			bp = str2prt(obp);
209			fprintf(stderr, "got: %s\n", bp);
210			bp = str2prt(inout[i].qic_out);
211			fprintf(stderr, "exp:%s\n", bp);
212			fprintf(stderr, "cmp=%d\n", cmp);
213			show_diff(inout[i].qic_in, inout[i].qic_out);
214		}
215		if (obp != line)
216		{
217			SM_TEST(!same);
218			if (same)
219				show_diff(obp, inout[i].qic_out);
220			SM_FREE(obp);
221		}
222	}
223
224	/* use NULL buffer for out */
225	for (i = 0; inout[i].qic_in != NULL; i++)
226	{
227		los = 0;
228		obp = quote_unquote(inout[i].qic_in, NULL, los,
229				inout[i].qic_exp, mode);
230		SM_TEST(obp != NULL);
231		cmp = strcmp(inout[i].qic_out, obp);
232		SM_TEST(inout[i].qic_exp == cmp);
233		if (inout[i].qic_exp != cmp && !SmTestVerbose)
234		{
235			char *bp;
236
237			bp = str2prt(obp);
238			fprintf(stderr, "got: %s\n", bp);
239			bp = str2prt(inout[i].qic_out);
240			fprintf(stderr, "exp:%s\n", bp);
241			fprintf(stderr, "cmp=%d\n", cmp);
242			show_diff(inout[i].qic_in, inout[i].qic_out);
243		}
244	}
245
246	los = -1;
247	obp = quote_internal_chars(NULL, NULL, &los, NULL);
248	SM_TEST(NULL == obp);
249	SM_TEST(-1 == los);
250
251	sm_strlcpy(line_in, "nothing", sizeof(line_in));
252	los = -123;
253	obp = quote_internal_chars(line_in, NULL, &los, NULL);
254	SM_TEST(NULL != obp);
255	SM_TEST(los > 0);
256
257	return sm_test_end();
258}
259