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