player: osd: fix zoom
[vlc.git] / src / text / url.c
blobe530da0075f11917b2269c769e6381554911f581
1 /*****************************************************************************
2 * url.c: URL related functions
3 *****************************************************************************
4 * Copyright (C) 2006 VLC authors and VideoLAN
5 * Copyright (C) 2008-2012 Rémi Denis-Courmont
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as published by
9 * the Free Software Foundation; either version 2.1 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public License
18 * along with this program; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
20 *****************************************************************************/
22 #ifdef HAVE_CONFIG_H
23 # include "config.h"
24 #endif
26 #include <errno.h>
27 #include <limits.h>
28 #include <stdint.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <assert.h>
33 #ifdef _WIN32
34 # include <io.h>
35 #endif
37 #include <vlc_common.h>
38 #include <vlc_memstream.h>
39 #include <vlc_url.h>
40 #include <vlc_fs.h>
41 #include <ctype.h>
43 char *vlc_uri_decode_duplicate (const char *str)
45 char *buf = strdup (str);
46 if (vlc_uri_decode (buf) == NULL)
48 free (buf);
49 buf = NULL;
51 return buf;
54 char *vlc_uri_decode (char *str)
56 char *in = str, *out = str;
57 if (in == NULL)
58 return NULL;
60 char c;
61 while ((c = *(in++)) != '\0')
63 if (c == '%')
65 char hex[3];
67 if (!(hex[0] = *(in++)) || !(hex[1] = *(in++)))
68 return NULL;
69 hex[2] = '\0';
70 *(out++) = strtoul (hex, NULL, 0x10);
72 else
73 *(out++) = c;
75 *out = '\0';
76 return str;
79 static bool isurialnum(int c)
81 return ((unsigned char)(c - 'a') < 26)
82 || ((unsigned char)(c - 'A') < 26)
83 || ((unsigned char)(c - '0') < 10);
86 static bool isurisafe(int c)
88 /* These are the _unreserved_ URI characters (RFC3986 §2.3) */
89 return isurialnum(c) || (strchr ("-._~", c) != NULL);
92 static bool isurisubdelim(int c)
94 return strchr("!$&'()*+,;=", c) != NULL;
97 static bool isurihex(int c)
98 { /* Same as isxdigit() but does not depend on locale and unsignedness */
99 return ((unsigned char)(c - '0') < 10)
100 || ((unsigned char)(c - 'A') < 6)
101 || ((unsigned char)(c - 'a') < 6);
104 static const char urihex[] = "0123456789ABCDEF";
106 static char *encode_URI_bytes (const char *str, size_t *restrict lenp)
108 char *buf = malloc (3 * *lenp + 1);
109 if (unlikely(buf == NULL))
110 return NULL;
112 char *out = buf;
113 for (size_t i = 0; i < *lenp; i++)
115 unsigned char c = str[i];
117 if (isurisafe (c))
118 *(out++) = c;
119 /* This is URI encoding, not HTTP forms:
120 * Space is encoded as '%20', not '+'. */
121 else
123 *(out++) = '%';
124 *(out++) = urihex[c >> 4];
125 *(out++) = urihex[c & 0xf];
129 *lenp = out - buf;
130 out = realloc (buf, *lenp + 1);
131 return likely(out != NULL) ? out : buf;
134 char *vlc_uri_encode (const char *str)
136 size_t len = strlen (str);
137 char *ret = encode_URI_bytes (str, &len);
138 if (likely(ret != NULL))
139 ret[len] = '\0';
140 return ret;
143 char *vlc_path2uri (const char *path, const char *scheme)
145 if (path == NULL)
147 errno = EINVAL;
148 return NULL;
150 if (scheme == NULL && !strcmp (path, "-"))
151 return strdup ("fd://0"); // standard input
152 /* Note: VLC cannot handle URI schemes without double slash after the
153 * scheme name (such as mailto: or news:). */
155 char *buf;
157 #ifdef __OS2__
158 char p[strlen (path) + 1];
160 for (buf = p; *path; buf++, path++)
161 *buf = (*path == '/') ? DIR_SEP_CHAR : *path;
162 *buf = '\0';
164 path = p;
165 #endif
167 #if defined (_WIN32) || defined (__OS2__)
168 /* Drive letter */
169 if (isalpha ((unsigned char)path[0]) && (path[1] == ':'))
171 if (asprintf (&buf, "%s:///%c:", scheme ? scheme : "file",
172 path[0]) == -1)
173 buf = NULL;
174 path += 2;
175 # warning Drive letter-relative path not implemented!
176 if (path[0] != DIR_SEP_CHAR)
178 errno = ENOTSUP;
179 return NULL;
182 else
183 if (!strncmp (path, "\\\\", 2))
184 { /* Windows UNC paths */
185 /* \\host\share\path -> file://host/share/path */
186 int hostlen = strcspn (path + 2, DIR_SEP);
188 if (asprintf (&buf, "file://%.*s", hostlen, path + 2) == -1)
189 buf = NULL;
190 path += 2 + hostlen;
192 if (path[0] == '\0')
193 return buf; /* Hostname without path */
195 else
196 #endif
197 if (path[0] != DIR_SEP_CHAR)
198 { /* Relative path: prepend the current working directory */
199 char *cwd, *ret;
201 if ((cwd = vlc_getcwd ()) == NULL)
202 return NULL;
203 if (asprintf (&buf, "%s"DIR_SEP"%s", cwd, path) == -1)
204 buf = NULL;
206 free (cwd);
207 ret = (buf != NULL) ? vlc_path2uri (buf, scheme) : NULL;
208 free (buf);
209 return ret;
211 else
212 if (asprintf (&buf, "%s://", scheme ? scheme : "file") == -1)
213 buf = NULL;
214 if (buf == NULL)
215 return NULL;
217 /* Absolute file path */
218 assert (path[0] == DIR_SEP_CHAR);
221 size_t len = strcspn (++path, DIR_SEP);
222 path += len;
224 char *component = encode_URI_bytes (path - len, &len);
225 if (unlikely(component == NULL))
227 free (buf);
228 return NULL;
230 component[len] = '\0';
232 char *uri;
233 int val = asprintf (&uri, "%s/%s", buf, component);
234 free (component);
235 free (buf);
236 if (unlikely(val == -1))
237 return NULL;
238 buf = uri;
240 while (*path);
242 return buf;
245 char *vlc_uri2path (const char *url)
247 char *ret = NULL;
248 char *end;
250 char *path = strstr (url, "://");
251 if (path == NULL)
252 return NULL; /* unsupported scheme or invalid syntax */
254 end = memchr (url, '/', path - url);
255 size_t schemelen = ((end != NULL) ? end : path) - url;
256 path += 3; /* skip "://" */
258 /* Remove request parameters and/or HTML anchor if present */
259 end = path + strcspn (path, "?#");
260 path = strndup (path, end - path);
261 if (unlikely(path == NULL))
262 return NULL; /* boom! */
264 /* Decode path */
265 vlc_uri_decode (path);
267 if (schemelen == 4 && !strncasecmp (url, "file", 4))
269 #if !defined (_WIN32) && !defined (__OS2__)
270 /* Leading slash => local path */
271 if (*path == '/')
272 return path;
273 /* Local path disguised as a remote one */
274 if (!strncasecmp (path, "localhost/", 10))
275 return memmove (path, path + 9, strlen (path + 9) + 1);
276 #else
277 /* cannot start with a space */
278 if (*path == ' ')
279 goto out;
280 for (char *p = strchr (path, '/'); p; p = strchr (p + 1, '/'))
281 *p = '\\';
283 /* Leading backslash => local path */
284 if (*path == '\\')
285 return memmove (path, path + 1, strlen (path + 1) + 1);
286 /* Local path disguised as a remote one */
287 if (!strncasecmp (path, "localhost\\", 10))
288 return memmove (path, path + 10, strlen (path + 10) + 1);
289 /* UNC path */
290 if (*path && asprintf (&ret, "\\\\%s", path) == -1)
291 ret = NULL;
292 #endif
293 /* non-local path :-( */
295 else
296 if (schemelen == 2 && !strncasecmp (url, "fd", 2))
298 int fd = strtol (path, &end, 0);
300 if (*end)
301 goto out;
303 #if !defined( _WIN32 ) && !defined( __OS2__ )
304 switch (fd)
306 case 0:
307 ret = strdup ("/dev/stdin");
308 break;
309 case 1:
310 ret = strdup ("/dev/stdout");
311 break;
312 case 2:
313 ret = strdup ("/dev/stderr");
314 break;
315 default:
316 if (asprintf (&ret, "/dev/fd/%d", fd) == -1)
317 ret = NULL;
319 #else
320 /* XXX: Does this work on WinCE? */
321 if (fd < 2)
322 ret = strdup ("CON");
323 #endif
326 out:
327 free (path);
328 return ret; /* unknown scheme */
331 static char *vlc_idna_to_ascii (const char *);
333 /* RFC3987 §3.1 */
334 static char *vlc_iri2uri(const char *iri)
336 size_t a = 0, u = 0;
338 for (size_t i = 0; iri[i] != '\0'; i++)
340 unsigned char c = iri[i];
342 if (c < 128)
343 a++;
344 else
345 u++;
348 if (unlikely((a + u) > (SIZE_MAX / 4)))
350 errno = ENOMEM;
351 return NULL;
354 char *uri = malloc(a + 3 * u + 1), *p;
355 if (unlikely(uri == NULL))
356 return NULL;
358 for (p = uri; *iri != '\0'; iri++)
360 unsigned char c = *iri;
362 if (c < 128)
363 *(p++) = c;
364 else
366 *(p++) = '%';
367 *(p++) = urihex[c >> 4];
368 *(p++) = urihex[c & 0xf];
372 *p = '\0';
373 return uri;
376 static bool vlc_uri_component_validate(const char *str, const char *extras)
378 assert(str != NULL);
380 for (size_t i = 0; str[i] != '\0'; i++)
382 int c = str[i];
384 if (isurisafe(c) || isurisubdelim(c))
385 continue;
386 if (strchr(extras, c) != NULL)
387 continue;
388 if (c == '%' && isurihex(str[i + 1]) && isurihex(str[i + 2]))
390 i += 2;
391 continue;
393 return false;
395 return true;
398 static bool vlc_uri_host_validate(const char *str)
400 return vlc_uri_component_validate(str, ":");
403 static bool vlc_uri_path_validate(const char *str)
405 return vlc_uri_component_validate(str, "/@:");
408 static int vlc_UrlParseInner(vlc_url_t *restrict url, const char *str)
410 url->psz_protocol = NULL;
411 url->psz_username = NULL;
412 url->psz_password = NULL;
413 url->psz_host = NULL;
414 url->i_port = 0;
415 url->psz_path = NULL;
416 url->psz_option = NULL;
417 url->psz_fragment = NULL;
418 url->psz_buffer = NULL;
419 url->psz_pathbuffer = NULL;
421 if (str == NULL)
423 errno = EINVAL;
424 return -1;
427 char *buf = vlc_iri2uri(str);
428 if (unlikely(buf == NULL))
429 return -1;
430 url->psz_buffer = buf;
432 char *cur = buf, *next;
433 int ret = 0;
435 /* URI scheme */
436 next = buf;
437 while (isurialnum(*next) || memchr ("+-.", *next, 3) != NULL)
438 next++;
440 if (*next == ':')
442 *(next++) = '\0';
443 url->psz_protocol = cur;
444 cur = next;
447 /* Fragment */
448 next = strchr(cur, '#');
449 if (next != NULL)
451 *(next++) = '\0';
452 if (vlc_uri_component_validate(next, "/?"))
453 url->psz_fragment = next;
456 /* Query parameters */
457 next = strchr(cur, '?');
458 if (next != NULL)
460 *(next++) = '\0';
461 url->psz_option = next;
464 /* Authority */
465 if (strncmp(cur, "//", 2) == 0)
467 cur += 2;
469 /* Path */
470 next = strchr(cur, '/');
471 if (next != NULL)
473 *next = '\0'; /* temporary nul, reset to slash later */
474 url->psz_path = next;
476 /*else
477 url->psz_path = "/";*/
479 /* User name */
480 next = strrchr(cur, '@');
481 if (next != NULL)
483 *(next++) = '\0';
484 url->psz_username = cur;
485 cur = next;
487 /* Password (obsolete) */
488 next = strchr(url->psz_username, ':');
489 if (next != NULL)
491 *(next++) = '\0';
492 url->psz_password = next;
493 vlc_uri_decode(url->psz_password);
495 vlc_uri_decode(url->psz_username);
498 /* Host name */
499 if (*cur == '[' && (next = strrchr(cur, ']')) != NULL)
500 { /* Try IPv6 numeral within brackets */
501 *(next++) = '\0';
502 url->psz_host = strdup(cur + 1);
504 if (*next == ':')
505 next++;
506 else
507 next = NULL;
509 else
511 next = strchr(cur, ':');
512 if (next != NULL)
513 *(next++) = '\0';
515 url->psz_host = vlc_idna_to_ascii(vlc_uri_decode(cur));
518 if (url->psz_host == NULL)
519 ret = -1;
520 else
521 if (!vlc_uri_host_validate(url->psz_host))
523 free(url->psz_host);
524 url->psz_host = NULL;
525 errno = EINVAL;
526 ret = -1;
529 /* Port number */
530 if (next != NULL && *next)
532 char* end;
533 unsigned long port = strtoul(next, &end, 10);
535 if (strchr("0123456789", *next) == NULL || *end || port > UINT_MAX)
537 errno = EINVAL;
538 ret = -1;
541 url->i_port = port;
544 if (url->psz_path != NULL)
545 *url->psz_path = '/'; /* restore leading slash */
547 else
549 url->psz_path = cur;
552 return ret;
555 int vlc_UrlParse(vlc_url_t *url, const char *str)
557 int ret = vlc_UrlParseInner(url, str);
559 if (url->psz_path != NULL && !vlc_uri_path_validate(url->psz_path))
561 url->psz_path = NULL;
562 errno = EINVAL;
563 ret = -1;
565 return ret;
568 static char *vlc_uri_fixup_inner(const char *str, const char *extras);
570 int vlc_UrlParseFixup(vlc_url_t *url, const char *str)
572 int ret = vlc_UrlParseInner(url, str);
574 static const char pathextras[] = "/@:";
576 if (url->psz_path != NULL
577 && !vlc_uri_component_validate(url->psz_path, pathextras))
579 url->psz_pathbuffer = vlc_uri_fixup_inner(url->psz_path, pathextras);
580 if (url->psz_pathbuffer == NULL)
582 url->psz_path = NULL;
583 errno = ENOMEM;
584 ret = -1;
586 else
588 url->psz_path = url->psz_pathbuffer;
589 assert(vlc_uri_path_validate(url->psz_path));
592 return ret;
595 void vlc_UrlClean (vlc_url_t *restrict url)
597 free (url->psz_host);
598 free (url->psz_buffer);
599 free (url->psz_pathbuffer);
603 * Merge paths
605 * See IETF RFC3986 section 5.2.3 for details.
607 static char *vlc_uri_merge_paths(const char *base, const char *ref)
609 char *str;
610 int len;
612 if (base == NULL)
613 len = asprintf(&str, "/%s", ref);
614 else
616 const char *end = strrchr(base, '/');
618 if (end != NULL)
619 end++;
620 else
621 end = base;
623 len = asprintf(&str, "%.*s%s", (int)(end - base), base, ref);
626 if (unlikely(len == -1))
627 str = NULL;
628 return str;
632 * Remove dot segments
634 * See IETF RFC3986 section 5.2.4 for details.
636 static char *vlc_uri_remove_dot_segments(char *str)
638 char *input = str, *output = str;
640 while (input[0] != '\0')
642 assert(output <= input);
644 if (strncmp(input, "../", 3) == 0)
646 input += 3;
647 continue;
649 if (strncmp(input, "./", 2) == 0)
651 input += 2;
652 continue;
654 if (strncmp(input, "/./", 3) == 0)
656 input += 2;
657 continue;
659 if (strcmp(input, "/.") == 0)
661 input[1] = '\0';
662 continue;
664 if (strncmp(input, "/../", 4) == 0)
666 input += 3;
667 output = memrchr(str, '/', output - str);
668 if (output == NULL)
669 output = str;
670 continue;
672 if (strcmp(input, "/..") == 0)
674 input[1] = '\0';
675 output = memrchr(str, '/', output - str);
676 if (output == NULL)
677 output = str;
678 continue;
680 if (strcmp(input, ".") == 0)
682 input++;
683 continue;
685 if (strcmp(input, "..") == 0)
687 input += 2;
688 continue;
691 if (input[0] == '/')
692 *(output++) = *(input++);
694 size_t len = strcspn(input, "/");
696 if (input != output)
697 memmove(output, input, len);
699 input += len;
700 output += len;
703 output[0] = '\0';
704 return str;
707 char *vlc_uri_compose(const vlc_url_t *uri)
709 struct vlc_memstream stream;
710 char *enc;
712 vlc_memstream_open(&stream);
714 if (uri->psz_protocol != NULL)
715 vlc_memstream_printf(&stream, "%s:", uri->psz_protocol);
717 if (uri->psz_host != NULL)
719 vlc_memstream_write(&stream, "//", 2);
721 if (uri->psz_username != NULL)
723 enc = vlc_uri_encode(uri->psz_username);
724 if (enc == NULL)
725 goto error;
727 vlc_memstream_puts(&stream, enc);
728 free(enc);
730 if (uri->psz_password != NULL)
732 enc = vlc_uri_encode(uri->psz_password);
733 if (unlikely(enc == NULL))
734 goto error;
736 vlc_memstream_printf(&stream, ":%s", enc);
737 free(enc);
739 vlc_memstream_putc(&stream, '@');
742 const char *fmt;
744 if (strchr(uri->psz_host, ':') != NULL)
745 fmt = (uri->i_port != 0) ? "[%s]:%d" : "[%s]";
746 else
747 fmt = (uri->i_port != 0) ? "%s:%d" : "%s";
748 /* No IDNA decoding here. Seems unnecessary, dangerous even. */
749 vlc_memstream_printf(&stream, fmt, uri->psz_host, uri->i_port);
752 if (uri->psz_path != NULL)
753 vlc_memstream_puts(&stream, uri->psz_path);
754 if (uri->psz_option != NULL)
755 vlc_memstream_printf(&stream, "?%s", uri->psz_option);
756 if (uri->psz_fragment != NULL)
757 vlc_memstream_printf(&stream, "#%s", uri->psz_fragment);
759 if (vlc_memstream_close(&stream))
760 return NULL;
761 return stream.ptr;
763 error:
764 if (vlc_memstream_close(&stream) == 0)
765 free(stream.ptr);
766 return NULL;
769 char *vlc_uri_resolve(const char *base, const char *ref)
771 vlc_url_t base_uri, rel_uri;
772 vlc_url_t tgt_uri;
773 char *pathbuf = NULL, *ret = NULL;
775 if (vlc_UrlParse(&rel_uri, ref))
777 vlc_UrlClean(&rel_uri);
778 return NULL;
781 if (rel_uri.psz_protocol != NULL)
782 { /* Short circuit in case of absolute URI */
783 vlc_UrlClean(&rel_uri);
784 return strdup(ref);
787 vlc_UrlParse(&base_uri, base);
789 /* RFC3986 section 5.2.2 */
792 tgt_uri = rel_uri;
793 tgt_uri.psz_protocol = base_uri.psz_protocol;
795 if (rel_uri.psz_host != NULL)
796 break;
798 tgt_uri.psz_username = base_uri.psz_username;
799 tgt_uri.psz_password = base_uri.psz_password;
800 tgt_uri.psz_host = base_uri.psz_host;
801 tgt_uri.i_port = base_uri.i_port;
803 if (rel_uri.psz_path == NULL || rel_uri.psz_path[0] == '\0')
805 tgt_uri.psz_path = base_uri.psz_path;
806 if (rel_uri.psz_option == NULL)
807 tgt_uri.psz_option = base_uri.psz_option;
808 break;
811 if (rel_uri.psz_path[0] == '/')
812 break;
814 pathbuf = vlc_uri_merge_paths(base_uri.psz_path, rel_uri.psz_path);
815 if (unlikely(pathbuf == NULL))
816 goto error;
818 tgt_uri.psz_path = pathbuf;
820 while (0);
822 if (tgt_uri.psz_path != NULL)
823 vlc_uri_remove_dot_segments(tgt_uri.psz_path);
825 ret = vlc_uri_compose(&tgt_uri);
826 error:
827 free(pathbuf);
828 vlc_UrlClean(&base_uri);
829 vlc_UrlClean(&rel_uri);
830 return ret;
833 static char *vlc_uri_fixup_inner(const char *str, const char *extras)
835 assert(str && extras);
837 bool encode_percent = false;
838 for (size_t i = 0; str[i] != '\0'; i++)
839 if (str[i] == '%' && !(isurihex(str[i+1]) && isurihex(str[i+2])))
841 encode_percent = true;
842 break;
845 struct vlc_memstream stream;
847 vlc_memstream_open(&stream);
849 for (size_t i = 0; str[i] != '\0'; i++)
851 unsigned char c = str[i];
853 if (isurisafe(c) || isurisubdelim(c) || (strchr(extras, c) != NULL)
854 || (c == '%' && !encode_percent))
855 vlc_memstream_putc(&stream, c);
856 else
857 vlc_memstream_printf(&stream, "%%%02hhX", c);
860 if (vlc_memstream_close(&stream))
861 return NULL;
862 return stream.ptr;
865 static void vlc_uri_putc(struct vlc_memstream *s, int c, const char *extras)
867 if (isurisafe(c) || isurisubdelim(c) || (strchr(extras, c) != NULL))
868 vlc_memstream_putc(s, c);
869 else
870 vlc_memstream_printf(s, "%%%02hhX", c);
873 char *vlc_uri_fixup(const char *str)
875 assert(str != NULL);
877 /* If percent sign is consistently followed by two hexadecimal digits,
878 * then URL encoding must be assumed.
879 * Otherwise, the percent sign itself must be URL-encoded.
881 bool encode_percent = false;
883 for (const char *p = str; *p != '\0'; p++)
884 if (p[0] == '%' && !(isurihex(p[1]) && isurihex(p[2])))
886 encode_percent = true;
887 break;
890 struct vlc_memstream stream;
891 vlc_memstream_open(&stream);
893 /* Handle URI scheme */
894 const char *p = str;
895 bool absolute = false;
896 bool encode_brackets = true;
898 while (isurialnum(*p) || memchr("+-.", *p, 3) != NULL)
899 vlc_memstream_putc(&stream, *(p++));
901 if (p > str && *p == ':')
902 { /* There is an URI scheme, assume an absolute URI. */
903 vlc_memstream_putc(&stream, *(p++));
904 absolute = true;
905 encode_brackets = false;
908 /* Handle URI authority */
909 if ((absolute || p == str) && strncmp(p, "//", 2) == 0)
911 vlc_memstream_write(&stream, p, 2);
912 p += 2;
913 encode_brackets = true;
915 while (memchr("/?#", *p, 4) == NULL)
916 vlc_uri_putc(&stream, *(p++), "%:[]@" + encode_percent);
919 /* Handle URI path and what follows */
920 const char *extras = encode_brackets ? "%/?#@" : "%:/?#[]@";
922 while (*p != '\0')
923 vlc_uri_putc(&stream, *(p++), extras + encode_percent);
925 return vlc_memstream_close(&stream) ? NULL : stream.ptr;
928 #if defined (HAVE_IDN)
929 # include <idna.h>
930 #elif defined (_WIN32)
931 # include <windows.h>
932 # include <vlc_charset.h>
933 #endif
936 * Converts a UTF-8 nul-terminated IDN to nul-terminated ASCII domain name.
937 * \param idn UTF-8 Internationalized Domain Name to convert
938 * \return a heap-allocated string or NULL on error.
940 static char *vlc_idna_to_ascii (const char *idn)
942 #if defined (HAVE_IDN)
943 char *adn;
945 switch (idna_to_ascii_8z(idn, &adn, IDNA_ALLOW_UNASSIGNED))
947 case IDNA_SUCCESS:
948 return adn;
949 case IDNA_MALLOC_ERROR:
950 errno = ENOMEM;
951 return NULL;
952 case IDNA_DLOPEN_ERROR:
953 errno = ENOSYS;
954 return NULL;
955 default:
956 errno = EINVAL;
957 return NULL;
960 #elif defined (_WIN32)
961 char *ret = NULL;
963 if (idn[0] == '\0')
964 return strdup("");
966 wchar_t *wide = ToWide (idn);
967 if (wide == NULL)
968 return NULL;
970 int len = IdnToAscii (IDN_ALLOW_UNASSIGNED, wide, -1, NULL, 0);
971 if (len == 0)
973 errno = EINVAL;
974 goto error;
977 wchar_t *buf = vlc_alloc (len, sizeof (*buf));
978 if (unlikely(buf == NULL))
979 goto error;
980 if (!IdnToAscii (IDN_ALLOW_UNASSIGNED, wide, -1, buf, len))
982 free (buf);
983 errno = EINVAL;
984 goto error;
986 ret = FromWide (buf);
987 free (buf);
988 error:
989 free (wide);
990 return ret;
992 #else
993 /* No IDN support, filter out non-ASCII domain names */
994 for (const char *p = idn; *p; p++)
995 if (((unsigned char)*p) >= 0x80)
997 errno = ENOSYS;
998 return NULL;
1001 return strdup (idn);
1003 #endif