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