Merged silc_1_1_branch to trunk.
[silc.git] / lib / silccore / silcattrs.c
1 /*
2
3   silcattrs.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 2002 - 2007 Pekka Riikonen
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; version 2 of the License.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18 */
19 /* Implementation of Attribute Payload routines */
20 /* $Id$ */
21
22 #include "silc.h"
23 #include "silcattrs.h"
24
25 /******************************************************************************
26
27                              Attribute Payload
28
29 ******************************************************************************/
30
31 struct SilcAttributePayloadStruct {
32   SilcAttribute attribute;
33   SilcAttributeFlags flags;
34   SilcUInt16 data_len;
35   unsigned char *data;
36 };
37
38 /* Internal routine for encoding a attribute */
39
40 static unsigned char *
41 silc_attribute_payload_encode_int(SilcAttribute attribute,
42                                   SilcAttributeFlags flags,
43                                   void *object,
44                                   SilcUInt32 object_size,
45                                   SilcUInt32 *ret_len)
46 {
47   SilcBuffer tmpbuf = NULL;
48   unsigned char tmp[4], *str = NULL, *ret;
49   SilcUInt32 len;
50
51   /* Encode according to attribute type */
52   if (flags & SILC_ATTRIBUTE_FLAG_VALID) {
53     if (!object && !object_size)
54       return NULL;
55
56     switch (attribute) {
57
58     case SILC_ATTRIBUTE_USER_INFO:
59       {
60         SilcVCard vcard = object;
61         if (object_size != sizeof(*vcard))
62           return NULL;
63         str = silc_vcard_encode(vcard, &object_size);
64         if (!str)
65           return NULL;
66         object = str;
67       }
68       break;
69
70     case SILC_ATTRIBUTE_SERVICE:
71       {
72         SilcAttributeObjService *service = object;
73         SilcUInt32 len2;
74         if (object_size != sizeof(*service))
75           return NULL;
76         len = strlen(service->address);
77         len2 = strlen(service->signon);
78         tmpbuf = silc_buffer_alloc_size(13 + len + len2);
79         if (!tmpbuf)
80           return NULL;
81         silc_buffer_format(tmpbuf,
82                            SILC_STR_UI_INT(service->port),
83                            SILC_STR_UI_SHORT(len),
84                            SILC_STR_UI_XNSTRING(service->address, len),
85                            SILC_STR_UI_CHAR(service->status),
86                            SILC_STR_UI_SHORT(len2),
87                            SILC_STR_UI_XNSTRING(service->signon, len2),
88                            SILC_STR_UI_INT(service->idle),
89                            SILC_STR_END);
90         object = tmpbuf->data;
91         object_size = silc_buffer_len(tmpbuf);
92       }
93       break;
94
95     case SILC_ATTRIBUTE_STATUS_MOOD:
96     case SILC_ATTRIBUTE_PREFERRED_CONTACT:
97       {
98         SilcUInt32 mask = SILC_PTR_TO_32(object);
99         if (object_size != sizeof(SilcUInt32))
100           return NULL;
101         SILC_PUT32_MSB(mask, tmp);
102         object = tmp;
103         object_size = sizeof(SilcUInt32);
104       }
105       break;
106
107     case SILC_ATTRIBUTE_STATUS_FREETEXT:
108     case SILC_ATTRIBUTE_PREFERRED_LANGUAGE:
109     case SILC_ATTRIBUTE_TIMEZONE:
110       {
111         unsigned char *string = object;
112         str = silc_malloc(2 + object_size);
113         if (!str)
114           return NULL;
115         SILC_PUT16_MSB(object_size, str);
116         memcpy(str + 2, string, object_size);
117         object = str;
118         object_size += 2;
119       }
120       break;
121
122     case SILC_ATTRIBUTE_STATUS_MESSAGE:
123     case SILC_ATTRIBUTE_EXTENSION:
124     case SILC_ATTRIBUTE_USER_ICON:
125       {
126         SilcMime mime = object;
127         if (object_size != sizeof(*mime))
128           return NULL;
129         str = silc_mime_encode(mime, &object_size);
130         if (!str)
131           return NULL;
132         object = str;
133       }
134       break;
135
136     case SILC_ATTRIBUTE_GEOLOCATION:
137       {
138         SilcAttributeObjGeo *geo = object;
139         SilcUInt32 len1, len2, len3, len4;
140         if (object_size != sizeof(*geo))
141           return NULL;
142         len1 = (geo->longitude ? strlen(geo->longitude) : 0);
143         len2 = (geo->latitude  ? strlen(geo->latitude)  : 0);
144         len3 = (geo->altitude  ? strlen(geo->altitude)  : 0);
145         len4 = (geo->accuracy  ? strlen(geo->accuracy)  : 0);
146         if (len1 + len2 + len3 + len4 == 0)
147           return NULL;
148         len = len1 + len2 + len3 + len4;
149         tmpbuf = silc_buffer_alloc_size(8 + len);
150         if (!tmpbuf)
151           return NULL;
152         silc_buffer_format(tmpbuf,
153                            SILC_STR_UI_SHORT(len1),
154                            SILC_STR_UI16_STRING(len1 ? geo->longitude : ""),
155                            SILC_STR_UI_SHORT(len2),
156                            SILC_STR_UI16_STRING(len2 ? geo->latitude : ""),
157                            SILC_STR_UI_SHORT(len3),
158                            SILC_STR_UI16_STRING(len3 ? geo->altitude : ""),
159                            SILC_STR_UI_SHORT(len4),
160                            SILC_STR_UI16_STRING(len4 ? geo->accuracy : ""),
161                            SILC_STR_END);
162         object = tmpbuf->data;
163         object_size = silc_buffer_len(tmpbuf);
164       }
165       break;
166
167     case SILC_ATTRIBUTE_DEVICE_INFO:
168       {
169         SilcAttributeObjDevice *dev = object;
170         SilcUInt32 len1, len2, len3, len4;
171         if (object_size != sizeof(*dev))
172           return NULL;
173         len1 = (dev->manufacturer ? strlen(dev->manufacturer) : 0);
174         len2 = (dev->version      ? strlen(dev->version)      : 0);
175         len3 = (dev->model        ? strlen(dev->model)        : 0);
176         len4 = (dev->language     ? strlen(dev->language)     : 0);
177         if (len1 + len2 + len3 + len4 == 0)
178           return NULL;
179         len = len1 + len2 + len3 + len4;
180         tmpbuf = silc_buffer_alloc_size(4 + 8 + len);
181         if (!tmpbuf)
182           return NULL;
183         silc_buffer_format(tmpbuf,
184                            SILC_STR_UI_INT(dev->type),
185                            SILC_STR_UI_SHORT(len1),
186                            SILC_STR_UI16_STRING(len1 ? dev->manufacturer : ""),
187                            SILC_STR_UI_SHORT(len2),
188                            SILC_STR_UI16_STRING(len2 ? dev->version : ""),
189                            SILC_STR_UI_SHORT(len3),
190                            SILC_STR_UI16_STRING(len3 ? dev->model : ""),
191                            SILC_STR_UI_SHORT(len4),
192                            SILC_STR_UI16_STRING(len4 ? dev->language : ""),
193                            SILC_STR_END);
194         object = tmpbuf->data;
195         object_size = silc_buffer_len(tmpbuf);
196       }
197       break;
198
199     case SILC_ATTRIBUTE_PHONE_NUMBER:
200       {
201         SilcAttributeObjPN *pn = object;
202         if (object_size != sizeof(*pn))
203           return NULL;
204         if (!pn->number || strlen(pn->number) < 5)
205           return NULL;
206         tmpbuf = silc_buffer_alloc(0);
207         if (!tmpbuf)
208           return NULL;
209         if (silc_buffer_format(tmpbuf,
210                                SILC_STR_UI_INT(pn->format),
211                                SILC_STR_UI_SHORT(strlen(pn->number)),
212                                SILC_STR_UI16_STRING(pn->number),
213                                SILC_STR_END) < 0)
214           return NULL;
215         object = tmpbuf->data;
216         object_size = silc_buffer_len(tmpbuf);
217       }
218       break;
219
220     case SILC_ATTRIBUTE_USER_PUBLIC_KEY:
221     case SILC_ATTRIBUTE_SERVER_PUBLIC_KEY:
222       {
223         SilcAttributeObjPk *pk = object;
224         if (object_size != sizeof(*pk))
225           return NULL;
226         len = (pk->type ? strlen(pk->type) : 0);
227         tmpbuf = silc_buffer_alloc_size(2 + len + pk->data_len);
228         if (!tmpbuf)
229           return NULL;
230         silc_buffer_format(tmpbuf,
231                            SILC_STR_UI_SHORT(len),
232                            SILC_STR_UI16_STRING(pk->type),
233                            SILC_STR_UI_XNSTRING(pk->data, pk->data_len),
234                            SILC_STR_END);
235         object = tmpbuf->data;
236         object_size = silc_buffer_len(tmpbuf);
237       }
238       break;
239
240     case SILC_ATTRIBUTE_USER_DIGITAL_SIGNATURE:
241     case SILC_ATTRIBUTE_SERVER_DIGITAL_SIGNATURE:
242       {
243         SilcAttributeObjPk *pk = object;
244         if (object_size != sizeof(*pk))
245           return NULL;
246         object = pk->data;
247         object_size = pk->data_len;
248       }
249       break;
250
251     default:
252       return NULL;
253       break;
254     }
255
256     ret = silc_memdup(object, object_size);
257
258     if (tmpbuf)
259       silc_buffer_free(tmpbuf);
260     silc_free(str);
261
262     if (ret_len)
263       *ret_len = object_size;
264
265     return ret;
266   }
267
268   return NULL;
269 }
270
271 /* Allocates attribute payload and encodes the attribute there */
272
273 SilcAttributePayload silc_attribute_payload_alloc(SilcAttribute attribute,
274                                                   SilcAttributeFlags flags,
275                                                   void *object,
276                                                   SilcUInt32 object_size)
277 {
278   SilcAttributePayload attr;
279   SilcUInt32 tmp_len;
280
281   attr = silc_calloc(1, sizeof(*attr));
282   if (!attr)
283     return NULL;
284
285   attr->attribute = attribute;
286   attr->flags = flags;
287   attr->data =
288     silc_attribute_payload_encode_int(attribute, flags, object,
289                                       object_size, &tmp_len);
290   attr->data_len = (SilcUInt16)tmp_len;
291   if (!attr->data) {
292     silc_free(attr);
293     return NULL;
294   }
295
296   return attr;
297 }
298
299 /* Parse list of payloads */
300
301 SilcDList silc_attribute_payload_parse(const unsigned char *payload,
302                                        SilcUInt32 payload_len)
303 {
304   SilcBufferStruct buffer;
305   SilcDList list;
306   SilcAttributePayload newp;
307   SilcUInt32 len;
308   int ret;
309
310   SILC_LOG_DEBUG(("Parsing Attribute Payload list"));
311
312   silc_buffer_set(&buffer, (unsigned char *)payload, payload_len);
313   list = silc_dlist_init();
314
315   while (silc_buffer_len(&buffer)) {
316     newp = silc_calloc(1, sizeof(*newp));
317     if (!newp)
318       goto err;
319     ret = silc_buffer_unformat(&buffer,
320                                SILC_STR_UI_CHAR(&newp->attribute),
321                                SILC_STR_UI_CHAR(&newp->flags),
322                                SILC_STR_UI16_NSTRING_ALLOC(&newp->data,
323                                                            &newp->data_len),
324                                SILC_STR_END);
325     if (ret == -1)
326       goto err;
327
328     if (newp->data_len > silc_buffer_len(&buffer) - 4) {
329       SILC_LOG_ERROR(("Incorrect attribute payload in list"));
330       goto err;
331     }
332
333     len = 4 + newp->data_len;
334     if (silc_buffer_len(&buffer) < len)
335       break;
336     silc_buffer_pull(&buffer, len);
337
338     silc_dlist_add(list, newp);
339   }
340
341   return list;
342
343  err:
344   silc_attribute_payload_list_free(list);
345   return NULL;
346 }
347
348 /* Encode one attribute payload to buffer */
349
350 SilcBuffer silc_attribute_payload_encode(SilcBuffer attrs,
351                                          SilcAttribute attribute,
352                                          SilcAttributeFlags flags,
353                                          void *object,
354                                          SilcUInt32 object_size)
355 {
356   object = silc_attribute_payload_encode_int(attribute, flags, object,
357                                              object_size, &object_size);
358   attrs = silc_attribute_payload_encode_data(attrs, attribute, flags,
359                                              (const unsigned char *)object,
360                                              object_size);
361   silc_free(object);
362   return attrs;
363 }
364
365 /* Encoded the attribute data directly to buffer */
366
367 SilcBuffer silc_attribute_payload_encode_data(SilcBuffer attrs,
368                                               SilcAttribute attribute,
369                                               SilcAttributeFlags flags,
370                                               const unsigned char *data,
371                                               SilcUInt32 data_len)
372 {
373   SilcBuffer buffer = attrs;
374   SilcUInt32 len;
375
376   len = 4 + (SilcUInt16)data_len;
377   buffer = silc_buffer_realloc(buffer,
378                                (buffer ? silc_buffer_truelen(buffer) +
379                                 len : len));
380   if (!buffer)
381     return NULL;
382   silc_buffer_pull(buffer, silc_buffer_len(buffer));
383   silc_buffer_pull_tail(buffer, len);
384   silc_buffer_format(buffer,
385                      SILC_STR_UI_CHAR(attribute),
386                      SILC_STR_UI_CHAR(flags),
387                      SILC_STR_UI_SHORT((SilcUInt16)data_len),
388                      SILC_STR_UI_XNSTRING(data, (SilcUInt16)data_len),
389                      SILC_STR_END);
390   silc_buffer_push(buffer, buffer->data - buffer->head);
391
392   return buffer;
393 }
394
395 /* Free Attribute Payload */
396
397 void silc_attribute_payload_free(SilcAttributePayload payload)
398 {
399   silc_free(payload->data);
400   silc_free(payload);
401 }
402
403 /* Free's list of Attribute Payloads */
404
405 void silc_attribute_payload_list_free(SilcDList list)
406 {
407   SilcAttributePayload entry;
408
409   silc_dlist_start(list);
410   while ((entry = silc_dlist_get(list)) != SILC_LIST_END) {
411     silc_attribute_payload_free(entry);
412     silc_dlist_del(list, entry);
413   }
414
415   silc_dlist_uninit(list);
416 }
417
418 /* Return attribute type */
419
420 SilcAttribute silc_attribute_get_attribute(SilcAttributePayload payload)
421 {
422   return payload->attribute;
423 }
424
425 /* Return attribute flags */
426
427 SilcAttributeFlags silc_attribute_get_flags(SilcAttributePayload payload)
428 {
429   return payload->flags;
430 }
431
432 /* Return attribute data from the payload */
433
434 const unsigned char *silc_attribute_get_data(SilcAttributePayload payload,
435                                              SilcUInt32 *data_len)
436 {
437   if (data_len)
438     *data_len = (SilcUInt32)payload->data_len;
439   return (const unsigned char *)payload->data;
440 }
441
442 /* Construct digital signature verification data */
443
444 unsigned char *silc_attribute_get_verify_data(SilcDList attrs,
445                                               SilcBool server_verification,
446                                               SilcUInt32 *data_len)
447 {
448   SilcAttributePayload attr;
449   SilcBufferStruct buffer;
450   unsigned char *data = NULL;
451   SilcUInt32 len = 0;
452
453   silc_dlist_start(attrs);
454   while ((attr = silc_dlist_get(attrs)) != SILC_LIST_END) {
455     switch (attr->attribute) {
456     case SILC_ATTRIBUTE_SERVER_DIGITAL_SIGNATURE:
457       /* Server signature is never part of the verification data */
458       break;
459
460     case SILC_ATTRIBUTE_USER_DIGITAL_SIGNATURE:
461       /* For user signature verification this is not part of the data */
462       if (!server_verification)
463         break;
464
465       /* Fallback, for server signature verification, user digital signature
466          is part of verification data. */
467
468     default:
469       /* All other data is part of the verification data */
470       data = silc_realloc(data, sizeof(*data) * (4 + attr->data_len + len));
471       if (!data)
472         return NULL;
473       silc_buffer_set(&buffer, data + len, 4 + attr->data_len);
474       silc_buffer_format(&buffer,
475                          SILC_STR_UI_CHAR(attr->attribute),
476                          SILC_STR_UI_CHAR(attr->flags),
477                          SILC_STR_UI_SHORT(attr->data_len),
478                          SILC_STR_UI_XNSTRING(attr->data, attr->data_len),
479                          SILC_STR_END);
480       len += 4 + attr->data_len;
481       break;
482     }
483   }
484
485   if (data_len)
486     *data_len = len;
487
488   return data;
489 }
490
491 /* Return parsed attribute object */
492
493 SilcBool silc_attribute_get_object(SilcAttributePayload payload,
494                                    void *object, SilcUInt32 object_size)
495 {
496   SilcUInt16 len;
497   SilcBool ret = FALSE;
498
499   if (!object || payload->flags & SILC_ATTRIBUTE_FLAG_INVALID)
500     return FALSE;
501
502   switch (payload->attribute) {
503   case SILC_ATTRIBUTE_USER_INFO:
504     {
505       SilcVCard vcard = object;
506       if (object_size != sizeof(*vcard))
507         break;
508       if (!silc_vcard_decode(payload->data, payload->data_len, vcard))
509         break;
510       ret = TRUE;
511     }
512     break;
513
514   case SILC_ATTRIBUTE_SERVICE:
515     {
516       SilcAttributeObjService *service = object;
517       SilcBufferStruct buf;
518       SilcUInt16 addr_len, signon_len;
519       char *addr, *signon;
520       int res;
521       if (object_size != sizeof(*service))
522         break;
523       if (payload->data_len < 13)
524         break;
525       silc_buffer_set(&buf, payload->data, payload->data_len);
526       res = silc_buffer_unformat(&buf,
527                                  SILC_STR_UI_INT(&service->port),
528                                  SILC_STR_UI16_NSTRING(&addr, &addr_len),
529                                  SILC_STR_UI_CHAR(&service->status),
530                                  SILC_STR_UI16_NSTRING(&signon, &signon_len),
531                                  SILC_STR_UI_INT(&service->idle),
532                                  SILC_STR_END);
533       if (res == -1)
534         break;
535       memset(service->address, 0, sizeof(service->address));
536       memset(service->signon, 0, sizeof(service->signon));
537       memcpy(service->address, addr,
538              (addr_len < sizeof(service->address) - 1 ? addr_len :
539               sizeof(service->address) - 1));
540       memcpy(service->signon, signon,
541              (signon_len < sizeof(service->signon) - 1 ? signon_len :
542               sizeof(service->signon) - 1));
543       ret = TRUE;
544     }
545     break;
546
547   case SILC_ATTRIBUTE_STATUS_MOOD:
548   case SILC_ATTRIBUTE_PREFERRED_CONTACT:
549     {
550       SilcUInt32 *mask = (SilcUInt32 *)object;
551       if (object_size != sizeof(SilcUInt32))
552         break;
553       if (payload->data_len < 4)
554         break;
555       SILC_GET32_MSB(*mask, payload->data);
556       ret = TRUE;
557     }
558     break;
559
560   case SILC_ATTRIBUTE_STATUS_FREETEXT:
561   case SILC_ATTRIBUTE_PREFERRED_LANGUAGE:
562   case SILC_ATTRIBUTE_TIMEZONE:
563     {
564       char *string = object;
565       if (payload->data_len < 2)
566         break;
567       SILC_GET16_MSB(len, payload->data);
568       if (payload->data_len < 2 + len)
569         break;
570       if (object_size < len)
571         break;
572       memcpy(string, payload->data + 2, len);
573       ret = TRUE;
574     }
575     break;
576
577   case SILC_ATTRIBUTE_STATUS_MESSAGE:
578   case SILC_ATTRIBUTE_EXTENSION:
579   case SILC_ATTRIBUTE_USER_ICON:
580     {
581       SilcMime mime = object;
582       if (object_size != sizeof(*mime))
583         break;
584       if (!silc_mime_decode(mime, payload->data, payload->data_len))
585         break;
586       ret = TRUE;
587     }
588     break;
589
590   case SILC_ATTRIBUTE_GEOLOCATION:
591     {
592       SilcAttributeObjGeo *geo = object;
593       SilcBufferStruct buffer;
594       int res;
595       if (object_size != sizeof(*geo))
596         break;
597       silc_buffer_set(&buffer, (unsigned char *)payload->data,
598                       payload->data_len);
599       res = silc_buffer_unformat(&buffer,
600                                  SILC_STR_UI16_STRING_ALLOC(&geo->longitude),
601                                  SILC_STR_UI16_STRING_ALLOC(&geo->latitude),
602                                  SILC_STR_UI16_STRING_ALLOC(&geo->altitude),
603                                  SILC_STR_UI16_STRING_ALLOC(&geo->accuracy),
604                                  SILC_STR_END);
605       if (res == -1)
606         break;
607       ret = TRUE;
608     }
609     break;
610
611   case SILC_ATTRIBUTE_DEVICE_INFO:
612     {
613       SilcAttributeObjDevice *dev = object;
614       SilcBufferStruct buffer;
615       SilcUInt32 type;
616       int res;
617       if (object_size != sizeof(*dev))
618         break;
619       silc_buffer_set(&buffer, (unsigned char *)payload->data,
620                       payload->data_len);
621       res =
622         silc_buffer_unformat(&buffer,
623                              SILC_STR_UI_INT(&type),
624                              SILC_STR_UI16_STRING_ALLOC(&dev->manufacturer),
625                              SILC_STR_UI16_STRING_ALLOC(&dev->version),
626                              SILC_STR_UI16_STRING_ALLOC(&dev->model),
627                              SILC_STR_UI16_STRING_ALLOC(&dev->language),
628                              SILC_STR_END);
629       if (res == -1)
630         break;
631       dev->type = type;
632       ret = TRUE;
633     }
634     break;
635
636   case SILC_ATTRIBUTE_PHONE_NUMBER:
637     {
638       SilcAttributeObjPN *pn = object;
639       SilcBufferStruct buffer;
640       SilcUInt32 pn_format;
641       int res;
642       if (object_size != sizeof(*pn))
643         break;
644       silc_buffer_set(&buffer, (unsigned char *)payload->data,
645                       payload->data_len);
646       res =
647         silc_buffer_unformat(&buffer,
648                              SILC_STR_UI_INT(&pn_format),
649                              SILC_STR_UI16_STRING_ALLOC(&pn->number),
650                              SILC_STR_END);
651       if (res == -1)
652         break;
653       pn->format = pn_format;
654       ret = TRUE;
655     }
656     break;
657
658   case SILC_ATTRIBUTE_USER_PUBLIC_KEY:
659   case SILC_ATTRIBUTE_SERVER_PUBLIC_KEY:
660     {
661       SilcAttributeObjPk *pk = object;
662       SilcBufferStruct buffer;
663       int res;
664       if (object_size != sizeof(*pk))
665         break;
666       silc_buffer_set(&buffer, (unsigned char *)payload->data,
667                       payload->data_len);
668       res =
669         silc_buffer_unformat(&buffer,
670                              SILC_STR_UI16_NSTRING_ALLOC(&pk->type, &len),
671                              SILC_STR_END);
672       if (res == -1 || len > silc_buffer_len(&buffer) - 2)
673         break;
674       pk->data = silc_memdup(payload->data + 2 + len,
675                              payload->data_len - 2 - len);
676       pk->data_len = payload->data_len - 2 - len;
677       ret = TRUE;
678     }
679     break;
680
681   case SILC_ATTRIBUTE_USER_DIGITAL_SIGNATURE:
682   case SILC_ATTRIBUTE_SERVER_DIGITAL_SIGNATURE:
683     {
684       SilcAttributeObjPk *pk = object;
685       if (object_size != sizeof(*pk))
686         break;
687       pk->type = NULL;
688       pk->data = silc_memdup(payload->data, payload->data_len);
689       pk->data_len = payload->data_len;
690       ret = TRUE;
691     }
692     break;
693
694   default:
695     break;
696   }
697
698   return ret;
699 }