NeoMutt  2020-06-26-30-g76c339
Teaching an old dog new tricks
DOXYGEN
url_parse.c
Go to the documentation of this file.
1 
23 #define TEST_NO_MAIN
24 #include "config.h"
25 #include "acutest.h"
26 #include "mutt/lib.h"
27 #include "address/lib.h"
28 #include "email/lib.h"
29 
30 struct UrlTest
31 {
32  const char *source; // source URL to parse
33  bool valid; // expected validity
34  struct Url url; // expected resulting URL
35  const char *qs_elem; // expected elements of the query string, separated
36  // and terminated by a pipe '|' character
37 };
38 
39 // clang-format off
40 static struct UrlTest test[] = {
41  {
42  "mailto:mail@example.com",
43  true,
44  {
45  U_MAILTO,
46  NULL,
47  NULL,
48  NULL,
49  0,
50  "mail@example.com"
51  },
52  NULL
53  },
54  {
55  "mailto:mail@example.com?subject=see%20this&cc=me%40example.com",
56  true,
57  {
58  U_MAILTO,
59  NULL,
60  NULL,
61  NULL,
62  0,
63  "mail@example.com"
64  },
65  "subject|see this|cc|me@example.com|"
66  },
67  {
68  "foobar foobar",
69  false,
70  },
71  {
72  "imaps://foouser:foopass@imap.example.com:456",
73  true,
74  {
75  U_IMAPS,
76  "foouser",
77  "foopass",
78  "imap.example.com",
79  456,
80  NULL
81  },
82  NULL
83  },
84  {
85  "SmTp://user@example.com", /* scheme is lower-cased */
86  true,
87  {
88  U_SMTP,
89  "user",
90  NULL,
91  "example.com",
92  0,
93  NULL
94  },
95  NULL
96  },
97  {
98  "pop://user@example.com@pop.example.com:234/some/where?encoding=binary"
99  "&second=third&some%20space=%22quoted%20content%22",
100  true,
101  {
102  U_POP,
103  "user@example.com",
104  NULL,
105  "pop.example.com",
106  234,
107  "some/where",
108  },
109  "encoding|binary|second|third|some space|\"quoted content\"|"
110  },
111  {
112  "snews://user@[2000:4860:0:2001::68]:563",
113  true,
114  {
115  U_NNTPS,
116  "user",
117  NULL,
118  "2000:4860:0:2001::68",
119  563
120  }
121  },
122  {
123  "notmuch:///Users/bob/.mail/gmail?type=messages&query=tag%3Ainbox",
124  true,
125  {
126  U_NOTMUCH,
127  NULL,
128  NULL,
129  NULL,
130  0,
131  "/Users/bob/.mail/gmail"
132  },
133  "type|messages|query|tag:inbox|",
134  },
135  {
136  "imaps://gmail.com/[GMail]/Sent messages",
137  true,
138  {
139  U_IMAPS,
140  NULL,
141  NULL,
142  "gmail.com",
143  0,
144  "[GMail]/Sent messages"
145  }
146  },
147  {
148  /* Invalid fragment (#) character, see also
149  * https://github.com/neomutt/neomutt/issues/2276 */
150  "mailto:a@b?subject=#",
151  false,
152  },
153  {
154  /* Correctly escaped fragment (#) chracter, see also
155  * https://github.com/neomutt/neomutt/issues/2276 */
156  "mailto:a@b?subject=%23",
157  true,
158  {
159  U_MAILTO,
160  NULL,
161  NULL,
162  NULL,
163  0,
164  "a@b"
165  },
166  "subject|#|"
167  },
168  {
169  /* UTF-8 mailbox name */
170  "imaps://foobar@gmail.com@imap.gmail.com/Отправленные письма",
171  true,
172  {
173  U_IMAPS,
174  "foobar@gmail.com",
175  NULL,
176  "imap.gmail.com",
177  0,
178  "Отправленные письма"
179  }
180  },
181  {
182  /* Notmuch queries */
183  "notmuch://?query=folder:\"[Gmail]/Sent Mail\"",
184  true,
185  {
186  U_NOTMUCH
187  },
188  "query|folder:\"[Gmail]/Sent Mail\"|"
189  }
190 };
191 // clang-format on
192 
193 void check_query_string(const char *exp, const struct UrlQueryList *act)
194 {
195  char *next = NULL;
196  char tmp[64] = { 0 };
197  const struct UrlQuery *np = STAILQ_FIRST(act);
198  while (exp && *exp)
199  {
200  next = strchr(exp, '|');
201  mutt_str_copy(tmp, exp, next - exp + 1);
202  exp = next + 1;
203  if (!TEST_CHECK(strcmp(tmp, np->name) == 0))
204  {
205  TEST_MSG("Expected: <%s>", tmp);
206  TEST_MSG("Actual : <%s>", np->name);
207  }
208 
209  next = strchr(exp, '|');
210  mutt_str_copy(tmp, exp, next - exp + 1);
211  exp = next + 1;
212  if (!TEST_CHECK(strcmp(tmp, np->value) == 0))
213  {
214  TEST_MSG("Expected: <%s>", tmp);
215  TEST_MSG("Actual : <%s>", np->value);
216  }
217 
218  np = STAILQ_NEXT(np, entries);
219  }
220 
221  if (!TEST_CHECK(np == NULL))
222  {
223  TEST_MSG("Expected: NULL");
224  TEST_MSG("Actual : (%s, %s)", np->name, np->value);
225  }
226 }
227 
228 void test_url_parse(void)
229 {
230  // struct Url *url_parse(const char *src);
231 
232  {
233  TEST_CHECK(!url_parse(NULL));
234  }
235 
236  {
237  for (size_t i = 0; i < mutt_array_size(test); i++)
238  {
239  TEST_CASE(test[i].source);
240  struct Url *url = url_parse(test[i].source);
241  if (!TEST_CHECK(!((!!url) ^ (!!test[i].valid))))
242  {
243  TEST_MSG("Expected: %sNULL", test[i].valid ? "not " : "");
244  TEST_MSG("Actual : %sNULL", url ? "not " : "");
245  }
246 
247  if (!url)
248  continue;
249 
250  if (!TEST_CHECK(test[i].url.scheme == url->scheme))
251  {
252  TEST_MSG("Expected: %d", test[i].url.scheme);
253  TEST_MSG("Actual : %d", url->scheme);
254  }
255  if (!TEST_CHECK(mutt_str_equal(test[i].url.user, url->user)))
256  {
257  TEST_MSG("Expected: %s", test[i].url.user);
258  TEST_MSG("Actual : %s", url->user);
259  }
260  if (!TEST_CHECK(mutt_str_equal(test[i].url.pass, url->pass)))
261  {
262  TEST_MSG("Expected: %s", test[i].url.pass);
263  TEST_MSG("Actual : %s", url->pass);
264  }
265  if (!TEST_CHECK(mutt_str_equal(test[i].url.host, url->host)))
266  {
267  TEST_MSG("Expected: %s", test[i].url.host);
268  TEST_MSG("Actual : %s", url->host);
269  }
270  if (!TEST_CHECK(test[i].url.port == url->port))
271  {
272  TEST_MSG("Expected: %hu", test[i].url.port);
273  TEST_MSG("Actual : %hu", url->port);
274  }
275  if (!TEST_CHECK(mutt_str_equal(test[i].url.path, url->path)))
276  {
277  TEST_MSG("Expected: %s", test[i].url.path);
278  TEST_MSG("Actual : %s", url->path);
279  }
280  check_query_string(test[i].qs_elem, &url->query_strings);
281 
282  url_free(&url);
283  }
284  }
285 
286  {
287  /* Test automatically generated URLs */
288  const char *const al[] = { "imap", "imaps" };
289  const char *const bl[] = { "", "user@", "user@host.com@", "user:pass@" };
290  const char *const cl[] = { "host.com", "[12AB::EF89]", "127.0.0.1" };
291  const char *const dl[] = { "", ":123" };
292  const char *const el[] = { "", "/", "/path", "/path/one/two", "/path.one.two" };
293  for (size_t a = 0; a < mutt_array_size(al); a++)
294  {
295  for (size_t b = 0; b < mutt_array_size(bl); b++)
296  {
297  for (size_t c = 0; c < mutt_array_size(cl); c++)
298  {
299  for (size_t d = 0; d < mutt_array_size(dl); d++)
300  {
301  for (size_t e = 0; e < mutt_array_size(el); e++)
302  {
303  char s[1024];
304  snprintf(s, sizeof(s), "%s://%s%s%s%s", al[a], bl[b], cl[c], dl[d], el[e]);
305  TEST_CASE(s);
306  struct Url *u = url_parse(s);
307  if (!TEST_CHECK(u != NULL))
308  {
309  TEST_MSG("Expected: parsed <%s>", s);
310  TEST_MSG("Actual: NULL");
311  }
312  url_free(&u);
313  }
314  }
315  }
316  }
317  }
318  }
319 }
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:879
char * name
Query name.
Definition: url.h:57
Url is notmuch://.
Definition: url.h:45
char * pass
Password.
Definition: url.h:70
void check_query_string(const char *exp, const struct UrlQueryList *act)
Definition: url_parse.c:193
Structs that make up an email.
A parsed URL proto://user:password@host:port/path?a=1&b=2
Definition: url.h:66
struct Url url
Definition: url_parse.c:34
enum UrlScheme scheme
Scheme, e.g. U_SMTPS.
Definition: url.h:68
Url is imaps://.
Definition: url.h:39
Parsed Query String.
Definition: url.h:55
#define TEST_MSG(...)
Definition: acutest.h:214
#define TEST_CHECK(cond)
Definition: acutest.h:83
char * value
Query value.
Definition: url.h:58
void url_free(struct Url **ptr)
Free the contents of a URL.
Definition: url.c:123
Email Address Handling.
#define mutt_array_size(x)
Definition: memory.h:33
Url is nntps://.
Definition: url.h:41
struct UrlQueryList query_strings
List of query strings.
Definition: url.h:74
char * user
Username.
Definition: url.h:69
#define TEST_CASE(name)
Definition: acutest.h:182
const char * qs_elem
Definition: url_parse.c:35
bool valid
Definition: url_parse.c:33
char * host
Host.
Definition: url.h:71
void test_url_parse(void)
Definition: url_parse.c:228
#define STAILQ_NEXT(elm, field)
Definition: queue.h:397
char * path
Path.
Definition: url.h:73
Url is pop://.
Definition: url.h:36
unsigned short port
Port.
Definition: url.h:72
size_t mutt_str_copy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination)
Definition: string.c:724
Url is smtp://.
Definition: url.h:42
Url is mailto://.
Definition: url.h:44
const char * source
Definition: url_parse.c:32
Convenience wrapper for the library headers.
#define STAILQ_FIRST(head)
Definition: queue.h:347
struct Url * url_parse(const char *src)
Fill in Url.
Definition: url.c:234