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