Change IAX variables to use datastores (closes issue #9315)
[asterisk/asterisk.git] / channels / chan_iax2.c
index 8f6e3dc..6fd9b16 100644 (file)
@@ -882,6 +882,8 @@ static struct iax2_user *build_user(const char *name, struct ast_variable *v, st
 static void realtime_update_peer(const char *peername, struct sockaddr_in *sin, time_t regtime);
 static void destroy_user(struct iax2_user *user);
 static void prune_peers(void);
 static void realtime_update_peer(const char *peername, struct sockaddr_in *sin, time_t regtime);
 static void destroy_user(struct iax2_user *user);
 static void prune_peers(void);
+static void *iax2_dup_variable_datastore(void *);
+static void iax2_free_variable_datastore(void *);
 
 static int acf_channel_read(struct ast_channel *chan, const char *funcname, char *preparse, char *buf, size_t buflen);
 static int acf_channel_write(struct ast_channel *chan, const char *function, char *data, const char *value);
 
 static int acf_channel_read(struct ast_channel *chan, const char *funcname, char *preparse, char *buf, size_t buflen);
 static int acf_channel_write(struct ast_channel *chan, const char *function, char *data, const char *value);
@@ -931,6 +933,50 @@ static void iax2_ami_channelupdate(struct chan_iax2_pvt *pvt)
 }
 
 
 }
 
 
