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