--- a
+++ b/branches/1.9/modules/mi_json/http_fnc.c
@@ -0,0 +1,579 @@
+/*
+ * This file is part of Open SIP Server (opensips).
+ *
+ * opensips is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * opensips is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ * History:
+ * ---------
+ *  2013-10-31  first version (shimaore)
+ */
+
+
+#include "../../str.h"
+#include "../../ut.h"
+#include "../../mem/mem.h"
+#include "../../mem/shm_mem.h"
+#include "../../mi/mi.h"
+#include "../../config.h"
+#include "../../globals.h"
+#include "../../locking.h"
+
+#include "http_fnc.h"
+
+extern str http_root;
+
+mi_json_page_data_t html_page_data;
+
+gen_lock_t* mi_json_lock;
+
+struct page_buf {
+  char *current;
+  char *buf;
+  int max_page_len;
+  short status;
+};
+
+static inline void MI_JSON_COPY(struct page_buf* pb, const str s) {
+  if ( pb->status ) {
+    return;
+  }
+  if ( s.s == NULL || s.len == 0 ) {
+    return;
+  }
+  if ( (int)(pb->current - pb->buf) + s.len > pb->max_page_len) {
+    pb->status = -1;
+  } else {
+    memcpy(pb->current, s.s, s.len);
+    pb->current += s.len;
+  }
+}
+
+static const str MI_JSON_ESC =  str_init("\\");
+
+static inline void MI_JSON_ESC_COPY(struct page_buf* pb, const str s) {
+  str temp_holder;
+  int temp_counter;
+  if( pb->status ) {
+    return;
+  }
+  if( s.s == NULL || s.len == 0 ) {
+    return;
+  }
+  temp_holder.s = s.s;
+  temp_holder.len = 0;
+  for(temp_counter=0;temp_counter<s.len;temp_counter++) {
+    switch(s.s[temp_counter]) {
+    case '"':
+    case '\\':
+      temp_holder.len = temp_counter - temp_holder.len;
+      MI_JSON_COPY(pb, temp_holder);
+      MI_JSON_COPY(pb, MI_JSON_ESC);
+      temp_holder.s = s.s + temp_counter;
+      temp_holder.len = temp_counter;
+      break;
+    }
+  }
+  temp_holder.len = temp_counter - temp_holder.len;
+  MI_JSON_COPY(pb, temp_holder);
+}
+
+
+static const str MI_JSON_CR = str_init("\n");
+
+static const str MI_JSON_KEY_NAME = str_init("\"name\":");
+static const str MI_JSON_KEY_VALUE = str_init("\"value\":");
+static const str MI_JSON_KEY_ATTRIBUTES = str_init("\"attributes\":");
+static const str MI_JSON_KEY_NODE = str_init("\"node\":");
+static const str MI_JSON_KEY_CHILDREN = str_init("\"children\":");
+
+static const str MI_JSON_NULL = str_init("null");
+
+static const str MI_JSON_COMMA = str_init(", ");
+static const str MI_JSON_COLON = str_init(": ");
+
+static const str MI_JSON_OBJECT_START = str_init("{");
+static const str MI_JSON_OBJECT_STOP = str_init("}");
+static const str MI_JSON_ARRAY_START = str_init("[");
+static const str MI_JSON_ARRAY_STOP = str_init("]");
+
+static const str MI_JSON_SQUOT =  str_init("\""); /* " */
+
+
+int mi_json_init_async_lock(void)
+{
+  mi_json_lock = lock_alloc();
+  if (mi_json_lock==NULL) {
+    LM_ERR("failed to create lock\n");
+    return -1;
+  }
+  if (lock_init(mi_json_lock)==NULL) {
+    LM_ERR("failed to init lock\n");
+    return -1;
+  }
+  return 0;
+}
+
+void mi_json_destroy_async_lock(void)
+{
+  if (mi_json_lock) {
+    lock_destroy(mi_json_lock);
+    lock_dealloc(mi_json_lock);
+  }
+}
+
+
+static int mi_json_recur_flush_tree(struct page_buf* pb, struct mi_node *tree);
+int mi_json_flush_content(str *page, int max_page_len,
+        struct mi_root* tree);
+
+
+
+int mi_json_flush_tree(void* param, struct mi_root *tree)
+{
+  if (param==NULL) {
+    LM_CRIT("null param\n");
+    return 0;
+  }
+  mi_json_page_data_t* html_p_data = (mi_json_page_data_t*)param;
+  mi_json_flush_content(&html_p_data->page,
+        html_p_data->buffer.len,
+        tree);
+  return 0;
+}
+
+
+static void mi_json_close_async(struct mi_root *mi_rpl, struct mi_handler *hdl, int done)
+{
+  struct mi_root *shm_rpl = NULL;
+  gen_lock_t* lock;
+  mi_json_async_resp_data_t *async_resp_data;
+
+  if (hdl==NULL) {
+    LM_CRIT("null mi handler\n");
+    return;
+  }
+
+  LM_DBG("mi_root [%p], hdl [%p], hdl->param [%p], "
+    "*hdl->param [%p] and done [%u]\n",
+    mi_rpl, hdl, hdl->param, *(struct mi_root **)hdl->param, done);
+
+  if (!done) {
+    /* we do not pass provisional stuff (yet) */
+    if (mi_rpl) free_mi_tree( mi_rpl );
+    return;
+  }
+
+  async_resp_data =
+    (mi_json_async_resp_data_t*)((char*)hdl+sizeof(struct mi_handler));
+  lock = async_resp_data->lock;
+  lock_get(lock);
+  if (mi_rpl!=NULL && (shm_rpl=clone_mi_tree( mi_rpl, 1))!=NULL) {
+    *(struct mi_root **)hdl->param = shm_rpl;
+  } else {
+    LM_WARN("Unable to process async reply [%p]\n", mi_rpl);
+    /* mark it as invalid */
+    hdl->param = NULL;
+  }
+  LM_DBG("shm_rpl [%p], hdl [%p], hdl->param [%p], *hdl->param [%p]\n",
+    shm_rpl, hdl, hdl->param,
+    (hdl->param)?*(struct mi_root **)hdl->param:NULL);
+  lock_release(lock);
+
+  if (mi_rpl) free_mi_tree(mi_rpl);
+
+  return;
+}
+
+static inline struct mi_handler* mi_json_build_async_handler(void)
+{
+  struct mi_handler *hdl;
+  mi_json_async_resp_data_t *async_resp_data;
+  unsigned int len;
+
+  len = sizeof(struct mi_handler)+sizeof(mi_json_async_resp_data_t);
+  hdl = (struct mi_handler*)shm_malloc(len);
+  if (hdl==NULL) {
+    LM_ERR("oom\n");
+    return NULL;
+  }
+
+  memset(hdl, 0, len);
+  async_resp_data =
+    (mi_json_async_resp_data_t*)((char*)hdl+sizeof(struct mi_handler));
+
+  hdl->handler_f = mi_json_close_async;
+  hdl->param = (void*)&async_resp_data->tree;
+
+  async_resp_data->lock = mi_json_lock;
+
+  LM_DBG("hdl [%p], hdl->param [%p], *hdl->param [%p] mi_json_lock=[%p]\n",
+    hdl, hdl->param, (hdl->param)?*(struct mi_root **)hdl->param:NULL,
+    async_resp_data->lock);
+
+  return hdl;
+}
+
+struct mi_root* mi_json_run_mi_cmd(const str* miCmd, const str* params,
+    str *page, str *buffer, struct mi_handler **async_hdl)
+{
+  struct mi_cmd *f;
+  struct mi_node *node;
+  struct mi_root *mi_cmd;
+  struct mi_root *mi_rpl;
+  struct mi_handler *hdl;
+  str val;
+  int i, j;
+
+  LM_DBG("got command=%.*s\n", miCmd->len, miCmd->s);
+
+  f = lookup_mi_cmd(miCmd->s, miCmd->len);
+  if (f == NULL) {
+    LM_ERR("unable to find mi command [%.*s]\n", miCmd->len, miCmd->s);
+    goto error;
+  }
+
+  if (f->flags&MI_ASYNC_RPL_FLAG) {
+    LM_DBG("command=%.*s is async\n", miCmd->len, miCmd->s);
+    /* We need to build an async handler */
+    hdl = mi_json_build_async_handler();
+    if (hdl==NULL) {
+      LM_ERR("failed to build async handler\n");
+      goto error;
+    }
+  } else {
+    hdl = NULL;
+  }
+  *async_hdl = hdl;
+
+  if (f->flags&MI_NO_INPUT_FLAG) {
+    LM_DBG("command=%.*s requires no parameters\n", miCmd->len, miCmd->s);
+    mi_cmd = NULL;
+  } else {
+    LM_DBG("command=%.*s accepts parameters\n", miCmd->len, miCmd->s);
+    if (params->s) {
+      mi_cmd = init_mi_tree(0,0,0);
+      if (mi_cmd==NULL) {
+        LM_ERR("the MI tree cannot be initialized!\n");
+        goto error;
+      }
+      i = 0;
+      j = 0;
+      for( i = 0; i < params->len; i++ ) {
+        if (params->s[i] == ',') {
+          val.s = params->s + j;
+          val.len = i-j;
+          LM_DBG("got string param [%.*s]\n", val.len, val.s);
+          node = &mi_cmd->node;
+          if(!add_mi_node_child(node,0,NULL,0,val.s,val.len)){
+            LM_ERR("cannot add the child node to the tree\n");
+            free_mi_tree(mi_cmd);
+            goto error;
+          }
+          j = i+1;
+        }
+      }
+      if( j < params->len ) {
+        val.s = params->s + j;
+        val.len = params->len-j;
+        LM_DBG("got string param [%.*s]\n", val.len, val.s);
+        node = &mi_cmd->node;
+        if(!add_mi_node_child(node,0,NULL,0,val.s,val.len)){
+          LM_ERR("cannot add the child node to the tree\n");
+          free_mi_tree(mi_cmd);
+          goto error;
+        }
+      }
+      mi_cmd->async_hdl = hdl;
+    } else {
+      LM_DBG("but no parameters were found\n");
+      mi_cmd = init_mi_tree(0,0,0);
+      if (mi_cmd==NULL) {
+        LM_ERR("the MI tree cannot be initialized!\n");
+        goto error;
+      }
+    }
+  }
+
+  html_page_data.page.s = buffer->s;
+  html_page_data.page.len = 0;
+  html_page_data.buffer.s = buffer->s;
+  html_page_data.buffer.len = buffer->len;
+
+  mi_rpl = run_mi_cmd(f, mi_cmd,
+        (mi_flush_f *)mi_json_flush_tree, &html_page_data);
+  if (mi_rpl == NULL) {
+    LM_ERR("failed to process the command\n");
+    if (mi_cmd) free_mi_tree(mi_cmd);
+    goto error;
+  } else if (mi_rpl != MI_ROOT_ASYNC_RPL) {
+    *page = html_page_data.page;
+  }
+  LM_DBG("got mi_rpl=[%p]\n",mi_rpl);
+
+  if (mi_cmd) free_mi_tree(mi_cmd);
+  return mi_rpl;
+
+error:
+  return NULL;
+}
+
+
+static inline int mi_json_write_node_array(struct page_buf* pb,
+          struct mi_node *node)
+{
+  LM_DBG("start\n");
+  MI_JSON_COPY(pb, MI_JSON_SQUOT);
+  MI_JSON_ESC_COPY(pb, node->value);
+  MI_JSON_COPY(pb, MI_JSON_SQUOT);
+
+  node->flags |= MI_WRITTEN;
+  return pb->status;
+}
+static inline int mi_json_write_node_hash(struct page_buf* pb,
+          struct mi_node *node)
+{
+  LM_DBG("start\n");
+
+  MI_JSON_COPY(pb, MI_JSON_SQUOT);
+  MI_JSON_ESC_COPY(pb, node->name);
+  MI_JSON_COPY(pb, MI_JSON_SQUOT);
+  MI_JSON_COPY(pb, MI_JSON_COLON);
+  if (node->value.s!=NULL) {
+    MI_JSON_COPY(pb, MI_JSON_SQUOT);
+    MI_JSON_ESC_COPY(pb, node->value);
+    MI_JSON_COPY(pb, MI_JSON_SQUOT);
+  } else {
+    MI_JSON_COPY(pb, MI_JSON_NULL);
+  }
+
+  node->flags |= MI_WRITTEN;
+  return pb->status;
+}
+
+static inline int mi_json_write_node(struct page_buf* pb,
+          struct mi_node *node)
+{
+  struct mi_attr *attr;
+  LM_DBG("start\n");
+
+  /* name */
+  MI_JSON_COPY(pb, MI_JSON_KEY_NAME);
+  if (node->name.s!=NULL) {
+    MI_JSON_COPY(pb, MI_JSON_SQUOT);
+    MI_JSON_ESC_COPY(pb, node->name);
+    MI_JSON_COPY(pb, MI_JSON_SQUOT);
+  } else {
+    MI_JSON_COPY(pb, MI_JSON_NULL);
+  }
+  MI_JSON_COPY(pb, MI_JSON_COMMA);
+
+  /* value */
+  MI_JSON_COPY(pb, MI_JSON_KEY_VALUE);
+  if (node->value.s!=NULL) {
+    MI_JSON_COPY(pb, MI_JSON_SQUOT);
+    MI_JSON_ESC_COPY(pb, node->value);
+    MI_JSON_COPY(pb, MI_JSON_SQUOT);
+  } else {
+    MI_JSON_COPY(pb, MI_JSON_NULL);
+  }
+  MI_JSON_COPY(pb, MI_JSON_COMMA);
+
+  /* attributes */
+  MI_JSON_COPY(pb, MI_JSON_KEY_ATTRIBUTES);
+  MI_JSON_COPY(pb, MI_JSON_OBJECT_START);
+  for(attr=node->attributes;attr!=NULL;attr=attr->next) {
+    if (attr->name.s!=NULL) {
+      /* attribute name */
+      MI_JSON_COPY(pb, MI_JSON_SQUOT);
+      MI_JSON_ESC_COPY(pb, attr->name);
+      MI_JSON_COPY(pb, MI_JSON_SQUOT);
+      MI_JSON_COPY(pb, MI_JSON_COLON);
+
+      /* attribute value */
+      if (attr->value.s!=NULL) {
+        MI_JSON_COPY(pb, MI_JSON_SQUOT);
+        MI_JSON_ESC_COPY(pb, attr->value);
+        MI_JSON_COPY(pb, MI_JSON_SQUOT);
+      } else {
+        MI_JSON_COPY(pb, MI_JSON_NULL);
+      }
+    }
+    if (attr->next!=NULL) {
+      MI_JSON_COPY(pb, MI_JSON_COMMA);
+    }
+  }
+  MI_JSON_COPY(pb, MI_JSON_OBJECT_STOP);
+
+  return pb->status;
+}
+
+/* sync case */
+static int mi_json_recur_write_tree(struct page_buf* pb,
+          struct mi_node *tree)
+{
+  int names = 0;
+  int values = 0;
+  int attributes = 0;
+  int kids = 0;
+  struct mi_node* t;
+  LM_DBG("start\n");
+  for( t = tree; t ; t=t->next ) {
+    if(t->name.s) {
+      names++;
+    }
+    if(t->value.s) {
+      values++;
+    }
+    if(t->attributes) {
+      attributes++;
+    }
+    if(t->kids) {
+      kids++;
+    }
+  }
+
+  if(names == 0 && values > 0 && attributes == 0 && kids == 0) {
+    LM_DBG("Treat as an array\n");
+    /* Treat as an array */
+    MI_JSON_COPY(pb, MI_JSON_ARRAY_START);
+    for( t = tree; t; t=t->next ) {
+      mi_json_write_node_array(pb,t);
+      t->flags |= MI_WRITTEN;
+      if(t->next) {
+        MI_JSON_COPY(pb, MI_JSON_COMMA);
+      }
+    }
+    MI_JSON_COPY(pb, MI_JSON_ARRAY_STOP);
+    LM_DBG("done\n");
+    return 0;
+  }
+  if(names >= values && attributes == 0 && kids == 0) {
+    LM_DBG("Treat as a hash\n");
+    /* Treat as a hash */
+    MI_JSON_COPY(pb, MI_JSON_OBJECT_START);
+    for( t = tree; t; t=t->next ) {
+      LM_DBG("t = %p\n",t);
+      mi_json_write_node_hash(pb,t);
+      t->flags |= MI_WRITTEN;
+      if(t->next) {
+        MI_JSON_COPY(pb, MI_JSON_COMMA);
+      }
+    }
+    MI_JSON_COPY(pb, MI_JSON_OBJECT_STOP);
+    LM_DBG("done\n");
+    return 0;
+  }
+  if(names == 0 && values == 0 && attributes == 0 && kids > 0) {
+    LM_DBG("Treat as an array of objects\n");
+    /* Treat as an array of objects */
+    MI_JSON_COPY(pb, MI_JSON_ARRAY_START);
+    for( t = tree; t; t=t->next ) {
+      mi_json_recur_write_tree(pb,t->kids);
+      t->flags |= MI_WRITTEN;
+      if(t->next) {
+        MI_JSON_COPY(pb, MI_JSON_COMMA);
+      }
+    }
+    MI_JSON_COPY(pb, MI_JSON_ARRAY_STOP);
+    LM_DBG("done\n");
+    return 0;
+  }
+
+  /* Otherwise */
+  LM_DBG("Treat as a complex array of hashes\n");
+  /* Treat as a complex array of hashes */
+  MI_JSON_COPY(pb, MI_JSON_ARRAY_START);
+  for( t = tree; t; t=t->next ) {
+    MI_JSON_COPY(pb, MI_JSON_OBJECT_START);
+    mi_json_write_node(pb,t);
+    if (t->kids) {
+      MI_JSON_COPY(pb, MI_JSON_COMMA);
+      MI_JSON_COPY(pb, MI_JSON_KEY_CHILDREN);
+      mi_json_recur_write_tree(pb, t->kids);
+    }
+    MI_JSON_COPY(pb, MI_JSON_OBJECT_STOP);
+    if(t->next) {
+      MI_JSON_COPY(pb, MI_JSON_COMMA);
+    }
+  }
+  MI_JSON_COPY(pb, MI_JSON_ARRAY_STOP);
+  LM_DBG("done\n");
+  return pb->status;
+}
+
+
+int mi_json_build_content(str *page, int max_page_len,
+        struct mi_root* tree)
+{
+  struct page_buf pb;
+  LM_DBG("start\n");
+
+  pb.buf = page->s;
+  pb.current = page->s + page->len;
+  pb.max_page_len = max_page_len;
+  pb.status = 0;
+
+  if (tree) { /* Build mi reply */
+    mi_json_recur_write_tree(&pb, tree->node.kids);
+    page->len = pb.current - page->s;
+  }
+  LM_DBG("done\n");
+  return pb.status;
+}
+
+
+int mi_json_build_page(str *page, int max_page_len,
+        struct mi_root *tree)
+{
+  LM_DBG("start\n");
+  return mi_json_build_content(page, max_page_len, tree);
+}
+
+
+/* async case */
+static int mi_json_recur_flush_tree(struct page_buf* pb,
+          struct mi_node *tree)
+{
+  struct mi_node *kid;
+  LM_DBG("start\n");
+
+  for(kid = tree->kids ; kid ; ){
+    if (kid->flags & MI_NOT_COMPLETED) {
+      return 1;
+    }
+  }
+
+  mi_json_recur_write_tree(pb,tree);
+  LM_DBG("done\n");
+  return pb->status;
+}
+
+int mi_json_flush_content(str *page, int max_page_len,
+        struct mi_root* tree)
+{
+  struct page_buf pb;
+  LM_DBG("start\n");
+  pb.buf = page->s;
+  pb.current = page->s + page->len;
+  pb.max_page_len = max_page_len;
+  pb.status = 0;
+
+  if (tree) { /* Build mi reply */
+    mi_json_recur_flush_tree(&pb, &tree->node);
+    page->len = pb.current - page->s;
+  }
+  LM_DBG("done\n");
+  return pb.status;
+}