Merged silc_1_1_branch to trunk.
[silc.git] / lib / silccore / silcargument.c
index 471e92c78fe18130da9bbbd89caac6ad4d7e8394..eac8bdf60e72d5dfef128924ba389df5fbee842f 100644 (file)
@@ -1,10 +1,10 @@
 /*
 
-  silcargument.c 
+  silcargument.c
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 2001 Pekka Riikonen
+  Copyright (C) 2001 - 2007 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   GNU General Public License for more details.
 
 */
-/* Implementation of Argument Payload routines */ 
+/* Implementation of Argument Payload routines */
 /* $Id$ */
 
-#include "silcincludes.h"
+#include "silc.h"
 #include "silcargument.h"
 
-/******************************************************************************
-
-                             Argument Payload
-
-******************************************************************************/
+/*************************** Argument Payload *******************************/
 
 struct SilcArgumentPayloadStruct {
-  uint32 argc;
+  SilcUInt32 argc;
   unsigned char **argv;
-  uint32 *argv_lens;
-  uint32 *argv_types;
-  uint32 pos;
+  SilcUInt32 *argv_lens;
+  SilcUInt32 *argv_types;
+  SilcUInt32 pos;
 };
 
 /* Parses arguments and returns them into Argument Payload structure. */
 
 SilcArgumentPayload silc_argument_payload_parse(const unsigned char *payload,
-                                               uint32 payload_len,
-                                               uint32 argc)
+                                               SilcUInt32 payload_len,
+                                               SilcUInt32 argc)
 {
   SilcBufferStruct buffer;
-  SilcArgumentPayload new;
-  uint16 p_len = 0;
+  SilcArgumentPayload newp;
+  SilcUInt16 p_len = 0;
   unsigned char arg_num = 0;
   unsigned char arg_type = 0;
-  uint32 pull_len = 0;
+  SilcUInt32 pull_len = 0;
   int i = 0, ret;
 
-  SILC_LOG_DEBUG(("Parsing argument payload"));
-
   silc_buffer_set(&buffer, (unsigned char *)payload, payload_len);
-  new = silc_calloc(1, sizeof(*new));
-  new->argv = silc_calloc(argc, sizeof(unsigned char *));
-  new->argv_lens = silc_calloc(argc, sizeof(uint32));
-  new->argv_types = silc_calloc(argc, sizeof(uint32));
-    
+  newp = silc_calloc(1, sizeof(*newp));
+  if (!newp)
+    return NULL;
+  newp->argv = silc_calloc(argc, sizeof(unsigned char *));
+  if (!newp->argv)
+    goto err;
+  newp->argv_lens = silc_calloc(argc, sizeof(SilcUInt32));
+  if (!newp->argv_lens)
+    goto err;
+  newp->argv_types = silc_calloc(argc, sizeof(SilcUInt32));
+  if (!newp->argv_types)
+    goto err;
+
   /* Get arguments */
   arg_num = 1;
   for (i = 0; i < argc; i++) {
@@ -65,84 +67,82 @@ SilcArgumentPayload silc_argument_payload_parse(const unsigned char *payload,
                               SILC_STR_UI_SHORT(&p_len),
                               SILC_STR_UI_CHAR(&arg_type),
                               SILC_STR_END);
-    if (ret == -1)
+    if (ret == -1 || p_len > silc_buffer_len(&buffer) - 3) {
+      SILC_LOG_DEBUG(("Malformed argument payload"));
       goto err;
-    
-    new->argv_lens[i] = p_len;
-    new->argv_types[i] = arg_type;
+    }
+
+    newp->argv_lens[i] = p_len;
+    newp->argv_types[i] = arg_type;
 
-    if (p_len > buffer.len - 3)
-      break;
-    
     /* Get argument data */
     silc_buffer_pull(&buffer, 3);
     ret = silc_buffer_unformat(&buffer,
-                              SILC_STR_UI_XNSTRING_ALLOC(&new->argv[i], 
+                              SILC_STR_UI_XNSTRING_ALLOC(&newp->argv[i],
                                                          p_len),
                               SILC_STR_END);
-    if (ret == -1)
+    if (ret == -1) {
+      SILC_LOG_DEBUG(("Malformed argument payload"));
       goto err;
+    }
 
     silc_buffer_pull(&buffer, p_len);
     pull_len += 3 + p_len;
   }
 
-  if (buffer.len != 0)
+  if (silc_buffer_len(&buffer) != 0) {
+    SILC_LOG_DEBUG(("Malformed argument payload"));
     goto err;
+  }
 
-  new->argc = argc;
-  new->pos = 0;
+  newp->argc = argc;
+  newp->pos = 0;
 
   silc_buffer_push(&buffer, pull_len);
 
-  return new;
+  return newp;
 
  err:
-  if (i) {
-    int k;
-
-    for (k = 0; k < i; k++)
-      silc_free(new->argv[k]);
-  }
+  SILC_LOG_DEBUG(("Error parsing argument payload"));
+  if (i)
+    for (ret = 0; ret < i; ret++)
+      silc_free(newp->argv[ret]);
 
-  silc_free(new->argv);
-  silc_free(new->argv_lens);
-  silc_free(new->argv_types);
-
-  if (new)
-    silc_free(new);
+  silc_free(newp->argv);
+  silc_free(newp->argv_lens);
+  silc_free(newp->argv_types);
+  silc_free(newp);
 
   return NULL;
 }
 
 /* Encodes arguments in to Argument Paylods returning them to SilcBuffer. */
 
-SilcBuffer silc_argument_payload_encode(uint32 argc,
+SilcBuffer silc_argument_payload_encode(SilcUInt32 argc,
                                        unsigned char **argv,
-                                       uint32 *argv_lens,
-                                       uint32 *argv_types)
+                                       SilcUInt32 *argv_lens,
+                                       SilcUInt32 *argv_types)
 {
   SilcBuffer buffer;
-  uint32 len;
+  SilcUInt32 len;
   int i;
 
-  SILC_LOG_DEBUG(("Encoding Argument payload"));
-
   len = 0;
   for (i = 0; i < argc; i++)
-    len += 3 + argv_lens[i];
+    len += 3 + (SilcUInt16)argv_lens[i];
 
-  buffer = silc_buffer_alloc(len);
-  silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer));
+  buffer = silc_buffer_alloc_size(len);
+  if (!buffer)
+    return NULL;
 
   /* Put arguments */
   for (i = 0; i < argc; i++) {
     silc_buffer_format(buffer,
                       SILC_STR_UI_SHORT(argv_lens[i]),
                       SILC_STR_UI_CHAR(argv_types[i]),
-                      SILC_STR_UI_XNSTRING(argv[i], argv_lens[i]),
+                      SILC_STR_UI_XNSTRING(argv[i], (SilcUInt16)argv_lens[i]),
                       SILC_STR_END);
-    silc_buffer_pull(buffer, 3 + argv_lens[i]);
+    silc_buffer_pull(buffer, 3 + (SilcUInt16)argv_lens[i]);
   }
 
   silc_buffer_push(buffer, len);
