summaryrefslogtreecommitdiffstats
path: root/src/libosmocc/screen.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libosmocc/screen.c')
-rw-r--r--src/libosmocc/screen.c684
1 files changed, 684 insertions, 0 deletions
diff --git a/src/libosmocc/screen.c b/src/libosmocc/screen.c
new file mode 100644
index 0000000..c8e17dc
--- /dev/null
+++ b/src/libosmocc/screen.c
@@ -0,0 +1,684 @@
+/* Endpoint and call process handling
+ *
+ * (C) 2019 by Andreas Eversberg <jolly@eversberg.eu>
+ * All Rights Reserved
+ *
+ * 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
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include "../libtimer/timer.h"
+#include "../libdebug/debug.h"
+#include "endpoint.h"
+#include "message.h"
+
+#define SCREEN_QUESTIONMARK 1
+#define SCREEN_STAR 2
+#define SCREEN_AT 3
+
+void osmo_cc_help_screen(void)
+{
+ printf("Screening options:\n\n");
+
+ printf("screen-calling-in [attrs] <current caller ID> [attrs] <new caller ID>\n");
+ printf("screen-called-in [attrs] <current dialed number> [attrs] <new dialed number>\n");
+ printf("screen-calling-out [attrs] <current caller ID> [attrs] <new caller ID>\n");
+ printf("screen-called-out [attrs] <current dialed number> [attrs] <new dialed number>\n\n");
+
+ printf("These options allow to screen an incoming or outgoing caller ID or dialed\n");
+ printf("number. If 'the current caller ID' or 'current dialed number' matches, it will\n");
+ printf("be replaced by 'new caller ID' or 'new dialed number'. 'incoming' means from\n");
+ printf(" the interface and 'outgoing' means towards the interface.\n\n");
+
+ printf("Attributes prior 'current caller ID' or 'new dialed number' may be used to\n");
+ printf("perform screening only if the attribute match. Attributes prior\n");
+ printf("'new caller ID' or 'new dialed number' may be used to alter them. Attribute to\n");
+ printf("define the type of number can be: 'unknown', 'international', 'national',\n");
+ printf("'network', 'subscriber', 'abbreviated' Attribute to define the restriction of a\n");
+ printf("caller ID: 'allowed', 'restricted'\n\n");
+
+ printf("The current caller ID or dialed number may contain one or more '?', to allow\n");
+ printf("any digit to match. The current caller ID or dialed number may contain a '*',\n");
+ printf("to allow any suffix to match from now on. The new caller ID or dialed number\n");
+ printf("may contain a '*', to append the suffix from the current caller ID or dialed\n");
+ printf("number.\n\n");
+}
+
+char *osmo_cc_strtok_quotes(const char **text_p)
+{
+ static char token[1024];
+ const char *text = *text_p;
+ int i, quote;
+
+ /* skip spaces */
+ while (*text) {
+ if (*text > 32)
+ break;
+ text++;
+ }
+
+ /* if eol, return NULL */
+ if (!(*text))
+ return NULL;
+
+ i = 0;
+ quote = 0;
+ while (*text) {
+ /* escape allows all following characters */
+ if (*text == '\\') {
+ text++;
+ if (*text)
+ token[i++] = *text++;
+ continue;
+ }
+ /* no quote, check for them or break on white space */
+ if (quote == 0) {
+ if (*text == '\'') {
+ quote = 1;
+ text++;
+ continue;
+ }
+ if (*text == '\"') {
+ quote = 2;
+ text++;
+ continue;
+ }
+ if (*text <= ' ')
+ break;
+ }
+ /* single quote, check for unquote */
+ if (quote == 1 && *text == '\'') {
+ quote = 0;
+ text++;
+ continue;
+ }
+ /* double quote, check for unquote */
+ if (quote == 2 && *text == '\"') {
+ quote = 0;
+ text++;
+ continue;
+ }
+ /* copy character */
+ token[i++] = *text++;
+ }
+ token[i] = '\0';
+
+ *text_p = text;
+ return token;
+}
+
+int osmo_cc_add_screen(osmo_cc_endpoint_t *ep, const char *text)
+{
+ osmo_cc_screen_list_t **list_p = NULL, *list;
+ const char *token;
+ int no_present = 0, calling_in = 0, star_used, at_used;
+ int i, j;
+
+ star_used = 0;
+ if (!strncasecmp(text, "screen-calling-in", 17)) {
+ text += 17;
+ list_p = &ep->screen_calling_in;
+ no_present = 1;
+ calling_in = 1;
+ } else if (!strncasecmp(text, "screen-called-in", 16)) {
+ text += 16;
+ list_p = &ep->screen_called_in;
+ } else if (!strncasecmp(text, "screen-calling-out", 18)) {
+ text += 18;
+ list_p = &ep->screen_calling_out;
+ no_present = 1;
+ } else if (!strncasecmp(text, "screen-called-out", 17)) {
+ text += 17;
+ list_p = &ep->screen_called_out;
+ } else {
+ PDEBUG(DCC, DEBUG_ERROR, "Invalid screening definition \"%s\". It must start with 'screen-calling-in' or 'screen-called-in' or 'screen-calling-out' or 'screen-called-out'\n", text);
+ return -EINVAL;
+ }
+
+ /* skip space behind screen list string */
+ while (*text) {
+ if (*text > 32)
+ break;
+ text++;
+ }
+
+ list = calloc(1, sizeof(*list));
+ if (!list)
+ return -ENOMEM;
+
+next_from:
+ token = osmo_cc_strtok_quotes(&text);
+ if (!token) {
+ free(list);
+ PDEBUG(DCC, DEBUG_ERROR, "Missing 'from' string in screening definition \"%s\". If the string shall be empty, use double quotes. (\'\' or \"\")\n", text);
+ return -EINVAL;
+ }
+ if (!strcasecmp(token, "unknown")) {
+ list->has_from_type = 1;
+ list->from_type = OSMO_CC_TYPE_UNKNOWN;
+ goto next_from;
+ } else
+ if (!strcasecmp(token, "international")) {
+ list->has_from_type = 1;
+ list->from_type = OSMO_CC_TYPE_INTERNATIONAL;
+ goto next_from;
+ } else
+ if (!strcasecmp(token, "national")) {
+ list->has_from_type = 1;
+ list->from_type = OSMO_CC_TYPE_NATIONAL;
+ goto next_from;
+ } else
+ if (!strcasecmp(token, "network")) {
+ list->has_from_type = 1;
+ list->from_type = OSMO_CC_TYPE_NETWORK;
+ goto next_from;
+ } else
+ if (!strcasecmp(token, "subscriber")) {
+ list->has_from_type = 1;
+ list->from_type = OSMO_CC_TYPE_SUBSCRIBER;
+ goto next_from;
+ } else
+ if (!strcasecmp(token, "abbreviated")) {
+ list->has_from_type = 1;
+ list->from_type = OSMO_CC_TYPE_ABBREVIATED;
+ goto next_from;
+ } else
+ if (!strcasecmp(token, "allowed")) {
+ if (no_present) {
+no_present_error:
+ free(list);
+ PDEBUG(DCC, DEBUG_ERROR, "Error in screening definition '%s'.\n", text);
+ PDEBUG(DCC, DEBUG_ERROR, "Keyword '%s' not allowed in screen entry for called number\n", token);
+ return -EINVAL;
+ }
+ list->has_from_present = 1;
+ list->from_present = OSMO_CC_PRESENT_ALLOWED;
+ goto next_from;
+ } else
+ if (!strcasecmp(token, "restricted")) {
+ if (no_present)
+ goto no_present_error;
+ list->has_from_present = 1;
+ list->from_present = OSMO_CC_PRESENT_RESTRICTED;
+ goto next_from;
+ } else {
+ for (i = j = 0; token[i] && j < (int)sizeof(list->from) - 1; i++, j++) {
+ if (token[i] == '?')
+ list->from[j] = SCREEN_QUESTIONMARK;
+ else
+ if (token[i] == '*') {
+ if (star_used) {
+ free(list);
+ PDEBUG(DCC, DEBUG_ERROR, "Error in screening definition '%s'.\n", text);
+ PDEBUG(DCC, DEBUG_ERROR, "The '*' may be used only once.\n");
+ return -EINVAL;
+ }
+ list->from[j] = SCREEN_STAR;
+ star_used = 1;
+ } else
+ if (token[i] == '\\' && token[i + 1] != '\0')
+ list->from[j] = token[++i];
+ else
+ list->from[j] = token[i];
+ }
+ list->from[j] = '\0';
+ }
+
+ star_used = 0;
+next_to:
+ token = osmo_cc_strtok_quotes(&text);
+ if (!token) {
+ free(list);
+ PDEBUG(DCC, DEBUG_ERROR, "Error in screening definition '%s'.\n", text);
+ PDEBUG(DCC, DEBUG_ERROR, "Missing screening result. If the string shall be empty, use double quotes. (\'\' or \"\")\n");
+ return -EINVAL;
+ }
+ if (!strcasecmp(token, "unknown")) {
+ list->has_to_type = 1;
+ list->to_type = OSMO_CC_TYPE_UNKNOWN;
+ goto next_to;
+ } else
+ if (!strcasecmp(token, "international")) {
+ list->has_to_type = 1;
+ list->to_type = OSMO_CC_TYPE_INTERNATIONAL;
+ goto next_to;
+ } else
+ if (!strcasecmp(token, "national")) {
+ list->has_to_type = 1;
+ list->to_type = OSMO_CC_TYPE_NATIONAL;
+ goto next_to;
+ } else
+ if (!strcasecmp(token, "network")) {
+ list->has_to_type = 1;
+ list->to_type = OSMO_CC_TYPE_NETWORK;
+ goto next_to;
+ } else
+ if (!strcasecmp(token, "subscriber")) {
+ list->has_to_type = 1;
+ list->to_type = OSMO_CC_TYPE_SUBSCRIBER;
+ goto next_to;
+ } else
+ if (!strcasecmp(token, "abbreviated")) {
+ list->has_to_type = 1;
+ list->to_type = OSMO_CC_TYPE_ABBREVIATED;
+ goto next_to;
+ } else
+ if (!strcasecmp(token, "allowed")) {
+ if (no_present)
+ goto no_present_error;
+ list->has_to_present = 1;
+ list->to_present = OSMO_CC_PRESENT_ALLOWED;
+ goto next_to;
+ } else
+ if (!strcasecmp(token, "restricted")) {
+ if (no_present)
+ goto no_present_error;
+ list->has_to_present = 1;
+ list->to_present = OSMO_CC_PRESENT_RESTRICTED;
+ goto next_to;
+ } else {
+ for (i = j = 0; token[i] && j < (int)sizeof(list->to) - 1; i++, j++) {
+ if (token[i] == '*') {
+ if (star_used) {
+ free(list);
+ PDEBUG(DCC, DEBUG_ERROR, "Error in screening definition '%s'.\n", text);
+ PDEBUG(DCC, DEBUG_ERROR, "The '*' may be used only once.\n");
+ return -EINVAL;
+ }
+ list->to[j] = SCREEN_STAR;
+ star_used = 1;
+ } else
+ if (token[i] == '@') {
+ if (!calling_in) {
+ free(list);
+ PDEBUG(DCC, DEBUG_ERROR, "Error in screening definition '%s'.\n", text);
+ PDEBUG(DCC, DEBUG_ERROR, "The '@' may be used only for incoming calls from interface.\n");
+ return -EINVAL;
+ }
+ if (at_used) {
+ free(list);
+ PDEBUG(DCC, DEBUG_ERROR, "Error in screening definition '%s'.\n", text);
+ PDEBUG(DCC, DEBUG_ERROR, "The '@' may be used only once.\n");
+ return -EINVAL;
+ }
+ list->to[j] = SCREEN_AT;
+ at_used = 1;
+ } else
+ if (token[i] == '\\' && token[i + 1] != '\0')
+ list->to[j] = token[++i];
+ else
+ list->to[j] = token[i];
+ }
+ list->to[j] = '\0';
+ }
+
+ token = osmo_cc_strtok_quotes(&text);
+ if (token) {
+ free(list);
+ PDEBUG(DCC, DEBUG_ERROR, "Error in screening definition '%s'.\n", text);
+ PDEBUG(DCC, DEBUG_ERROR, "Got garbage behind screening result.\n");
+ return -EINVAL;
+ }
+
+ /* attach screen entry to list */
+ while (*list_p)
+ list_p = &((*list_p)->next);
+ *list_p = list;
+
+ return 0;
+}
+
+void osmo_cc_flush_screen(osmo_cc_screen_list_t *list)
+{
+ osmo_cc_screen_list_t *temp;
+
+ while (list) {
+ temp = list;
+ list = list->next;
+ free(temp);
+ }
+}
+
+const char *print_rule_string(const char *input)
+{
+ static char output[256];
+ int i;
+
+ for (i = 0; *input && i < (int)sizeof(output) - 1; i++, input++) {
+ switch (*input) {
+ case SCREEN_QUESTIONMARK:
+ output[i] = '?';
+ break;
+ case SCREEN_STAR:
+ output[i] = '*';
+ break;
+ case SCREEN_AT:
+ output[i] = '@';
+ break;
+ default:
+ output[i] = *input;
+ }
+ }
+
+ output[i] = '\0';
+ return output;
+}
+
+static int osmo_cc_screen(const char *what, osmo_cc_screen_list_t *list, uint8_t *type, uint8_t *present, char *id_to, int id_to_size, const char *id_from, const char **routing_p)
+{
+ const char *suffix;
+ int i, j, rule;
+
+ PDEBUG(DCC, DEBUG_INFO, "Screening %s '%s':\n", what, id_from);
+ switch (*type) {
+ case OSMO_CC_TYPE_UNKNOWN:
+ PDEBUG(DCC, DEBUG_INFO, " -> type = unknown\n");
+ break;
+ case OSMO_CC_TYPE_INTERNATIONAL:
+ PDEBUG(DCC, DEBUG_INFO, " -> type = international\n");
+ break;
+ case OSMO_CC_TYPE_NATIONAL:
+ PDEBUG(DCC, DEBUG_INFO, " -> type = national\n");
+ break;
+ case OSMO_CC_TYPE_NETWORK:
+ PDEBUG(DCC, DEBUG_INFO, " -> type = network\n");
+ break;
+ case OSMO_CC_TYPE_SUBSCRIBER:
+ PDEBUG(DCC, DEBUG_INFO, " -> type = subscriber\n");
+ break;
+ case OSMO_CC_TYPE_ABBREVIATED:
+ PDEBUG(DCC, DEBUG_INFO, " -> type = abbreviated\n");
+ break;
+ }
+ if (present) switch (*present) {
+ case OSMO_CC_PRESENT_ALLOWED:
+ PDEBUG(DCC, DEBUG_INFO, " -> present = allowed\n");
+ break;
+ case OSMO_CC_PRESENT_RESTRICTED:
+ PDEBUG(DCC, DEBUG_INFO, " -> present = restricted\n");
+ break;
+ }
+
+ rule = 0;
+ while (list) {
+ rule++;
+ PDEBUG(DCC, DEBUG_INFO, "Comparing with rule #%d: '%s':\n", rule, print_rule_string(list->from));
+ if (list->has_from_type) switch (list->from_type) {
+ case OSMO_CC_TYPE_UNKNOWN:
+ PDEBUG(DCC, DEBUG_INFO, " -> type = unknown\n");
+ break;
+ case OSMO_CC_TYPE_INTERNATIONAL:
+ PDEBUG(DCC, DEBUG_INFO, " -> type = international\n");
+ break;
+ case OSMO_CC_TYPE_NATIONAL:
+ PDEBUG(DCC, DEBUG_INFO, " -> type = national\n");
+ break;
+ case OSMO_CC_TYPE_NETWORK:
+ PDEBUG(DCC, DEBUG_INFO, " -> type = network\n");
+ break;
+ case OSMO_CC_TYPE_SUBSCRIBER:
+ PDEBUG(DCC, DEBUG_INFO, " -> type = subscriber\n");
+ break;
+ case OSMO_CC_TYPE_ABBREVIATED:
+ PDEBUG(DCC, DEBUG_INFO, " -> type = abbreviated\n");
+ break;
+ }
+ if (list->has_from_present) switch (list->from_present) {
+ case OSMO_CC_PRESENT_ALLOWED:
+ PDEBUG(DCC, DEBUG_INFO, " -> present = allowed\n");
+ break;
+ case OSMO_CC_PRESENT_RESTRICTED:
+ PDEBUG(DCC, DEBUG_INFO, " -> present = restricted\n");
+ break;
+ }
+ suffix = NULL;
+ /* attributes do not match */
+ if (list->has_from_type && list->from_type != *type) {
+ PDEBUG(DCC, DEBUG_INFO, "Rule does not match, because 'type' is different.\n");
+ continue;
+ }
+ if (present && list->has_from_present && list->from_present != *present) {
+ PDEBUG(DCC, DEBUG_INFO, "Rule does not match, because 'present' is different.\n");
+ continue;
+ }
+ for (i = 0; list->from[i] && id_from[i]; i++) {
+ /* '?' means: any digit, so it machtes */
+ if (list->from[i] == SCREEN_QUESTIONMARK) {
+ continue;
+ }
+ /* '*' means: anything may follow, so it machtes */
+ if (list->from[i] == SCREEN_STAR) {
+ suffix = id_from + i;
+ break;
+ }
+ /* check if digit doesn't matches */
+ if (list->from[i] != id_from[i])
+ break;
+ }
+ /* if last checked digit is '*', we have a match */
+ /* also if we hit EOL at id_from and next check digit is '*' */
+ if (list->from[i] == SCREEN_STAR)
+ break;
+ /* if all digits have matched */
+ if (list->from[i] == '\0' && id_from[i] == '\0')
+ break;
+ PDEBUG(DCC, DEBUG_INFO, "Rule does not match, because %s is different.\n", what);
+ list = list->next;
+ }
+
+ /* if no list entry matches */
+ if (!list)
+ return -1;
+
+ /* replace ID */
+ if (list->has_to_type) {
+ *type = list->to_type;
+ }
+ if (present && list->has_to_present) {
+ *present = list->to_present;
+ }
+ for (i = j = 0; list->to[i]; i++) {
+ if (j == id_to_size - 1)
+ break;
+ /* '*' means to use suffix of input string */
+ if (list->to[i] == SCREEN_STAR && suffix) {
+ while (*suffix) {
+ id_to[j++] = *suffix++;
+ if (j == id_to_size - 1)
+ break;
+ }
+ continue;
+ /* '@' means to stop and return routing also */
+ } else if (list->to[i] == SCREEN_AT) {
+ *routing_p = &list->to[i];
+ break;
+ }
+ /* copy output digit */
+ id_to[j++] = list->to[i];
+ }
+ id_to[j] = '\0';
+
+ PDEBUG(DCC, DEBUG_INFO, "Rule matches, changing %s to '%s'.\n", what, print_rule_string(id_to));
+ if (list->has_to_type) switch (list->to_type) {
+ case OSMO_CC_TYPE_UNKNOWN:
+ PDEBUG(DCC, DEBUG_INFO, " -> type = unknown\n");
+ break;
+ case OSMO_CC_TYPE_INTERNATIONAL:
+ PDEBUG(DCC, DEBUG_INFO, " -> type = international\n");
+ break;
+ case OSMO_CC_TYPE_NATIONAL:
+ PDEBUG(DCC, DEBUG_INFO, " -> type = national\n");
+ break;
+ case OSMO_CC_TYPE_NETWORK:
+ PDEBUG(DCC, DEBUG_INFO, " -> type = network\n");
+ break;
+ case OSMO_CC_TYPE_SUBSCRIBER:
+ PDEBUG(DCC, DEBUG_INFO, " -> type = subscriber\n");
+ break;
+ case OSMO_CC_TYPE_ABBREVIATED:
+ PDEBUG(DCC, DEBUG_INFO, " -> type = abbreviated\n");
+ break;
+ }
+ if (list->has_to_present) switch (list->to_present) {
+ case OSMO_CC_PRESENT_ALLOWED:
+ PDEBUG(DCC, DEBUG_INFO, " -> present = allowed\n");
+ break;
+ case OSMO_CC_PRESENT_RESTRICTED:
+ PDEBUG(DCC, DEBUG_INFO, " -> present = restricted\n");
+ break;
+ }
+
+ return 0;
+}
+
+osmo_cc_msg_t *osmo_cc_screen_msg(osmo_cc_endpoint_t *ep, osmo_cc_msg_t *old_msg, int in, const char **routing_p)
+{
+ osmo_cc_msg_t *new_msg;
+ char id[256], calling[256], called[256], redir[256];
+ uint8_t calling_type, calling_plan, calling_present, calling_screen;
+ uint8_t called_type, called_plan;
+ uint8_t redir_type, redir_plan, redir_present, redir_screen, redir_reason;
+ int calling_status = 0, called_status = 0, redir_status = 0;
+ int rc;
+ void *ie, *to_ie;
+ uint8_t ie_type;
+ uint16_t ie_length;
+ void *ie_value;
+
+ if (in && ep->screen_calling_in) {
+ rc = osmo_cc_get_ie_calling(old_msg, 0, &calling_type, &calling_plan, &calling_present, &calling_screen, id, sizeof(id));
+ if (rc >= 0) {
+ rc = osmo_cc_screen("incoming caller ID", ep->screen_calling_in, &calling_type, &calling_present, calling, sizeof(calling), id, routing_p);
+ if (rc >= 0)
+ calling_status = 1;
+ } else {
+ calling_type = OSMO_CC_TYPE_UNKNOWN;
+ calling_plan = OSMO_CC_PLAN_TELEPHONY;
+ calling_present = OSMO_CC_PRESENT_ALLOWED;
+ calling_screen = OSMO_CC_SCREEN_NETWORK;
+ rc = osmo_cc_screen("incoming caller ID", ep->screen_calling_in, &calling_type, &calling_present, calling, sizeof(calling), "", routing_p);
+ if (rc >= 0)
+ calling_status = 1;
+ }
+ rc = osmo_cc_get_ie_redir(old_msg, 0, &redir_type, &redir_plan, &redir_present, &redir_screen, &redir_reason, id, sizeof(id));
+ if (rc >= 0) {
+ rc = osmo_cc_screen("incoming redirecting number", ep->screen_calling_in, &redir_type, &redir_present, redir, sizeof(redir), id, routing_p);
+ if (rc >= 0)
+ redir_status = 1;
+ }
+ }
+ if (in && ep->screen_called_in) {
+ rc = osmo_cc_get_ie_called(old_msg, 0, &called_type, &called_plan, id, sizeof(id));
+ if (rc >= 0) {
+ rc = osmo_cc_screen("incoming dialed number", ep->screen_called_in, &called_type, NULL, called, sizeof(called), id, NULL);
+ if (rc >= 0)
+ called_status = 1;
+ } else {
+ called_type = OSMO_CC_TYPE_UNKNOWN;
+ called_plan = OSMO_CC_PLAN_TELEPHONY;
+ rc = osmo_cc_screen("incoming dialed number", ep->screen_called_in, &called_type, NULL, called, sizeof(called), "", NULL);
+ if (rc >= 0)
+ called_status = 1;
+ }
+ }
+ if (!in && ep->screen_calling_out) {
+ rc = osmo_cc_get_ie_calling(old_msg, 0, &calling_type, &calling_plan, &calling_present, &calling_screen, id, sizeof(id));
+ if (rc >= 0) {
+ rc = osmo_cc_screen("outgoing caller ID", ep->screen_calling_out, &calling_type, &calling_present, calling, sizeof(calling), id, NULL);
+ if (rc >= 0)
+ calling_status = 1;
+ } else {
+ calling_type = OSMO_CC_TYPE_UNKNOWN;
+ calling_plan = OSMO_CC_PLAN_TELEPHONY;
+ calling_present = OSMO_CC_PRESENT_ALLOWED;
+ calling_screen = OSMO_CC_SCREEN_NETWORK;
+ rc = osmo_cc_screen("outgoing caller ID", ep->screen_calling_out, &calling_type, &calling_present, calling, sizeof(calling), "", NULL);
+ if (rc >= 0)
+ calling_status = 1;
+ }
+ rc = osmo_cc_get_ie_redir(old_msg, 0, &redir_type, &redir_plan, &redir_present, &redir_screen, &redir_reason, id, sizeof(id));
+ if (rc >= 0) {
+ rc = osmo_cc_screen("outgoing redirecting number", ep->screen_calling_out, &redir_type, &redir_present, redir, sizeof(redir), id, NULL);
+ if (rc >= 0)
+ redir_status = 1;
+ }
+ }
+ if (!in && ep->screen_called_out) {
+ rc = osmo_cc_get_ie_called(old_msg, 0, &called_type, &called_plan, id, sizeof(id));
+ if (rc >= 0) {
+ rc = osmo_cc_screen("outgoing dialed number", ep->screen_called_out, &called_type, NULL, called, sizeof(called), id, NULL);
+ if (rc >= 0)
+ called_status = 1;
+ } else {
+ called_type = OSMO_CC_TYPE_UNKNOWN;
+ called_plan = OSMO_CC_PLAN_TELEPHONY;
+ rc = osmo_cc_screen("outgoing dialed number", ep->screen_called_out, &called_type, NULL, called, sizeof(called), "", NULL);
+ if (rc >= 0)
+ called_status = 1;
+ }
+ }
+
+ /* nothing screened */
+ if (!calling_status && !called_status && !redir_status)
+ return old_msg;
+
+ new_msg = osmo_cc_new_msg(old_msg->type);
+
+ /* copy and replace */
+ ie = old_msg->data;
+ while ((ie_value = osmo_cc_msg_sep_ie(old_msg, &ie, &ie_type, &ie_length))) {
+ switch (ie_type) {
+ case OSMO_CC_IE_CALLING:
+ if (calling_status) {
+ osmo_cc_add_ie_calling(new_msg, calling_type, calling_plan, calling_present, calling_screen, calling);
+ calling_status = 0;
+ break;
+ }
+ goto copy;
+ case OSMO_CC_IE_CALLED:
+ if (called_status) {
+ osmo_cc_add_ie_called(new_msg, called_type, called_plan, called);
+ called_status = 0;
+ break;
+ }
+ goto copy;
+ case OSMO_CC_IE_REDIR:
+ if (redir_status) {
+ osmo_cc_add_ie_redir(new_msg, redir_type, redir_plan, redir_present, redir_screen, redir_reason, redir);
+ redir_status = 0;
+ break;
+ }
+ goto copy;
+ default:
+ copy:
+ to_ie = osmo_cc_add_ie(new_msg, ie_type, ie_length);
+ memcpy(to_ie, ie_value, ie_length);
+ }
+ }
+
+ /* applend, if not yet in message (except redir, since it must exist) */
+ if (calling_status)
+ osmo_cc_add_ie_calling(new_msg, calling_type, calling_plan, calling_present, calling_screen, calling);
+ if (called_status)
+ osmo_cc_add_ie_called(new_msg, called_type, called_plan, called);
+
+ free(old_msg);
+ return new_msg;
+}
+