#ifndef lint static char *rcsid = "$Id: res.c,v 1.1 2003/06/04 00:26:10 marka Exp $"; #endif /* * Copyright (c) 2000,2002 Japan Network Information Center. * All rights reserved. * * By using this file, you agree to the terms and conditions set forth bellow. * * LICENSE TERMS AND CONDITIONS * * The following License Terms and Conditions apply, unless a different * license is obtained from Japan Network Information Center ("JPNIC"), * a Japanese association, Kokusai-Kougyou-Kanda Bldg 6F, 2-3-4 Uchi-Kanda, * Chiyoda-ku, Tokyo 101-0047, Japan. * * 1. Use, Modification and Redistribution (including distribution of any * modified or derived work) in source and/or binary forms is permitted * under this License Terms and Conditions. * * 2. Redistribution of source code must retain the copyright notices as they * appear in each source code file, this License Terms and Conditions. * * 3. Redistribution in binary form must reproduce the Copyright Notice, * this License Terms and Conditions, in the documentation and/or other * materials provided with the distribution. For the purposes of binary * distribution the "Copyright Notice" refers to the following language: * "Copyright (c) 2000-2002 Japan Network Information Center. All rights reserved." * * 4. The name of JPNIC may not be used to endorse or promote products * derived from this Software without specific prior written approval of * JPNIC. * * 5. Disclaimer/Limitation of Liability: THIS SOFTWARE IS PROVIDED BY JPNIC * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JPNIC BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef IDN_UTF8_ENCODING_NAME #define IDN_UTF8_ENCODING_NAME "UTF-8" /* by IANA */ #endif #ifndef WITHOUT_ICONV #define ENCODE_MASK \ (IDN_LOCALCONV | IDN_DELIMMAP | IDN_LOCALMAP | IDN_MAP | \ IDN_NORMALIZE | IDN_PROHCHECK | IDN_UNASCHECK | IDN_BIDICHECK | \ IDN_ASCCHECK | IDN_IDNCONV | IDN_LENCHECK | IDN_ENCODE_QUERY | \ IDN_UNDOIFERR) #define DECODE_MASK \ (IDN_DELIMMAP | IDN_MAP | IDN_NORMALIZE | IDN_PROHCHECK | \ IDN_UNASCHECK | IDN_BIDICHECK | IDN_IDNCONV | IDN_ASCCHECK | \ IDN_RTCHECK | IDN_LOCALCONV | IDN_DECODE_QUERY) #else #define ENCODE_MASK \ (IDN_DELIMMAP | IDN_LOCALMAP | IDN_MAP | IDN_NORMALIZE | \ IDN_PROHCHECK | IDN_UNASCHECK | IDN_BIDICHECK | IDN_ASCCHECK | \ IDN_IDNCONV | IDN_LENCHECK | IDN_ENCODE_QUERY | IDN_UNDOIFERR) #define DECODE_MASK \ (IDN_DELIMMAP | IDN_MAP | IDN_NORMALIZE | IDN_PROHCHECK | \ IDN_UNASCHECK | IDN_BIDICHECK | IDN_IDNCONV | IDN_ASCCHECK | \ IDN_RTCHECK | IDN_DECODE_QUERY) #endif #define MAX_LABEL_LENGTH 63 /* * label to convert. */ typedef struct labellist * labellist_t; struct labellist { unsigned long *name; size_t name_length; unsigned long *undo_name; labellist_t next; labellist_t previous; int dot_followed; }; typedef idn_result_t (*res_insnproc_t)(idn_resconf_t ctx, labellist_t label); static void idn_res_initialize(void); static idn_result_t copy_verbatim(const char *from, char *to, size_t tolen); static idn_result_t labellist_create(const unsigned long *name, labellist_t *labelp); static void labellist_destroy(labellist_t label); static idn_result_t labellist_setname(labellist_t label, const unsigned long *name); static const unsigned long * labellist_getname(labellist_t label); static const unsigned long * labellist_gettldname(labellist_t label); static idn_result_t labellist_getnamelist(labellist_t label, unsigned long *name, size_t label_length); static void labellist_undo(labellist_t label); static labellist_t labellist_tail(labellist_t label); static labellist_t labellist_previous(labellist_t label); #ifndef WITHOUT_ICONV static idn_result_t label_localdecodecheck(idn_resconf_t ctx, labellist_t label); #endif static idn_result_t label_idnencode_ace(idn_resconf_t ctx, labellist_t label); static idn_result_t label_idndecode(idn_resconf_t ctx, labellist_t label); static idn_result_t label_localmap(idn_resconf_t ctx, labellist_t label); static idn_result_t label_map(idn_resconf_t ctx, labellist_t label); static idn_result_t label_normalize(idn_resconf_t ctx, labellist_t label); static idn_result_t label_prohcheck(idn_resconf_t ctx, labellist_t label); static idn_result_t label_unascheck(idn_resconf_t ctx, labellist_t label); static idn_result_t label_bidicheck(idn_resconf_t ctx, labellist_t label); static idn_result_t label_asccheck(idn_resconf_t ctx, labellist_t label); static idn_result_t label_lencheck_ace(idn_resconf_t ctx, labellist_t label); static idn_result_t label_lencheck_nonace(idn_resconf_t ctx, labellist_t label); static idn_result_t label_rtcheck(idn_resconf_t ctx, idn_action_t actions, labellist_t label, const unsigned long *original_name); static int initialized; static int enabled; void idn_res_enable(int on_off) { if (!initialized) { idn_res_initialize(); } if (on_off == 0) { enabled = 0; } else { enabled = 1; } } static void idn_res_initialize(void) { if (!initialized) { char *value = getenv("IDN_DISABLE"); if (value == NULL) { enabled = 1; } else { enabled = 0; } initialized = 1; } } idn_result_t idn_res_encodename(idn_resconf_t ctx, idn_action_t actions, const char *from, char *to, size_t tolen) { idn_converter_t local_converter = NULL; idn_converter_t idn_converter = NULL; idn_delimitermap_t delimiter_mapper; idn_result_t r; labellist_t labels = NULL, l; unsigned long *buffer = NULL; size_t buffer_length; int from_is_root; int idn_is_ace; assert(ctx != NULL && from != NULL && to != NULL); TRACE(("idn_res_encodename(actions=%s, from=\"%s\", tolen=%d)\n", idn__res_actionstostring(actions), idn__debug_xstring(from, 50), (int)tolen)); if (actions & ~ENCODE_MASK) { WARNING(("idn_res_encodename: invalid actions 0x%x\n", actions)); r = idn_invalid_action; goto ret; } if (!initialized) idn_res_initialize(); if (!enabled || actions == 0) { r = copy_verbatim(from, to, tolen); goto ret; } else if (tolen <= 0) { r = idn_buffer_overflow; goto ret; } if (actions & IDN_ENCODE_QUERY) { #ifndef WITHOUT_ICONV actions |= (IDN_LOCALCONV | IDN_DELIMMAP | IDN_LOCALMAP | \ IDN_MAP | IDN_NORMALIZE | IDN_PROHCHECK | \ IDN_BIDICHECK | IDN_IDNCONV | IDN_LENCHECK); #else actions |= (IDN_DELIMMAP | IDN_LOCALMAP | IDN_MAP | \ IDN_NORMALIZE | IDN_PROHCHECK | IDN_BIDICHECK | \ IDN_IDNCONV | IDN_LENCHECK); #endif } /* * Convert `from' to UCS4. */ local_converter = idn_resconf_getlocalconverter(ctx); #ifndef WITHOUT_ICONV if (local_converter == NULL) { r = idn_invalid_name; goto ret; } #endif idn_converter = idn_resconf_getidnconverter(ctx); if (idn_converter != NULL && idn_converter_isasciicompatible(idn_converter)) idn_is_ace = 1; else idn_is_ace = 0; buffer_length = tolen * 2; for (;;) { void *new_buffer; new_buffer = realloc(buffer, sizeof(*buffer) * buffer_length); if (new_buffer == NULL) { r = idn_nomemory; goto ret; } buffer = (unsigned long *)new_buffer; if (actions & IDN_LOCALCONV) { r = idn_converter_convtoucs4(local_converter, from, buffer, buffer_length); } else { r = idn_ucs4_utf8toucs4(from, buffer, buffer_length); } if (r == idn_success) break; else if (r != idn_buffer_overflow) goto ret; buffer_length *= 2; } if (*buffer == '\0') { if (tolen <= 0) { r = idn_buffer_overflow; goto ret; } *to = '\0'; r = idn_success; goto ret; } /* * Delimiter map. */ if (actions & IDN_DELIMMAP) { TRACE(("res delimitermap(name=\"%s\")\n", idn__debug_ucs4xstring(buffer, 50))); delimiter_mapper = idn_resconf_getdelimitermap(ctx); if (delimiter_mapper != NULL) { r = idn_delimitermap_map(delimiter_mapper, buffer, buffer, buffer_length); idn_delimitermap_destroy(delimiter_mapper); if (r != idn_success) goto ret; } TRACE(("res delimitermap(): success (name=\"%s\")\n", idn__debug_ucs4xstring(buffer, 50))); } from_is_root = (buffer[0] == '.' && buffer[1] == '\0'); /* * Split the name into a list of labels. */ r = labellist_create(buffer, &labels); if (r != idn_success) goto ret; /* * Perform conversions and tests. */ for (l = labellist_tail(labels); l != NULL; l = labellist_previous(l)) { if (!idn__util_ucs4isasciirange(labellist_getname(l))) { if (actions & IDN_LOCALMAP) { r = label_localmap(ctx, l); if (r != idn_success) goto ret; } } if (!idn__util_ucs4isasciirange(labellist_getname(l))) { if (actions & IDN_MAP) { r = label_map(ctx, l); if (r != idn_success) goto ret; } if (actions & IDN_NORMALIZE) { r = label_normalize(ctx, l); if (r != idn_success) goto ret; } if (actions & IDN_PROHCHECK) { r = label_prohcheck(ctx, l); if (r == idn_prohibited && (actions & IDN_UNDOIFERR)) { labellist_undo(l); continue; } else if (r != idn_success) { goto ret; } } if (actions & IDN_UNASCHECK) { r = label_unascheck(ctx, l); if (r == idn_prohibited && (actions & IDN_UNDOIFERR)) { labellist_undo(l); continue; } else if (r != idn_success) { goto ret; } } if (actions & IDN_BIDICHECK) { r = label_bidicheck(ctx, l); if (r == idn_prohibited && (actions & IDN_UNDOIFERR)) { labellist_undo(l); continue; } else if (r != idn_success) { goto ret; } } } if (actions & IDN_ASCCHECK) { r = label_asccheck(ctx, l); if (r == idn_prohibited && (actions & IDN_UNDOIFERR)) { labellist_undo(l); continue; } else if (r != idn_success) { goto ret; } } if (!idn__util_ucs4isasciirange(labellist_getname(l))) { if ((actions & IDN_IDNCONV) && idn_is_ace) { r = label_idnencode_ace(ctx, l); if (r != idn_success) goto ret; } } if (!from_is_root && (actions & IDN_LENCHECK)) { if (idn_is_ace) r = label_lencheck_ace(ctx, l); else r = label_lencheck_nonace(ctx, l); if (r == idn_invalid_length && (actions & IDN_UNDOIFERR)) { labellist_undo(l); continue; } else if (r != idn_success) { goto ret; } } } /* * Concat a list of labels to a name. */ for (;;) { void *new_buffer; new_buffer = realloc(buffer, sizeof(*buffer) * buffer_length); if (new_buffer == NULL) { r = idn_nomemory; goto ret; } buffer = (unsigned long *)new_buffer; r = labellist_getnamelist(labels, buffer, buffer_length); if (r == idn_success) break; else if (r != idn_buffer_overflow) goto ret; buffer_length *= 2; } if ((actions & IDN_IDNCONV) && idn_converter != NULL && !idn_is_ace) { r = idn_converter_convfromucs4(idn_converter, buffer, to, tolen); } else { r = idn_ucs4_ucs4toutf8(buffer, to, tolen); } ret: if (r == idn_success) { TRACE(("idn_res_encodename(): success (to=\"%s\")\n", idn__debug_xstring(to, 50))); } else { TRACE(("idn_res_encodename(): %s\n", idn_result_tostring(r))); } free(buffer); if (local_converter != NULL) idn_converter_destroy(local_converter); if (idn_converter != NULL) idn_converter_destroy(idn_converter); if (labels != NULL) labellist_destroy(labels); return (r); } idn_result_t idn_res_decodename(idn_resconf_t ctx, idn_action_t actions, const char *from, char *to, size_t tolen) { idn_converter_t local_converter = NULL; idn_converter_t idn_converter = NULL; idn_delimitermap_t delimiter_mapper; idn_result_t r; labellist_t labels = NULL, l; unsigned long *buffer = NULL; unsigned long *saved_name = NULL; size_t buffer_length; int idn_is_ace; assert(ctx != NULL && from != NULL && to != NULL); TRACE(("idn_res_decodename(actions=%s, from=\"%s\", tolen=%d)\n", idn__res_actionstostring(actions), idn__debug_xstring(from, 50), (int)tolen)); if (actions & ~DECODE_MASK) { WARNING(("idn_res_decodename: invalid actions 0x%x\n", actions)); r = idn_invalid_action; goto ret; } if (!initialized) idn_res_initialize(); if (!enabled || actions == 0) { r = copy_verbatim(from, to, tolen); goto ret; } else if (tolen <= 0) { r = idn_buffer_overflow; goto ret; } if (actions & IDN_DECODE_QUERY) { #ifndef WITHOUT_ICONV actions |= (IDN_DELIMMAP | IDN_MAP | IDN_NORMALIZE | \ IDN_PROHCHECK | IDN_BIDICHECK | IDN_IDNCONV | \ IDN_RTCHECK | IDN_LOCALCONV); #else actions |= (IDN_DELIMMAP | IDN_MAP | IDN_NORMALIZE | \ IDN_PROHCHECK | IDN_BIDICHECK | IDN_IDNCONV | \ IDN_RTCHECK); #endif } /* * Convert `from' to UCS4. */ local_converter = idn_resconf_getlocalconverter(ctx); #ifndef WITHOUT_ICONV if (local_converter == NULL) { r = idn_invalid_name; goto ret; } #endif idn_converter = idn_resconf_getidnconverter(ctx); if (idn_converter != NULL && idn_converter_isasciicompatible(idn_converter)) idn_is_ace = 1; else idn_is_ace = 0; buffer_length = tolen * 2; TRACE(("res idndecode(name=\"%s\")\n", idn__debug_xstring(from, 50))); for (;;) { void *new_buffer; new_buffer = realloc(buffer, sizeof(*buffer) * buffer_length); if (new_buffer == NULL) { r = idn_nomemory; goto ret; } buffer = (unsigned long *)new_buffer; if ((actions & IDN_IDNCONV) && idn_converter != NULL && !idn_is_ace) { r = idn_converter_convtoucs4(idn_converter, from, buffer, buffer_length); } else { r = idn_ucs4_utf8toucs4(from, buffer, buffer_length); } if (r == idn_success) break; else if (r != idn_buffer_overflow) goto ret; buffer_length *= 2; } if (*buffer == '\0') { if (tolen <= 0) { r = idn_buffer_overflow; goto ret; } *to = '\0'; r = idn_success; goto ret; } /* * Delimiter map. */ if (actions & IDN_DELIMMAP) { TRACE(("res delimitermap(name=\"%s\")\n", idn__debug_ucs4xstring(buffer, 50))); delimiter_mapper = idn_resconf_getdelimitermap(ctx); if (delimiter_mapper != NULL) { r = idn_delimitermap_map(delimiter_mapper, buffer, buffer, buffer_length); idn_delimitermap_destroy(delimiter_mapper); if (r != idn_success) goto ret; } TRACE(("res delimitermap(): success (name=\"%s\")\n", idn__debug_ucs4xstring(buffer, 50))); } /* * Split the name into a list of labels. */ r = labellist_create(buffer, &labels); if (r != idn_success) goto ret; /* * Perform conversions and tests. */ for (l = labellist_tail(labels); l != NULL; l = labellist_previous(l)) { free(saved_name); saved_name = NULL; if (!idn__util_ucs4isasciirange(labellist_getname(l))) { if (actions & IDN_MAP) { r = label_map(ctx, l); if (r != idn_success) goto ret; } if (actions & IDN_NORMALIZE) { r = label_normalize(ctx, l); if (r != idn_success) goto ret; } if (actions & IDN_PROHCHECK) { r = label_prohcheck(ctx, l); if (r == idn_prohibited) { labellist_undo(l); continue; } else if (r != idn_success) { goto ret; } } if (actions & IDN_UNASCHECK) { r = label_unascheck(ctx, l); if (r == idn_prohibited) { labellist_undo(l); continue; } else if (r != idn_success) { goto ret; } } if (actions & IDN_BIDICHECK) { r = label_bidicheck(ctx, l); if (r == idn_prohibited) { labellist_undo(l); continue; } else if (r != idn_success) { goto ret; } } } if ((actions & IDN_IDNCONV) && idn_is_ace) { saved_name = idn_ucs4_strdup(labellist_getname(l)); if (saved_name == NULL) { r = idn_nomemory; goto ret; } r = label_idndecode(ctx, l); if (r == idn_invalid_encoding) { labellist_undo(l); continue; } else if (r != idn_success) { goto ret; } } if ((actions & IDN_RTCHECK) && saved_name != NULL) { r = label_rtcheck(ctx, actions, l, saved_name); if (r == idn_invalid_encoding) { labellist_undo(l); continue; } else if (r != idn_success) { goto ret; } } #ifndef WITHOUT_ICONV if (actions & IDN_LOCALCONV) { r = label_localdecodecheck(ctx, l); if (r != idn_success) goto ret; } #endif } /* * Concat a list of labels to a name. */ for (;;) { void *new_buffer; new_buffer = realloc(buffer, sizeof(*buffer) * buffer_length); if (new_buffer == NULL) { r = idn_nomemory; goto ret; } buffer = (unsigned long *)new_buffer; r = labellist_getnamelist(labels, buffer, buffer_length); if (r == idn_success) break; else if (r != idn_buffer_overflow) goto ret; buffer_length *= 2; } if (actions & IDN_LOCALCONV) { r = idn_converter_convfromucs4(local_converter, buffer, to, tolen); } else { r = idn_ucs4_ucs4toutf8(buffer, to, tolen); } ret: if (r == idn_success) { TRACE(("idn_res_decodename(): success (to=\"%s\")\n", idn__debug_xstring(to, 50))); } else { TRACE(("idn_res_decodename(): %s\n", idn_result_tostring(r))); } free(saved_name); free(buffer); if (local_converter != NULL) idn_converter_destroy(local_converter); if (idn_converter != NULL) idn_converter_destroy(idn_converter); if (labels != NULL) labellist_destroy(labels); return (r); } idn_result_t idn_res_decodename2(idn_resconf_t ctx, idn_action_t actions, const char *from, char *to, size_t tolen, const char *auxencoding) { #ifdef WITHOUT_ICONV return idn_failure; #else /* WITHOUT_ICONV */ idn_result_t r; idn_converter_t aux_converter = NULL; unsigned long *buffer_ucs4 = NULL; char *buffer_utf8 = NULL; size_t buffer_length; assert(ctx != NULL && from != NULL && to != NULL); TRACE(("idn_res_decodename2(actions=%s, from=\"%s\", tolen=%d, " "auxencoding=\"%s\")\n", idn__res_actionstostring(actions), idn__debug_xstring(from, 50), (int)tolen, (auxencoding != NULL) ? auxencoding : "")); if (!initialized) idn_res_initialize(); if (!enabled || actions == 0) { r = copy_verbatim(from, to, tolen); goto ret; } else if (tolen <= 0) { r = idn_buffer_overflow; goto ret; } if (auxencoding == NULL || strcmp(auxencoding, IDN_UTF8_ENCODING_NAME) == 0 || strcmp(auxencoding, "UTF-8") == 0) { return idn_res_decodename(ctx, actions, from, to, tolen); } /* * Convert `from' to UCS4. */ r = idn_resconf_setauxidnconvertername(ctx, auxencoding, IDN_CONVERTER_DELAYEDOPEN); if (r != idn_success) { goto ret; } aux_converter = idn_resconf_getauxidnconverter(ctx); if (aux_converter == NULL) { r = idn_failure; goto ret; } buffer_length = tolen * 2; for (;;) { void *new_buffer; new_buffer = realloc(buffer_ucs4, sizeof(*buffer_ucs4) * buffer_length); if (new_buffer == NULL) { r = idn_nomemory; goto ret; } buffer_ucs4 = (unsigned long *)new_buffer; r = idn_converter_convtoucs4(aux_converter, from, buffer_ucs4, buffer_length); if (r == idn_success) break; else if (r != idn_buffer_overflow) goto ret; buffer_length *= 2; } if (*buffer_ucs4 == '\0') { if (tolen <= 0) { r = idn_buffer_overflow; goto ret; } *to = '\0'; r = idn_success; goto ret; } /* * Convert `buffer_ucs4' to UTF-8. */ buffer_length = tolen * 2; for (;;) { void *new_buffer; new_buffer = realloc(buffer_utf8, sizeof(*buffer_utf8) * buffer_length); if (new_buffer == NULL) { r = idn_nomemory; goto ret; } buffer_utf8 = (char *)new_buffer; r = idn_ucs4_ucs4toutf8(buffer_ucs4, buffer_utf8, buffer_length); if (r == idn_success) break; else if (r != idn_buffer_overflow) goto ret; buffer_length *= 2; } if (*buffer_utf8 == '\0') { if (tolen <= 0) { r = idn_buffer_overflow; goto ret; } *to = '\0'; r = idn_success; goto ret; } r = idn_res_decodename(ctx, actions, buffer_utf8, to, tolen); ret: if (r == idn_success) { TRACE(("idn_res_decodename2(): success (to=\"%s\")\n", idn__debug_xstring(to, 50))); } else { TRACE(("idn_res_decodename2(): %s\n", idn_result_tostring(r))); } free(buffer_ucs4); free(buffer_utf8); if (aux_converter != NULL) idn_converter_destroy(aux_converter); return (r); #endif /* WITHOUT_ICONV */ } static idn_result_t copy_verbatim(const char *from, char *to, size_t tolen) { size_t fromlen = strlen(from); if (fromlen + 1 > tolen) return (idn_buffer_overflow); (void)memcpy(to, from, fromlen + 1); return (idn_success); } static idn_result_t labellist_create(const unsigned long *name, labellist_t *labelp) { size_t length, malloc_length; labellist_t head_label = NULL; labellist_t tail_label = NULL; labellist_t new_label = NULL; const unsigned long *endp = NULL; idn_result_t r; while (*name != '\0') { for (endp = name; *endp != '.' && *endp != '\0'; endp++) ; /* nothing to be done */ length = (endp - name) + 1; malloc_length = length + 15; /* add 15 for margin */ new_label = (labellist_t) malloc(sizeof(struct labellist)); if (new_label == NULL) { r = idn_nomemory; goto ret; } if (head_label == NULL) head_label = new_label; new_label->name = NULL; new_label->undo_name = NULL; new_label->name_length = malloc_length; new_label->next = NULL; new_label->previous = NULL; new_label->dot_followed = (*endp == '.'); new_label->name = (unsigned long *) malloc(sizeof(long) * malloc_length); if (new_label->name == NULL) { r = idn_nomemory; goto ret; } memcpy(new_label->name, name, sizeof(long) * length); *(new_label->name + length - 1) = '\0'; new_label->undo_name = (unsigned long *) malloc(sizeof(long) * malloc_length); if (new_label->undo_name == NULL) { r = idn_nomemory; goto ret; } memcpy(new_label->undo_name, name, sizeof(long) * length); *(new_label->undo_name + length - 1) = '\0'; if (tail_label != NULL) { tail_label->next = new_label; new_label->previous = tail_label; } tail_label = new_label; if (*endp == '.') name = endp + 1; else name = endp; } *labelp = head_label; r = idn_success; ret: if (r != idn_success) { if (new_label != NULL) { free(new_label->name); free(new_label->undo_name); free(new_label); } if (head_label != NULL) labellist_destroy(head_label); } return (r); } static void labellist_destroy(labellist_t label) { labellist_t l, l_next; for (l = label; l != NULL; l = l_next) { l_next = l->next; free(l->name); free(l->undo_name); free(l); } } static idn_result_t labellist_setname(labellist_t label, const unsigned long *name) { unsigned long *new_name; size_t length, new_length; length = idn_ucs4_strlen(name) + 1; new_length = length + 15; /* add 15 for margin */ if (label->name_length < new_length) { new_name = (unsigned long *) realloc(label->name, sizeof(long) * new_length); if (new_name == NULL) return (idn_nomemory); label->name = new_name; label->name_length = new_length; } memcpy(label->name, name, sizeof(long) * length); return (idn_success); } static const unsigned long * labellist_getname(labellist_t label) { return (label->name); } static const unsigned long * labellist_gettldname(labellist_t label) { labellist_t l; if (label->previous == NULL && label->next == NULL && !label->dot_followed) return (idn_mapselector_getnotld()); for (l = label; l->next != NULL; l = l->next) ; /* nothing to be done */ return (l->name); } static idn_result_t labellist_getnamelist(labellist_t label, unsigned long *name, size_t name_length) { static const unsigned long dot_string[] = {0x002e, 0x0000}; /* "." */ size_t length; labellist_t l; for (l = label, length = 0; l != NULL; l = l->next) length += idn_ucs4_strlen(l->name) + 1; /* name + `.' */ length++; /* for NUL */ if (name_length < length) return (idn_buffer_overflow); *name = '\0'; for (l = label; l != NULL; l = l->next) { idn_ucs4_strcat(name, l->name); name += idn_ucs4_strlen(name); if (l->dot_followed) idn_ucs4_strcat(name, dot_string); } return (idn_success); } static void labellist_undo(labellist_t label) { size_t length; length = idn_ucs4_strlen(label->undo_name) + 1; memcpy(label->name, label->undo_name, sizeof(long) * length); } static labellist_t labellist_tail(labellist_t label) { labellist_t l; if (label == NULL) return (NULL); for (l = label; l->next != NULL; l = l->next) ; /* nothing to be done */ return (l); } static labellist_t labellist_previous(labellist_t label) { return (label->previous); } #ifndef WITHOUT_ICONV static idn_result_t label_localdecodecheck(idn_resconf_t ctx, labellist_t label) { idn_converter_t local_converter = NULL; const unsigned long *from; char *to = NULL; size_t to_length; idn_result_t r; from = labellist_getname(label); to_length = idn_ucs4_strlen(from) + 1 + 15; /* 15 for margin */ TRACE(("res ucs4tolocal_check(label=\"%s\")\n", idn__debug_ucs4xstring(from, 50))); local_converter = idn_resconf_getlocalconverter(ctx); if (local_converter == NULL) { r = idn_success; goto ret; } for (;;) { char *new_buffer; new_buffer = (char *)realloc(to, to_length); if (new_buffer == NULL) { r = idn_nomemory; goto ret; } to = new_buffer; r = idn_converter_convfromucs4(local_converter, from, to, to_length); if (r == idn_success) break; else if (r == idn_nomapping) { r = label_idnencode_ace(ctx, label); if (r != idn_success) goto ret; break; } else if (r != idn_buffer_overflow) { goto ret; } to_length *= 2; } r = idn_success; ret: TRACE(("res ucs4tolocal_check(): %s\n", idn_result_tostring(r))); if (local_converter != NULL) idn_converter_destroy(local_converter); free(to); return (r); } #endif /* !WITHOUT_ICONV */ static idn_result_t label_idndecode(idn_resconf_t ctx, labellist_t label) { idn_converter_t idn_converter = NULL; const unsigned long *from; char *ascii_from = NULL; unsigned long *to = NULL; size_t from_length, to_length; idn_result_t r; from = labellist_getname(label); from_length = idn_ucs4_strlen(from) + 1; TRACE(("res idntoucs4(label=\"%s\")\n", idn__debug_ucs4xstring(from, 50))); idn_converter = idn_resconf_getidnconverter(ctx); if (idn_converter == NULL) { r = idn_success; goto ret; } for (;;) { char *new_buffer; new_buffer = (char *) realloc(ascii_from, from_length); if (new_buffer == NULL) { r = idn_nomemory; goto ret; } ascii_from = new_buffer; r = idn_ucs4_ucs4toutf8(from, ascii_from, from_length); if (r == idn_success) break; else if (r != idn_buffer_overflow) goto ret; from_length *= 2; } to = NULL; to_length = from_length; for (;;) { unsigned long *new_buffer; new_buffer = (unsigned long *) realloc(to, sizeof(long) * to_length); if (new_buffer == NULL) { r = idn_nomemory; goto ret; } to = new_buffer; r = idn_converter_convtoucs4(idn_converter, ascii_from, to, to_length); if (r == idn_success) break; else if (r != idn_buffer_overflow) goto ret; to_length *= 2; } r = labellist_setname(label, to); ret: if (r == idn_success) { TRACE(("res idntoucs4(): success (label=\"%s\")\n", idn__debug_ucs4xstring(labellist_getname(label), 50))); } else { TRACE(("res idntoucs4(): %s\n", idn_result_tostring(r))); } if (idn_converter != NULL) idn_converter_destroy(idn_converter); free(to); free(ascii_from); return (r); } static idn_result_t label_idnencode_ace(idn_resconf_t ctx, labellist_t label) { idn_converter_t idn_converter = NULL; const unsigned long *from; char *ascii_to = NULL; unsigned long *to = NULL; size_t to_length; idn_result_t r; from = labellist_getname(label); TRACE(("res ucs4toidn(label=\"%s\")\n", idn__debug_ucs4xstring(from, 50))); idn_converter = idn_resconf_getidnconverter(ctx); if (idn_converter == NULL) { r = idn_success; goto ret; } ascii_to = NULL; to_length = idn_ucs4_strlen(from) * 4 + 16; /* add mergin */ for (;;) { char *new_buffer; new_buffer = (char *) realloc(ascii_to, to_length); if (new_buffer == NULL) { r = idn_nomemory; goto ret; } ascii_to = new_buffer; r = idn_converter_convfromucs4(idn_converter, from, ascii_to, to_length); if (r == idn_success) break; else if (r != idn_buffer_overflow) goto ret; to_length *= 2; } for (;;) { unsigned long *new_buffer; new_buffer = (unsigned long *) realloc(to, sizeof(long) * to_length); if (new_buffer == NULL) { r = idn_nomemory; goto ret; } to = new_buffer; r = idn_ucs4_utf8toucs4(ascii_to, to, to_length); if (r == idn_success) break; else if (r != idn_buffer_overflow) goto ret; to_length *= 2; } if (r != idn_success) goto ret; r = labellist_setname(label, to); ret: if (r == idn_success) { TRACE(("res ucs4toidn(): success (label=\"%s\")\n", idn__debug_ucs4xstring(labellist_getname(label), 50))); } else { TRACE(("res ucs4toidn(): %s\n", idn_result_tostring(r))); } if (idn_converter != NULL) idn_converter_destroy(idn_converter); free(to); free(ascii_to); return (r); } static idn_result_t label_localmap(idn_resconf_t ctx, labellist_t label) { const unsigned long *from; const unsigned long *tld; unsigned long *to = NULL; size_t to_length; idn_mapselector_t local_mapper; idn_result_t r; from = labellist_getname(label); tld = labellist_gettldname(label); TRACE(("res localmap(label=\"%s\", tld=\"%s\")\n", idn__debug_ucs4xstring(from, 50), idn__debug_ucs4xstring(tld, 50))); local_mapper = idn_resconf_getlocalmapselector(ctx); if (local_mapper == NULL) { r = idn_success; goto ret; } if (tld == from) tld = idn_mapselector_getdefaulttld(); to_length = idn_ucs4_strlen(from) + 1 + 15; /* 15 for margin */ for (;;) { unsigned long *new_buffer; new_buffer = (unsigned long *) realloc(to, sizeof(long) * to_length); if (new_buffer == NULL) { r = idn_nomemory; goto ret; } to = new_buffer; r = idn_mapselector_map2(local_mapper, from, tld, to, to_length); if (r == idn_success) break; else if (r != idn_buffer_overflow) goto ret; to_length *= 2; } r = labellist_setname(label, to); ret: if (r == idn_success) { TRACE(("res localmap(): success (label=\"%s\")\n", idn__debug_ucs4xstring(labellist_getname(label), 50))); } else { TRACE(("res localmap(): %s\n", idn_result_tostring(r))); } if (local_mapper != NULL) idn_mapselector_destroy(local_mapper); free(to); return (r); } static idn_result_t label_map(idn_resconf_t ctx, labellist_t label) { const unsigned long *from; unsigned long *to = NULL; size_t to_length; idn_mapper_t mapper; idn_result_t r; from = labellist_getname(label); TRACE(("res map(label=\"%s\")\n", idn__debug_ucs4xstring(from, 50))); mapper = idn_resconf_getmapper(ctx); if (mapper == NULL) { r = idn_success; goto ret; } to_length = idn_ucs4_strlen(from) + 1 + 15; /* 15 for margin */ for (;;) { unsigned long *new_buffer; new_buffer = (unsigned long *) realloc(to, sizeof(long) * to_length); if (new_buffer == NULL) { r = idn_nomemory; goto ret; } to = new_buffer; r = idn_mapper_map(mapper, from, to, to_length); if (r == idn_success) break; else if (r != idn_buffer_overflow) goto ret; to_length *= 2; } r = labellist_setname(label, to); ret: if (r == idn_success) { TRACE(("res map(): success (label=\"%s\")\n", idn__debug_ucs4xstring(labellist_getname(label), 50))); } else { TRACE(("res map(): %s\n", idn_result_tostring(r))); } if (mapper != NULL) idn_mapper_destroy(mapper); free(to); return (r); } static idn_result_t label_normalize(idn_resconf_t ctx, labellist_t label) { const unsigned long *from; unsigned long *to = NULL; size_t to_length; idn_normalizer_t normalizer; idn_result_t r; from = labellist_getname(label); TRACE(("res normalzie(label=\"%s\")\n", idn__debug_ucs4xstring(from, 50))); normalizer = idn_resconf_getnormalizer(ctx); if (normalizer == NULL) { r = idn_success; goto ret; } to_length = idn_ucs4_strlen(from) + 1 + 15; /* 15 for margin */ for (;;) { unsigned long *new_buffer; new_buffer = (unsigned long *) realloc(to, sizeof(long) * to_length); if (new_buffer == NULL) { r = idn_nomemory; goto ret; } to = new_buffer; r = idn_normalizer_normalize(normalizer, from, to, to_length); if (r == idn_success) break; else if (r != idn_buffer_overflow) goto ret; to_length *= 2; } r = labellist_setname(label, to); ret: if (r == idn_success) { TRACE(("res normalize(): success (label=\"%s\")\n", idn__debug_ucs4xstring(labellist_getname(label), 50))); } else { TRACE(("res normalize(): %s\n", idn_result_tostring(r))); } if (normalizer != NULL) idn_normalizer_destroy(normalizer); free(to); return (r); } static idn_result_t label_prohcheck(idn_resconf_t ctx, labellist_t label) { const unsigned long *name, *found; idn_checker_t prohibit_checker; idn_result_t r; name = labellist_getname(label); TRACE(("res prohcheck(label=\"%s\")\n", idn__debug_ucs4xstring(name, 50))); prohibit_checker = idn_resconf_getprohibitchecker(ctx); if (prohibit_checker == NULL) { r = idn_success; goto ret; } r = idn_checker_lookup(prohibit_checker, name, &found); idn_checker_destroy(prohibit_checker); if (r == idn_success && found != NULL) r = idn_prohibited; ret: TRACE(("res prohcheck(): %s\n", idn_result_tostring(r))); return (r); } static idn_result_t label_unascheck(idn_resconf_t ctx, labellist_t label) { const unsigned long *name, *found; idn_checker_t unassigned_checker; idn_result_t r; name = labellist_getname(label); TRACE(("res unascheck(label=\"%s\")\n", idn__debug_ucs4xstring(name, 50))); unassigned_checker = idn_resconf_getunassignedchecker(ctx); if (unassigned_checker == NULL) { r = idn_success; goto ret; } r = idn_checker_lookup(unassigned_checker, name, &found); idn_checker_destroy(unassigned_checker); if (r == idn_success && found != NULL) r = idn_prohibited; ret: TRACE(("res unascheck(): %s\n", idn_result_tostring(r))); return (r); } static idn_result_t label_bidicheck(idn_resconf_t ctx, labellist_t label) { const unsigned long *name, *found; idn_checker_t bidi_checker; idn_result_t r; name = labellist_getname(label); TRACE(("res bidicheck(label=\"%s\")\n", idn__debug_ucs4xstring(name, 50))); bidi_checker = idn_resconf_getbidichecker(ctx); if (bidi_checker == NULL) { r = idn_success; goto ret; } r = idn_checker_lookup(bidi_checker, name, &found); idn_checker_destroy(bidi_checker); if (r == idn_success && found != NULL) r = idn_prohibited; ret: TRACE(("res bidicheck(): %s\n", idn_result_tostring(r))); return (r); } static idn_result_t label_asccheck(idn_resconf_t ctx, labellist_t label) { const unsigned long *name, *n; idn_result_t r; name = labellist_getname(label); TRACE(("res asccheck(label=\"%s\")\n", idn__debug_ucs4xstring(name, 50))); if (*name == '-') { r = idn_prohibited; goto ret; } for (n = name; *n != '\0'; n++) { if (*n <= '\177') { if ((*n < '0' || *n > '9') && (*n < 'A' || *n > 'Z') && (*n < 'a' || *n > 'z') && *n != '-') { r = idn_prohibited; goto ret; } } } if (n > name && *(n - 1) == '-') { r = idn_prohibited; goto ret; } r = idn_success; ret: TRACE(("res asccheck(): %s\n", idn_result_tostring(r))); return (r); } static idn_result_t label_lencheck_ace(idn_resconf_t ctx, labellist_t label) { const unsigned long *name; size_t name_length; idn_result_t r; name = labellist_getname(label); name_length = idn_ucs4_strlen(name); TRACE(("res lencheck(label=\"%s\")\n", idn__debug_ucs4xstring(name, 50))); if (name_length == 0 || name_length > MAX_LABEL_LENGTH) { r = idn_invalid_length; goto ret; } r = idn_success; ret: TRACE(("res lencheck(): %s\n", idn_result_tostring(r))); return (r); } static idn_result_t label_lencheck_nonace(idn_resconf_t ctx, labellist_t label) { idn_converter_t idn_converter; const unsigned long *from; size_t to_length; idn_result_t r; char *buffer = NULL; size_t buffer_length; from = labellist_getname(label); TRACE(("res lencheck(label=\"%s\")\n", idn__debug_ucs4xstring(from, 50))); buffer_length = idn_ucs4_strlen(from) * 4 + 16; /* 16 for margin */ idn_converter = idn_resconf_getidnconverter(ctx); for (;;) { void *new_buffer; new_buffer = realloc(buffer, sizeof(*buffer) * buffer_length); if (new_buffer == NULL) { r = idn_nomemory; goto ret; } buffer = (char *)new_buffer; if (idn_converter != NULL) { r = idn_converter_convfromucs4(idn_converter, from, buffer, buffer_length); } else { r = idn_ucs4_ucs4toutf8(from, buffer, buffer_length); } if (r == idn_success) break; else if (r != idn_buffer_overflow) goto ret; buffer_length *= 2; } to_length = strlen(buffer); if (to_length == 0 || to_length > MAX_LABEL_LENGTH) { r = idn_invalid_length; goto ret; } r = idn_success; ret: TRACE(("res lencheck(): %s\n", idn_result_tostring(r))); if (idn_converter != NULL) idn_converter_destroy(idn_converter); free(buffer); return (r); } static idn_result_t label_rtcheck(idn_resconf_t ctx, idn_action_t actions, labellist_t label, const unsigned long *original_name) { labellist_t rt_label = NULL; const unsigned long *rt_name; const unsigned long *cur_name; idn_result_t r; cur_name = labellist_getname(label); TRACE(("res rtcheck(label=\"%s\", org_label=\"%s\")\n", idn__debug_ucs4xstring(cur_name, 50), idn__debug_ucs4xstring(original_name, 50))); r = labellist_create(cur_name, &rt_label); if (r != idn_success) goto ret; if (rt_label == NULL) { if (*original_name == '\0') r = idn_success; else r = idn_invalid_encoding; goto ret; } if (!idn__util_ucs4isasciirange(labellist_getname(rt_label))) { r = label_map(ctx, rt_label); if (r != idn_success) goto ret; r = label_normalize(ctx, rt_label); if (r != idn_success) goto ret; r = label_prohcheck(ctx, rt_label); if (r != idn_success) goto ret; if (actions & IDN_UNASCHECK) { r = label_unascheck(ctx, rt_label); if (r != idn_success) goto ret; } r = label_bidicheck(ctx, rt_label); if (r != idn_success) goto ret; } if (actions & IDN_ASCCHECK) { r = label_asccheck(ctx, rt_label); if (r != idn_success) goto ret; } if (!idn__util_ucs4isasciirange(labellist_getname(rt_label))) { r = label_idnencode_ace(ctx, rt_label); if (r != idn_success) goto ret; } r = label_lencheck_ace(ctx, rt_label); if (r != idn_success) goto ret; rt_name = labellist_getname(rt_label); if (idn_ucs4_strcasecmp(rt_name, original_name) != 0) { TRACE(("res rtcheck(): round trip failed, org =\"%s\", rt=\"%s\"\n", idn__debug_ucs4xstring(original_name, 50), idn__debug_ucs4xstring(rt_name, 50))); r = idn_invalid_encoding; goto ret; } r = idn_success; ret: if (r != idn_nomemory && r != idn_success) r = idn_invalid_encoding; TRACE(("res rtcheck(): %s\n", idn_result_tostring(r))); if (rt_label != NULL) labellist_destroy(rt_label); return (r); } const char * idn__res_actionstostring(idn_action_t actions) { static char buf[100]; buf[0] = '\0'; if (actions == IDN_ENCODE_QUERY) strcpy(buf, "encode-query"); else if (actions == IDN_DECODE_QUERY) strcpy(buf, "decode-query"); else if (actions == IDN_ENCODE_APP) strcpy(buf, "encode-app"); else if (actions == IDN_DECODE_APP) strcpy(buf, "decode-app"); else if (actions == IDN_ENCODE_STORED) strcpy(buf, "encode-stored"); else if (actions == IDN_DECODE_STORED) strcpy(buf, "decode-stored"); else { if (actions & IDN_LOCALCONV) strcat(buf, "|localconv"); if (actions & IDN_DELIMMAP) strcat(buf, "|delimmap"); if (actions & IDN_LOCALMAP) strcat(buf, "|localmap"); if (actions & IDN_MAP) strcat(buf, "|map"); if (actions & IDN_NORMALIZE) strcat(buf, "|normalize"); if (actions & IDN_PROHCHECK) strcat(buf, "|prohcheck"); if (actions & IDN_UNASCHECK) strcat(buf, "|unascheck"); if (actions & IDN_BIDICHECK) strcat(buf, "|bidicheck"); if (actions & IDN_IDNCONV) strcat(buf, "|idnconv"); if (actions & IDN_ASCCHECK) strcat(buf, "|asccheck"); if (actions & IDN_LENCHECK) strcat(buf, "|lencheck"); if (actions & IDN_RTCHECK) strcat(buf, "|rtcheck"); } if (buf[0] == '|') return (buf + 1); else return (buf); }