+static struct ast_datastore_info iax2_variable_datastore_info = {
+       .type = "IAX2_VARIABLE",
+       .duplicate = iax2_dup_variable_datastore,
+       .destroy = iax2_free_variable_datastore,
+};
+
+static void *iax2_dup_variable_datastore(void *old)
+{
+       AST_LIST_HEAD(, ast_var_t) *oldlist = old, *newlist;
+       struct ast_var_t *oldvar, *newvar;
+
+       newlist = ast_calloc(sizeof(*newlist), 1);
+       if (!newlist) {
+               ast_log(LOG_ERROR, "Unable to duplicate iax2 variables\n");
+               return NULL;
+       }
+
+       AST_LIST_HEAD_INIT(newlist);
+       AST_LIST_LOCK(oldlist);
+       AST_LIST_TRAVERSE(oldlist, oldvar, entries) {
+               newvar = ast_var_assign(ast_var_name(oldvar), ast_var_value(oldvar));
+               if (newvar)
+                       AST_LIST_INSERT_TAIL(newlist, newvar, entries);
+               else
+                       ast_log(LOG_ERROR, "Unable to duplicate iax2 variable '%s'\n", ast_var_name(oldvar));
+       }
+       AST_LIST_UNLOCK(oldlist);
+       return newlist;
+}
+
+static void iax2_free_variable_datastore(void *old)
+{
+       AST_LIST_HEAD(, ast_var_t) *oldlist = old;
+       struct ast_var_t *oldvar;
+
+       AST_LIST_LOCK(oldlist);
+       while ((oldvar = AST_LIST_REMOVE_HEAD(oldlist, entries))) {
+               ast_free(oldvar);
+       }
+       AST_LIST_UNLOCK(oldlist);
+       AST_LIST_HEAD_DESTROY(oldlist);
+       ast_free(oldlist);
+}
+
 static void insert_idle_thread(struct iax2_thread *thread)
 {
        if (thread->type == IAX_THREAD_TYPE_DYNAMIC) {
 static void insert_idle_thread(struct iax2_thread *thread)
 {
        if (thread->type == IAX_THREAD_TYPE_DYNAMIC) {
@@ -2970,6 +3016,7 @@ static int iax2_call(struct ast_channel *c, char *dest, int timeout)
        struct parsed_dial_string pds;
        struct create_addr_info cai;
        struct ast_var_t *var;
        struct parsed_dial_string pds;
        struct create_addr_info cai;
        struct ast_var_t *var;
+       struct ast_datastore *variablestore = ast_channel_datastore_find(c, &iax2_variable_datastore_info, NULL);
        const char* osp_token_ptr;
        unsigned int osp_token_length;
        unsigned char osp_block_index;
        const char* osp_token_ptr;
        unsigned int osp_token_length;
        unsigned char osp_block_index;
@@ -3118,16 +3165,19 @@ static int iax2_call(struct ast_channel *c, char *dest, int timeout)
        iaxs[callno]->sockfd = cai.sockfd;
 
        /* Add remote vars */
        iaxs[callno]->sockfd = cai.sockfd;
 
        /* Add remote vars */
-       AST_LIST_TRAVERSE(&c->varshead, var, entries) {
-               if (!strncmp(ast_var_name(var), "~IAX2~", strlen("~IAX2~"))) {
+       if (variablestore) {
+               AST_LIST_HEAD(, ast_var_t) *variablelist = variablestore->data;
+               AST_LIST_LOCK(variablelist);
+               AST_LIST_TRAVERSE(variablelist, var, entries) {
                        char tmp[256];
                        int i;
                        /* Automatically divide the value up into sized chunks */
                        char tmp[256];
                        int i;
                        /* Automatically divide the value up into sized chunks */
-                       for (i = 0; i < strlen(ast_var_value(var)); i += 255 - (strlen(ast_var_name(var)) - strlen("~IAX2~") + 1)) {
-                               snprintf(tmp, sizeof(tmp), "%s=%s", ast_var_name(var) + strlen("~IAX2~"), ast_var_value(var) + i);
+                       for (i = 0; i < strlen(ast_var_value(var)); i += 255 - (strlen(ast_var_name(var)) + 1)) {
+                               snprintf(tmp, sizeof(tmp), "%s=%s", ast_var_name(var), ast_var_value(var) + i);
                                iax_ie_append_str(&ied, IAX_IE_VARIABLE, tmp);
                        }
                }
                                iax_ie_append_str(&ied, IAX_IE_VARIABLE, tmp);
                        }
                }
+               AST_LIST_UNLOCK(variablelist);
        }
 
        /* Transmit the string in a "NEW" request */
        }
 
        /* Transmit the string in a "NEW" request */
@@ -3135,7 +3185,7 @@ static int iax2_call(struct ast_channel *c, char *dest, int timeout)
 
        ast_mutex_unlock(&iaxsl[callno]);
        ast_setstate(c, AST_STATE_RINGING);
 
        ast_mutex_unlock(&iaxsl[callno]);
        ast_setstate(c, AST_STATE_RINGING);
-       
+
        return 0;
 }
 
        return 0;
 }
 
@@ -3478,6 +3528,7 @@ static struct ast_channel *ast_iax2_new(int callno, int state, int capability, u
        struct ast_channel *tmp;
        struct chan_iax2_pvt *i;
        struct ast_variable *v = NULL;
        struct ast_channel *tmp;
        struct chan_iax2_pvt *i;
        struct ast_variable *v = NULL;
+       struct ast_datastore *variablestore = NULL;
 
        if (!(i = iaxs[callno])) {
                ast_log(LOG_WARNING, "No IAX2 pvt found for callno '%d' !\n", callno);
 
        if (!(i = iaxs[callno])) {
                ast_log(LOG_WARNING, "No IAX2 pvt found for callno '%d' !\n", callno);
@@ -3526,8 +3577,32 @@ static struct ast_channel *ast_iax2_new(int callno, int state, int capability, u
        i->owner = tmp;
        i->capability = capability;
 
        i->owner = tmp;
        i->capability = capability;
 
-       for (v = i->vars ; v ; v = v->next)
-               pbx_builtin_setvar_helper(tmp, v->name, v->value);
+       /* Set inherited variables */
+       if (i->vars) {
+               AST_LIST_HEAD(, ast_var_t) *varlist;
+               varlist = ast_calloc(1, sizeof(*varlist));
+               variablestore = ast_channel_datastore_alloc(&iax2_variable_datastore_info, NULL);
+               if (variablestore && varlist) {
+                       variablestore->data = varlist;
+                       variablestore->inheritance = DATASTORE_INHERIT_FOREVER;
+                       AST_LIST_HEAD_INIT(varlist);
+                       for (v = i->vars ; v ; v = v->next) {
+                               struct ast_var_t *newvar = ast_var_assign(v->name, v->value);
+                               if (!newvar) {
+                                       ast_log(LOG_ERROR, "Out of memory\n");
+                                       break;
+                               }
+                               AST_LIST_INSERT_TAIL(varlist, newvar, entries);
+                       }
+                       ast_channel_datastore_add(tmp, variablestore);
+               } else {
+                       ast_log(LOG_ERROR, "Out of memory\n");
+                       if (variablestore)
+                               ast_channel_datastore_free(variablestore);
+                       if (varlist)
+                               ast_free(varlist);
+               }
+       }
 
        if (delaypbx) {
                ast_set_flag(i, IAX_DELAYPBXSTART);
 
        if (delaypbx) {
                ast_set_flag(i, IAX_DELAYPBXSTART);
@@ -5460,6 +5535,42 @@ static int authenticate_reply(struct chan_iax2_pvt *p, struct sockaddr_in *sin,
        }
        if (ies->encmethods)
                ast_set_flag(p, IAX_ENCRYPTED | IAX_KEYPOPULATED);
        }
        if (ies->encmethods)
                ast_set_flag(p, IAX_ENCRYPTED | IAX_KEYPOPULATED);
+       if (!res) {
+               struct ast_datastore *variablestore;
+               struct ast_variable *var, *prev = NULL;
+               AST_LIST_HEAD(, ast_var_t) *varlist;
+               varlist = ast_calloc(1, sizeof(*varlist));
+               variablestore = ast_channel_datastore_alloc(&iax2_variable_datastore_info, NULL);
+               if (variablestore && varlist && p->owner) {
+                       variablestore->data = varlist;
+                       variablestore->inheritance = DATASTORE_INHERIT_FOREVER;
+                       AST_LIST_HEAD_INIT(varlist);
+                       for (var = ies->vars; var; var = var->next) {
+                               struct ast_var_t *newvar = ast_var_assign(var->name, var->value);
+                               if (prev)
+                                       ast_free(prev);
+                               prev = var;
+                               if (!newvar) {
+                                       /* Don't abort list traversal, as this would leave ies->vars in an inconsistent state. */
+                                       ast_log(LOG_ERROR, "Memory allocation error while processing IAX2 variables\n");
+                               } else {
+                                       AST_LIST_INSERT_TAIL(varlist, newvar, entries);
+                               }
+                       }
+                       if (prev)
+                               free(prev);
+                       ies->vars = NULL;
+                       ast_channel_datastore_add(p->owner, variablestore);
+               } else {
+                       if (p->owner)
+                               ast_log(LOG_ERROR, "Memory allocation error while processing IAX2 variables\n");
+                       if (variablestore)
+                               ast_channel_datastore_free(variablestore);
+                       if (varlist)
+                               ast_free(varlist);
+               }
+       }
+
        if (!res)
                res = send_command(p, AST_FRAME_IAX, IAX_COMMAND_AUTHREP, 0, ied.buf, ied.pos, -1);
        return res;
        if (!res)
                res = send_command(p, AST_FRAME_IAX, IAX_COMMAND_AUTHREP, 0, ied.buf, ied.pos, -1);
        return res;
@@ -6782,20 +6893,67 @@ static int socket_process_meta(int packet_len, struct ast_iax2_meta_hdr *meta, s
 
 static int acf_iaxvar_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
 {
 
 static int acf_iaxvar_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
 {
-       const char *value;
-       char tmp[256];
-       snprintf(tmp, sizeof(tmp), "~IAX2~%s", data);
-       value = pbx_builtin_getvar_helper(chan, tmp);
-       ast_copy_string(buf, value ? value : "", len);
+       struct ast_datastore *variablestore = ast_channel_datastore_find(chan, &iax2_variable_datastore_info, NULL);
+       AST_LIST_HEAD(, ast_var_t) *varlist;
+       struct ast_var_t *var;
+
+       if (!variablestore) {
+               *buf = '\0';
+               return 0;
+       }
+       varlist = variablestore->data;
+
+       AST_LIST_LOCK(varlist);
+       AST_LIST_TRAVERSE(varlist, var, entries) {
+               if (strcmp(var->name, data) == 0) {
+                       ast_copy_string(buf, var->value, len);
+                       break;
+               }
+       }
+       AST_LIST_UNLOCK(varlist);
        return 0;
 }
 
        return 0;
 }
 
-static int acf_iaxvar_write(struct ast_channel *chan, const char *cmd, char *varname, const char *value)
+static int acf_iaxvar_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
 {
 {
-       char tmp[256];
-       /* Inherit forever */
-       snprintf(tmp, sizeof(tmp), "__~IAX2~%s", varname);
-       pbx_builtin_setvar_helper(chan, tmp, value);
+       struct ast_datastore *variablestore = ast_channel_datastore_find(chan, &iax2_variable_datastore_info, NULL);
+       AST_LIST_HEAD(, ast_var_t) *varlist;
+       struct ast_var_t *var;
+
+       if (!variablestore) {
+               variablestore = ast_channel_datastore_alloc(&iax2_variable_datastore_info, NULL);
+               if (!variablestore) {
+                       ast_log(LOG_ERROR, "Memory allocation error\n");
+                       return -1;
+               }
+               varlist = ast_calloc(1, sizeof(*varlist));
+               if (!varlist) {
+                       ast_log(LOG_ERROR, "Unable to assign new variable '%s'\n", data);
+                       return -1;
+               }
+
+               AST_LIST_HEAD_INIT(varlist);
+               variablestore->data = varlist;
+               variablestore->inheritance = DATASTORE_INHERIT_FOREVER;
+               ast_channel_datastore_add(chan, variablestore);
+       } else
+               varlist = variablestore->data;
+
+       AST_LIST_LOCK(varlist);
+       AST_LIST_TRAVERSE_SAFE_BEGIN(varlist, var, entries) {
+               if (strcmp(var->name, data) == 0) {
+                       AST_LIST_REMOVE_CURRENT(varlist, entries);
+                       ast_var_delete(var);
+                       break;
+               }
+       }
+       AST_LIST_TRAVERSE_SAFE_END
+       var = ast_var_assign(data, value);
+       if (var)
+               AST_LIST_INSERT_TAIL(varlist, var, entries);
+       else
+               ast_log(LOG_ERROR, "Unable to assign new variable '%s'\n", data);
+       AST_LIST_UNLOCK(varlist);
        return 0;
 }
 
        return 0;
 }
 
@@ -7403,16 +7561,38 @@ retryowner:
                                                                if (!(c = ast_iax2_new(fr->callno, AST_STATE_RING, format, 1)))
                                                                        iax2_destroy(fr->callno);
                                                                else if (ies.vars) {
                                                                if (!(c = ast_iax2_new(fr->callno, AST_STATE_RING, format, 1)))
                                                                        iax2_destroy(fr->callno);
                                                                else if (ies.vars) {
+                                                                       struct ast_datastore *variablestore;
                                                                        struct ast_variable *var, *prev = NULL;
                                                                        struct ast_variable *var, *prev = NULL;
-                                                                       char tmp[256];
-                                                                       for (var = ies.vars; var; var = var->next) {
+                                                                       AST_LIST_HEAD(, ast_var_t) *varlist;
+                                                                       varlist = ast_calloc(1, sizeof(*varlist));
+                                                                       variablestore = ast_channel_datastore_alloc(&iax2_variable_datastore_info, NULL);
+                                                                       if (variablestore && varlist) {
+                                                                               variablestore->data = varlist;
+                                                                               variablestore->inheritance = DATASTORE_INHERIT_FOREVER;
+                                                                               AST_LIST_HEAD_INIT(varlist);
+                                                                               for (var = ies.vars; var; var = var->next) {
+                                                                                       struct ast_var_t *newvar = ast_var_assign(var->name, var->value);
+                                                                                       if (prev)
+                                                                                               ast_free(prev);
+                                                                                       prev = var;
+                                                                                       if (!newvar) {
+                                                                                               /* Don't abort list traversal, as this would leave ies.vars in an inconsistent state. */
+                                                                                               ast_log(LOG_ERROR, "Memory allocation error while processing IAX2 variables\n");
+                                                                                       } else {
+                                                                                               AST_LIST_INSERT_TAIL(varlist, newvar, entries);
+                                                                                       }
+                                                                               }
                                                                                if (prev)
                                                                                        ast_free(prev);
                                                                                if (prev)
                                                                                        ast_free(prev);
-                                                                               prev = var;
-                                                                               snprintf(tmp, sizeof(tmp), "__~IAX2~%s", var->name);
-                                                                               pbx_builtin_setvar_helper(c, tmp, var->value);
+                                                                               ies.vars = NULL;
+                                                                               ast_channel_datastore_add(c, variablestore);
+                                                                       } else {
+                                                                               ast_log(LOG_ERROR, "Memory allocation error while processing IAX2 variables\n");
+                                                                               if (variablestore)
+                                                                                       ast_channel_datastore_free(variablestore);
+                                                                               if (varlist)
+                                                                                       ast_free(varlist);
                                                                        }
                                                                        }
-                                                                       ies.vars = NULL;
                                                                }
                                                        } else {
                                                                ast_set_flag(&iaxs[fr->callno]->state, IAX_STATE_TBD);
                                                                }
                                                        } else {
                                                                ast_set_flag(&iaxs[fr->callno]->state, IAX_STATE_TBD);
@@ -7792,8 +7972,42 @@ retryowner2:
                                                                                        using_prefs);
 
                                                        ast_set_flag(&iaxs[fr->callno]->state, IAX_STATE_STARTED);
                                                                                        using_prefs);
 
                                                        ast_set_flag(&iaxs[fr->callno]->state, IAX_STATE_STARTED);
-                                                       if(!(c = ast_iax2_new(fr->callno, AST_STATE_RING, format, 0)))
+                                                       if (!(c = ast_iax2_new(fr->callno, AST_STATE_RING, format, 0)))
                                                                iax2_destroy(fr->callno);
                                                                iax2_destroy(fr->callno);
+                                                       else if (ies.vars) {
+                                                               struct ast_datastore *variablestore;
+                                                               struct ast_variable *var, *prev = NULL;
+                                                               AST_LIST_HEAD(, ast_var_t) *varlist;
+                                                               varlist = ast_calloc(1, sizeof(*varlist));
+                                                               variablestore = ast_channel_datastore_alloc(&iax2_variable_datastore_info, NULL);
+                                                               if (variablestore && varlist) {
+                                                                       variablestore->data = varlist;
+                                                                       variablestore->inheritance = DATASTORE_INHERIT_FOREVER;
+                                                                       AST_LIST_HEAD_INIT(varlist);
+                                                                       for (var = ies.vars; var; var = var->next) {
+                                                                               struct ast_var_t *newvar = ast_var_assign(var->name, var->value);
+                                                                               if (prev)
+                                                                                       ast_free(prev);
+                                                                               prev = var;
+                                                                               if (!newvar) {
+                                                                                       /* Don't abort list traversal, as this would leave ies.vars in an inconsistent state. */
+                                                                                       ast_log(LOG_ERROR, "Memory allocation error while processing IAX2 variables\n");
+                                                                               } else {
+                                                                                       AST_LIST_INSERT_TAIL(varlist, newvar, entries);
+                                                                               }
+                                                                       }
+                                                                       if (prev)
+                                                                               free(prev);
+                                                                       ies.vars = NULL;
+                                                                       ast_channel_datastore_add(c, variablestore);
+                                                               } else {
+                                                                       ast_log(LOG_ERROR, "Memory allocation error while processing IAX2 variables\n");
+                                                                       if (variablestore)
+                                                                               ast_channel_datastore_free(variablestore);
+                                                                       if (varlist)
+                                                                               ast_free(varlist);
+                                                               }
+                                                       }
                                                } else {
                                                        ast_set_flag(&iaxs[fr->callno]->state, IAX_STATE_TBD);
                                                        /* If this is a TBD call, we're ready but now what...  */
                                                } else {
                                                        ast_set_flag(&iaxs[fr->callno]->state, IAX_STATE_TBD);
                                                        /* If this is a TBD call, we're ready but now what...  */