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