Merged silc_1_0_branch to trunk.
[silc.git] / lib / silccore / silcattrs.c
index 370d91ef7b6c8165c4739a171a61ebec67ccf097..7af8f6a052ed5068ee121ee5c3577e6d34b1edd5 100644 (file)
@@ -35,83 +35,58 @@ struct SilcAttributePayloadStruct {
   unsigned char *data;
 };
 
-/* Parse one attribute payload */
-
-SilcAttributePayload
-silc_attribute_payload_parse(const unsigned char *payload,
-                            SilcUInt32 payload_len)
+/* Internal routine for encoding a attribute */
+
+static unsigned char *
+silc_attribute_payload_encode_int(SilcAttribute attribute,
+                                 SilcAttributeFlags flags,
+                                 void *object,
+                                 SilcUInt32 object_size,
+                                 SilcUInt32 *ret_len)
 {
-  SilcBufferStruct buffer;
-  SilcAttributePayload newp;
-  int ret;
-
-  SILC_LOG_DEBUG(("Parsing attribute payload"));
-
-  silc_buffer_set(&buffer, (unsigned char *)payload, payload_len);
-  newp = silc_calloc(1, sizeof(*newp));
-  if (!newp)
-    return NULL;
-
-  /* Parse the Attribute Payload. */
-  ret = silc_buffer_unformat(&buffer,
-                            SILC_STR_UI_CHAR(&newp->attribute),
-                            SILC_STR_UI_CHAR(&newp->flags),
-                            SILC_STR_UI16_NSTRING_ALLOC(&newp->data, 
-                                                        &newp->data_len),
-                            SILC_STR_END);
-  if (ret == -1)
-    goto err;
-
-  if (newp->data_len > buffer.len - 4) {
-    SILC_LOG_ERROR(("Incorrect attribute payload"));
-    goto err;
-  }
-
-  return newp;
-
- err:
-  silc_attribute_payload_free(newp);
-  return NULL;
-}
-
-/* Encode one attribute payload to buffer */
-
-SilcBuffer silc_attribute_payload_encode(SilcBuffer attrs,
-                                        SilcAttribute attribute,
-                                        SilcAttributeFlags flags,
-                                        void *object,
-                                        SilcUInt32 object_size)
-{
-  SilcBuffer buffer, tmpbuf = NULL;
-  unsigned char tmp[4], *str = NULL;
-  int len;
-
-  SILC_LOG_DEBUG(("Encoding Attribute Payload"));
+  SilcBuffer tmpbuf = NULL;
+  unsigned char tmp[4], *str = NULL, *ret;
+  SilcUInt32 len;
 
   /* Encode according to attribute type */
   if (flags & SILC_ATTRIBUTE_FLAG_VALID) {
-    if (!object || !object_size)
+    if (!object && !object_size)
       return NULL;
 
     switch (attribute) {
 
     case SILC_ATTRIBUTE_USER_INFO:
-      SILC_NOT_IMPLEMENTED("SILC_ATTRIBUTE_USER_INFO");
+      {
+       SilcVCard vcard = object;
+       if (object_size != sizeof(*vcard))
+         return NULL;
+       str = silc_vcard_encode(vcard, &object_size);
+       if (!str)
+         return NULL;
+       object = str;
+      }
       break;
 
     case SILC_ATTRIBUTE_SERVICE:
       {
        SilcAttributeObjService *service = object;
+       SilcUInt32 len2;
        if (object_size != sizeof(*service))
          return NULL;
        len = strlen(service->address);
-       str = silc_malloc(7 + len);
-       if (!str)
-         return NULL;
-       SILC_PUT32_MSB(service->port, str);
-       SILC_PUT16_MSB(len, str + 4);
-       memcpy(str + 6, service->address, len);
-       str[6 + len] = service->status;
+       len2 = strlen(service->signon);
+       tmpbuf = silc_buffer_alloc_size(13 + len + len2);
+       silc_buffer_format(tmpbuf,
+                          SILC_STR_UI_INT(service->port),
+                          SILC_STR_UI_SHORT(len),
+                          SILC_STR_UI_XNSTRING(service->address, len),
+                          SILC_STR_UI_CHAR(service->status),
+                          SILC_STR_UI_SHORT(len2),
+                          SILC_STR_UI_XNSTRING(service->signon, len2),
+                          SILC_STR_UI_INT(service->idle),
+                          SILC_STR_END);
+       object = tmpbuf->data;
+       object_size = tmpbuf->len;
       }
       break;
 
@@ -156,47 +131,63 @@ SilcBuffer silc_attribute_payload_encode(SilcBuffer attrs,
     case SILC_ATTRIBUTE_GEOLOCATION:
       {
        SilcAttributeObjGeo *geo = object;
+       SilcUInt32 len1, len2, len3, len4;
        if (object_size != sizeof(*geo))
          return NULL;
-       len =
-         (geo->longitude ? strlen(geo->longitude) : 0) +
-         (geo->latitude  ? strlen(geo->latitude)  : 0) +
-         (geo->altitude  ? strlen(geo->altitude)  : 0) +
-         (geo->accuracy  ? strlen(geo->accuracy)  : 0);
-       if (!len)
+       len1 = (geo->longitude ? strlen(geo->longitude) : 0);
+       len2 = (geo->latitude  ? strlen(geo->latitude)  : 0);
+       len3 = (geo->altitude  ? strlen(geo->altitude)  : 0);
+       len4 = (geo->accuracy  ? strlen(geo->accuracy)  : 0);
+       if (len1 + len2 + len3 + len4 == 0)
          return NULL;
+       len = len1 + len2 + len3 + len4;
        tmpbuf = silc_buffer_alloc_size(8 + len);
        if (!tmpbuf)
          return NULL;
        silc_buffer_format(tmpbuf,
-                          SILC_STR_UI16_STRING(geo->longitude),
-                          SILC_STR_UI16_STRING(geo->latitude),
-                          SILC_STR_UI16_STRING(geo->altitude),
-                          SILC_STR_UI16_STRING(geo->accuracy),
+                          SILC_STR_UI_SHORT(len1),
+                          SILC_STR_UI16_STRING(len1 ? geo->longitude : ""),
+                          SILC_STR_UI_SHORT(len2),
+                          SILC_STR_UI16_STRING(len2 ? geo->latitude : ""),
+                          SILC_STR_UI_SHORT(len3),
+                          SILC_STR_UI16_STRING(len3 ? geo->altitude : ""),
+                          SILC_STR_UI_SHORT(len4),
+                          SILC_STR_UI16_STRING(len4 ? geo->accuracy : ""),
                           SILC_STR_END);
+       object = tmpbuf->data;
+       object_size = tmpbuf->len;
       }
       break;
 
     case SILC_ATTRIBUTE_DEVICE_INFO:
       {
        SilcAttributeObjDevice *dev = object;
+       SilcUInt32 len1, len2, len3, len4;
        if (object_size != sizeof(*dev))
          return NULL;
-       len =
-         (dev->manufacturer ? strlen(dev->manufacturer) : 0) +
-         (dev->version      ? strlen(dev->version)      : 0) +
-         (dev->model        ? strlen(dev->model)        : 0) +
-         (dev->language     ? strlen(dev->language)     : 0);
+       len1 = (dev->manufacturer ? strlen(dev->manufacturer) : 0);
+       len2 = (dev->version      ? strlen(dev->version)      : 0);
+       len3 = (dev->model        ? strlen(dev->model)        : 0);
+       len4 = (dev->language     ? strlen(dev->language)     : 0);
+       if (len1 + len2 + len3 + len4 == 0)
+         return NULL;
+       len = len1 + len2 + len3 + len4;
        tmpbuf = silc_buffer_alloc_size(4 + 8 + len);
        if (!tmpbuf)
          return NULL;
        silc_buffer_format(tmpbuf,
                           SILC_STR_UI_INT(dev->type),
-                          SILC_STR_UI16_STRING(dev->manufacturer),
-                          SILC_STR_UI16_STRING(dev->version),
-                          SILC_STR_UI16_STRING(dev->model),
-                          SILC_STR_UI16_STRING(dev->language),
+                          SILC_STR_UI_SHORT(len1),
+                          SILC_STR_UI16_STRING(len1 ? dev->manufacturer : ""),
+                          SILC_STR_UI_SHORT(len2),
+                          SILC_STR_UI16_STRING(len2 ? dev->version : ""),
+                          SILC_STR_UI_SHORT(len3),
+                          SILC_STR_UI16_STRING(len3 ? dev->model : ""),
+                          SILC_STR_UI_SHORT(len4),
+                          SILC_STR_UI16_STRING(len4 ? dev->language : ""),
                           SILC_STR_END);
+       object = tmpbuf->data;
+       object_size = tmpbuf->len;
       }
       break;
 
@@ -215,6 +206,8 @@ SilcBuffer silc_attribute_payload_encode(SilcBuffer attrs,
                           SILC_STR_UI16_STRING(pk->type),
                           SILC_STR_UI_XNSTRING(pk->data, pk->data_len),
                           SILC_STR_END);
+       object = tmpbuf->data;
+       object_size = tmpbuf->len;
       }
       break;
 
@@ -233,46 +226,60 @@ SilcBuffer silc_attribute_payload_encode(SilcBuffer attrs,
       return NULL;
       break;
     }
-  }
 
-  buffer = attrs;
-  len = 4 + object_size;
+    ret = silc_memdup(object, object_size);
 
-  if (!buffer) {
-    buffer = silc_buffer_alloc_size(len);
-  } else {
-    buffer = silc_buffer_realloc(buffer,
-                                (buffer ? buffer->truelen + len : len));
-    silc_buffer_pull_tail(buffer, (buffer->end - buffer->data));
+    if (tmpbuf)
+      silc_buffer_free(tmpbuf);
+    silc_free(str);
+
+    if (ret_len)
+      *ret_len = object_size;
+
+    return ret;
   }
 
-  silc_buffer_format(buffer, 
-                    SILC_STR_UI_CHAR(attribute),
-                    SILC_STR_UI_CHAR(flags),
-                    SILC_STR_UI_SHORT((SilcUInt16)object_size),
-                    SILC_STR_UI_XNSTRING(object, object_size),
-                    SILC_STR_END);
+  return NULL;
+}
 
-  silc_buffer_pull(buffer, len);
-  if (buffer)
-    silc_buffer_push(buffer, buffer->data - buffer->head);
+/* Allocates attribute payload and encodes the attribute there */
 
-  if (tmpbuf)
-    silc_buffer_free(tmpbuf);
-  silc_free(str);
+SilcAttributePayload silc_attribute_payload_alloc(SilcAttribute attribute,
+                                                 SilcAttributeFlags flags,
+                                                 void *object,
+                                                 SilcUInt32 object_size)
+{
+  SilcAttributePayload attr;
+  SilcUInt32 tmp_len;
 
-  return buffer;
+  attr = silc_calloc(1, sizeof(*attr));
+  if (!attr)
+    return NULL;
+
+  attr->attribute = attribute;
+  attr->flags = flags;
+  attr->data =
+    silc_attribute_payload_encode_int(attribute, flags, object,
+                                     object_size, &tmp_len);
+  attr->data_len = (SilcUInt16)tmp_len;
+  if (!attr->data) {
+    silc_free(attr);
+    return NULL;
+  }
+
+  return attr;
 }
 
 /* Parse list of payloads */
 
-SilcDList silc_attribute_payload_parse_list(const unsigned char *payload,
-                                           SilcUInt32 payload_len)
+SilcDList silc_attribute_payload_parse(const unsigned char *payload,
+                                      SilcUInt32 payload_len)
 {
   SilcBufferStruct buffer;
   SilcDList list;
   SilcAttributePayload newp;
-  int len, ret;
+  SilcUInt32 len;
+  int ret;
 
   SILC_LOG_DEBUG(("Parsing Attribute Payload list"));
 
@@ -292,7 +299,7 @@ SilcDList silc_attribute_payload_parse_list(const unsigned char *payload,
     if (ret == -1)
       goto err;
 
-    if (newp->data_len > buffer.len) {
+    if (newp->data_len > buffer.len - 4) {
       SILC_LOG_ERROR(("Incorrect attribute payload in list"));
       goto err;
     }
@@ -312,6 +319,52 @@ SilcDList silc_attribute_payload_parse_list(const unsigned char *payload,
   return NULL;
 }
 
+/* Encode one attribute payload to buffer */
+
+SilcBuffer silc_attribute_payload_encode(SilcBuffer attrs,
+                                        SilcAttribute attribute,
+                                        SilcAttributeFlags flags,
+                                        void *object,
+                                        SilcUInt32 object_size)
+{
+  object = silc_attribute_payload_encode_int(attribute, flags, object,
+                                            object_size, &object_size);
+  attrs = silc_attribute_payload_encode_data(attrs, attribute, flags,
+                                            (const unsigned char *)object,
+                                            object_size);
+  silc_free(object);
+  return attrs;
+}
+
+/* Encoded the attribute data directly to buffer */
+
+SilcBuffer silc_attribute_payload_encode_data(SilcBuffer attrs,
+                                             SilcAttribute attribute,
+                                             SilcAttributeFlags flags,
+                                             const unsigned char *data,
+                                             SilcUInt32 data_len)
+{
+  SilcBuffer buffer = attrs;
+  SilcUInt32 len;
+
+  len = 4 + (SilcUInt16)data_len;
+  buffer = silc_buffer_realloc(buffer,
+                              (buffer ? buffer->truelen + len : len));
+  if (!buffer)
+    return NULL;
+  silc_buffer_pull(buffer, buffer->len);
+  silc_buffer_pull_tail(buffer, len);
+  silc_buffer_format(buffer, 
+                    SILC_STR_UI_CHAR(attribute),
+                    SILC_STR_UI_CHAR(flags),
+                    SILC_STR_UI_SHORT((SilcUInt16)data_len),
+                    SILC_STR_UI_XNSTRING(data, (SilcUInt16)data_len),
+                    SILC_STR_END);
+  silc_buffer_push(buffer, buffer->data - buffer->head);
+
+  return buffer;
+}
+
 /* Free Attribute Payload */
 
 void silc_attribute_payload_free(SilcAttributePayload payload)
@@ -355,43 +408,111 @@ const unsigned char *silc_attribute_get_data(SilcAttributePayload payload,
                                             SilcUInt32 *data_len)
 {
   if (data_len)
-    *data_len = payload->data_len;
+    *data_len = (SilcUInt32)payload->data_len;
   return (const unsigned char *)payload->data;
 }
 
+/* Construct digital signature verification data */
+
+unsigned char *silc_attribute_get_verify_data(SilcDList attrs,
+                                             bool server_verification,
+                                             SilcUInt32 *data_len)
+{
+  SilcAttributePayload attr;
+  SilcBufferStruct buffer;
+  unsigned char *data = NULL;
+  SilcUInt32 len = 0;
+
+  silc_dlist_start(attrs);
+  while ((attr = silc_dlist_get(attrs)) != SILC_LIST_END) {
+    switch (attr->attribute) {
+    case SILC_ATTRIBUTE_SERVER_DIGITAL_SIGNATURE:
+      /* Server signature is never part of the verification data */
+      break;
+
+    case SILC_ATTRIBUTE_USER_DIGITAL_SIGNATURE:
+      /* For user signature verification this is not part of the data */
+      if (!server_verification)
+       break;
+
+      /* Fallback, for server signature verification, user digital signature
+        is part of verification data. */
+
+    default:
+      /* All other data is part of the verification data */
+      data = silc_realloc(data, sizeof(*data) * (4 + attr->data_len + len));
+      if (!data)
+       return NULL;
+      silc_buffer_set(&buffer, data + len, 4 + attr->data_len);
+      silc_buffer_format(&buffer, 
+                        SILC_STR_UI_CHAR(attr->attribute),
+                        SILC_STR_UI_CHAR(attr->flags),
+                        SILC_STR_UI_SHORT(attr->data_len),
+                        SILC_STR_UI_XNSTRING(attr->data, attr->data_len),
+                        SILC_STR_END);
+      len += 4 + attr->data_len;
+      break;
+    }
+  }
+
+  if (data_len)
+    *data_len = len;
+
+  return data;
+}
+
 /* Return parsed attribute object */
 
 bool silc_attribute_get_object(SilcAttributePayload payload,
-                              SilcAttribute attribute,
-                              void **object, SilcUInt32 object_size)
+                              void *object, SilcUInt32 object_size)
 {
   SilcUInt16 len;
   bool ret = FALSE;
 
-  if (!attribute || payload->attribute != attribute || !object || !(*object) ||
-      payload->flags & SILC_ATTRIBUTE_FLAG_INVALID)
+  if (!object || payload->flags & SILC_ATTRIBUTE_FLAG_INVALID)
     return FALSE;
 
-  switch (attribute) {
+  switch (payload->attribute) {
   case SILC_ATTRIBUTE_USER_INFO:
-    SILC_NOT_IMPLEMENTED("SILC_ATTRIBUTE_USER_INFO");
+    {
+      SilcVCard vcard = object;
+      if (object_size != sizeof(*vcard))
+       break;
+      if (!silc_vcard_decode(payload->data, payload->data_len, vcard))
+       break;
+      ret = TRUE;
+    }
     break;
 
   case SILC_ATTRIBUTE_SERVICE:
     {
-      SilcAttributeObjService *service = *object;
+      SilcAttributeObjService *service = object;
+      SilcBufferStruct buf;
+      SilcUInt16 addr_len, signon_len;
+      char *addr, *signon;
+      int res;
       if (object_size != sizeof(*service))
        break;
-      if (payload->data_len < 7)
+      if (payload->data_len < 13)
        break;
-      SILC_GET32_MSB(service->port, payload->data);
-      SILC_GET16_MSB(len, payload->data + 4);
-      if (payload->data_len < 7 + len)
+      silc_buffer_set(&buf, payload->data, payload->data_len);
+      res = silc_buffer_unformat(&buf,
+                                SILC_STR_UI_INT(&service->port),
+                                SILC_STR_UI16_NSTRING(&addr, &addr_len),
+                                SILC_STR_UI_CHAR(&service->status),
+                                SILC_STR_UI16_NSTRING(&signon, &signon_len),
+                                SILC_STR_UI_INT(&service->idle),
+                                SILC_STR_END);
+      if (res == -1)
        break;
-      memcpy(service->address, payload->data + 6,
-            (len < sizeof(service->address) - 1 ? len :
+      memset(service->address, 0, sizeof(service->address));
+      memset(service->signon, 0, sizeof(service->signon));
+      memcpy(service->address, addr,
+            (addr_len < sizeof(service->address) - 1 ? addr_len :
              sizeof(service->address) - 1));
-      service->status = payload->data[6 + len] ? TRUE : FALSE;
+      memcpy(service->signon, signon,
+            (signon_len < sizeof(service->signon) - 1 ? signon_len :
+             sizeof(service->signon) - 1));
       ret = TRUE;
     }
     break;
@@ -399,7 +520,7 @@ bool silc_attribute_get_object(SilcAttributePayload payload,
   case SILC_ATTRIBUTE_STATUS_MOOD:
   case SILC_ATTRIBUTE_PREFERRED_CONTACT:
     {
-      SilcUInt32 *mask = *object;
+      SilcUInt32 *mask = (SilcUInt32 *)object;
       if (object_size != sizeof(SilcUInt32))
        break;
       if (payload->data_len < 4)
@@ -413,7 +534,7 @@ bool silc_attribute_get_object(SilcAttributePayload payload,
   case SILC_ATTRIBUTE_PREFERRED_LANGUAGE:
   case SILC_ATTRIBUTE_TIMEZONE:
     {
-      char *string = *object;
+      char *string = object;
       if (payload->data_len < 2)
        break;
       SILC_GET16_MSB(len, payload->data);
@@ -429,7 +550,7 @@ bool silc_attribute_get_object(SilcAttributePayload payload,
   case SILC_ATTRIBUTE_STATUS_MESSAGE:
   case SILC_ATTRIBUTE_EXTENSION:
     {
-      SilcAttributeObjMime *mime = *object;
+      SilcAttributeObjMime *mime = object;
       if (object_size != sizeof(*mime))
        break;
       mime->mime = (const unsigned char *)payload->data;
@@ -440,7 +561,7 @@ bool silc_attribute_get_object(SilcAttributePayload payload,
 
   case SILC_ATTRIBUTE_GEOLOCATION:
     {
-      SilcAttributeObjGeo *geo = *object;
+      SilcAttributeObjGeo *geo = object;
       SilcBufferStruct buffer;
       int res;
       if (object_size != sizeof(*geo))
@@ -453,7 +574,7 @@ bool silc_attribute_get_object(SilcAttributePayload payload,
                                 SILC_STR_UI16_STRING_ALLOC(&geo->altitude),
                                 SILC_STR_UI16_STRING_ALLOC(&geo->accuracy),
                                 SILC_STR_END);
-      if (res == 1)
+      if (res == -1)
        break;
       ret = TRUE;
     }
@@ -461,7 +582,7 @@ bool silc_attribute_get_object(SilcAttributePayload payload,
 
   case SILC_ATTRIBUTE_DEVICE_INFO:
     {
-      SilcAttributeObjDevice *dev = *object;
+      SilcAttributeObjDevice *dev = object;
       SilcBufferStruct buffer;
       SilcUInt32 type;
       int res;
@@ -477,7 +598,7 @@ bool silc_attribute_get_object(SilcAttributePayload payload,
                             SILC_STR_UI16_STRING_ALLOC(&dev->model),
                             SILC_STR_UI16_STRING_ALLOC(&dev->language),
                             SILC_STR_END);
-      if (res == 1)
+      if (res == -1)
        break;
       dev->type = type;
       ret = TRUE;
@@ -487,7 +608,7 @@ bool silc_attribute_get_object(SilcAttributePayload payload,
   case SILC_ATTRIBUTE_USER_PUBLIC_KEY:
   case SILC_ATTRIBUTE_SERVER_PUBLIC_KEY:
     {
-      SilcAttributeObjPk *pk = *object;
+      SilcAttributeObjPk *pk = object;
       SilcBufferStruct buffer;
       int res;
       if (object_size != sizeof(*pk))
@@ -498,7 +619,7 @@ bool silc_attribute_get_object(SilcAttributePayload payload,
        silc_buffer_unformat(&buffer,
                             SILC_STR_UI16_NSTRING_ALLOC(&pk->type, &len),
                             SILC_STR_END);
-      if (res == 1)
+      if (res == -1 || len > buffer.len - 2)
        break;
       pk->data = silc_memdup(payload->data + 2 + len,
                             payload->data_len - 2 - len);
@@ -510,7 +631,7 @@ bool silc_attribute_get_object(SilcAttributePayload payload,
   case SILC_ATTRIBUTE_USER_DIGITAL_SIGNATURE:
   case SILC_ATTRIBUTE_SERVER_DIGITAL_SIGNATURE:
     {
-      SilcAttributeObjPk *pk = *object;
+      SilcAttributeObjPk *pk = object;
       if (object_size != sizeof(*pk))
        break;
       pk->type = NULL;