]> git.meshlink.io Git - catta/blobdiff - avahi-core/util.c
* utf-8 collation of domain names
[catta] / avahi-core / util.c
index fa97eecedbcc3eb6cb33482c09a0eb6becb98955..086c041a79ca9490b52590ac1b49310297a39ec5 100644 (file)
@@ -43,19 +43,60 @@ gchar *avahi_get_host_name(void) {
     return avahi_normalize_name(t);
 }
 
+static gchar *unescape_uneeded(const gchar *src, gchar *ret_dest, size_t size) {
+    gboolean escaped = FALSE;
+    
+    g_assert(src);
+    g_assert(ret_dest);
+    g_assert(size > 0);
+    
+    for (; *src; src++) {
+
+        if (!escaped && *src == '\\')
+            escaped = TRUE;
+        else if (escaped && (*src == '.' || *src == '\\')) {
+
+            if ((size -= 2) <= 1) break;
+            
+            *(ret_dest++) = '\\';
+            *(ret_dest++) = *src;
+
+            escaped = FALSE;
+        } else {
+            if (--size <= 1) break;
+
+            *(ret_dest++) = *src;
+            escaped = FALSE;
+        }
+
+    }
+
+    *ret_dest = 0;
+    
+    return ret_dest;
+}
+
 gchar *avahi_normalize_name(const gchar *s) {
-    size_t l;
+    gchar tmp[256];
+    gchar *n, *t;
+    guint l;
     g_assert(s);
 
-    l = strlen(s);
+    unescape_uneeded(s, tmp, sizeof(tmp));
 
-    if (!l)
+    n = g_utf8_normalize(tmp, -1, G_NORMALIZE_DEFAULT);
+
+    if ((l = strlen(n)) == 0) {
+        g_free(n);
         return g_strdup(".");
+    }
 
-    if (s[l-1] == '.')
-        return g_strdup(s);
-    
-    return g_strdup_printf("%s.", s);
+    if (n[l-1] == '.')
+        return n;
+
+    t = g_strdup_printf("%s.", n);
+    g_free(n);
+    return t;
 }
 
 gint avahi_timeval_compare(const GTimeVal *a, const GTimeVal *b) {
@@ -158,50 +199,151 @@ gint avahi_age(const GTimeVal *a) {
     return avahi_timeval_diff(&now, a);
 }
 
-gboolean avahi_domain_cmp(const gchar *a, const gchar *b) {
-    int escaped_a = 0, escaped_b = 0;
-    g_assert(a);
-    g_assert(b);
+/* Read the first label from string *name, unescape "\" and write it to dest */
+gchar *avahi_unescape_label(const gchar **name, gchar *dest, guint size) {
+    guint i = 0;
+    gchar *d;
+    
+    g_assert(dest);
+    g_assert(size > 0);
+    g_assert(name);
 
-    for (;;) {
-        /* Check for escape characters "\" */
-        if ((escaped_a = *a == '\\'))
-            a ++;
+    if (!**name)
+        return NULL;
 
-        if ((escaped_b = *b == '\\'))
-            b++;
+    d = dest;
+    
+    for (;;) {
+        if (i >= size)
+            return NULL;
 
-        /* Check for string end */
-        if (*a == 0 && *b == 0)
-            return 0;
+        if (**name == '.') {
+            (*name)++;
+            break;
+        }
         
-        if (*a == 0 && !escaped_b && *b == '.' && *(b+1) == 0)
-            return 0;
+        if (**name == 0)
+            break;
+        
+        if (**name == '\\') {
+            (*name) ++;
+            
+            if (**name == 0)
+                break;
+        }
+        
+        *(d++) = *((*name) ++);
+        i++;
+    }
 
-        if (!escaped_a && *a == '.' && *(a+1) == 0 && *b == 0)
-            return 0;
+    g_assert(i < size);
+
+    *d = 0;
+
+    return dest;
+}
 
-        /* Compare characters */
-        if (escaped_a == escaped_b && *a != *b)
-            return *a < *b ? -1 : 1;
+/* Escape "\" and ".", append \0 */
+gchar *avahi_escape_label(const guint8* src, guint src_length, gchar **ret_name, guint *ret_size) {
+    gchar *r;
+
+    g_assert(src);
+    g_assert(ret_name);
+    g_assert(*ret_name);
+    g_assert(ret_size);
+    g_assert(*ret_size > 0);
+
+    r = *ret_name;
+
+    while (src_length > 0) {
+        if (*src == '.' || *src == '\\') {
+            if (*ret_size < 3)
+                return NULL;
+            
+            *((*ret_name) ++) = '\\';
+            (*ret_size) --;
+        }
 
-        /* Next characters */
-        a++;
-        b++;
+        if (*ret_size < 2)
+            return NULL;
         
+        *((*ret_name)++) = *src;
+        (*ret_size) --;
+
+        src_length --;
+        src++;
     }
+
+    **ret_name = 0;
+
+    return r;
+}
+
+static gint utf8_strcasecmp(const gchar *a, const gchar *b) {
+    gchar *ta, *tb;
+    gint r;
+    
+    g_assert(a);
+    g_assert(b);
+
+    ta = g_utf8_casefold(a, -1);
+    tb = g_utf8_casefold(b, -1);
+    r = g_utf8_collate(ta, tb);
+    g_free(ta);
+    g_free(tb);
+
+    return r;
 }
 
 gboolean avahi_domain_equal(const gchar *a, const gchar *b) {
-    return avahi_domain_cmp(a, b) == 0;
+    g_assert(a);
+    g_assert(b);
+
+    if (a == b)
+        return TRUE;
+    
+    for (;;) {
+        gchar ca[65], cb[65], *pa, *pb;
+
+        pa = avahi_unescape_label(&a, ca, sizeof(ca));
+        pb = avahi_unescape_label(&b, cb, sizeof(cb));
+
+        if (!pa && !pb)
+            return TRUE;
+        else if ((pa && !pb) || (!pa && pb))
+            return FALSE;
+
+        if (utf8_strcasecmp(pa, pb))
+            return FALSE;
+    }
+
+    return TRUE;
 }
 
-guint avahi_domain_hash(const gchar *p) {
-    char t[256];
-    strncpy(t, p, sizeof(t)-1);
-    t[sizeof(t)-1] = 0;
+gint avahi_binary_domain_cmp(const gchar *a, const gchar *b) {
+    g_assert(a);
+    g_assert(b);
+
+    if (a == b)
+        return 0;
+
+    for (;;) {
+        gchar ca[65], cb[65], *pa, *pb;
+        gint r;
+
+        pa = avahi_unescape_label(&a, ca, sizeof(ca));
+        pb = avahi_unescape_label(&b, cb, sizeof(cb));
 
-    return g_int_hash(t);
+        if (!pa && !pb)
+            return 0;
+        else if (pa && !pb)
+            return 1;
+        else if (!pa && pb)
+            return -1;
+        
+        if ((r = strcmp(pa, pb)))
+            return r;
+    }
 }
 
 void avahi_hexdump(gconstpointer p, guint size) {
@@ -237,3 +379,25 @@ void avahi_hexdump(gconstpointer p, guint size) {
         size -= 16;
     }
 }
+
+gint avahi_domain_hash(const gchar *s) {
+    guint hash = 0;
+    
+    for (;;) {
+        gchar c[65], *n, *m;
+
+        if (!avahi_unescape_label(&s, c, sizeof(c)))
+            return hash;
+
+        if (!c[0])
+            continue;
+        
+        n = g_utf8_normalize(c, -1, G_NORMALIZE_DEFAULT);
+        m = g_utf8_strdown(n, -1);
+
+        hash += g_str_hash(m);
+
+        g_free(m);
+        g_free(n);
+    }
+}