1 /*-------------------------------------------------------------------------
2  * Copyright (C) 2000 Caldera Systems, Inc
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  *    Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  *
12  *    Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  *    Neither the name of Caldera Systems nor the names of its
17  *    contributors may be used to endorse or promote products derived
18  *    from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * `AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE CALDERA
24  * SYSTEMS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;  LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
28  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  *-------------------------------------------------------------------------*/
32 
33 /** Utility functions that deal with SLP strings and string-lists.
34  *
35  * These functions provide "case" and "no-case" versions of string matching
36  * routines.
37  *
38  * @file       slp_compare.c
39  * @author     Matthew Peterson, John Calcote (jcalcote@novell.com)
40  * @attention  Please submit patches to http://www.openslp.org
41  * @ingroup    CommonCodeStrings
42  */
43 
44 #include "slp_types.h"
45 #include "slp_xmalloc.h"
46 #include "slp_compare.h"
47 #include "slp_net.h"
48 #include "slp_iface.h"
49 
50 #ifdef HAVE_ICU
51 /* --- ICU header stuff - so we don't have to include their headers. --- */
52 #define u_strFromUTF8 u_strFromUTF8_3_2
53 #define u_strncasecmp u_strncasecmp_3_2
54 
55 typedef int UErrorCode;
56 typedef uint16_t UChar;
57 
58 UChar * u_strFromUTF8(UChar * dest, int32_t destCapacity,
59       int32_t * pDestLength, const char * src, int32_t srcLength,
60       UErrorCode * pErrorCode);
61 
62 int32_t u_strncasecmp(const UChar * s1, const UChar * s2,
63       int32_t n, uint32_t options);
64 /* --- End of ICU header stuff --- */
65 #endif	/* HAVE_ICU */
66 
67 #ifndef _WIN32
68 # ifndef HAVE_STRNCASECMP
69 /** Case-insensitive, size-constrained, lexical comparison.
70  *
71  * Compares a specified maximum number of characters of two strings for
72  * lexical equivalence in a case-insensitive manner.
73  *
74  * @param[in] s1 - The first string to be compared.
75  * @param[in] s2 - The second string to be compared.
76  * @param[in] len - The maximum number of characters to compare.
77  *
78  * @return Zero if at least @p len characters of @p s1 are the same as
79  *    the corresponding characters in @p s2 within the ASCII printable
80  *    range; a value less than zero if @p s1 is lexically less than
81  *    @p s2; or a value greater than zero if @p s1 is lexically greater
82  *    than @p s2.
83  *
84  * @internal
85  */
strncasecmp(const char * s1,const char * s2,size_t len)86 int strncasecmp(const char * s1, const char * s2, size_t len)
87 {
88    while (*s1 && (*s1 == *s2 || tolower(*s1) == tolower(*s2)))
89    {
90       len--;
91       if (len == 0)
92          return 0;
93       s1++;
94       s2++;
95    }
96    return len? (int)(*(unsigned char *)s1 - (int)*(unsigned char *)s2): 0;
97 }
98 # endif
99 
100 # ifndef HAVE_STRCASECMP
101 /** Case-insensitive lexical comparison.
102  *
103  * Compares two strings for lexical equivalence in a case-insensitive
104  * manner.
105  *
106  * @param[in] s1 - The first string to be compared.
107  * @param[in] s2 - The second string to be compared.
108  *
109  * @return Zero if @p s1 is the same as @p s2 within the ASCII printable
110  *    range; a value less than zero if @p s1 is lexically less than @p s2;
111  *    or a value greater than zero if @p s1 is lexically greater than
112  *    @p s2.
113  *
114  * @internal
115  */
strcasecmp(const char * s1,const char * s2)116 int strcasecmp(const char * s1, const char * s2)
117 {
118    while (*s1 && (*s1 == *s2 || tolower(*s1) == tolower(*s2)))
119       s1++, s2++;
120    return (int)(*(unsigned char *)s1 - (int)*(unsigned char *)s2);
121 }
122 # endif
123 #endif
124 
125 /** Convert a character to upper case using US ASCII rules. */
126 #define usaupr(c) (((c) & 0xC0) == 0x40? (c) & ~0x20: (c))
127 
128 /** Determines if a specified character is a valid hexadecimal character.
129  *
130  * @param[in] c - The character to examine.
131  *
132  * @returns 1 if @p c is a valid hexadecimal character; 0 if not.
133  */
ishex(int c)134 static int ishex(int c)
135 {
136    c = usaupr(c);
137    return (c >= '0' && c <= '9') || ((c >= 'A' && c <= 'F')? 1: 0);
138 }
139 
140 /** Converts a valid hexadecimal character into its binary equivalent.
141  *
142  * @param[in] c - The character to be converted.
143  *
144  * @returns The binary value of @p c.
145  */
hex2bin(int c)146 static int hex2bin(int c)
147 {
148    c = usaupr(c);
149    return c - (c <= '9'? '0': 'A' - 10);
150 }
151 
152 /** Unescape an SLP string in place.
153  *
154  * Replace escape sequences with corresponding character codes in a
155  * specified string.
156  *
157  * @param[in] len - The length in bytes of @p str.
158  * @param[in,out] str - The string in which escape sequences should be
159  *    replaced with corresponding characters.
160  *
161  * @returns The new (shorter) length of @p str.
162  *
163  * @note Since only valid escapeable characters may be escaped, we will
164  *    assume that escaped characters are sequences of bytes within the
165  *    0x00 - 0x7F range (ascii subset of utf-8).
166  */
SLPUnescapeInPlace(size_t len,char * str)167 static int SLPUnescapeInPlace(size_t len, char * str)
168 {
169    char * fp = str, * tp = str, * ep = str + len;
170    while (fp < ep - 2)
171    {
172       char c = *fp++;
173       if (c == '\\' && *fp && ishex(fp[0]) && ishex(fp[1]))
174       {
175          c = (char)(hex2bin(fp[0]) * 16 + hex2bin(fp[1]));
176          fp += 2;
177          len -= 2;
178       }
179       *tp++ = c;
180    }
181    return (int)len;
182 }
183 
184 /** Fold internal white space within a string.
185  *
186  * Folds all internal white space to a single space character within a
187  * specified string. Modified the @p str parameter with the result and
188  * returns the new length of the string.
189  *
190  * @param[in] len - The length in bytes of @p str.
191  * @param[in,out] str - The string from which extraneous white space
192  *    should be removed.
193  *
194  * @return The new (shorter) length of @p str.
195  *
196  * @note This routine assumes that leading and trailing white space have
197  *    already been removed from @p str.
198  */
SLPFoldWhiteSpace(size_t len,char * str)199 static int SLPFoldWhiteSpace(size_t len, char * str)
200 {
201    char * p = str, * ep = str + len;
202    while (p < ep)
203    {
204       if (isspace(*p))
205       {
206          char * ws2p = ++p;         /* Point ws2p to the second ws char. */
207          while (isspace(*p))        /* Scan till we hit a non-ws char. */
208             p++;
209          len -= p - ws2p;           /* Reduce the length by extra ws. */
210          memmove(ws2p, p, ep - p);  /* Overwrite the extra white space. */
211       }
212       p++;
213    }
214    return (int)len;
215 }
216 
217 /** Lexical compare routine.
218  *
219  * Performs a lexical string compare on two normalized UTF-8 strings as
220  * described in RFC 2608, section 6.4.
221  *
222  * @param[in] str1 - A pointer to string to be compared.
223  * @param[in] str2 - A pointer to string to be compared.
224  * @param[in] length - The maximum length to compare in bytes.
225  *
226  * @return Zero if @p str1 is equal to @p str2, less than zero if @p str1
227  *    is greater than @p str2, greater than zero if @p str1 is less than
228  *    @p str2.
229  */
SLPCompareNormalizedString(const char * str1,const char * str2,size_t length)230 static int SLPCompareNormalizedString(const char * str1,
231       const char * str2, size_t length)
232 {
233 #ifdef HAVE_ICU
234    int result;
235    UErrorCode uerr = 0;
236    UChar * ustr1 = xmalloc((length + 1) * sizeof(UChar));
237    UChar * ustr2 = xmalloc((length + 1) * sizeof(UChar));
238    if (ustr1 && ustr2)
239    {
240       u_strFromUTF8(ustr1, (int32_t)length + 1, 0, str1,
241             (int32_t)length, &uerr);
242       u_strFromUTF8(ustr2, (int32_t)length + 1, 0, str2,
243             (int32_t)length, &uerr);
244    }
245    if (ustr1 != 0 && ustr2 != 0 && uerr == 0)
246       result = (int)u_strncasecmp(ustr1, ustr2, (int32_t)length, 0);
247    else
248       result = strncasecmp(str1, str2, length);
249    xfree(ustr1);
250    xfree(ustr2);
251    return result;
252 #else
253    return strncasecmp(str1, str2, length);
254 #endif /* HAVE_ICU */
255 }
256 
257 /** Normalizes a string
258  *
259  * Normalizes a string by (optionally) removing leading and trailing white space,
260  * folding internal white space, folding case (upper->lower), and unescaping
261  * the string.
262  *
263  * @param[in] len - The length of the string to be normalised, in bytes.
264  * @param[in] srcstr - A pointer to the string to be normalised.
265  * @param[in] dststr - A pointer to the buffer for the normalised string.
266  * @param[in] trim - A flag to specify whether to trim leading and trailing space
267  *                   completely (if non-zero) or just fold it (if zero).
268  *
269  * @return Size of normalised string in bytes.
270  *
271  * @remarks @p dststr may be the same as @p srcstr for "update in place".
272  */
SLPNormalizeString(size_t len,const char * srcstr,char * dststr,int trim)273 size_t SLPNormalizeString(size_t len, const char * srcstr, char * dststr, int trim)
274 {
275    char *upd = dststr;
276    while (len > 0 && *srcstr)
277    {
278       if (isspace(*srcstr))
279       {
280          while (isspace(*srcstr) && len > 0)
281          {
282             ++srcstr, --len;
283          }
284          if (!trim || (upd != dststr && len > 0))
285             /* Internal whitespace */
286             *upd++ = ' ';
287       }
288       else if (*srcstr == '\\')
289       {
290          if (len < 3)
291          {
292             /* This indicates incorrect escaping, but just copy verbatim */
293             *upd++ = *srcstr++;
294             --len;
295          }
296          else
297          {
298             if (ishex(srcstr[1]) && ishex(srcstr[2]))
299             {
300                *upd++ = (char)(hex2bin(srcstr[0]) * 16 + hex2bin(srcstr[1]));
301                len -= 3;
302             }
303             else
304             {
305                /* This indicates incorrect escaping, but just copy verbatim */
306                *upd++ = *srcstr++;
307                --len;
308             }
309          }
310       }
311       else
312       {
313          *upd++ = (char)tolower(*srcstr++);
314          --len;
315       }
316    }
317    return upd - dststr;
318 }
319 
320 /** Compares two non-normalized strings.
321  *
322  * Normalizes two strings by removing leading and trailing white space,
323  * folding internal white space and unescaping the strings first, and then
324  * calling SLPCompareNormalizedString (as per RFC 2608, section 6.4).
325  *
326  * @param[in] str1 - A pointer to string to be compared.
327  * @param[in] str1len - The length of str1 in bytes.
328  * @param[in] str2 - A pointer to string to be compared.
329  * @param[in] str2len - The length of str2 in bytes.
330  *
331  * @return Zero if @p str1 is equal to @p str2, less than zero if @p str1
332  *    is greater than @p str2, greater than zero if @p str1 is less than
333  *    @p str2.
334  */
SLPCompareString(size_t str1len,const char * str1,size_t str2len,const char * str2)335 int SLPCompareString(size_t str1len, const char * str1,
336       size_t str2len, const char * str2)
337 {
338    int result;
339    char * cpy1, * cpy2;
340 
341    /* Remove leading white space. */
342    while (str1len && isspace(*str1))
343       str1++, str1len--;
344    while (str2len && isspace(*str2))
345       str2++, str2len--;
346 
347    /* Remove trailing white space. */
348    while (str1len && isspace(str1[str1len - 1]))
349       str1len--;
350    while (str2len && isspace(str2[str2len - 1]))
351       str2len--;
352 
353    /*A quick check for empty strings before we start xmemduping and xfreeing*/
354    if (str1len == 0 || str2len == 0)
355    {
356       if(str1len == str2len)
357          return 0;
358       if(str1len < str2len)
359          return -1;
360       return 1;
361    }
362 
363    /* Make modifiable copies. If either fails, compare original strings. */
364    cpy1 = xmemdup(str1, str1len);
365    cpy2 = xmemdup(str2, str2len);
366    if (cpy1 != 0 && cpy2 != 0)
367    {
368       /* Unescape copies in place. */
369       str1len = SLPUnescapeInPlace(str1len, cpy1);
370       str2len = SLPUnescapeInPlace(str2len, cpy2);
371 
372       /* Fold white space in place. */
373       str1len = SLPFoldWhiteSpace(str1len, cpy1);
374       str2len = SLPFoldWhiteSpace(str2len, cpy2);
375 
376       /* Reset original pointers to modified copies. */
377       str1 = cpy1;
378       str2 = cpy2;
379    }
380 
381    /* Comparison logic. */
382    if (str1len == str2len)
383       result = SLPCompareNormalizedString(str1, str2, str1len);
384    else if (str1len > str2len)
385       result = -1;
386    else
387       result = 1;
388 
389    xfree(cpy1);
390    xfree(cpy2);
391 
392    return result;
393 }
394 
395 /** Compare service type for matching naming authority.
396  *
397  * Compares a service type string with a naming authority to determine
398  * if they refer to the same naming authority, as described in RFC 2608,
399  * section ??.
400  *
401  * @param[in] srvtype - The service type to be compared.
402  * @param[in] srvtypelen - The length of @p srvtype in bytes.
403  * @param[in] namingauth - The naming authority to be matched.
404  * @param[in] namingauthlen - The length of @p namingauth in bytes.
405  *
406  * @return Zero if @p srvtype matches @p namingauth; non-zero if not.
407  */
SLPCompareNamingAuth(size_t srvtypelen,const char * srvtype,size_t namingauthlen,const char * namingauth)408 int SLPCompareNamingAuth(size_t srvtypelen, const char * srvtype,
409       size_t namingauthlen, const char * namingauth)
410 {
411    const char * dot;
412    size_t srvtypenalen;
413 
414    if (namingauthlen == 0xffff)
415       return 0;            /* match all naming authorities */
416 
417    dot = memchr(srvtype, '.', srvtypelen);
418    if (!namingauthlen)
419       return dot? 1: 0;    /* IANA naming authority */
420 
421    srvtypenalen = srvtypelen - (dot + 1 - srvtype);
422    if (srvtypenalen != namingauthlen)
423       return 1;
424 
425    if (SLPCompareNormalizedString(dot + 1, namingauth, namingauthlen) == 0)
426       return 0;
427 
428    return 1;
429 }
430 
431 /** Compare service types.
432  *
433  * Determine if two service type strings refer to the same service type.
434  *
435  * @param[in] lsrvtype - The first service type to be compared.
436  * @param[in] lsrvtypelen - The length of @p lsrvtype in bytes.
437  * @param[in] rsrvtype - The second service type to be compared.
438  * @param[in] rsrvtypelen - The length of @p rsrvtype in bytes.
439  *
440  * @return Zero if @p lsrvtype is the same as @p lsrvtype; non-zero
441  *    if they are different.
442  */
SLPCompareSrvType(size_t lsrvtypelen,const char * lsrvtype,size_t rsrvtypelen,const char * rsrvtype)443 int SLPCompareSrvType(size_t lsrvtypelen, const char * lsrvtype,
444       size_t rsrvtypelen, const char * rsrvtype)
445 {
446    char * colon;
447 
448    /* Skip "service:" */
449    if (strncasecmp(lsrvtype, "service:", lsrvtypelen > 8?
450          8: lsrvtypelen) == 0)
451    {
452       lsrvtypelen = lsrvtypelen - 8;
453       lsrvtype = lsrvtype + 8;
454    }
455    if (strncasecmp(rsrvtype, "service:", rsrvtypelen > 8?
456          8: rsrvtypelen) == 0)
457    {
458       rsrvtypelen = rsrvtypelen - 8;
459       rsrvtype = rsrvtype + 8;
460    }
461    if (memchr(lsrvtype, ':', lsrvtypelen))
462    {
463       /* lsrvtype is uses concrete type so strings must be identical. */
464       if (lsrvtypelen == rsrvtypelen)
465          return SLPCompareNormalizedString(lsrvtype, rsrvtype, lsrvtypelen);
466       return 1;
467    }
468    colon = memchr(rsrvtype, ':', rsrvtypelen);
469    if (colon)
470    {
471       /* lsrvtype is abstract only and rsrvtype is concrete. */
472       if (lsrvtypelen == (size_t)(colon - rsrvtype))
473          return SLPCompareNormalizedString(lsrvtype, rsrvtype, lsrvtypelen);
474       return 1;
475    }
476 
477    /* lsrvtype and rsrvtype are abstract only. */
478    if (lsrvtypelen == rsrvtypelen)
479       return SLPCompareNormalizedString(lsrvtype, rsrvtype, lsrvtypelen);
480    return 1;
481 }
482 
483 /** Scan a string list for a string.
484  *
485  * Determine if a specified string list contains a specified string.
486  *
487  * @param[in] list - A list of strings to search for @p string.
488  * @param[in] listlen - The length of @p list in bytes.
489  * @param[in] string - A string to locate in @p list.
490  * @param[in] stringlen - The length of @p string in bytes.
491  *
492  * @return 1-based index of position of @p string in @p list; zero if not in list.
493  *
494  * @remarks The @p list parameter is a zero-terminated string consisting
495  *    of a comma-separated list of sub-strings. This routine actually
496  *    determines if a specified sub-string (@p string) matches one of
497  *    the sub-strings in this list.
498  */
SLPContainsStringList(size_t listlen,const char * list,size_t stringlen,const char * string)499 int SLPContainsStringList(size_t listlen, const char * list, size_t stringlen,
500       const char * string)
501 {
502    const char * listend = list + listlen;
503    const char * itembegin = list;
504    const char * itemend = itembegin;
505 
506    while (itemend < listend)
507    {
508       itembegin = itemend;
509 
510       /* Seek to the end of the next list item, break on commas. */
511       while (itemend != listend && itemend[0] != ',')
512          itemend++;
513 
514       if (SLPCompareString(itemend - itembegin, itembegin,
515             stringlen, string) == 0)
516          return (int)(1 + (itembegin - list));  /* 1-based index of the position of the string in the list */
517 
518       itemend++;
519    }
520    return 0;
521 }
522 
523 /** Intersects two string lists.
524  *
525  * Calculates the number of common entries between two string-lists.
526  *
527  * @param[in] list1 - A pointer to the string-list to be checked
528  * @param[in] list1len - The length in bytes of the list to be checked
529  * @param[in] list2 - A pointer to the string-list to be checked
530  * @param[in] list2len - The length in bytes of the list to be checked
531  *
532  * @return The number of common entries between @p list1 and @p list2.
533  */
SLPIntersectStringList(size_t list1len,const char * list1,size_t list2len,const char * list2)534 int SLPIntersectStringList(size_t list1len, const char * list1,
535       size_t list2len, const char * list2)
536 {
537    int result = 0;
538    const char * listend = list1 + list1len;
539    const char * itembegin = list1;
540    const char * itemend = itembegin;
541 
542    while (itemend < listend)
543    {
544       itembegin = itemend;
545 
546       /* Seek to the end of the next list item, break on commas. */
547       while (itemend < listend && itemend[0] != ',')
548          itemend++;
549 
550       if (SLPContainsStringList(list2len, list2,
551             itemend - itembegin, itembegin))
552          result++;
553 
554       itemend++;
555    }
556    return result;
557 }
558 
559 /** Intersects two string lists, and removes the common entries from
560  * the second list.
561  *
562  * @param[in] list1len - The length in bytes of the list to be checked
563  * @param[in] list1 - A pointer to the string-list to be checked
564  * @param[in] list2len - The length in bytes of the list to be checked
565  *     and updated
566  * @param[in] list2 - A pointer to the string-list to be checked and
567  *     updated
568  *
569  * @return The number of common entries between @p list1 and @p list2.
570  */
SLPIntersectRemoveStringList(int list1len,const char * list1,int * list2len,char * list2)571 int SLPIntersectRemoveStringList(int list1len,
572                                  const char* list1,
573                                  int* list2len,
574                                  char* list2)
575 {
576     int result = 0;
577     int pos;
578     char* listend = (char*)list1 + list1len;
579     char* itembegin = (char*)list1;
580     char* itemend = itembegin;
581     char* list2end = (char*)list2 + (*list2len);
582 
583     while(itemend < listend)
584     {
585         itembegin = itemend;
586 
587         /* seek to the end of the next list item */
588         while(1)
589         {
590             if(itemend == listend || *itemend == ',')
591             {
592                 if(*(itemend - 1) != '\\')
593                 {
594                     break;
595                 }
596             }
597 
598             itemend++;
599         }
600 
601         if ((pos = SLPContainsStringList(*list2len,
602                                        list2,
603                                        itemend - itembegin,
604                                        itembegin)) != 0)
605         {
606             /* String found in the list at position pos (1-based) */
607             /* Remove it from list2                               */
608             char* dest = list2+(pos-1);
609             char* src = dest+(itemend-itembegin);
610 
611             result++;
612 
613             if (src < list2end)
614             {
615                 if (*src == ',')
616                     ++src;
617             }
618             while (src < list2end)
619             {
620                 *dest++ = *src++;
621             }
622             list2end = dest;
623         }
624 
625         itemend++;
626     }
627 
628     *list2len = (int)(list2end - (char *)list2);
629 
630     return result;
631 }
632 
633 /** Take the union of two string lists.
634  *
635  * Generates a string list that contains all unique strings within
636  * two specified string lists.
637  *
638  * @param[in] list1 - A pointer to the first string-list.
639  * @param[in] list1len - The length in bytes of @p list1.
640  * @param[in] list2 - A pointer to the second string-list.
641  * @param[in] list2len - The length in bytes of @p list2.
642  * @param[out] unionlist - A pointer to a buffer that will receive
643  *    the union of @p list1 and @p list2.
644  * @param[in,out] unionlistlen - A pointer to the size in bytes of the
645  *    @p unionlist buffer on entry; also receives the number of bytes
646  * written to the @p unionlist buffer on success.
647  *
648  * @return The length of the resulting union list, or a negative value
649  *    if @p unionlist is not large enough. If a negative value is returned
650  *    @p *unionlist will return the required size of @p unionlist.
651  *
652  * @remarks In order ensure that @p unionlist does not contain duplicates,
653  *    @p list1 must not have any duplicates. Also, as a speed optimization,
654  *    if neither @p list1 nor @p list2 contain internal duplicates, the
655  *    larger list should be passed in as @p list1.
656  *
657  * @remarks To avoid buffer overflow errors pass @p list1len +
658  *    @p list2len + 1 as the value for @p unionlistlen.
659  */
SLPUnionStringList(size_t list1len,const char * list1,size_t list2len,const char * list2,size_t * unionlistlen,char * unionlist)660 int SLPUnionStringList(size_t list1len, const char * list1, size_t list2len,
661       const char * list2, size_t * unionlistlen, char * unionlist)
662 {
663    char * listend = (char *)list2 + list2len;
664    char * itembegin = (char *)list2;
665    char * itemend = itembegin;
666    size_t itemlen;
667    size_t copiedlen;
668 
669    if (unionlist == 0 || *unionlistlen == 0 || *unionlistlen < list1len)
670    {
671       *unionlistlen = list1len + list2len + 1;
672       return  -1;
673    }
674 
675    /* Copy list1 into the unionlist since it should not have any duplicates */
676    memcpy(unionlist, list1, list1len);
677    copiedlen = list1len;
678 
679    while (itemend < listend)
680    {
681       itembegin = itemend;
682 
683       /* seek to the end of the next list item */
684       while (1)
685       {
686          if (itemend == listend || *itemend == ',')
687             if (*(itemend - 1) != '\\')
688                break;
689          itemend++;
690       }
691 
692       itemlen = itemend - itembegin;
693       if (SLPContainsStringList(list1len, list1, itemlen, itembegin) == 0)
694       {
695          if (copiedlen + itemlen + 1 > *unionlistlen)
696          {
697             *unionlistlen = list1len + list2len + 1;
698             return  -1;
699          }
700 
701          /* append a comma if not the first entry*/
702          if (copiedlen)
703          {
704             unionlist[copiedlen] = ',';
705             copiedlen++;
706          }
707          memcpy(unionlist + copiedlen, itembegin, itemlen);
708          copiedlen += itemlen;
709       }
710       itemend++;
711    }
712    *unionlistlen = copiedlen;
713    return (int)copiedlen;
714 }
715 
716 /** Test if a list is a proper sub-set of another list.
717  *
718  * Determines if @p sublist is a proper sub-set of @p list.
719  *
720  * @param[in] list - The list to compare @p sublist against.
721  * @param[in] listlen - The length of @p list in bytes.
722  * @param[in] sublist - The sub-list to be compared against @p list.
723  * @param[in] sublistlen - The length of @p sublist in bytes.
724  *
725  * @return A Boolean value; true if @p sublist is a proper subset of
726  *    @p list; false if not.
727  */
SLPSubsetStringList(size_t listlen,const char * list,size_t sublistlen,const char * sublist)728 int SLPSubsetStringList(size_t listlen, const char * list,
729       size_t sublistlen, const char * sublist)
730 {
731    unsigned curpos;
732    int sublistcount;
733 
734    /* Quick check for empty lists. Note that an empty sub-list is not
735     * considered a proper subset of any value of list.
736     */
737    if (sublistlen == 0 || listlen == 0)
738       return 0;
739 
740    /* Count the items in sublist. */
741    curpos = 0;
742    sublistcount = 1;
743    while (curpos < sublistlen)
744    {
745       if (sublist[curpos] == ',')
746          sublistcount++;
747       curpos++;
748    }
749 
750    /* Intersect the lists, return 1 if proper subset, 0 if not. */
751    return SLPIntersectStringList(listlen, list, sublistlen,
752          sublist) == sublistcount? 1 : 0;
753 }
754 
755 /** Test URL conformance.
756  *
757  * Test if a service URL conforms to accepted syntax as described in
758  * RFC 2608, section ??.
759  *
760  * @param[in] srvurl     The service url string to check.
761  * @param[in] srvurllen  The length of @p srvurl in bytes.
762  *
763  * @return Zero if @p srvurl has acceptable syntax, non-zero on failure.
764  */
SLPCheckServiceUrlSyntax(const char * srvurl,size_t srvurllen)765 int SLPCheckServiceUrlSyntax(const char * srvurl, size_t srvurllen)
766 {
767    (void)srvurl;
768    (void)srvurllen;
769 
770    /*!@todo Do we actually need to do something here to ensure correct
771     * service-url syntax, or should we expect that it will be used
772     * by smart developers who know that ambiguities could be encountered
773     * if they don't?
774 
775    if (srvurllen < 8)
776       return 1;
777    if (strncasecmp(srvurl, "service:",8))
778       return 1;
779    return 0;
780 
781     */
782    return 0;
783 }
784 
785 /** Test URL conformance.
786  *
787  * Test if a service URL conforms to accepted syntax as described in
788  * RFC 2608, section ??.
789  *
790  * @param[in] attrlist - The attribute list string to check.
791  * @param[in] attrlistlen - The length of @p attrlist in bytes.
792  *
793  * @return Zero if @p srvurl has acceptable syntax, non-zero on failure.
794  */
SLPCheckAttributeListSyntax(const char * attrlist,size_t attrlistlen)795 int SLPCheckAttributeListSyntax(const char * attrlist, size_t attrlistlen)
796 {
797    const char * slider;
798    const char * end;
799 
800    if (attrlistlen)
801    {
802       slider = attrlist;
803       end = attrlist + attrlistlen;
804       while (slider != end)
805       {
806          if (*slider == '(')
807          {
808             while (slider != end)
809             {
810                if (*slider == '=')
811                   return 0;
812                slider++;
813             }
814             return 1;
815          }
816          slider++;
817       }
818    }
819    return 0;
820 }
821 
822 #ifdef SLP_COMPARE_TEST
823 
824 /* ---------------- Test main for the slp_compare.c module ----------------
825  *
826  * Compile with:
827  *    gcc -g -Wall -I .. -o0 -D SLP_COMPARE_TEST -D DEBUG -D HAVE_CONFIG_H \
828  *       -o slp-compare-test slp_compare.c slp_linkedlist.c slp_xmalloc.c
829  */
main(void)830 int main(void)
831 {
832    /* test data */
833    static char lst1[] = "item_a_1,item_aa_2,item_aaa_3, item_aaaa_4";
834    static char lst2[] = "item_a_1";
835    static char lst3[] = "item_aa_2, item_aaaaa_5";
836    static char lst4[] = "item_aaaaa_5,item_aaaaaa_6";
837    static char str1[] = "item_x";
838    static char str2[] = "item_a_1";
839    static char str3[] = "item_aa_2";
840 
841    int count;
842 
843    /* *** SLPContainsStringList ***
844     */
845    count = SLPContainsStringList(sizeof lst1 - 1, lst1, sizeof str1 - 1, str1);
846    if (count != 0)
847       return -1;
848 
849    count = SLPContainsStringList(sizeof lst1 - 1, lst1, sizeof str2 - 1 , str2);
850    if (count != 1)
851       return -1;
852 
853    count = SLPContainsStringList(sizeof lst1 - 1, lst1, sizeof str3 - 1, str3);
854    if (count != 10)
855       return -1;
856 
857    /* *** SLPIntersectStringList ***
858     */
859    count = SLPIntersectStringList(sizeof lst1 - 1, lst1, sizeof lst2 - 1, lst2);
860    if (count != 1)
861       return -1;
862 
863    count = SLPIntersectStringList(sizeof lst1 - 1, lst1, sizeof lst3 - 1, lst3);
864    if (count != 1)
865       return -1;
866 
867    count = SLPIntersectStringList(sizeof lst1 - 1, lst1, sizeof lst4 - 1, lst4);
868    if (count != 0)
869       return -1;
870 
871    return 0;
872 }
873 
874 #endif /* SLP_COMPARE_TEST */
875 
876 /*=========================================================================*/
877