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 ß 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 ß 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 ß 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 |