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