NeoMutt  2019-12-07-60-g0cfa53
Teaching an old dog new tricks
DOXYGEN
url.c
Go to the documentation of this file.
1 
29 #include "config.h"
30 #include <ctype.h>
31 #include <string.h>
32 #include "mutt/mutt.h"
33 #include "url.h"
34 #include "mime.h"
35 
39 static const struct Mapping UrlMap[] = {
40  { "file", U_FILE }, { "imap", U_IMAP }, { "imaps", U_IMAPS },
41  { "pop", U_POP }, { "pops", U_POPS }, { "news", U_NNTP },
42  { "snews", U_NNTPS }, { "mailto", U_MAILTO }, { "notmuch", U_NOTMUCH },
43  { "smtp", U_SMTP }, { "smtps", U_SMTPS }, { NULL, U_UNKNOWN },
44 };
45 
53 static int parse_query_string(struct UrlQueryList *list, char *src)
54 {
55  struct UrlQuery *qs = NULL;
56  char *k = NULL, *v = NULL;
57 
58  while (src && *src)
59  {
60  qs = mutt_mem_calloc(1, sizeof(struct UrlQuery));
61  k = strchr(src, '&');
62  if (k)
63  *k = '\0';
64 
65  v = strchr(src, '=');
66  if (v)
67  {
68  *v = '\0';
69  qs->value = v + 1;
70  if (url_pct_decode(qs->value) < 0)
71  {
72  FREE(&qs);
73  return -1;
74  }
75  }
76  qs->name = src;
77  if (url_pct_decode(qs->name) < 0)
78  {
79  FREE(&qs);
80  return -1;
81  }
82  STAILQ_INSERT_TAIL(list, qs, entries);
83 
84  if (!k)
85  break;
86  src = k + 1;
87  }
88  return 0;
89 }
90 
100 int url_pct_decode(char *s)
101 {
102  if (!s)
103  return -1;
104 
105  char *d = NULL;
106 
107  for (d = s; *s; s++)
108  {
109  if (*s == '%')
110  {
111  if ((s[1] != '\0') && (s[2] != '\0') && isxdigit((unsigned char) s[1]) &&
112  isxdigit((unsigned char) s[2]) && (hexval(s[1]) >= 0) && (hexval(s[2]) >= 0))
113  {
114  *d++ = (hexval(s[1]) << 4) | (hexval(s[2]));
115  s += 2;
116  }
117  else
118  return -1;
119  }
120  else
121  *d++ = *s;
122  }
123  *d = '\0';
124  return 0;
125 }
126 
132 enum UrlScheme url_check_scheme(const char *s)
133 {
134  char sbuf[256];
135  char *t = NULL;
136  int i;
137 
138  if (!s || !(t = strchr(s, ':')))
139  return U_UNKNOWN;
140  if ((size_t)(t - s) >= (sizeof(sbuf) - 1))
141  return U_UNKNOWN;
142 
143  mutt_str_strfcpy(sbuf, s, t - s + 1);
144  mutt_str_strlower(sbuf);
145 
146  i = mutt_map_get_value(sbuf, UrlMap);
147  if (i == -1)
148  return U_UNKNOWN;
149 
150  return (enum UrlScheme) i;
151 }
152 
161 struct Url *url_parse(const char *src)
162 {
163  if (!src || !*src)
164  return NULL;
165 
166  enum UrlScheme scheme = url_check_scheme(src);
167  if (scheme == U_UNKNOWN)
168  return NULL;
169 
170  char *p = NULL;
171  size_t srcsize = strlen(src) + 1;
172  struct Url *u = mutt_mem_calloc(1, sizeof(struct Url) + srcsize);
173 
174  u->scheme = scheme;
175  u->user = NULL;
176  u->pass = NULL;
177  u->host = NULL;
178  u->port = 0;
179  u->path = NULL;
181  u->src = (char *) u + sizeof(struct Url);
182  mutt_str_strfcpy(u->src, src, srcsize);
183 
184  char *it = u->src;
185 
186  it = strchr(it, ':') + 1;
187 
188  if (strncmp(it, "//", 2) != 0)
189  {
190  u->path = it;
191  if (url_pct_decode(u->path) < 0)
192  {
193  url_free(&u);
194  }
195  return u;
196  }
197 
198  it += 2;
199 
200  /* We have the length of the string, so let's be fancier than strrchr */
201  for (char *q = u->src + srcsize - 1; q >= it; --q)
202  {
203  if (*q == '?')
204  {
205  *q = '\0';
206  if (parse_query_string(&u->query_strings, q + 1) < 0)
207  {
208  goto err;
209  }
210  break;
211  }
212  }
213 
214  u->path = strchr(it, '/');
215  if (u->path)
216  {
217  *u->path++ = '\0';
218  if (url_pct_decode(u->path) < 0)
219  goto err;
220  }
221 
222  char *at = strrchr(it, '@');
223  if (at)
224  {
225  *at = '\0';
226  p = strchr(it, ':');
227  if (p)
228  {
229  *p = '\0';
230  u->pass = p + 1;
231  if (url_pct_decode(u->pass) < 0)
232  goto err;
233  }
234  u->user = it;
235  if (url_pct_decode(u->user) < 0)
236  goto err;
237  it = at + 1;
238  }
239 
240  /* IPv6 literal address. It may contain colons, so set p to start the port
241  * scan after it. */
242  if ((*it == '[') && (p = strchr(it, ']')))
243  {
244  it++;
245  *p++ = '\0';
246  }
247  else
248  p = it;
249 
250  p = strchr(p, ':');
251  if (p)
252  {
253  int num;
254  *p++ = '\0';
255  if ((mutt_str_atoi(p, &num) < 0) || (num < 0) || (num > 0xffff))
256  goto err;
257  u->port = (unsigned short) num;
258  }
259  else
260  u->port = 0;
261 
262  if (mutt_str_strlen(it) != 0)
263  {
264  u->host = it;
265  if (url_pct_decode(u->host) < 0)
266  goto err;
267  }
268  else if (u->path)
269  {
270  /* No host are provided, we restore the / because this is absolute path */
271  u->path = it;
272  *it++ = '/';
273  }
274 
275  return u;
276 
277 err:
278  url_free(&u);
279  return NULL;
280 }
281 
288 void url_free(struct Url **u)
289 {
290  if (!u || !*u)
291  return;
292 
293  struct UrlQuery *np = STAILQ_FIRST(&(*u)->query_strings);
294  struct UrlQuery *next = NULL;
295  while (np)
296  {
297  next = STAILQ_NEXT(np, entries);
298  /* NOTE(sileht): We don't free members, they will be freed when
299  * the src char* passed to url_parse() is freed */
300  FREE(&np);
301  np = next;
302  }
303  STAILQ_INIT(&(*u)->query_strings);
304  FREE(u);
305 }
306 
315 void url_pct_encode(char *buf, size_t buflen, const char *src)
316 {
317  static const char *hex = "0123456789ABCDEF";
318 
319  if (!buf)
320  return;
321 
322  *buf = '\0';
323  buflen--;
324  while (src && *src && (buflen != 0))
325  {
326  if (strchr("/:&%", *src))
327  {
328  if (buflen < 3)
329  break;
330 
331  *buf++ = '%';
332  *buf++ = hex[(*src >> 4) & 0xf];
333  *buf++ = hex[*src & 0xf];
334  src++;
335  buflen -= 3;
336  continue;
337  }
338  *buf++ = *src++;
339  buflen--;
340  }
341  *buf = '\0';
342 }
343 
352 int url_tobuffer(struct Url *u, struct Buffer *buf, int flags)
353 {
354  if (!u || !buf)
355  return -1;
356  if (u->scheme == U_UNKNOWN)
357  return -1;
358 
359  mutt_buffer_printf(buf, "%s:", mutt_map_get_name(u->scheme, UrlMap));
360 
361  if (u->host)
362  {
363  if (!(flags & U_PATH))
364  mutt_buffer_addstr(buf, "//");
365 
366  if (u->user && (u->user[0] || !(flags & U_PATH)))
367  {
368  char str[256];
369  url_pct_encode(str, sizeof(str), u->user);
370  mutt_buffer_add_printf(buf, "%s@", str);
371  }
372 
373  if (strchr(u->host, ':'))
374  mutt_buffer_add_printf(buf, "[%s]", u->host);
375  else
376  mutt_buffer_add_printf(buf, "%s", u->host);
377 
378  if (u->port)
379  mutt_buffer_add_printf(buf, ":%hu/", u->port);
380  else
381  mutt_buffer_addstr(buf, "/");
382  }
383 
384  if (u->path)
385  mutt_buffer_addstr(buf, u->path);
386 
387  return 0;
388 }
389 
399 int url_tostring(struct Url *u, char *dest, size_t len, int flags)
400 {
401  if (!u || !dest)
402  return -1;
403 
404  struct Buffer *dest_buf = mutt_buffer_pool_get();
405 
406  int retval = url_tobuffer(u, dest_buf, flags);
407  if (retval == 0)
408  mutt_str_strfcpy(dest, mutt_b2s(dest_buf), len);
409 
410  mutt_buffer_pool_release(&dest_buf);
411 
412  return retval;
413 }
char * name
Query name.
Definition: url.h:57
Url is notmuch://.
Definition: url.h:45
int url_pct_decode(char *s)
Decode a percent-encoded string.
Definition: url.c:100
void * mutt_mem_calloc(size_t nmemb, size_t size)
Allocate zeroed memory on the heap.
Definition: memory.c:50
int mutt_str_atoi(const char *str, int *dst)
Convert ASCII string to an integer.
Definition: string.c:262
char * pass
Password.
Definition: url.h:70
struct Buffer * mutt_buffer_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:101
A parsed URL proto://user:password@host:port/path?a=1&b=2
Definition: url.h:66
enum UrlScheme scheme
Scheme, e.g. U_SMTPS.
Definition: url.h:68
void mutt_buffer_pool_release(struct Buffer **pbuf)
Free a Buffer from the pool.
Definition: pool.c:112
Url is imaps://.
Definition: url.h:39
String manipulation buffer.
Definition: buffer.h:33
Parsed Query String.
Definition: url.h:55
Url wasn&#39;t recognised.
Definition: url.h:34
Url is imap://.
Definition: url.h:38
#define STAILQ_INSERT_TAIL(head, elm, field)
Definition: queue.h:386
size_t mutt_str_strlen(const char *a)
Calculate the length of a string, safely.
Definition: string.c:666
int url_tostring(struct Url *u, char *dest, size_t len, int flags)
Output the URL string for a given Url object.
Definition: url.c:399
char * value
Query value.
Definition: url.h:58
int mutt_buffer_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:160
enum UrlScheme url_check_scheme(const char *s)
Check the protocol of a URL.
Definition: url.c:132
const char * mutt_map_get_name(int val, const struct Mapping *map)
Lookup a string for a constant.
Definition: mapping.c:42
char * mutt_str_strlower(char *s)
convert all characters in the string to lowercase
Definition: string.c:509
#define STAILQ_INIT(head)
Definition: queue.h:369
UrlScheme
All recognised Url types.
Definition: url.h:32
int mutt_buffer_add_printf(struct Buffer *buf, const char *fmt,...)
Format a string appending a Buffer.
Definition: buffer.c:203
int url_tobuffer(struct Url *u, struct Buffer *buf, int flags)
Output the URL string for a given Url object.
Definition: url.c:352
Url is nntps://.
Definition: url.h:41
size_t mutt_buffer_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition: buffer.c:225
Constants and macros for managing MIME encoding.
#define mutt_b2s(buf)
Definition: buffer.h:41
void url_pct_encode(char *buf, size_t buflen, const char *src)
Percent-encode a string.
Definition: url.c:315
struct UrlQueryList query_strings
List of query strings.
Definition: url.h:74
char * user
Username.
Definition: url.h:69
Url is smtps://.
Definition: url.h:43
size_t mutt_str_strfcpy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination)
Definition: string.c:750
char * host
Host.
Definition: url.h:71
#define hexval(ch)
Definition: mime.h:75
#define STAILQ_NEXT(elm, field)
Definition: queue.h:397
static int parse_query_string(struct UrlQueryList *list, char *src)
Parse a URL query string.
Definition: url.c:53
char * path
Path.
Definition: url.h:73
Url is pop://.
Definition: url.h:36
unsigned short port
Port.
Definition: url.h:72
Url is nntp://.
Definition: url.h:40
void url_free(struct Url **u)
Free the contents of a URL.
Definition: url.c:288
#define FREE(x)
Definition: memory.h:40
Url is smtp://.
Definition: url.h:42
Mapping between user-readable string and a constant.
Definition: mapping.h:29
Url is mailto://.
Definition: url.h:44
#define STAILQ_FIRST(head)
Definition: queue.h:347
Parse and identify different URL schemes.
char * src
Raw URL string.
Definition: url.h:75
Url is file://.
Definition: url.h:35
#define U_PATH
Definition: url.h:48
Url is pops://.
Definition: url.h:37
struct Url * url_parse(const char *src)
Fill in Url.
Definition: url.c:161
int mutt_map_get_value(const char *name, const struct Mapping *map)
Lookup the constant for a string.
Definition: mapping.c:61