]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
FS-8585: [mod_commands] group_call: expand {} and <> to [] for each dial string
authorRob Wu <rob@robwu.nl>
Sat, 28 Nov 2015 18:17:56 +0000 (19:17 +0100)
committerRob Wu <rob@robwu.nl>
Sat, 28 Nov 2015 18:17:56 +0000 (19:17 +0100)
Channel variables in dial strings can be set through <>, {} or [].
<foo=bar> applies to every channel.
{foo=bar} applies to every channel, excluding channels delimited by :_:.
[foo=bar] only applies to the channel that follows [foo=bar].

Before this patch, group_call looked up users in the directory and
replaced {} with []. This logic does not work if the user has more than
one dial string, e.g. if multiple-registrations is set to true, and more
than two devices register under the same user.

This patch fixes the issue by stripping <> and {}, and inserting the
dial string before each individual dial string for each user. The
semantics of the "local_clobber" variable and the '|', ',' and ':_:'
delimiters are fully supported.

src/mod/applications/mod_commands/mod_commands.c

index 26f895e8a4eef19ca287a506d2f814e6ea140e9d..abcd79490fb69cbb57d7a01c9c3577e6061b3e7e 100644 (file)
@@ -299,6 +299,117 @@ end:
        return;
 }
 
+char *find_channel_brackets(char *data, char start, char end, char **front, int *local_clobber)
+{
+       char *p;
+       char *last_end = NULL;
+
+       *front = NULL;
+       p = data;
+       while ((p = switch_strchr_strict(p, start, " "))) {
+               char *next_end = switch_find_end_paren(p, start, end);
+               if (!next_end) {
+                       break;
+               }
+               if (!*front) {
+                       *front = p;
+               }
+               *p = '[';
+               last_end = next_end;
+               *last_end = ']';
+               p = last_end + 1;
+       }
+       if (!last_end) {
+               if (local_clobber) {
+                       *local_clobber = 0;
+               }
+               return data;
+       }
+
+       *last_end = '\0';
+       if (local_clobber) {
+               /* Would be nice to use switch_true to account for other valid boolean
+                  representations, but this is better than nothing: */
+               *local_clobber = strstr(data, "local_var_clobber=true") != NULL;
+       }
+       return last_end + 1;
+}
+
+char *find_channel_delim(char *p, const char **out)
+{
+       *out = "";
+       for (; *p; p++) {
+               if (*p == ',') {
+                       *out = ",";
+                       break;
+               }
+               if (*p == '|') {
+                       *out = "|";
+                       break;
+               }
+               if (!strncmp(p, SWITCH_ENT_ORIGINATE_DELIM, strlen(SWITCH_ENT_ORIGINATE_DELIM))) {
+                       *out = SWITCH_ENT_ORIGINATE_DELIM;
+                       break;
+               }
+       }
+       return p;
+}
+
+/* Read <..> and {..}, and inserts [..] before every leg dial string. */
+void output_flattened_dial_string(char *data, switch_stream_handle_t *stream)
+{
+       char *p;
+       char *vars_start_ent;
+       char *vars_start_all;
+       char *vars_start_leg;
+       int local_clobber_ent;
+       int local_clobber_all;
+       const char *delim;
+       char *leg_dial_string;
+
+       /* -3 because ":_:" is the longest delimiter, of length 3. */
+       p = find_channel_delim(end_of_p(data) - 3, &delim);
+       *p = '\0';
+
+       p = data;
+       p = find_channel_brackets(p, '<', '>', &vars_start_ent, &local_clobber_ent);
+       p = find_channel_brackets(p, '{', '}', &vars_start_all, &local_clobber_all);
+
+       while (*p) {
+               p = find_channel_brackets(p, '[', ']', &vars_start_leg, NULL);
+               if (vars_start_leg) {
+                       if (vars_start_ent && !local_clobber_ent) {
+                               stream->write_function(stream, "%s]", vars_start_ent);
+                       }
+                       if (vars_start_all && !local_clobber_all) {
+                               stream->write_function(stream, "%s]", vars_start_all);
+                       }
+                       stream->write_function(stream, "%s]", vars_start_leg);
+               }
+
+               while (*p == ' ') p++;
+
+               if (*p) {
+                       if (vars_start_all && (!vars_start_leg || local_clobber_all)) {
+                               stream->write_function(stream, "%s]", vars_start_all);
+                       }
+                       if (vars_start_ent && (!vars_start_leg || local_clobber_ent)) {
+                               stream->write_function(stream, "%s]", vars_start_ent);
+                       }
+                       leg_dial_string = p;
+                       p = find_channel_delim(p, &delim);
+                       if (*p) {
+                               *p = '\0';
+                               p += strlen(delim);
+                               if (!strcmp(delim, SWITCH_ENT_ORIGINATE_DELIM)) {
+                                       p = find_channel_brackets(p, '{', '}', &vars_start_all, &local_clobber_all);
+                               }
+                       }
+                       stream->write_function(stream, "%s%s", leg_dial_string, delim);
+               }
+       }
+}
+
 #define LIST_USERS_SYNTAX "[group <group>] [domain <domain>] [user <user>] [context <context>]"
 SWITCH_STANDARD_API(list_users_function)
 {
@@ -904,9 +1015,6 @@ SWITCH_STANDARD_API(group_call_function)
        if (!zstr(domain)) {
                switch_xml_t xml, x_domain, x_group;
                switch_event_t *params;
-               switch_stream_handle_t dstream = { 0 };
-
-               SWITCH_STANDARD_STREAM(dstream);
 
                switch_event_create(&params, SWITCH_EVENT_REQUEST_PARAMS);
                switch_event_add_header_string(params, SWITCH_STACK_BOTTOM, "group", group_name);
@@ -917,8 +1025,6 @@ SWITCH_STANDARD_API(group_call_function)
                        switch_xml_t x_user, x_users, x_param, x_params, my_x_user;
 
                        if ((x_users = switch_xml_child(x_group, "users"))) {
-                               ok++;
-
                                for (x_user = switch_xml_child(x_users, "user"); x_user; x_user = x_user->next) {
                                        const char *id = switch_xml_attr_soft(x_user, "id");
                                        const char *x_user_type = switch_xml_attr_soft(x_user, "type");
@@ -1006,11 +1112,22 @@ SWITCH_STANDARD_API(group_call_function)
                                        }
 
                                        if (d_dest) {
-                                               dstream.write_function(&dstream, "%s%s", d_dest, call_delim);
+                                               switch_stream_handle_t dstream = { 0 };
+                                               SWITCH_STANDARD_STREAM(dstream);
+                                               dstream.write_function(&dstream, "%s", d_dest);
 
                                                if (d_dest != dest) {
                                                        free(d_dest);
                                                }
+                                               if (dstream.data) {
+                                                       if (++ok > 1) {
+                                                               stream->write_function(stream, "%s", call_delim);
+                                                       }
+
+                                                       output_flattened_dial_string((char*)dstream.data, stream);
+
+                                                       free(dstream.data);
+                                               }
                                        }
 
                                  done_x_user:
@@ -1019,30 +1136,6 @@ SWITCH_STANDARD_API(group_call_function)
                                                xml_for_pointer = NULL;
                                        }
                                }
-
-                               if (ok && dstream.data) {
-                                       char *data = (char *) dstream.data;
-                                       char *p;
-
-                                       if ((p = strstr(end_of_p(data) - 3, call_delim))) {
-                                               *p = '\0';
-                                       }
-
-                                       for (p = data; p && *p; p++) {
-                                               if (*p == '{') {
-                                                       *p = '[';
-                                               } else if (*p == '}') {
-                                                       *p = ']';
-                                               }
-                                       }
-
-
-                                       stream->write_function(stream, "%s", data);
-                                       free(dstream.data);
-                               } else {
-                                       ok = 0;
-                               }
-
                        }
                }
                switch_xml_free(xml);