@@ -150,30 +150,57 @@ SilcBuffer silc_argument_payload_encode(uint32 argc,
   return buffer;
 }
 
+/* Encode one argument to buffer */
+
+SilcBuffer silc_argument_payload_encode_one(SilcBuffer args,
+                                           unsigned char *arg,
+                                           SilcUInt32 arg_len,
+                                           SilcUInt32 arg_type)
+{
+  SilcBuffer buffer = args;
+  SilcUInt32 len;
+
+  len = 3 + (SilcUInt16)arg_len;
+  buffer = silc_buffer_realloc(buffer,
+                              (buffer ? silc_buffer_truelen(buffer) +
+                               len : len));
+  if (!buffer)
+    return NULL;
+  silc_buffer_pull(buffer, silc_buffer_len(buffer));
+  silc_buffer_pull_tail(buffer, len);
+  silc_buffer_format(buffer,
+                    SILC_STR_UI_SHORT(arg_len),
+                    SILC_STR_UI_CHAR(arg_type),
+                    SILC_STR_UI_XNSTRING(arg, (SilcUInt16)arg_len),
+                    SILC_STR_END);
+  silc_buffer_push(buffer, buffer->data - buffer->head);
+
+  return buffer;
+}
+
 /* Same as above but encode the buffer from SilcArgumentPayload structure
    instead of raw data. */
 
 SilcBuffer silc_argument_payload_encode_payload(SilcArgumentPayload payload)
 {
   SilcBuffer buffer;
-  uint32 len;
+  SilcUInt32 len;
   int i;
 
-  SILC_LOG_DEBUG(("Encoding Argument payload"));
-
   len = 0;
   for (i = 0; i < payload->argc; i++)
     len += 3 + payload->argv_lens[i];
 
-  buffer = silc_buffer_alloc(len);
-  silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer));
+  buffer = silc_buffer_alloc_size(len);
+  if (!buffer)
+    return NULL;
 
   /* Put arguments */
   for (i = 0; i < payload->argc; i++) {
     silc_buffer_format(buffer,
                       SILC_STR_UI_SHORT(payload->argv_lens[i]),
                       SILC_STR_UI_CHAR(payload->argv_types[i]),
-                      SILC_STR_UI_XNSTRING(payload->argv[i], 
+                      SILC_STR_UI_XNSTRING(payload->argv[i],
                                            payload->argv_lens[i]),
                       SILC_STR_END);
     silc_buffer_pull(buffer, 3 + payload->argv_lens[i]);
@@ -203,7 +230,7 @@ void silc_argument_payload_free(SilcArgumentPayload payload)
 
 /* Returns number of arguments in payload */
 
-uint32 silc_argument_get_arg_num(SilcArgumentPayload payload)
+SilcUInt32 silc_argument_get_arg_num(SilcArgumentPayload payload)
 {
   return payload ? payload->argc : 0;
 }
@@ -211,13 +238,16 @@ uint32 silc_argument_get_arg_num(SilcArgumentPayload payload)
 /* Returns first argument from payload. */
 
 unsigned char *silc_argument_get_first_arg(SilcArgumentPayload payload,
-                                          uint32 *ret_len)
+                                          SilcUInt32 *type,
+                                          SilcUInt32 *ret_len)
 {
   if (!payload)
     return NULL;
 
   payload->pos = 0;
 
+  if (type)
+    *type = payload->argv_types[payload->pos];
   if (ret_len)
     *ret_len = payload->argv_lens[payload->pos];
 
@@ -227,7 +257,8 @@ unsigned char *silc_argument_get_first_arg(SilcArgumentPayload payload,
 /* Returns next argument from payload or NULL if no more arguments. */
 
 unsigned char *silc_argument_get_next_arg(SilcArgumentPayload payload,
-                                         uint32 *ret_len)
+                                         SilcUInt32 *type,
+                                         SilcUInt32 *ret_len)
 {
   if (!payload)
     return NULL;
@@ -235,6 +266,8 @@ unsigned char *silc_argument_get_next_arg(SilcArgumentPayload payload,
   if (payload->pos >= payload->argc)
     return NULL;
 
+  if (type)
+    *type = payload->argv_types[payload->pos];
   if (ret_len)
     *ret_len = payload->argv_lens[payload->pos];
 
@@ -244,8 +277,8 @@ unsigned char *silc_argument_get_next_arg(SilcArgumentPayload payload,
 /* Returns argument which type is `type'. */
 
 unsigned char *silc_argument_get_arg_type(SilcArgumentPayload payload,
-                                         uint32 type,
-                                         uint32 *ret_len)
+                                         SilcUInt32 type,
+                                         SilcUInt32 *ret_len)
 {
   int i;
 
@@ -264,3 +297,205 @@ unsigned char *silc_argument_get_arg_type(SilcArgumentPayload payload,
 
   return payload->argv[i];
 }
+
+/* Return argument already decoded */
+
+static SilcBool silc_argument_decode(unsigned char *data,
+                                    SilcUInt32 data_len,
+                                    SilcArgumentDecodeType dec_type,
+                                    void *ret_arg,
+                                    void **ret_arg_alloc)
+{
+  switch (dec_type) {
+
+  case SILC_ARGUMENT_ID:
+    if (ret_arg)
+      if (!silc_id_payload_parse_id(data, data_len, (SilcID *)ret_arg))
+       return FALSE;
+
+    if (ret_arg_alloc) {
+      SilcID id;
+      if (!silc_id_payload_parse_id(data, data_len, &id))
+       return FALSE;
+      *ret_arg_alloc = silc_memdup(&id, sizeof(id));
+    }
+    break;
+
+  case SILC_ARGUMENT_PUBLIC_KEY:
+    {
+      SilcPublicKey public_key;
+
+      if (!ret_arg_alloc)
+       return FALSE;
+
+      if (!silc_public_key_payload_decode(data, data_len, &public_key))
+       return FALSE;
+
+      *ret_arg_alloc = public_key;
+    }
+    break;
+
+  case SILC_ARGUMENT_ATTRIBUTES:
+    if (!ret_arg_alloc)
+      return FALSE;
+
+    *ret_arg_alloc = silc_attribute_payload_parse(data, data_len);
+    break;
+
+  case SILC_ARGUMENT_UINT32:
+    if (data_len != 4)
+      return FALSE;
+
+    if (ret_arg) {
+      SilcUInt32 *i = ret_arg;
+      SILC_GET32_MSB(*i, data);
+    }
+
+    if (ret_arg_alloc) {
+      SilcUInt32 i;
+      SILC_GET32_MSB(i, data);
+      *ret_arg_alloc = silc_memdup(&i, sizeof(i));
+    }
+    break;
+
+  case SILC_ARGUMENT_BOOL:
+    if (data_len != sizeof(SilcBool))
+      return FALSE;
+
+    if (ret_arg) {
+      SilcBool *b = ret_arg;
+      *b = (data[0] == 0x01 ? TRUE : FALSE);
+    }
+
+    if (ret_arg_alloc) {
+      SilcBool b;
+      b = (data[0] == 0x01 ? TRUE : FALSE);
+      *ret_arg_alloc = silc_memdup(&b, sizeof(b));
+    }
+    break;
+
+  default:
+    return FALSE;
+  }
+
+  return TRUE;
+}
+
+/* Return argument already decoded */
+
+SilcBool silc_argument_get_decoded(SilcArgumentPayload payload,
+                                  SilcUInt32 type,
+                                  SilcArgumentDecodeType dec_type,
+                                  void *ret_arg,
+                                  void **ret_arg_alloc)
+{
+  unsigned char *tmp;
+  SilcUInt32 tmp_len;
+
+  tmp = silc_argument_get_arg_type(payload, type, &tmp_len);
+  if (!tmp)
+    return FALSE;
+
+  return silc_argument_decode(tmp, tmp_len, dec_type, ret_arg, ret_arg_alloc);
+}
+
+/************************* Argument List Payload ****************************/
+
+/* Parses argument payload list */
+
+SilcArgumentPayload
+silc_argument_list_parse(const unsigned char *payload,
+                        SilcUInt32 payload_len)
+{
+  SilcArgumentPayload arg;
+  SilcUInt16 argc;
+
+  if (payload_len < 5)
+    return NULL;
+
+  SILC_GET16_MSB(argc, payload);
+
+  arg = silc_argument_payload_parse(payload + 2, payload_len - 2, argc);
+
+  return arg;
+}
+
+/* Parses argument payload list of specific argument types */
+
+SilcDList
+silc_argument_list_parse_decoded(const unsigned char *payload,
+                                SilcUInt32 payload_len,
+                                SilcArgumentDecodeType dec_type)
+{
+  SilcArgumentPayload arg;
+  SilcArgumentDecodedList dec;
+  unsigned char *data;
+  SilcUInt32 data_len, type;
+  SilcDList list;
+
+  arg = silc_argument_list_parse(payload, payload_len);
+  if (!arg)
+    return NULL;
+
+  list = silc_dlist_init();
+  if (!list) {
+    silc_argument_payload_free(arg);
+    return NULL;
+  }
+
+  data = silc_argument_get_first_arg(arg, &type, &data_len);
+  while (data) {
+    dec = silc_calloc(1, sizeof(*dec));
+    if (!dec)
+      continue;
+    dec->arg_type = type;
+    if (silc_argument_decode(data, data_len, dec_type, NULL, &dec->argument))
+      silc_dlist_add(list, dec);
+    else
+      silc_free(dec);
+    data = silc_argument_get_next_arg(arg, &type, &data_len);
+  }
+
+  silc_argument_payload_free(arg);
+
+  silc_dlist_start(list);
+
+  return list;
+}
+
+/* Free decoded argument payload list */
+
+void silc_argument_list_free(SilcDList list, SilcArgumentDecodeType dec_type)
+{
+  SilcArgumentDecodedList dec;
+
+  if (!list)
+    return;
+
+  silc_dlist_start(list);
+  while ((dec = silc_dlist_get(list))) {
+    switch (dec_type) {
+
+    case SILC_ARGUMENT_ID:
+    case SILC_ARGUMENT_UINT32:
+    case SILC_ARGUMENT_BOOL:
+      silc_free(dec->argument);
+      break;
+
+    case SILC_ARGUMENT_PUBLIC_KEY:
+      silc_pkcs_public_key_free(dec->argument);
+      break;
+
+    case SILC_ARGUMENT_ATTRIBUTES:
+      silc_attribute_payload_free(dec->argument);
+      break;
+
+    default:
+      break;
+    }
+
+    silc_free(dec);
+  }
+
+  silc_dlist_uninit(list);
+}