mcabber/libjabber/jid.c
changeset 417 c3ae9251c197
parent 25 bf3d6e241714
equal deleted inserted replaced
416:48e7808c4191 417:c3ae9251c197
    15  *
    15  *
    16  *  Jabber
    16  *  Jabber
    17  *  Copyright (C) 1998-1999 The Jabber Team http://jabber.org/
    17  *  Copyright (C) 1998-1999 The Jabber Team http://jabber.org/
    18  */
    18  */
    19 
    19 
       
    20 /**
       
    21  * @file jid.c
       
    22  * @brief representation and normalization of JabberIDs
       
    23  */
       
    24 
    20 #include "jabber.h"
    25 #include "jabber.h"
    21 
    26 
    22 jid jid_safe(jid id)
    27 #ifdef LIBIDN
    23 {
    28 
       
    29 #  include <stringprep.h>
       
    30 
       
    31 
       
    32 /**
       
    33  * @brief datastructure to build the stringprep caches
       
    34  */
       
    35 typedef struct _jid_prep_entry_st {
       
    36     char *preped;	/**< the result of the preparation, NULL if unchanged */
       
    37     time_t last_used;	/**< when this result has last been successfully used */
       
    38     unsigned int used_count; /**< how often this result has been successfully used */
       
    39     int size;		/**< the min buffer size needed to hold the result (strlen+1) */
       
    40 } *_jid_prep_entry_t;
       
    41 
       
    42 /**
       
    43  * @brief string preparation cache
       
    44  */
       
    45 typedef struct _jid_prep_cache_st {
       
    46     xht hashtable;	/**< the hash table containing the preped strings */
       
    47     pth_mutex_t mutex;	/**< mutex controling the access to the hashtable */
       
    48     const Stringprep_profile *profile;
       
    49     			/**< the stringprep profile used for this cache */
       
    50 } *_jid_prep_cache_t;
       
    51 
       
    52 /**
       
    53  * stringprep cache containging already preped nodes
       
    54  *
       
    55  * we are using global caches here for two reasons:
       
    56  * - I do not see why different instances would want
       
    57  *   to have different caches as we are always doing
       
    58  *   the same
       
    59  * - For per instance caches I would have to modify the
       
    60  *   interface of the jid_*() functions which would break
       
    61  *   compatibility with transports
       
    62  */
       
    63 _jid_prep_cache_t _jid_prep_cache_node = NULL;
       
    64 
       
    65 /**
       
    66  * stringprep cache containing already preped domains
       
    67  */
       
    68 _jid_prep_cache_t _jid_prep_cache_domain = NULL;
       
    69 
       
    70 /**
       
    71  * stringprep cache containing already preped resources
       
    72  */
       
    73 _jid_prep_cache_t _jid_prep_cache_resource = NULL;
       
    74 
       
    75 /**
       
    76  * walker for cleaning up stringprep caches
       
    77  *
       
    78  * @param h the hash we are walking through
       
    79  * @param key the key of this item
       
    80  * @param val the value of this item
       
    81  * @param arg delete entries older as this unix timestamp
       
    82  */
       
    83 void _jid_clean_walker(xht h, const char *key, void *val, void *arg) {
       
    84     time_t *keep_newer_as = (time_t*)arg;
       
    85     _jid_prep_entry_t entry = (_jid_prep_entry_t)val;
       
    86 
       
    87     if (entry == NULL)
       
    88 	return;
       
    89 
       
    90     if (entry->last_used <= *keep_newer_as) {
       
    91 	xhash_zap(h, key);
       
    92 	if (entry->preped != NULL)
       
    93 	    free(entry->preped);
       
    94 	free(entry);
       
    95 
       
    96 	/* sorry, I have to cast the const away */
       
    97 	/* any idea how I could delete the key else? */
       
    98 	if (key != NULL)
       
    99 	    free((void*)key);
       
   100     }
       
   101 }
       
   102 
       
   103 /**
       
   104  * walk through a single stringprep cache and check which entries have expired
       
   105  */
       
   106 void _jid_clean_single_cache(_jid_prep_cache_t cache, time_t keep_newer_as) {
       
   107     /* acquire the lock on the cache */
       
   108     pth_mutex_acquire(&(cache->mutex), FALSE, NULL);
       
   109 
       
   110     /* walk over all entries */
       
   111     xhash_walk(cache->hashtable, _jid_clean_walker, (void*)&keep_newer_as);
       
   112 
       
   113     /* we're done, release the lock on the cache */
       
   114     pth_mutex_release(&(cache->mutex));
       
   115 }
       
   116 
       
   117 /**
       
   118  * walk through the stringprep caches and check which entries have expired
       
   119  */
       
   120 void jid_clean_cache() {
       
   121     /* XXX make this configurable? */
       
   122     time_t keep_newer_as = time(NULL) - 900;
       
   123 
       
   124     /* cleanup the nodeprep cache */
       
   125     _jid_clean_single_cache(_jid_prep_cache_node, keep_newer_as);
       
   126     
       
   127     /* cleanup the domain preparation cache */
       
   128     _jid_clean_single_cache(_jid_prep_cache_domain, keep_newer_as);
       
   129     
       
   130     /* cleanup the resourceprep cache */
       
   131     _jid_clean_single_cache(_jid_prep_cache_resource, keep_newer_as);
       
   132 }
       
   133 
       
   134 /**
       
   135  * caching wrapper around a stringprep function
       
   136  *
       
   137  * @param in_out_buffer buffer containing what has to be stringpreped and that gets the result
       
   138  * @param max_len size of the buffer
       
   139  * @param cache the used cache, defining also the used stringprep profile
       
   140  * @return the return code of the stringprep call
       
   141  */
       
   142 int _jid_cached_stringprep(char *in_out_buffer, int max_len, _jid_prep_cache_t cache) {
       
   143     _jid_prep_entry_t preped;
       
   144     int result = STRINGPREP_OK;
       
   145 
       
   146     /* check that the cache already exists
       
   147      * we can not do anything as we don't know which profile has to be used */
       
   148     if (cache == NULL) {
       
   149 	return STRINGPREP_UNKNOWN_PROFILE;
       
   150     }
       
   151 
       
   152     /* is there something that has to be stringpreped? */
       
   153     if (in_out_buffer == NULL) {
       
   154 	return STRINGPREP_OK;
       
   155     }
       
   156 
       
   157     /* acquire the lock on the cache */
       
   158     pth_mutex_acquire(&(cache->mutex), FALSE, NULL);
       
   159 
       
   160     /* check if the requested preparation has already been done */
       
   161     preped = (_jid_prep_entry_t)xhash_get(cache->hashtable, in_out_buffer);
       
   162     if (preped != NULL) {
       
   163 	/* we already prepared this argument */
       
   164 	if (preped->size <= max_len) {
       
   165 	    /* we can use the result */
       
   166 
       
   167 	    /* update the statistic */
       
   168 	    preped->used_count++;
       
   169 	    preped->last_used = time(NULL);
       
   170 
       
   171 	    /* do we need to copy the result? */
       
   172 	    if (preped->preped != NULL) {
       
   173 		/* copy the result */
       
   174 		strcpy(in_out_buffer, preped->preped);
       
   175 	    }
       
   176 
       
   177 	    result = STRINGPREP_OK;
       
   178 	} else {
       
   179 	    /* we need a bigger buffer */
       
   180 	    result = STRINGPREP_TOO_SMALL_BUFFER;
       
   181 	}
       
   182 	
       
   183 	/* we're done, release the lock on the cache */
       
   184 	pth_mutex_release(&(cache->mutex));
       
   185     } else {
       
   186 	char *original;
       
   187 
       
   188 	/* stringprep needs time, release the lock on the cache for the meantime */
       
   189 	pth_mutex_release(&(cache->mutex));
       
   190 
       
   191 	/* we have to keep the key */
       
   192 	original = strdup(in_out_buffer);
       
   193 	
       
   194 	/* try to prepare the string */
       
   195 	result = stringprep(in_out_buffer, max_len, STRINGPREP_NO_UNASSIGNED, cache->profile);
       
   196 
       
   197 	/* did we manage to prepare the string? */
       
   198 	if (result == STRINGPREP_OK && original != NULL) {
       
   199 	    /* generate an entry for the cache */
       
   200 	    preped = (_jid_prep_entry_t)malloc(sizeof(struct _jid_prep_entry_st));
       
   201 	    if (preped != NULL) {
       
   202 		/* has there been modified something? */
       
   203 		if (j_strcmp(in_out_buffer, original) == 0) {
       
   204 		    /* no, we don't need to store a copy of the original string */
       
   205 		    preped->preped = NULL;
       
   206 		} else {
       
   207 		    /* yes, store the stringpreped string */
       
   208 		    preped->preped = strdup(in_out_buffer);
       
   209 		}
       
   210 		preped->last_used = time(NULL);
       
   211 		preped->used_count = 1;
       
   212 		preped->size = strlen(in_out_buffer)+1;
       
   213 
       
   214 		/* acquire the lock on the cache again */
       
   215 		pth_mutex_acquire(&(cache->mutex), FALSE, NULL);
       
   216 
       
   217 		/* store the entry in the cache */
       
   218 		xhash_put(cache->hashtable, original, preped);
       
   219 
       
   220 		/* we're done, release the lock on the cache */
       
   221 		pth_mutex_release(&(cache->mutex));
       
   222 	    } else {
       
   223 		/* we don't need the copy of the key, if there is no memory to store it */
       
   224 		free(original);
       
   225 	    }
       
   226 	} else {
       
   227 	    /* we don't need the copy of the original value */
       
   228 	    if (original != NULL)
       
   229 		free(original);
       
   230 	}
       
   231     }
       
   232 
       
   233     return result;
       
   234 }
       
   235 
       
   236 /**
       
   237  * free a single stringprep cache
       
   238  *
       
   239  * @param cache the cache to free
       
   240  */
       
   241 void _jid_stop_single_cache(_jid_prep_cache_t *cache) {
       
   242     if (*cache == NULL)
       
   243 	return;
       
   244 
       
   245     _jid_clean_single_cache(*cache, time(NULL));
       
   246     
       
   247     pth_mutex_acquire(&((*cache)->mutex), FALSE, NULL);
       
   248     xhash_free((*cache)->hashtable);
       
   249 
       
   250     free(*cache);
       
   251 
       
   252     *cache = NULL;
       
   253 }
       
   254 
       
   255 /**
       
   256  * init a single stringprep cache
       
   257  *
       
   258  * @param cache the cache to init
       
   259  * @param prime the prime used to init the hashtable
       
   260  * @param profile profile used to prepare the strings
       
   261  */
       
   262 void _jid_init_single_cache(_jid_prep_cache_t *cache, int prime, const Stringprep_profile *profile) {
       
   263     /* do not init a cache twice */
       
   264     if (*cache == NULL) {
       
   265 	*cache = (_jid_prep_cache_t)malloc(sizeof(struct _jid_prep_cache_st));
       
   266 	pth_mutex_init(&((*cache)->mutex));
       
   267 	(*cache)->hashtable = xhash_new(prime);
       
   268 	(*cache)->profile = profile;
       
   269     }
       
   270 }
       
   271 
       
   272 /**
       
   273  * free the stringprep caches
       
   274  */
       
   275 void jid_stop_caching() {
       
   276     _jid_stop_single_cache(&_jid_prep_cache_node);
       
   277     _jid_stop_single_cache(&_jid_prep_cache_domain);
       
   278     _jid_stop_single_cache(&_jid_prep_cache_resource);
       
   279 }
       
   280 
       
   281 /**
       
   282  * init the stringprep caches
       
   283  * (do not call this twice at the same time, we do not have the mutexes yet)
       
   284  */
       
   285 void jid_init_cache() {
       
   286     /* init the nodeprep cache */
       
   287     _jid_init_single_cache(&_jid_prep_cache_node, 2003, stringprep_xmpp_nodeprep);
       
   288 
       
   289     /* init the nameprep cache (domains) */
       
   290     _jid_init_single_cache(&_jid_prep_cache_domain, 2003, stringprep_nameprep);
       
   291 
       
   292     /* init the resourceprep cache */
       
   293     _jid_init_single_cache(&_jid_prep_cache_resource, 2003, stringprep_xmpp_resourceprep);
       
   294 }
       
   295 
       
   296 /**
       
   297  * nameprep the domain identifier in a JID and check if it is valid
       
   298  *
       
   299  * @param jid data structure holding the JID
       
   300  * @return 0 if JID is valid, non zero otherwise
       
   301  */
       
   302 int _jid_safe_domain(jid id) {
       
   303     int result=0;
       
   304 
       
   305     /* there must be a domain identifier */
       
   306     if (j_strlen(id->server) == 0)
       
   307 	return 1;
       
   308 
       
   309     /* nameprep the domain identifier */
       
   310     result = _jid_cached_stringprep(id->server, strlen(id->server)+1, _jid_prep_cache_domain);
       
   311     if (result == STRINGPREP_TOO_SMALL_BUFFER) {
       
   312 	/* nameprep wants to expand the string, e.g. conversion from &szlig; to ss */
       
   313 	size_t biggerbuffersize = 1024;
       
   314 	char *biggerbuffer = pmalloc(id->p, biggerbuffersize);
       
   315 	if (biggerbuffer == NULL)
       
   316 	    return 1;
       
   317 	strcpy(biggerbuffer, id->server);
       
   318 	result = _jid_cached_stringprep(biggerbuffer, biggerbuffersize, _jid_prep_cache_domain);
       
   319 	id->server = biggerbuffer;
       
   320     }
       
   321     if (result != STRINGPREP_OK)
       
   322 	return 1;
       
   323 
       
   324     /* the namepreped domain must not be longer than 1023 bytes */
       
   325     if (j_strlen(id->server) > 1023)
       
   326 	return 1;
       
   327 
       
   328     /* if nothing failed, the domain is valid */
       
   329     return 0;
       
   330 }
       
   331 
       
   332 /**
       
   333  * nodeprep the node identifier in a JID and check if it is valid
       
   334  *
       
   335  * @param jid data structure holding the JID
       
   336  * @return 0 if JID is valid, non zero otherwise
       
   337  */
       
   338 int _jid_safe_node(jid id) {
       
   339     int result=0;
       
   340 
       
   341     /* it is valid to have no node identifier in the JID */
       
   342     if (id->user == NULL)
       
   343 	return 0;
       
   344 
       
   345     /* nodeprep */
       
   346     result = _jid_cached_stringprep(id->user, strlen(id->user)+1, _jid_prep_cache_node);
       
   347     if (result == STRINGPREP_TOO_SMALL_BUFFER) {
       
   348 	/* nodeprep wants to expand the string, e.g. conversion from &szlig; to ss */
       
   349 	size_t biggerbuffersize = 1024;
       
   350 	char *biggerbuffer = pmalloc(id->p, biggerbuffersize);
       
   351 	if (biggerbuffer == NULL)
       
   352 	    return 1;
       
   353 	strcpy(biggerbuffer, id->user);
       
   354 	result = _jid_cached_stringprep(biggerbuffer, biggerbuffersize, _jid_prep_cache_node);
       
   355 	id->user = biggerbuffer;
       
   356     }
       
   357     if (result != STRINGPREP_OK)
       
   358 	return 1;
       
   359 
       
   360     /* the nodepreped node must not be longer than 1023 bytes */
       
   361     if (j_strlen(id->user) > 1023)
       
   362 	return 1;
       
   363 
       
   364     /* if nothing failed, the node is valid */
       
   365     return 0;
       
   366 }
       
   367 
       
   368 /**
       
   369  * resourceprep the resource identifier in a JID and check if it is valid
       
   370  *
       
   371  * @param jid data structure holding the JID
       
   372  * @return 0 if JID is valid, non zero otherwise
       
   373  */
       
   374 int _jid_safe_resource(jid id) {
       
   375     int result=0;
       
   376 
       
   377     /* it is valid to have no resource identifier in the JID */
       
   378     if (id->resource == NULL)
       
   379 	return 0;
       
   380 
       
   381     /* resource prep the resource identifier */
       
   382     result = _jid_cached_stringprep(id->resource, strlen(id->resource)+1, _jid_prep_cache_resource);
       
   383     if (result == STRINGPREP_TOO_SMALL_BUFFER) {
       
   384 	/* resourceprep wants to expand the string, e.g. conversion from &szlig; to ss */
       
   385 	size_t biggerbuffersize = 1024;
       
   386 	char *biggerbuffer = pmalloc(id->p, biggerbuffersize);
       
   387 	if (biggerbuffer == NULL)
       
   388 	    return 1;
       
   389 	strcpy(biggerbuffer, id->resource);
       
   390 	result = _jid_cached_stringprep(id->resource, strlen(id->resource)+1, _jid_prep_cache_resource);
       
   391 	id->resource = biggerbuffer;
       
   392     }
       
   393     if (result != STRINGPREP_OK)
       
   394 	return 1;
       
   395 
       
   396     /* the resourcepreped node must not be longer than 1023 bytes */
       
   397     if (j_strlen(id->resource) > 1023)
       
   398 	return 1;
       
   399 
       
   400     /* if nothing failed, the resource is valid */
       
   401     return 0;
       
   402 
       
   403 }
       
   404 
       
   405 #else /* no LIBIDN */
       
   406 
       
   407 /**
       
   408  * check if the domain identifier in a JID is valid
       
   409  *
       
   410  * @param jid data structure holding the JID
       
   411  * @return 0 if domain is valid, non zero otherwise
       
   412  */
       
   413 int _jid_safe_domain(jid id) {
    24     char *str;
   414     char *str;
    25 
   415 
    26     if(strlen(id->server) == 0 || strlen(id->server) > 255)
   416     /* there must be a domain identifier */
    27 	return NULL;
   417     if (j_strlen(id->server) == 0)
       
   418 	return 1;
       
   419 
       
   420     /* and it must not be longer than 1023 bytes */
       
   421     if (strlen(id->server) > 1023)
       
   422 	return 1;
    28 
   423 
    29     /* lowercase the hostname, make sure it's valid characters */
   424     /* lowercase the hostname, make sure it's valid characters */
    30     for(str = id->server; *str != '\0'; str++)
   425     for(str = id->server; *str != '\0'; str++)
    31     {
   426     {
    32 	*str = tolower(*str);
   427         *str = tolower(*str);
    33 	if(!(isalnum(*str) || *str == '.' || *str == '-' || *str == '_')) return NULL;
   428         if(!(isalnum(*str) || *str == '.' || *str == '-' || *str == '_')) return 1;
    34     }
   429     }
    35 
   430 
    36     /* cut off the user */
   431     /* otherwise it's okay as far as we can tell without LIBIDN */
    37     if(id->user != NULL && strlen(id->user) > 64)
   432     return 0;
    38 	id->user[64] = '\0';
   433 }
       
   434 
       
   435 /**
       
   436  * check if the node identifier in a JID is valid
       
   437  *
       
   438  * @param jid data structure holding the JID
       
   439  * @return 0 if node is valid, non zero otherwise
       
   440  */
       
   441 int _jid_safe_node(jid id) {
       
   442     char *str;
       
   443 
       
   444     /* node identifiers may not be longer than 1023 bytes */
       
   445     if (j_strlen(id->user) > 1023)
       
   446 	return 1;
    39 
   447 
    40     /* check for low and invalid ascii characters in the username */
   448     /* check for low and invalid ascii characters in the username */
    41     if(id->user != NULL)
   449     if(id->user != NULL)
    42 	for(str = id->user; *str != '\0'; str++)
   450         for(str = id->user; *str != '\0'; str++)
    43 	    if(*str <= 32 || *str == ':' || *str == '@' || *str == '<' || *str == '>' || *str == '\'' || *str == '"' || *str == '&') return NULL;
   451             if(*str <= 32 || *str == ':' || *str == '@' || *str == '<' || *str == '>' || *str == '\'' || *str == '"' || *str == '&') return 1;
       
   452 
       
   453     /* otherwise it's okay as far as we can tell without LIBIDN */
       
   454     return 0;
       
   455 }
       
   456 
       
   457 /**
       
   458  * check if the resource identifier in a JID is valid
       
   459  *
       
   460  * @param jid data structure holding the JID
       
   461  * @return 0 if resource is valid, non zero otherwise
       
   462  */
       
   463 int _jid_safe_resource(jid id) {
       
   464     /* resources may not be longer than 1023 bytes */
       
   465     if (j_strlen(id->resource) > 1023)
       
   466 	return 1;
       
   467 
       
   468     /* otherwise it's okay as far as we can tell without LIBIDN */
       
   469     return 0;
       
   470 }
       
   471 
       
   472 #endif
       
   473 
       
   474 /**
       
   475  * nodeprep/nameprep/resourceprep the JID and check if it is valid
       
   476  *
       
   477  * @param jid data structure holding the JID
       
   478  * @return NULL if the JID is invalid, pointer to the jid otherwise
       
   479  */
       
   480 jid jid_safe(jid id)
       
   481 {
       
   482     if (_jid_safe_domain(id))
       
   483 	return NULL;
       
   484     if (_jid_safe_node(id))
       
   485 	return NULL;
       
   486     if (_jid_safe_resource(id))
       
   487 	return NULL;
    44 
   488 
    45     return id;
   489     return id;
    46 }
   490 }
    47 
   491 
    48 jid jid_new(pool p, char *idstr)
   492 jid jid_new(pool p, char *idstr)
    49 {
   493 {
    50     char *server, *resource, *type, *str;
   494     char *server, *resource, *type, *str;
    51     jid id;
   495     jid id;
    52 
   496 
    53     if(p == NULL || idstr == NULL || strlen(idstr) == 0)
   497     if(p == NULL || idstr == NULL || strlen(idstr) == 0)
    54 	return NULL;
   498         return NULL;
    55 
   499 
    56     /* user@server/resource */
   500     /* user@server/resource */
    57 
   501 
    58     str = pstrdup(p, idstr);
   502     str = pstrdup(p, idstr);
    59 
   503 
    60     id = pmalloc(p,sizeof(struct jid_struct));
   504     id = pmalloco(p,sizeof(struct jid_struct));
    61     id->full = id->server = id->user = id->resource = NULL;
       
    62     id->p = p;
   505     id->p = p;
    63     id->next = NULL;
       
    64 
   506 
    65     resource = strstr(str,"/");
   507     resource = strstr(str,"/");
    66     if(resource != NULL)
   508     if(resource != NULL)
    67     {
   509     {
    68 	*resource = '\0';
   510         *resource = '\0';
    69 	++resource;
   511         ++resource;
    70 	if(strlen(resource) > 0)
   512         if(strlen(resource) > 0)
    71 	    id->resource = resource;
   513             id->resource = resource;
    72     }else{
   514     }else{
    73 	resource = str + strlen(str); /* point to end */
   515         resource = str + strlen(str); /* point to end */
    74     }
   516     }
    75 
   517 
    76     type = strstr(str,":");
   518     type = strstr(str,":");
    77     if(type != NULL && type < resource)
   519     if(type != NULL && type < resource)
    78     {
   520     {
    79 	*type = '\0';
   521         *type = '\0';
    80 	++type;
   522         ++type;
    81 	str = type; /* ignore the type: prefix */
   523         str = type; /* ignore the type: prefix */
    82     }
   524     }
    83 
   525 
    84     server = strstr(str,"@");
   526     server = strstr(str,"@");
    85     if(server == NULL || server > resource)
   527     if(server == NULL || server > resource)
    86     { /* if there's no @, it's just the server address */
   528     { /* if there's no @, it's just the server address */
    87 	id->server = str;
   529         id->server = str;
    88     }else{
   530     }else{
    89 	*server = '\0';
   531         *server = '\0';
    90 	++server;
   532         ++server;
    91 	id->server = server;
   533         id->server = server;
    92 	if(strlen(str) > 0)
   534         if(strlen(str) > 0)
    93 	    id->user = str;
   535             id->user = str;
    94     }
   536     }
    95 
   537 
    96     return jid_safe(id);
   538     return jid_safe(id);
    97 }
   539 }
    98 
   540 
    99 void jid_set(jid id, char *str, int item)
   541 void jid_set(jid id, char *str, int item)
   100 {
   542 {
   101     char *old;
   543     char *old;
   102 
   544 
   103     if(id == NULL)
   545     if(id == NULL)
   104 	return;
   546         return;
   105 
   547 
   106     /* invalidate the cached copy */
   548     /* invalidate the cached copy */
   107     id->full = NULL;
   549     id->full = NULL;
   108 
   550 
   109     switch(item)
   551     switch(item)
   110     {
   552     {
   111     case JID_RESOURCE:
   553     case JID_RESOURCE:
   112 	if(str != NULL && strlen(str) != 0)
   554 	old = id->resource;
   113 	    id->resource = pstrdup(id->p, str);
   555         if(str != NULL && strlen(str) != 0)
   114 	else
   556             id->resource = pstrdup(id->p, str);
   115 	    id->resource = NULL;
   557         else
   116 	break;
   558             id->resource = NULL;
       
   559         if(_jid_safe_resource(id))
       
   560             id->resource = old; /* revert if invalid */
       
   561         break;
   117     case JID_USER:
   562     case JID_USER:
   118 	old = id->user;
   563         old = id->user;
   119 	if(str != NULL && strlen(str) != 0)
   564         if(str != NULL && strlen(str) != 0)
   120 	    id->user = pstrdup(id->p, str);
   565             id->user = pstrdup(id->p, str);
   121 	else
   566         else
   122 	    id->user = NULL;
   567             id->user = NULL;
   123 	if(jid_safe(id) == NULL)
   568         if(_jid_safe_node(id))
   124 	    id->user = old; /* revert if invalid */
   569             id->user = old; /* revert if invalid */
   125 	break;
   570         break;
   126     case JID_SERVER:
   571     case JID_SERVER:
   127 	old = id->server;
   572         old = id->server;
   128 	id->server = pstrdup(id->p, str);
   573         id->server = pstrdup(id->p, str);
   129 	if(jid_safe(id) == NULL)
   574         if(_jid_safe_domain(id))
   130 	    id->server = old; /* revert if invalid */
   575             id->server = old; /* revert if invalid */
   131 	break;
   576         break;
   132     }
   577     }
   133 
   578 
   134 }
   579 }
   135 
   580 
   136 char *jid_full(jid id)
   581 char *jid_full(jid id)
   137 {
   582 {
   138     spool s;
   583     spool s;
   139 
   584 
   140     if(id == NULL)
   585     if(id == NULL)
   141 	return NULL;
   586         return NULL;
   142 
   587 
   143     /* use cached copy */
   588     /* use cached copy */
   144     if(id->full != NULL)
   589     if(id->full != NULL)
   145 	return id->full;
   590         return id->full;
   146 
   591 
   147     s = spool_new(id->p);
   592     s = spool_new(id->p);
   148 
   593 
   149     if(id->user != NULL)
   594     if(id->user != NULL)
   150 	spooler(s, id->user,"@",s);
   595         spooler(s, id->user,"@",s);
   151 
   596 
   152     spool_add(s, id->server);
   597     spool_add(s, id->server);
   153 
   598 
   154     if(id->resource != NULL)
   599     if(id->resource != NULL)
   155 	spooler(s, "/",id->resource,s);
   600         spooler(s, "/",id->resource,s);
   156 
   601 
   157     id->full = spool_print(s);
   602     id->full = spool_print(s);
   158     return id->full;
   603     return id->full;
   159 }
   604 }
   160 
   605 
   175     x = _xmlnode_new(id->p, cur, NTYPE_TAG);
   620     x = _xmlnode_new(id->p, cur, NTYPE_TAG);
   176 
   621 
   177     cur = qmark;
   622     cur = qmark;
   178     while(cur != '\0')
   623     while(cur != '\0')
   179     {
   624     {
   180 	eq = strstr(cur, "=");
   625         eq = strstr(cur, "=");
   181 	if(eq == NULL) break;
   626         if(eq == NULL) break;
   182 	*eq = '\0';
   627         *eq = '\0';
   183 	eq++;
   628         eq++;
   184 
   629 
   185 	amp = strstr(eq, "&");
   630         amp = strstr(eq, "&");
   186 	if(amp != NULL)
   631         if(amp != NULL)
   187 	{
   632         {
   188 	    *amp = '\0';
   633             *amp = '\0';
   189 	    amp++;
   634             amp++;
   190 	}
   635         }
   191 
   636 
   192 	xmlnode_put_attrib(x,cur,eq);
   637         xmlnode_put_attrib(x,cur,eq);
   193 
   638 
   194 	if(amp != NULL)
   639         if(amp != NULL)
   195 	    cur = amp;
   640             cur = amp;
   196 	else
   641         else
   197 	    break;
   642             break;
   198     }
   643     }
   199 
   644 
   200     return x;
   645     return x;
   201 }
   646 }
   202 
   647 
   215 }
   660 }
   216 
   661 
   217 int jid_cmp(jid a, jid b)
   662 int jid_cmp(jid a, jid b)
   218 {
   663 {
   219     if(a == NULL || b == NULL)
   664     if(a == NULL || b == NULL)
   220 	return -1;
   665         return -1;
   221 
   666 
   222     if(_jid_nullstrcmp(a->resource, b->resource) != 0) return -1;
   667     if(_jid_nullstrcmp(a->resource, b->resource) != 0) return -1;
   223     if(_jid_nullstrcasecmp(a->user, b->user) != 0) return -1;
   668     if(_jid_nullstrcasecmp(a->user, b->user) != 0) return -1;
   224     if(_jid_nullstrcmp(a->server, b->server) != 0) return -1;
   669     if(_jid_nullstrcmp(a->server, b->server) != 0) return -1;
   225 
   670 
   228 
   673 
   229 /* suggested by Anders Qvist <quest@valdez.netg.se> */
   674 /* suggested by Anders Qvist <quest@valdez.netg.se> */
   230 int jid_cmpx(jid a, jid b, int parts)
   675 int jid_cmpx(jid a, jid b, int parts)
   231 {
   676 {
   232     if(a == NULL || b == NULL)
   677     if(a == NULL || b == NULL)
   233 	return -1;
   678         return -1;
   234 
   679 
   235     if(parts & JID_RESOURCE && _jid_nullstrcmp(a->resource, b->resource) != 0) return -1;
   680     if(parts & JID_RESOURCE && _jid_nullstrcmp(a->resource, b->resource) != 0) return -1;
   236     if(parts & JID_USER && _jid_nullstrcasecmp(a->user, b->user) != 0) return -1;
   681     if(parts & JID_USER && _jid_nullstrcasecmp(a->user, b->user) != 0) return -1;
   237     if(parts & JID_SERVER && _jid_nullstrcmp(a->server, b->server) != 0) return -1;
   682     if(parts & JID_SERVER && _jid_nullstrcmp(a->server, b->server) != 0) return -1;
   238 
   683 
   243 jid jid_append(jid a, jid b)
   688 jid jid_append(jid a, jid b)
   244 {
   689 {
   245     jid next;
   690     jid next;
   246 
   691 
   247     if(a == NULL)
   692     if(a == NULL)
   248 	return NULL;
   693         return NULL;
   249 
   694 
   250     if(b == NULL)
   695     if(b == NULL)
   251 	return a;
   696         return a;
   252 
   697 
   253     next = a;
   698     next = a;
   254     while(next != NULL)
   699     while(next != NULL)
   255     {
   700     {
   256 	/* check for dups */
   701         /* check for dups */
   257 	if(jid_cmp(next,b) == 0)
   702         if(jid_cmp(next,b) == 0)
   258 	    break;
   703             break;
   259 	if(next->next == NULL)
   704         if(next->next == NULL)
   260 	    next->next = jid_new(a->p,jid_full(b));
   705             next->next = jid_new(a->p,jid_full(b));
   261 	next = next->next;
   706         next = next->next;
   262     }
   707     }
   263     return a;
   708     return a;
   264 }
   709 }
   265 
   710 
   266 xmlnode jid_nodescan(jid id, xmlnode x)
   711 xmlnode jid_nodescan(jid id, xmlnode x)
   272     if(id == NULL || xmlnode_get_firstchild(x) == NULL) return NULL;
   717     if(id == NULL || xmlnode_get_firstchild(x) == NULL) return NULL;
   273 
   718 
   274     p = pool_new();
   719     p = pool_new();
   275     for(cur = xmlnode_get_firstchild(x); cur != NULL; cur = xmlnode_get_nextsibling(cur))
   720     for(cur = xmlnode_get_firstchild(x); cur != NULL; cur = xmlnode_get_nextsibling(cur))
   276     {
   721     {
   277 	if(xmlnode_get_type(cur) != NTYPE_TAG) continue;
   722         if(xmlnode_get_type(cur) != NTYPE_TAG) continue;
   278 
   723 
   279 	tmp = jid_new(p,xmlnode_get_attrib(cur,"jid"));
   724         tmp = jid_new(p,xmlnode_get_attrib(cur,"jid"));
   280 	if(tmp == NULL) continue;
   725         if(tmp == NULL) continue;
   281 
   726 
   282 	if(jid_cmp(tmp,id) == 0) break;
   727         if(jid_cmp(tmp,id) == 0) break;
   283     }
   728     }
   284     pool_free(p);
   729     pool_free(p);
   285 
   730 
   286     return cur;
   731     return cur;
   287 }
   732 }
       
   733 
       
   734 jid jid_user(jid a)
       
   735 {
       
   736     jid ret;
       
   737 
       
   738     if(a == NULL || a->resource == NULL) return a;
       
   739 
       
   740     ret = pmalloco(a->p,sizeof(struct jid_struct));
       
   741     ret->p = a->p;
       
   742     ret->user = a->user;
       
   743     ret->server = a->server;
       
   744 
       
   745     return ret;
       
   746 }