summaryrefslogtreecommitdiff
path: root/main/channel.c
diff options
context:
space:
mode:
authorMark Michelson <mmichelson@digium.com>2009-04-03 22:41:46 +0000
committerMark Michelson <mmichelson@digium.com>2009-04-03 22:41:46 +0000
commit6f53ed4c6707b30078ed4863e27facb7b454b600 (patch)
tree2e466f746a2e29094d6dcc3c6f2577f4dd85f4c0 /main/channel.c
parent3525e37e633b8b7bcf59262fbab21c16afadfa35 (diff)
This commit introduces COLP/CONP and Redirecting party information into Asterisk.
The channel drivers which have been most heavily tested with these enhancements are chan_sip and chan_misdn. Further work is being done to add Q.SIG support and will be introduced in a later commit. chan_skinny has code added to it here, but according to user pj, the support on chan_skinny is not working as of now. This will be fixed in a later commit. A special thanks goes out to bugtracker user gareth for getting the ball rolling and providing the initial support for this work. Without his initial work on this, this would not have been nearly as painless as it was. This functionality has been tested by Digium's product quality department, as well as a customer site running thousands of calls every day. In addition, many many many many bugtracker users have tested this, too. (closes issue #8824) Reported by: gareth Review: http://reviewboard.digium.com/r/201 git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@186525 65c4cc65-6c06-0410-ace0-fbb531ad65f3
Diffstat (limited to 'main/channel.c')
-rw-r--r--main/channel.c936
1 files changed, 925 insertions, 11 deletions
diff --git a/main/channel.c b/main/channel.c
index 9b2ad405c..718677b39 100644
--- a/main/channel.c
+++ b/main/channel.c
@@ -796,6 +796,13 @@ struct ast_channel *ast_channel_alloc(int needqueue, int state, const char *cid_
return NULL;
}
+ if (!(tmp->cid.cid_name = ast_strdup(cid_name)) || !(tmp->cid.cid_num = ast_strdup(cid_num))) {
+ ast_string_field_free_memory(tmp);
+ sched_context_destroy(tmp->sched);
+ ast_free(tmp);
+ return NULL;
+ }
+
#ifdef HAVE_EPOLL
tmp->epfd = epoll_create(25);
#endif
@@ -866,9 +873,6 @@ alertpipe_failed:
ast_string_field_build(tmp, uniqueid, "%s-%li.%d", ast_config_AST_SYSTEM_NAME,
(long) time(NULL), ast_atomic_fetchadd_int(&uniqueint, 1));
}
-
- tmp->cid.cid_name = ast_strdup(cid_name);
- tmp->cid.cid_num = ast_strdup(cid_num);
if (!ast_strlen_zero(name_fmt)) {
/* Almost every channel is calling this function, and setting the name via the ast_string_field_build() call.
@@ -1311,6 +1315,279 @@ static void free_cid(struct ast_callerid *cid)
cid->cid_dnid = cid->cid_num = cid->cid_name = cid->cid_ani = cid->cid_rdnis = NULL;
}
+/*!
+ * \internal
+ * \brief Initialize the given party id structure.
+ *
+ * \param init Party id structure to initialize.
+ *
+ * \return Nothing
+ */
+static void ast_party_id_init(struct ast_party_id *init)
+{
+ init->number = NULL;
+ init->name = NULL;
+ init->number_type = 0; /* Unknown */
+ init->number_presentation = AST_PRES_ALLOWED_USER_NUMBER_NOT_SCREENED;
+}
+
+/*!
+ * \internal
+ * \brief Copy the source party id information to the destination party id.
+ *
+ * \param dest Destination party id
+ * \param src Source party id
+ *
+ * \return Nothing
+ */
+static void ast_party_id_copy(struct ast_party_id *dest, const struct ast_party_id *src)
+{
+ if (dest == src) {
+ /* Don't copy to self */
+ return;
+ }
+
+ if (dest->number) {
+ ast_free(dest->number);
+ }
+ dest->number = ast_strdup(src->number);
+
+ if (dest->name) {
+ ast_free(dest->name);
+ }
+ dest->name = ast_strdup(src->name);
+
+ dest->number_type = src->number_type;
+ dest->number_presentation = src->number_presentation;
+}
+
+/*!
+ * \internal
+ * \brief Initialize the given party id structure using the given guide
+ * for a set update operation.
+ *
+ * \details
+ * The initialization is needed to allow a set operation to know if a
+ * value needs to be updated. Simple integers need the guide's original
+ * value in case the set operation is not trying to set a new value.
+ * String values are simply set to NULL pointers if they are not going
+ * to be updated.
+ *
+ * \param init Party id structure to initialize.
+ * \param guide Source party id to use as a guide in initializing.
+ *
+ * \return Nothing
+ */
+static void ast_party_id_set_init(struct ast_party_id *init, const struct ast_party_id *guide)
+{
+ init->number = NULL;
+ init->name = NULL;
+ init->number_type = guide->number_type;
+ init->number_presentation = guide->number_presentation;
+}
+
+/*!
+ * \internal
+ * \brief Set the source party id information into the destination party id.
+ *
+ * \param dest Destination party id
+ * \param src Source party id
+ *
+ * \return Nothing
+ */
+static void ast_party_id_set(struct ast_party_id *dest, const struct ast_party_id *src)
+{
+ if (dest == src) {
+ /* Don't set to self */
+ return;
+ }
+
+ if (src->name && src->name != dest->name) {
+ if (dest->name) {
+ ast_free(dest->name);
+ }
+ dest->name = ast_strdup(src->name);
+ }
+
+ if (src->number && src->number != dest->number) {
+ if (dest->number) {
+ ast_free(dest->number);
+ }
+ dest->number = ast_strdup(src->number);
+ }
+
+ dest->number_type = src->number_type;
+ dest->number_presentation = src->number_presentation;
+}
+
+/*!
+ * \internal
+ * \brief Destroy the party id contents
+ *
+ * \param doomed The party id to destroy.
+ *
+ * \return Nothing
+ */
+static void ast_party_id_free(struct ast_party_id *doomed)
+{
+ if (doomed->number) {
+ ast_free(doomed->number);
+ doomed->number = NULL;
+ }
+
+ if (doomed->name) {
+ ast_free(doomed->name);
+ doomed->name = NULL;
+ }
+}
+
+void ast_party_caller_copy(struct ast_callerid *dest, const struct ast_callerid *src)
+{
+ if (dest == src) {
+ /* Don't copy to self */
+ return;
+ }
+
+#if 1
+ /* Copy caller-id specific information ONLY from struct ast_callerid */
+ if (dest->cid_num)
+ {
+ ast_free(dest->cid_num);
+ }
+ dest->cid_num = ast_strdup(src->cid_num);
+
+ if (dest->cid_name)
+ {
+ ast_free(dest->cid_name);
+ }
+ dest->cid_name = ast_strdup(src->cid_name);
+
+ dest->cid_ton = src->cid_ton;
+ dest->cid_pres = src->cid_pres;
+
+
+ if (dest->cid_ani)
+ {
+ ast_free(dest->cid_ani);
+ }
+ dest->cid_ani = ast_strdup(src->cid_ani);
+
+ dest->cid_ani2 = src->cid_ani2;
+
+#else
+
+ /* The src and dest parameter types will become struct ast_party_caller ptrs. */
+ /* This is future code */
+
+ ast_party_id_copy(&dest->id, &src->id);
+
+ if (dest->ani) {
+ ast_free(dest->ani);
+ }
+ dest->ani = ast_strdup(src->ani);
+
+ dest->ani2 = src->ani2;
+#endif
+}
+
+void ast_party_connected_line_init(struct ast_party_connected_line *init)
+{
+ ast_party_id_init(&init->id);
+ init->ani = NULL;
+ init->ani2 = 0;
+ init->source = AST_CONNECTED_LINE_UPDATE_SOURCE_UNKNOWN;
+}
+
+void ast_party_connected_line_copy(struct ast_party_connected_line *dest, const struct ast_party_connected_line *src)
+{
+ if (dest == src) {
+ /* Don't copy to self */
+ return;
+ }
+
+ ast_party_id_copy(&dest->id, &src->id);
+
+ if (dest->ani) {
+ ast_free(dest->ani);
+ }
+ dest->ani = ast_strdup(src->ani);
+
+ dest->ani2 = src->ani2;
+ dest->source = src->source;
+}
+
+void ast_party_connected_line_set_init(struct ast_party_connected_line *init, const struct ast_party_connected_line *guide)
+{
+ ast_party_id_set_init(&init->id, &guide->id);
+ init->ani = NULL;
+ init->ani2 = guide->ani2;
+ init->source = guide->source;
+}
+
+void ast_party_connected_line_set(struct ast_party_connected_line *dest, const struct ast_party_connected_line *src)
+{
+ ast_party_id_set(&dest->id, &src->id);
+
+ if (src->ani && src->ani != dest->ani) {
+ if (dest->ani) {
+ ast_free(dest->ani);
+ }
+ dest->ani = ast_strdup(src->ani);
+ }
+
+ dest->ani2 = src->ani2;
+ dest->source = src->source;
+}
+
+void ast_party_connected_line_collect_caller(struct ast_party_connected_line *connected, struct ast_callerid *cid)
+{
+ connected->id.number = cid->cid_num;
+ connected->id.name = cid->cid_name;
+ connected->id.number_type = cid->cid_ton;
+ connected->id.number_presentation = cid->cid_pres;
+
+ connected->ani = cid->cid_ani;
+ connected->ani2 = cid->cid_ani2;
+ connected->source = AST_CONNECTED_LINE_UPDATE_SOURCE_UNKNOWN;
+}
+
+void ast_party_connected_line_free(struct ast_party_connected_line *doomed)
+{
+ ast_party_id_free(&doomed->id);
+
+ if (doomed->ani) {
+ ast_free(doomed->ani);
+ doomed->ani = NULL;
+ }
+}
+
+void ast_party_redirecting_copy(struct ast_party_redirecting *dest, const struct ast_party_redirecting *src)
+{
+ if (dest == src) {
+ /* Don't copy to self */
+ return;
+ }
+
+ ast_party_id_copy(&dest->from, &src->from);
+ ast_party_id_copy(&dest->to, &src->to);
+ dest->count = src->count;
+ dest->reason = src->reason;
+}
+
+void ast_party_redirecting_set_init(struct ast_party_redirecting *init, const struct ast_party_redirecting *guide)
+{
+ ast_party_id_set_init(&init->from, &guide->from);
+ ast_party_id_set_init(&init->to, &guide->to);
+ init->count = guide->count;
+ init->reason = guide->reason;
+}
+
+void ast_party_redirecting_free(struct ast_party_redirecting *doomed)
+{
+ ast_party_id_free(&doomed->from);
+ ast_party_id_free(&doomed->to);
+}
+
/*! \brief Free a channel structure */
void ast_channel_free(struct ast_channel *chan)
{
@@ -1374,7 +1651,11 @@ void ast_channel_free(struct ast_channel *chan)
ast_translator_free_path(chan->writetrans);
if (chan->pbx)
ast_log(LOG_WARNING, "PBX may not have been terminated properly on '%s'\n", chan->name);
+
free_cid(&chan->cid);
+ ast_party_connected_line_free(&chan->connected);
+ ast_party_redirecting_free(&chan->redirecting);
+
/* Close pipes if appropriate */
if ((fd = chan->alertpipe[0]) > -1)
close(fd);
@@ -2396,6 +2677,8 @@ int ast_waitfordigit_full(struct ast_channel *c, int ms, int audiofd, int cmdfd)
case AST_CONTROL_RINGING:
case AST_CONTROL_ANSWER:
case AST_CONTROL_SRCUPDATE:
+ case AST_CONTROL_CONNECTED_LINE:
+ case AST_CONTROL_REDIRECTING:
/* Unimportant */
break;
default:
@@ -2994,8 +3277,10 @@ static int attribute_const is_visible_indication(enum ast_control_frame_type con
case AST_CONTROL_ANSWER:
case AST_CONTROL_HANGUP:
case AST_CONTROL_T38:
+ case AST_CONTROL_CONNECTED_LINE:
+ case AST_CONTROL_REDIRECTING:
case AST_CONTROL_TRANSFER:
- return 0;
+ break;
case AST_CONTROL_CONGESTION:
case AST_CONTROL_BUSY:
@@ -3016,7 +3301,7 @@ int ast_indicate_data(struct ast_channel *chan, int _condition,
* in switch statements. */
enum ast_control_frame_type condition = _condition;
struct ast_tone_zone_sound *ts = NULL;
- int res = -1;
+ int res;
ast_channel_lock(chan);
@@ -3025,10 +3310,41 @@ int ast_indicate_data(struct ast_channel *chan, int _condition,
ast_channel_unlock(chan);
return -1;
}
+ switch (condition) {
+ case AST_CONTROL_CONNECTED_LINE:
+ {
+ struct ast_party_connected_line connected;
+
+ ast_party_connected_line_set_init(&connected, &chan->connected);
+ res = ast_connected_line_parse_data(data, datalen, &connected);
+ if (!res) {
+ ast_channel_set_connected_line(chan, &connected);
+ }
+ ast_party_connected_line_free(&connected);
+ }
+ break;
+
+ case AST_CONTROL_REDIRECTING:
+ {
+ struct ast_party_redirecting redirecting;
+ ast_party_redirecting_set_init(&redirecting, &chan->redirecting);
+ res = ast_redirecting_parse_data(data, datalen, &redirecting);
+ if (!res) {
+ ast_channel_set_redirecting(chan, &redirecting);
+ }
+ ast_party_redirecting_free(&redirecting);
+ }
+ break;
+
+ default:
+ break;
+ }
if (chan->tech->indicate) {
/* See if the channel driver can handle this condition. */
res = chan->tech->indicate(chan, condition, data, datalen);
+ } else {
+ res = -1;
}
ast_channel_unlock(chan);
@@ -3082,6 +3398,8 @@ int ast_indicate_data(struct ast_channel *chan, int _condition,
case AST_CONTROL_UNHOLD:
case AST_CONTROL_T38:
case AST_CONTROL_TRANSFER:
+ case AST_CONTROL_CONNECTED_LINE:
+ case AST_CONTROL_REDIRECTING:
/* Nothing left to do for these. */
res = 0;
break;
@@ -3544,6 +3862,7 @@ struct ast_channel *__ast_request_and_dial(const char *type, int format, void *d
struct ast_channel *chan;
int res = 0;
int last_subclass = 0;
+ struct ast_party_connected_line connected;
if (outstate)
*outstate = 0;
@@ -3574,7 +3893,13 @@ struct ast_channel *__ast_request_and_dial(const char *type, int format, void *d
if (oh->account)
ast_cdr_setaccount(chan, oh->account);
}
+
ast_set_callerid(chan, cid_num, cid_name, cid_num);
+ ast_party_connected_line_set_init(&connected, &chan->connected);
+ connected.id.number = (char *) cid_num;
+ connected.id.name = (char *) cid_name;
+ connected.id.number_presentation = AST_PRES_ALLOWED_USER_NUMBER_NOT_SCREENED;
+ ast_channel_set_connected_line(chan, &connected);
if (ast_call(chan, data, 0)) { /* ast_call failed... */
ast_log(LOG_NOTICE, "Unable to call channel %s/%s\n", type, (char *)data);
@@ -3613,6 +3938,8 @@ struct ast_channel *__ast_request_and_dial(const char *type, int format, void *d
case AST_CONTROL_UNHOLD:
case AST_CONTROL_VIDUPDATE:
case AST_CONTROL_SRCUPDATE:
+ case AST_CONTROL_CONNECTED_LINE:
+ case AST_CONTROL_REDIRECTING:
case -1: /* Ignore -- just stopping indications */
break;
@@ -4088,7 +4415,11 @@ int ast_do_masquerade(struct ast_channel *original)
struct ast_frame *current;
const struct ast_channel_tech *t;
void *t_pvt;
- struct ast_callerid tmpcid;
+ union {
+ struct ast_callerid cid;
+ struct ast_party_connected_line connected;
+ struct ast_party_redirecting redirecting;
+ } exchange;
struct ast_channel *clonechan = original->masq;
struct ast_cdr *cdr;
int rformat = original->readformat;
@@ -4269,11 +4600,19 @@ int ast_do_masquerade(struct ast_channel *original)
/* Stream stuff stays the same */
/* Keep the original state. The fixup code will need to work with it most likely */
- /* Just swap the whole structures, nevermind the allocations, they'll work themselves
- out. */
- tmpcid = original->cid;
+ /*
+ * Just swap the whole structures, nevermind the allocations,
+ * they'll work themselves out.
+ */
+ exchange.cid = original->cid;
original->cid = clonechan->cid;
- clonechan->cid = tmpcid;
+ clonechan->cid = exchange.cid;
+ exchange.connected = original->connected;
+ original->connected = clonechan->connected;
+ clonechan->connected = exchange.connected;
+ exchange.redirecting = original->redirecting;
+ original->redirecting = clonechan->redirecting;
+ clonechan->redirecting = exchange.redirecting;
/* Restore original timing file descriptor */
ast_channel_set_fd(original, AST_TIMING_FD, original->timingfd);
@@ -4566,8 +4905,10 @@ static enum ast_bridge_result ast_generic_bridge(struct ast_channel *c0, struct
case AST_CONTROL_HOLD:
case AST_CONTROL_UNHOLD:
case AST_CONTROL_VIDUPDATE:
- case AST_CONTROL_SRCUPDATE:
case AST_CONTROL_T38:
+ case AST_CONTROL_SRCUPDATE:
+ case AST_CONTROL_CONNECTED_LINE:
+ case AST_CONTROL_REDIRECTING:
ast_indicate_data(other, f->subclass, f->data.ptr, f->datalen);
if (jb_in_use) {
ast_jb_empty_and_reset(c0, c1);
@@ -5530,3 +5871,576 @@ int ast_say_digits_full(struct ast_channel *chan, int num,
return ast_say_digit_str_full(chan, buf, ints, lang, audiofd, ctrlfd);
}
+
+void ast_connected_line_copy_from_caller(struct ast_party_connected_line *dest, const struct ast_callerid *src)
+{
+#if 1
+ /* Must manually fill in struct ast_party_id until struct ast_callerid goes away */
+ if (dest->id.number) {
+ ast_free(dest->id.number);
+ }
+ dest->id.number = ast_strdup(src->cid_num);
+
+ if (dest->id.name) {
+ ast_free(dest->id.name);
+ }
+ dest->id.name = ast_strdup(src->cid_name);
+
+ dest->id.number_type = src->cid_ton;
+ dest->id.number_presentation = src->cid_pres;
+
+
+ if (dest->ani) {
+ ast_free(dest->ani);
+ }
+ dest->ani = ast_strdup(src->cid_ani);
+
+ dest->ani2 = src->cid_ani2;
+
+#else
+
+ /* The src parameter type will become a struct ast_party_caller ptr. */
+ /* This is future code */
+
+ ast_party_id_copy(&dest->id, &src->id);
+
+ if (dest->ani) {
+ ast_free(dest->ani);
+ }
+ dest->ani = ast_strdup(src->ani);
+
+ dest->ani2 = src->ani2;
+#endif
+}
+
+void ast_connected_line_copy_to_caller(struct ast_callerid *dest, const struct ast_party_connected_line *src)
+{
+#if 1
+ /* Must manually extract from struct ast_party_id until struct ast_callerid goes away */
+ if (dest->cid_num) {
+ ast_free(dest->cid_num);
+ }
+ dest->cid_num = ast_strdup(src->id.number);
+
+ if (dest->cid_name) {
+ ast_free(dest->cid_name);
+ }
+ dest->cid_name = ast_strdup(src->id.name);
+
+ dest->cid_ton = src->id.number_type;
+ dest->cid_pres = src->id.number_presentation;
+
+
+ if (dest->cid_ani) {
+ ast_free(dest->cid_ani);
+ }
+ dest->cid_ani = ast_strdup(src->ani);
+
+ dest->cid_ani2 = src->ani2;
+
+#else
+
+ /* The dest parameter type will become a struct ast_party_caller ptr. */
+ /* This is future code */
+
+ ast_party_id_copy(&dest->id, &src->id);
+
+ if (dest->ani) {
+ ast_free(dest->ani);
+ }
+ dest->ani = ast_strdup(src->ani);
+
+ dest->ani2 = src->ani2;
+#endif
+}
+
+void ast_channel_set_connected_line(struct ast_channel *chan, const struct ast_party_connected_line *connected)
+{
+ if (&chan->connected == connected) {
+ /* Don't set to self */
+ return;
+ }
+
+ ast_channel_lock(chan);
+ ast_party_connected_line_set(&chan->connected, connected);
+ ast_channel_unlock(chan);
+}
+
+/*!
+ * \brief Element identifiers for connected line indication frame data
+ * \note Only add to the end of this enum.
+ */
+enum {
+ AST_CONNECTED_LINE_NUMBER,
+ AST_CONNECTED_LINE_NAME,
+ AST_CONNECTED_LINE_NUMBER_TYPE,
+ AST_CONNECTED_LINE_NUMBER_PRESENTATION,
+ AST_CONNECTED_LINE_SOURCE
+};
+
+int ast_connected_line_build_data(unsigned char *data, size_t datalen, const struct ast_party_connected_line *connected)
+{
+ int32_t value;
+ size_t length;
+ size_t pos = 0;
+
+ /*
+ * The size of integer values must be fixed in case the frame is
+ * shipped to another machine.
+ */
+
+ /* *************** Connected line party id *************** */
+ if (connected->id.number) {
+ length = strlen(connected->id.number);
+ if (datalen < pos + (sizeof(data[0]) * 2) + length) {
+ ast_log(LOG_WARNING, "No space left for connected line number\n");
+ return -1;
+ }
+ data[pos++] = AST_CONNECTED_LINE_NUMBER;
+ data[pos++] = length;
+ memcpy(data + pos, connected->id.number, length);
+ pos += length;
+ }
+
+ if (connected->id.name) {
+ length = strlen(connected->id.name);
+ if (datalen < pos + (sizeof(data[0]) * 2) + length) {
+ ast_log(LOG_WARNING, "No space left for connected line name\n");
+ return -1;
+ }
+ data[pos++] = AST_CONNECTED_LINE_NAME;
+ data[pos++] = length;
+ memcpy(data + pos, connected->id.name, length);
+ pos += length;
+ }
+
+ if (datalen < pos + (sizeof(data[0]) * 2) + 1) {
+ ast_log(LOG_WARNING, "No space left for connected line type of number\n");
+ return -1;
+ }
+ data[pos++] = AST_CONNECTED_LINE_NUMBER_TYPE;
+ data[pos++] = 1;
+ data[pos++] = connected->id.number_type;
+
+ if (datalen < pos + (sizeof(data[0]) * 2) + 1) {
+ ast_log(LOG_WARNING, "No space left for connected line presentation\n");
+ return -1;
+ }
+ data[pos++] = AST_CONNECTED_LINE_NUMBER_PRESENTATION;
+ data[pos++] = 1;
+ data[pos++] = connected->id.number_presentation;
+
+ /* Connected line source */
+ if (datalen < pos + (sizeof(data[0]) * 2) + sizeof(value)) {
+ ast_log(LOG_WARNING, "No space left for connected line source\n");
+ return -1;
+ }
+ data[pos++] = AST_CONNECTED_LINE_SOURCE;
+ data[pos++] = sizeof(value);
+ value = htonl(connected->source);
+ memcpy(data + pos, &value, sizeof(value));
+ pos += sizeof(value);
+
+ return pos;
+}
+
+int ast_connected_line_parse_data(const unsigned char *data, size_t datalen, struct ast_party_connected_line *connected)
+{
+ size_t pos;
+ unsigned char ie_len;
+ unsigned char ie_id;
+ int32_t value;
+
+ for (pos = 0; pos < datalen; pos += ie_len) {
+ if (datalen < pos + sizeof(ie_id) + sizeof(ie_len)) {
+ ast_log(LOG_WARNING, "Invalid connected line update\n");
+ return -1;
+ }
+ ie_id = data[pos++];
+ ie_len = data[pos++];
+ if (datalen < pos + ie_len) {
+ ast_log(LOG_WARNING, "Invalid connected line update\n");
+ return -1;
+ }
+
+ switch (ie_id) {
+ case AST_CONNECTED_LINE_NUMBER:
+ if (connected->id.number) {
+ ast_free(connected->id.number);
+ }
+ connected->id.number = ast_malloc(ie_len + 1);
+ if (connected->id.number) {
+ memcpy(connected->id.number, data + pos, ie_len);
+ connected->id.number[ie_len] = 0;
+ }
+ break;
+ case AST_CONNECTED_LINE_NAME:
+ if (connected->id.name) {
+ ast_free(connected->id.name);
+ }
+ connected->id.name = ast_malloc(ie_len + 1);
+ if (connected->id.name) {
+ memcpy(connected->id.name, data + pos, ie_len);
+ connected->id.name[ie_len] = 0;
+ }
+ break;
+ case AST_CONNECTED_LINE_NUMBER_TYPE:
+ if (ie_len != 1) {
+ ast_log(LOG_WARNING, "Invalid connected line type of number (%u)\n", (unsigned) ie_len);
+ break;
+ }
+ connected->id.number_type = data[pos];
+ break;
+ case AST_CONNECTED_LINE_NUMBER_PRESENTATION:
+ if (ie_len != 1) {
+ ast_log(LOG_WARNING, "Invalid connected line presentation (%u)\n", (unsigned) ie_len);
+ break;
+ }
+ connected->id.number_presentation = data[pos];
+ break;
+ case AST_CONNECTED_LINE_SOURCE:
+ if (ie_len != sizeof(value)) {
+ ast_log(LOG_WARNING, "Invalid connected line source (%u)\n", (unsigned) ie_len);
+ break;
+ }
+ memcpy(&value, data + pos, sizeof(value));
+ connected->source = ntohl(value);
+ break;
+ default:
+ ast_log(LOG_DEBUG, "Unknown connected line element: %u (%u)\n", (unsigned) ie_id, (unsigned) ie_len);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+void ast_channel_update_connected_line(struct ast_channel *chan, const struct ast_party_connected_line *connected)
+{
+ unsigned char data[1024]; /* This should be large enough */
+ size_t datalen;
+
+ datalen = ast_connected_line_build_data(data, sizeof(data), connected);
+ if (datalen == (size_t) -1) {
+ return;
+ }
+
+ ast_indicate_data(chan, AST_CONTROL_CONNECTED_LINE, data, datalen);
+}
+
+void ast_channel_queue_connected_line_update(struct ast_channel *chan, const struct ast_party_connected_line *connected)
+{
+ unsigned char data[1024]; /* This should be large enough */
+ size_t datalen;
+
+ datalen = ast_connected_line_build_data(data, sizeof(data), connected);
+ if (datalen == (size_t) -1) {
+ return;
+ }
+
+ ast_queue_control_data(chan, AST_CONTROL_CONNECTED_LINE, data, datalen);
+}
+
+void ast_channel_set_redirecting(struct ast_channel *chan, const struct ast_party_redirecting *redirecting)
+{
+ if (&chan->redirecting == redirecting) {
+ /* Don't set to self */
+ return;
+ }
+
+ ast_channel_lock(chan);
+
+ ast_party_id_set(&chan->redirecting.from, &redirecting->from);
+ if (redirecting->from.number
+ && redirecting->from.number != chan->redirecting.from.number) {
+ /*
+ * Must move string to ast_channel.cid.cid_rdnis until it goes away.
+ */
+ if (chan->cid.cid_rdnis) {
+ ast_free(chan->cid.cid_rdnis);
+ }
+ chan->cid.cid_rdnis = chan->redirecting.from.number;
+ chan->redirecting.from.number = NULL;
+ }
+
+ ast_party_id_set(&chan->redirecting.to, &redirecting->to);
+ chan->redirecting.reason = redirecting->reason;
+ chan->redirecting.count = redirecting->count;
+
+ ast_channel_unlock(chan);
+}
+
+/*!
+ * \brief Element identifiers for redirecting indication frame data
+ * \note Only add to the end of this enum.
+ */
+enum {
+ AST_REDIRECTING_FROM_NUMBER,
+ AST_REDIRECTING_FROM_NAME,
+ AST_REDIRECTING_FROM_NUMBER_TYPE,
+ AST_REDIRECTING_FROM_NUMBER_PRESENTATION,
+ AST_REDIRECTING_TO_NUMBER,
+ AST_REDIRECTING_TO_NAME,
+ AST_REDIRECTING_TO_NUMBER_TYPE,
+ AST_REDIRECTING_TO_NUMBER_PRESENTATION,
+ AST_REDIRECTING_REASON,
+ AST_REDIRECTING_COUNT
+};
+
+int ast_redirecting_build_data(unsigned char *data, size_t datalen, const struct ast_party_redirecting *redirecting)
+{
+ int32_t value;
+ size_t length;
+ size_t pos = 0;
+
+ /*
+ * The size of integer values must be fixed in case the frame is
+ * shipped to another machine.
+ */
+
+ /* *************** Redirecting from party id *************** */
+ if (redirecting->from.number) {
+ length = strlen(redirecting->from.number);
+ if (datalen < pos + (sizeof(data[0]) * 2) + length) {
+ ast_log(LOG_WARNING, "No space left for redirecting from number\n");
+ return -1;
+ }
+ data[pos++] = AST_REDIRECTING_FROM_NUMBER;
+ data[pos++] = length;
+ memcpy(data + pos, redirecting->from.number, length);
+ pos += length;
+ }
+
+ if (redirecting->from.name) {
+ length = strlen(redirecting->from.name);
+ if (datalen < pos + (sizeof(data[0]) * 2) + length) {
+ ast_log(LOG_WARNING, "No space left for redirecting from name\n");
+ return -1;
+ }
+ data[pos++] = AST_REDIRECTING_FROM_NAME;
+ data[pos++] = length;
+ memcpy(data + pos, redirecting->from.name, length);
+ pos += length;
+ }
+
+ if (datalen < pos + (sizeof(data[0]) * 2) + 1) {
+ ast_log(LOG_WARNING, "No space left for redirecting from type of number\n");
+ return -1;
+ }
+ data[pos++] = AST_REDIRECTING_FROM_NUMBER_TYPE;
+ data[pos++] = 1;
+ data[pos++] = redirecting->from.number_type;
+
+ if (datalen < pos + (sizeof(data[0]) * 2) + 1) {
+ ast_log(LOG_WARNING, "No space left for redirecting from presentation\n");
+ return -1;
+ }
+ data[pos++] = AST_REDIRECTING_FROM_NUMBER_PRESENTATION;
+ data[pos++] = 1;
+ data[pos++] = redirecting->from.number_presentation;
+
+ /* *************** Redirecting to party id *************** */
+ if (redirecting->to.number) {
+ length = strlen(redirecting->to.number);
+ if (datalen < pos + (sizeof(data[0]) * 2) + length) {
+ ast_log(LOG_WARNING, "No space left for redirecting to number\n");
+ return -1;
+ }
+ data[pos++] = AST_REDIRECTING_TO_NUMBER;
+ data[pos++] = length;
+ memcpy(data + pos, redirecting->to.number, length);
+ pos += length;
+ }
+
+ if (redirecting->to.name) {
+ length = strlen(redirecting->to.name);
+ if (datalen < pos + (sizeof(data[0]) * 2) + length) {
+ ast_log(LOG_WARNING, "No space left for redirecting to name\n");
+ return -1;
+ }
+ data[pos++] = AST_REDIRECTING_TO_NAME;
+ data[pos++] = length;
+ memcpy(data + pos, redirecting->to.name, length);
+ pos += length;
+ }
+
+ if (datalen < pos + (sizeof(data[0]) * 2) + 1) {
+ ast_log(LOG_WARNING, "No space left for redirecting to type of number\n");
+ return -1;
+ }
+ data[pos++] = AST_REDIRECTING_TO_NUMBER_TYPE;
+ data[pos++] = 1;
+ data[pos++] = redirecting->to.number_type;
+
+ if (datalen < pos + (sizeof(data[0]) * 2) + 1) {
+ ast_log(LOG_WARNING, "No space left for redirecting to presentation\n");
+ return -1;
+ }
+ data[pos++] = AST_REDIRECTING_TO_NUMBER_PRESENTATION;
+ data[pos++] = 1;
+ data[pos++] = redirecting->to.number_presentation;
+
+ /* Redirecting reason */
+ if (datalen < pos + (sizeof(data[0]) * 2) + sizeof(value)) {
+ ast_log(LOG_WARNING, "No space left for redirecting reason\n");
+ return -1;
+ }
+ data[pos++] = AST_REDIRECTING_REASON;
+ data[pos++] = sizeof(value);
+ value = htonl(redirecting->reason);
+ memcpy(data + pos, &value, sizeof(value));
+ pos += sizeof(value);
+
+ /* Redirecting count */
+ if (datalen < pos + (sizeof(data[0]) * 2) + sizeof(value)) {
+ ast_log(LOG_WARNING, "No space left for redirecting count\n");
+ return -1;
+ }
+ data[pos++] = AST_REDIRECTING_COUNT;
+ data[pos++] = sizeof(value);
+ value = htonl(redirecting->count);
+ memcpy(data + pos, &value, sizeof(value));
+ pos += sizeof(value);
+
+ return pos;
+}
+
+int ast_redirecting_parse_data(const unsigned char *data, size_t datalen, struct ast_party_redirecting *redirecting)
+{
+ size_t pos;
+ unsigned char ie_len;
+ unsigned char ie_id;
+ int32_t value;
+
+ for (pos = 0; pos < datalen; pos += ie_len) {
+ if (datalen < pos + sizeof(ie_id) + sizeof(ie_len)) {
+ ast_log(LOG_WARNING, "Invalid redirecting update\n");
+ return -1;
+ }
+ ie_id = data[pos++];
+ ie_len = data[pos++];
+ if (datalen < pos + ie_len) {
+ ast_log(LOG_WARNING, "Invalid redirecting update\n");
+ return -1;
+ }
+
+ switch (ie_id) {
+ case AST_REDIRECTING_FROM_NUMBER:
+ if (redirecting->from.number) {
+ ast_free(redirecting->from.number);
+ }
+ redirecting->from.number = ast_malloc(ie_len + 1);
+ if (redirecting->from.number) {
+ memcpy(redirecting->from.number, data + pos, ie_len);
+ redirecting->from.number[ie_len] = 0;
+ }
+ break;
+ case AST_REDIRECTING_FROM_NAME:
+ if (redirecting->from.name) {
+ ast_free(redirecting->from.name);
+ }
+ redirecting->from.name = ast_malloc(ie_len + 1);
+ if (redirecting->from.name) {
+ memcpy(redirecting->from.name, data + pos, ie_len);
+ redirecting->from.name[ie_len] = 0;
+ }
+ break;
+ case AST_REDIRECTING_FROM_NUMBER_TYPE:
+ if (ie_len != 1) {
+ ast_log(LOG_WARNING, "Invalid redirecting from type of number (%u)\n", (unsigned) ie_len);
+ break;
+ }
+ redirecting->from.number_type = data[pos];
+ break;
+ case AST_REDIRECTING_FROM_NUMBER_PRESENTATION:
+ if (ie_len != 1) {
+ ast_log(LOG_WARNING, "Invalid redirecting from presentation (%u)\n", (unsigned) ie_len);
+ break;
+ }
+ redirecting->from.number_presentation = data[pos];
+ break;
+ case AST_REDIRECTING_TO_NUMBER:
+ if (redirecting->to.number) {
+ ast_free(redirecting->to.number);
+ }
+ redirecting->to.number = ast_malloc(ie_len + 1);
+ if (redirecting->to.number) {
+ memcpy(redirecting->to.number, data + pos, ie_len);
+ redirecting->to.number[ie_len] = 0;
+ }
+ break;
+ case AST_REDIRECTING_TO_NAME:
+ if (redirecting->to.name) {
+ ast_free(redirecting->to.name);
+ }
+ redirecting->to.name = ast_malloc(ie_len + 1);
+ if (redirecting->to.name) {
+ memcpy(redirecting->to.name, data + pos, ie_len);
+ redirecting->to.name[ie_len] = 0;
+ }
+ break;
+ case AST_REDIRECTING_TO_NUMBER_TYPE:
+ if (ie_len != 1) {
+ ast_log(LOG_WARNING, "Invalid redirecting to type of number (%u)\n", (unsigned) ie_len);
+ break;
+ }
+ redirecting->to.number_type = data[pos];
+ break;
+ case AST_REDIRECTING_TO_NUMBER_PRESENTATION:
+ if (ie_len != 1) {
+ ast_log(LOG_WARNING, "Invalid redirecting to presentation (%u)\n", (unsigned) ie_len);
+ break;
+ }
+ redirecting->to.number_presentation = data[pos];
+ break;
+ case AST_REDIRECTING_REASON:
+ if (ie_len != sizeof(value)) {
+ ast_log(LOG_WARNING, "Invalid redirecting reason (%u)\n", (unsigned) ie_len);
+ break;
+ }
+ memcpy(&value, data + pos, sizeof(value));
+ redirecting->reason = ntohl(value);
+ break;
+ case AST_REDIRECTING_COUNT:
+ if (ie_len != sizeof(value)) {
+ ast_log(LOG_WARNING, "Invalid redirecting count (%u)\n", (unsigned) ie_len);
+ break;
+ }
+ memcpy(&value, data + pos, sizeof(value));
+ redirecting->count = ntohl(value);
+ break;
+ default:
+ ast_log(LOG_DEBUG, "Unknown redirecting element: %u (%u)\n", (unsigned) ie_id, (unsigned) ie_len);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+void ast_channel_update_redirecting(struct ast_channel *chan, const struct ast_party_redirecting *redirecting)
+{
+ unsigned char data[1024]; /* This should be large enough */
+ size_t datalen;
+
+ datalen = ast_redirecting_build_data(data, sizeof(data), redirecting);
+ if (datalen == (size_t) -1) {
+ return;
+ }
+
+ ast_indicate_data(chan, AST_CONTROL_REDIRECTING, data, datalen);
+}
+
+void ast_channel_queue_redirecting_update(struct ast_channel *chan, const struct ast_party_redirecting *redirecting)
+{
+ unsigned char data[1024]; /* This should be large enough */
+ size_t datalen;
+
+ datalen = ast_redirecting_build_data(data, sizeof(data), redirecting);
+ if (datalen == (size_t) -1) {
+ return;
+ }
+
+ ast_queue_control_data(chan, AST_CONTROL_REDIRECTING, data, datalen);
+}
+