From dcd62e467fc2382e0265120baf0c8850bbd7e8ca Mon Sep 17 00:00:00 2001 From: Richard Mudgett Date: Tue, 18 Aug 2009 23:53:32 +0000 Subject: [PATCH] Add COLP support to libpri for ETSI PTP, ETSI PTMP, and Q.SIG. Add Connected Line Presentation (COLP) support to chan_dahdi/libpri as an addition to issue 8824. This is the libpri portion. COLP support is now available for ETSI PTP, ETSI PTMP, and Q.SIG with this patch. (closes issue #14068) Tested by: rmudgett Review: https://reviewboard.asterisk.org/r/339/ git-svn-id: https://origsvn.digium.com/svn/libpri/branches/1.4@982 2fbb986a-6c06-0410-b554-c9c1f0a7f128 --- Makefile | 4 + libpri.h | 219 +++++- pri.c | 301 +++++++- pri_facility.c | 1563 ++++++++++++++++++++++++++++++++++----- pri_facility.h | 13 + pri_internal.h | 291 +++++++- q931.c | 1159 ++++++++++++++++++++++++----- rose.c | 186 +++++ rose.h | 653 +++++++++++++++++ rose_etsi_diversion.c | 1623 +++++++++++++++++++++++++++++++++++++++++ rose_etsi_ect.c | 332 +++++++++ rose_internal.h | 101 +++ rosetest.c | 448 ++++++++++++ 13 files changed, 6448 insertions(+), 445 deletions(-) create mode 100644 rose_etsi_diversion.c create mode 100644 rose_etsi_ect.c diff --git a/Makefile b/Makefile index 1cdaa3a..dc5e187 100644 --- a/Makefile +++ b/Makefile @@ -52,6 +52,8 @@ STATIC_OBJS= \ rose.o \ rose_address.o \ rose_etsi_aoc.o \ + rose_etsi_diversion.o \ + rose_etsi_ect.o \ rose_other.o \ rose_q931.o \ rose_qsig_aoc.o \ @@ -71,6 +73,8 @@ DYNAMIC_OBJS= \ rose.lo \ rose_address.lo \ rose_etsi_aoc.lo \ + rose_etsi_diversion.lo \ + rose_etsi_ect.lo \ rose_other.lo \ rose_q931.lo \ rose_qsig_aoc.lo \ diff --git a/libpri.h b/libpri.h index 0dbcde7..04b24f4 100644 --- a/libpri.h +++ b/libpri.h @@ -80,7 +80,8 @@ #define PRI_EVENT_ANSWER 8 /* Call has been answered (CONNECT) */ #define PRI_EVENT_HANGUP_ACK 9 /* Call hangup has been acknowledged */ #define PRI_EVENT_RESTART_ACK 10 /* Restart complete on a given channel (RESTART_ACKNOWLEDGE) */ -#define PRI_EVENT_FACNAME 11 /* Caller*ID Name received on Facility */ +#define PRI_EVENT_FACNAME 11 /* Caller*ID Name received on Facility (DEPRECATED) */ +#define PRI_EVENT_FACILITY 11 /* Facility received (FACILITY) */ #define PRI_EVENT_INFO_RECEIVED 12 /* Additional info (digits) received (INFORMATION) */ #define PRI_EVENT_PROCEEDING 13 /* When we get CALL_PROCEEDING */ #define PRI_EVENT_SETUP_ACK 14 /* When we get SETUP_ACKNOWLEDGE */ @@ -301,8 +302,8 @@ #define PRI_RATE_ADAPT_ASYNC 0x40 /* Notifications */ -#define PRI_NOTIFY_USER_SUSPENDED 0x00 /* User suspended */ -#define PRI_NOTIFY_USER_RESUMED 0x01 /* User resumed */ +#define PRI_NOTIFY_USER_SUSPENDED 0x00 /* User suspended (Q.931) (Call is placed on hold) */ +#define PRI_NOTIFY_USER_RESUMED 0x01 /* User resumed (Q.931) (Call is taken off hold) */ #define PRI_NOTIFY_BEARER_CHANGE 0x02 /* Bearer service change (DSS1) */ #define PRI_NOTIFY_ASN1_COMPONENT 0x03 /* ASN.1 encoded component (DSS1) */ #define PRI_NOTIFY_COMPLETION_DELAY 0x04 /* Call completion delay */ @@ -317,12 +318,12 @@ #define PRI_NOTIFY_CONF_OTHER_DISCONNECTED 0x4a /* Other party disconnected */ #define PRI_NOTIFY_CONF_FLOATING 0x4b /* Conference floating */ #define PRI_NOTIFY_WAITING_CALL 0x60 /* Call is waiting call */ -#define PRI_NOTIFY_DIVERSION_ACTIVATED 0x68 /* Diversion activated (DSS1) */ -#define PRI_NOTIFY_TRANSFER_ALERTING 0x69 /* Call transfer, alerting */ -#define PRI_NOTIFY_TRANSFER_ACTIVE 0x6a /* Call transfer, active */ +#define PRI_NOTIFY_DIVERSION_ACTIVATED 0x68 /* Diversion activated (DSS1) (cfu, cfb, cfnr) (EN 300 207-1 Section 7.2.1) */ +#define PRI_NOTIFY_TRANSFER_ALERTING 0x69 /* Call transfer, alerting (EN 300 369-1 Section 7.2) */ +#define PRI_NOTIFY_TRANSFER_ACTIVE 0x6a /* Call transfer, active(answered) (EN 300 369-1 Section 7.2) */ #define PRI_NOTIFY_REMOTE_HOLD 0x79 /* Remote hold */ #define PRI_NOTIFY_REMOTE_RETRIEVAL 0x7a /* Remote retrieval */ -#define PRI_NOTIFY_CALL_DIVERTING 0x7b /* Call is diverting */ +#define PRI_NOTIFY_CALL_DIVERTING 0x7b /* Call is diverting (EN 300 207-1 Section 7.2.1) */ #define PRI_COPY_DIGITS_CALLED_NUMBER @@ -347,6 +348,151 @@ typedef struct q931_call q931_call; +/* Name character set enumeration values */ +#define PRI_CHAR_SET_UNKNOWN 0 +#define PRI_CHAR_SET_ISO8859_1 1 +#define PRI_CHAR_SET_WITHDRAWN 2 +#define PRI_CHAR_SET_ISO8859_2 3 +#define PRI_CHAR_SET_ISO8859_3 4 +#define PRI_CHAR_SET_ISO8859_4 5 +#define PRI_CHAR_SET_ISO8859_5 6 +#define PRI_CHAR_SET_ISO8859_7 7 +#define PRI_CHAR_SET_ISO10646_BMPSTRING 8 +#define PRI_CHAR_SET_ISO10646_UTF_8STRING 9 + +/*! \brief Q.SIG name information. */ +struct pri_party_name { + /*! \brief TRUE if the name information is valid/present */ + int valid; + /*! + * \brief Q.931 presentation-indicator encoded field + * \note Must tollerate the Q.931 screening-indicator field values being present. + */ + int presentation; + /*! + * \brief Character set the name is using. + * \details + * unknown(0), + * iso8859-1(1), + * enum-value-withdrawn-by-ITU-T(2) + * iso8859-2(3), + * iso8859-3(4), + * iso8859-4(5), + * iso8859-5(6), + * iso8859-7(7), + * iso10646-BmpString(8), + * iso10646-utf-8String(9) + * \details + * Set to iso8859-1(1) if unsure what to use. + */ + int char_set; + /*! \brief Name data with null terminator. */ + char str[64]; +}; + +struct pri_party_number { + /*! \brief TRUE if the number information is valid/present */ + int valid; + /*! \brief Q.931 presentation-indicator and screening-indicator encoded fields */ + int presentation; + /*! \brief Q.931 Type-Of-Number and numbering-plan encoded fields */ + int plan; + /*! \brief Number data with null terminator. */ + char str[64]; +}; + +/*! + * \note This structure is a place holder for possible future subaddress support + * to maintain ABI compatibility. + */ +struct pri_party_subaddress { + /*! \brief TRUE if the subaddress information is valid/present */ + int valid; + /*! + * \brief Subaddress type. + * \details + * nsap(0), + * user_specified(2) + */ + int type; + /*! + * \brief TRUE if odd number of address signals + * \note The odd/even indicator is used when the type of subaddress is + * user_specified and the coding is BCD. + */ + int odd_even_indicator; + /*! \brief Length of the subaddress data */ + int length; + /*! + * \brief Subaddress data with null terminator. + * \note The null terminator is a convenience only since the data could be + * BCD/binary and thus have a null byte as part of the contents. + */ + char data[32]; +}; + +/*! \brief Information needed to identify an endpoint in a call. */ +struct pri_party_id { + /*! \brief Subscriber name */ + struct pri_party_name name; + /*! \brief Subscriber phone number */ + struct pri_party_number number; + /*! \brief Subscriber subaddress */ + struct pri_party_subaddress subaddress; +}; + +/*! \brief Connected Line/Party information */ +struct pri_party_connected_line { + /*! Connected party ID */ + struct pri_party_id id; +}; + +/*! + * \brief Redirecting Line information. + * \details + * RDNIS (Redirecting Directory Number Information Service) + * Where a call diversion or transfer was invoked. + */ +struct pri_party_redirecting { + /*! Who is redirecting the call (Sent to the party the call is redirected toward) */ + struct pri_party_id from; + /*! Call is redirecting to a new party (Sent to the caller) */ + struct pri_party_id to; + /*! Originally called party (in cases of multiple redirects) */ + struct pri_party_id orig_called; + /*! Number of times the call was redirected */ + int count; + /*! Original reason for redirect (in cases of multiple redirects) */ + int orig_reason; + /*! Redirection reason */ + int reason; +}; + +/* Subcommands derived from supplementary services. */ +#define PRI_SUBCMD_REDIRECTING 1 +#define PRI_SUBCMD_CONNECTED_LINE 2 + + +struct pri_subcommand { + /*! PRI_SUBCMD_xxx defined values */ + int cmd; + union { + /*! Reserve room for possible expansion to maintain ABI compatibility. */ + char reserve_space[512]; + struct pri_party_connected_line connected_line; + struct pri_party_redirecting redirecting; + } u; +}; + +/* Max number of subcommands per event message */ +#define PRI_MAX_SUBCOMMANDS 8 + +struct pri_subcommands { + int counter_subcmd; + struct pri_subcommand subcmd[PRI_MAX_SUBCOMMANDS]; +}; + + typedef struct pri_event_generic { /* Events with no additional information fall in this category */ int e; @@ -370,6 +516,7 @@ typedef struct pri_event_ringing { int progressmask; q931_call *call; char useruserinfo[260]; /* User->User info */ + struct pri_subcommands *subcmds; } pri_event_ringing; typedef struct pri_event_answer { @@ -380,8 +527,10 @@ typedef struct pri_event_answer { int progressmask; q931_call *call; char useruserinfo[260]; /* User->User info */ + struct pri_subcommands *subcmds; } pri_event_answer; +/*! Deprecated replaced by struct pri_event_facility. */ typedef struct pri_event_facname { int e; char callingname[256]; @@ -393,6 +542,18 @@ typedef struct pri_event_facname { int callingplan; /* Dialing plan of Calling entity */ } pri_event_facname; +struct pri_event_facility { + int e; + char callingname[256]; /*!< Deprecated, preserved for struct pri_event_facname compatibility */ + char callingnum[256]; /*!< Deprecated, preserved for struct pri_event_facname compatibility */ + int channel; + int cref; + q931_call *call; + int callingpres; /*!< Presentation of Calling CallerID (Deprecated, preserved for struct pri_event_facname compatibility) */ + int callingplan; /*!< Dialing plan of Calling entity (Deprecated, preserved for struct pri_event_facname compatibility) */ + struct pri_subcommands *subcmds; +}; + #define PRI_CALLINGPLANANI #define PRI_CALLINGPLANRDNIS typedef struct pri_event_ring { @@ -426,6 +587,7 @@ typedef struct pri_event_ring { int callingplanorigcalled; /* Dialing plan of Originally Called Number */ int origredirectingreason; int reversecharge; + struct pri_subcommands *subcmds; } pri_event_ring; typedef struct pri_event_hangup { @@ -436,6 +598,7 @@ typedef struct pri_event_hangup { q931_call *call; /* Opaque call pointer */ long aoc_units; /* Advise of Charge number of charged units */ char useruserinfo[260]; /* User->User info */ + struct pri_subcommands *subcmds; } pri_event_hangup; typedef struct pri_event_restart_ack { @@ -452,18 +615,21 @@ typedef struct pri_event_proceeding { int progressmask; int cause; q931_call *call; + struct pri_subcommands *subcmds; } pri_event_proceeding; typedef struct pri_event_setup_ack { int e; int channel; q931_call *call; + struct pri_subcommands *subcmds; } pri_event_setup_ack; typedef struct pri_event_notify { int e; int channel; int info; + struct pri_subcommands *subcmds; } pri_event_notify; typedef struct pri_event_keypad_digit { @@ -471,6 +637,7 @@ typedef struct pri_event_keypad_digit { int channel; q931_call *call; char digits[64]; + struct pri_subcommands *subcmds; } pri_event_keypad_digit; typedef struct pri_event_service { @@ -490,7 +657,7 @@ typedef union { pri_event_generic gen; /* Generic view */ pri_event_restart restart; /* Restart view */ pri_event_error err; /* Error view */ - pri_event_facname facname; /* Caller*ID Name on Facility */ + pri_event_facname facname; /* Caller*ID Name on Facility (Deprecated, use pri_event.facility) */ pri_event_ring ring; /* Ring */ pri_event_hangup hangup; /* Hang up */ pri_event_ringing ringing; /* Ringing */ @@ -502,6 +669,7 @@ typedef union { pri_event_keypad_digit digit; /* Digits that come during a call */ pri_event_service service; /* service message */ pri_event_service_ack service_ack; /* service acknowledgement message */ + struct pri_event_facility facility; } pri_event; struct pri; @@ -588,6 +756,18 @@ int pri_need_more_info(struct pri *pri, q931_call *call, int channel, int nonisd Set non-isdn to non-zero if you are not connecting to ISDN equipment */ int pri_answer(struct pri *pri, q931_call *call, int channel, int nonisdn); +/*! + * \brief Give connected line information to a call + * \note Could be used instead of pri_sr_set_caller_party() before calling pri_setup(). + */ +int pri_connected_line_update(struct pri *pri, q931_call *call, const struct pri_party_connected_line *connected); + +/*! + * \brief Give redirection information to a call + * \note Could be used instead of pri_sr_set_redirecting_parties() before calling pri_setup(). + */ +int pri_redirecting_update(struct pri *pri, q931_call *call, const struct pri_party_redirecting *redirecting); + /* Set CRV reference for GR-303 calls */ @@ -642,8 +822,31 @@ void pri_sr_free(struct pri_sr *sr); int pri_sr_set_channel(struct pri_sr *sr, int channel, int exclusive, int nonisdn); int pri_sr_set_bearer(struct pri_sr *sr, int transmode, int userl1); int pri_sr_set_called(struct pri_sr *sr, char *called, int calledplan, int complete); + +/*! + * \brief Set the caller party ID information in the call SETUP record. + * + * \param sr New call SETUP record. + * \param caller Caller party ID information to set. + * + * \return Nothing + */ +void pri_sr_set_caller_party(struct pri_sr *sr, const struct pri_party_id *caller); +/*! \note Use pri_sr_set_caller_party() instead to pass more precise caller information. */ int pri_sr_set_caller(struct pri_sr *sr, char *caller, char *callername, int callerplan, int callerpres); + +/*! + * \brief Set the redirecting information in the call SETUP record. + * + * \param sr New call SETUP record. + * \param caller Redirecting information to set. + * + * \return Nothing + */ +void pri_sr_set_redirecting_parties(struct pri_sr *sr, const struct pri_party_redirecting *redirecting); +/*! \note Use pri_sr_set_redirecting_parties() instead to pass more precise redirecting information. */ int pri_sr_set_redirecting(struct pri_sr *sr, char *num, int plan, int pres, int reason); + #define PRI_USER_USER_TX /* Set the user user field. Warning! don't send binary data accross this field */ void pri_sr_set_useruser(struct pri_sr *sr, const char *userchars); diff --git a/pri.c b/pri.c index 0084f7f..d3a7c3e 100644 --- a/pri.c +++ b/pri.c @@ -374,8 +374,8 @@ char *pri_event2str(int id) return "Hangup ACK"; case PRI_EVENT_RESTART_ACK: return "Restart ACK"; - case PRI_EVENT_FACNAME: - return "FacName"; + case PRI_EVENT_FACILITY: + return "Facility"; case PRI_EVENT_INFO_RECEIVED: return "Info Received"; case PRI_EVENT_PROCEEDING: @@ -579,6 +579,203 @@ int pri_answer(struct pri *pri, q931_call *call, int channel, int nonisdn) return q931_connect(pri, call, channel, nonisdn); } +/*! + * \internal + * \brief Copy the PRI party name to the Q.931 party name structure. + * + * \param q931_name Q.931 party name structure + * \param pri_name PRI party name structure + * + * \return Nothing + */ +static void pri_copy_party_name_to_q931(struct q931_party_name *q931_name, const struct pri_party_name *pri_name) +{ + q931_party_name_init(q931_name); + if (pri_name->valid) { + q931_name->valid = 1; + q931_name->presentation = pri_name->presentation; + q931_name->char_set = pri_name->char_set; + libpri_copy_string(q931_name->str, pri_name->str, sizeof(q931_name->str)); + } +} + +/*! + * \internal + * \brief Copy the PRI party number to the Q.931 party number structure. + * + * \param q931_number Q.931 party number structure + * \param pri_number PRI party number structure + * + * \return Nothing + */ +static void pri_copy_party_number_to_q931(struct q931_party_number *q931_number, const struct pri_party_number *pri_number) +{ + q931_party_number_init(q931_number); + if (pri_number->valid) { + q931_number->valid = 1; + q931_number->presentation = pri_number->presentation; + q931_number->plan = pri_number->plan; + libpri_copy_string(q931_number->str, pri_number->str, sizeof(q931_number->str)); + } +} + +/*! + * \internal + * \brief Copy the PRI party id to the Q.931 party id structure. + * + * \param q931_id Q.931 party id structure + * \param pri_id PRI party id structure + * + * \return Nothing + */ +static void pri_copy_party_id_to_q931(struct q931_party_id *q931_id, const struct pri_party_id *pri_id) +{ + pri_copy_party_name_to_q931(&q931_id->name, &pri_id->name); + pri_copy_party_number_to_q931(&q931_id->number, &pri_id->number); +} + +int pri_connected_line_update(struct pri *ctrl, q931_call *call, const struct pri_party_connected_line *connected) +{ + struct q931_party_id party_id; + + if (!ctrl || !call) { + return -1; + } + + pri_copy_party_id_to_q931(&party_id, &connected->id); + q931_party_id_fixup(ctrl, &party_id); + if (!q931_party_id_cmp(&party_id, &call->local_id)) { + /* The local party information did not change so do nothing. */ + return 0; + } + call->local_id = party_id; + + switch (call->ourcallstate) { + case Q931_CALL_STATE_CALL_INITIATED: + case Q931_CALL_STATE_OVERLAP_SENDING: + case Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING: + case Q931_CALL_STATE_CALL_DELIVERED: + /* + * The local party transferred to someone else before + * the remote end answered. + */ + case Q931_CALL_STATE_ACTIVE: + switch (ctrl->switchtype) { + case PRI_SWITCH_EUROISDN_E1: + case PRI_SWITCH_EUROISDN_T1: + if (q931_is_ptmp(ctrl)) { + /* PTMP mode */ + q931_notify_redirection(ctrl, call, PRI_NOTIFY_TRANSFER_ACTIVE, + &call->local_id.number); + } else { + /* PTP mode */ + /* Immediately send EctInform APDU, callStatus=answered(0) */ + send_call_transfer_complete(ctrl, call, 0); + } + break; + case PRI_SWITCH_QSIG: + /* Immediately send CallTransferComplete APDU, callStatus=answered(0) */ + send_call_transfer_complete(ctrl, call, 0); + break; + default: + break; + } + break; + default: + /* Just save the data for further developments. */ + break; + } + + return 0; +} + +int pri_redirecting_update(struct pri *ctrl, q931_call *call, const struct pri_party_redirecting *redirecting) +{ + if (!ctrl || !call) { + return -1; + } + + /* Save redirecting.to information and reason. */ + pri_copy_party_id_to_q931(&call->redirecting.to, &redirecting->to); + q931_party_id_fixup(ctrl, &call->redirecting.to); + call->redirecting.reason = redirecting->reason; + + switch (call->ourcallstate) { + case Q931_CALL_STATE_NULL: + /* Save the remaining redirecting information before we place a call. */ + pri_copy_party_id_to_q931(&call->redirecting.from, &redirecting->from); + q931_party_id_fixup(ctrl, &call->redirecting.from); + pri_copy_party_id_to_q931(&call->redirecting.orig_called, &redirecting->orig_called); + q931_party_id_fixup(ctrl, &call->redirecting.orig_called); + call->redirecting.orig_reason = redirecting->orig_reason; + if (redirecting->count <= 0) { + if (call->redirecting.from.number.valid) { + /* + * We are redirecting with an unknown count + * so assume the count is one. + */ + call->redirecting.count = 1; + } else { + call->redirecting.count = 0; + } + } else if (redirecting->count < PRI_MAX_REDIRECTS) { + call->redirecting.count = redirecting->count; + } else { + call->redirecting.count = PRI_MAX_REDIRECTS; + } + break; + case Q931_CALL_STATE_OVERLAP_RECEIVING: + case Q931_CALL_STATE_INCOMING_CALL_PROCEEDING: + case Q931_CALL_STATE_CALL_RECEIVED: + /* This is an incoming call that has not connected yet. */ + if (!call->redirecting.to.number.valid) { + /* Not being redirected toward valid number data. Ignore. */ + break; + } + + switch (ctrl->switchtype) { + case PRI_SWITCH_EUROISDN_E1: + case PRI_SWITCH_EUROISDN_T1: + if (q931_is_ptmp(ctrl)) { + /* PTMP mode */ + q931_notify_redirection(ctrl, call, PRI_NOTIFY_CALL_DIVERTING, + &call->redirecting.to.number); + break; + } + /* PTP mode - same behaviour as Q.SIG */ + /* fall through */ + case PRI_SWITCH_QSIG: + if (call->redirecting.state != Q931_REDIRECTING_STATE_PENDING_TX_DIV_LEG_3 + || strcmp(call->redirecting.to.number.str, call->called.number.str) != 0) { + /* immediately send divertingLegInformation1 APDU */ + if (rose_diverting_leg_information1_encode(ctrl, call) + || q931_facility(ctrl, call)) { + pri_message(ctrl, + "Could not schedule facility message for divertingLegInfo1\n"); + } + } + call->redirecting.state = Q931_REDIRECTING_STATE_IDLE; + + /* immediately send divertingLegInformation3 APDU */ + if (rose_diverting_leg_information3_encode(ctrl, call, Q931_FACILITY) + || q931_facility(ctrl, call)) { + pri_message(ctrl, + "Could not schedule facility message for divertingLegInfo3\n"); + } + break; + default: + break; + } + break; + default: + pri_message(ctrl, "Ignored redirecting update because call in state %s(%d).\n", + q931_call_state_str(call->ourcallstate), call->ourcallstate); + break; + } + + return 0; +} + #if 0 /* deprecated routines, use pri_hangup */ int pri_release(struct pri *pri, q931_call *call, int cause) @@ -703,6 +900,9 @@ void pri_dump_event(struct pri *pri, pri_event *e) static void pri_sr_init(struct pri_sr *req) { memset(req, 0, sizeof(struct pri_sr)); + q931_party_redirecting_init(&req->redirecting); + q931_party_id_init(&req->caller); + q931_party_address_init(&req->called); req->reversecharge = PRI_REVERSECHARGE_NONE; } @@ -725,13 +925,8 @@ int pri_mwi_activate(struct pri *pri, q931_call *c, char *caller, int callerplan pri_sr_init(&req); pri_sr_set_connection_call_independent(&req); - - req.caller = caller; - req.callerplan = callerplan; - req.callername = callername; - req.callerpres = callerpres; - req.called = called; - req.calledplan = calledplan; + pri_sr_set_caller(&req, caller, callername, callerplan, callerpres); + pri_sr_set_called(&req, called, calledplan, 0); if (mwi_message_send(pri, c, &req, 1) < 0) { pri_message(pri, "Unable to send MWI activate message\n"); @@ -750,13 +945,8 @@ int pri_mwi_deactivate(struct pri *pri, q931_call *c, char *caller, int callerpl pri_sr_init(&req); pri_sr_set_connection_call_independent(&req); - - req.caller = caller; - req.callerplan = callerplan; - req.callername = callername; - req.callerpres = callerpres; - req.called = called; - req.calledplan = calledplan; + pri_sr_set_caller(&req, caller, callername, callerplan, callerpres); + pri_sr_set_called(&req, called, calledplan, 0); if(mwi_message_send(pri, c, &req, 0) < 0) { pri_message(pri, "Unable to send MWI deactivate message\n"); @@ -782,16 +972,12 @@ int pri_call(struct pri *pri, q931_call *c, int transmode, int channel, int excl if (!pri || !c) return -1; pri_sr_init(&req); + pri_sr_set_caller(&req, caller, callername, callerplan, callerpres); + pri_sr_set_called(&req, called, calledplan, 0); req.transmode = transmode; req.channel = channel; req.exclusive = exclusive; req.nonisdn = nonisdn; - req.caller = caller; - req.callerplan = callerplan; - req.callername = callername; - req.callerpres = callerpres; - req.called = called; - req.calledplan = calledplan; req.userl1 = ulayer1; return q931_setup(pri, c, &req); } @@ -1002,30 +1188,81 @@ int pri_sr_set_bearer(struct pri_sr *sr, int transmode, int userl1) int pri_sr_set_called(struct pri_sr *sr, char *called, int calledplan, int numcomplete) { - sr->called = called; - sr->calledplan = calledplan; + q931_party_address_init(&sr->called); + if (called) { + sr->called.number.valid = 1; + sr->called.number.plan = calledplan; + libpri_copy_string(sr->called.number.str, called, sizeof(sr->called.number.str)); + } sr->numcomplete = numcomplete; return 0; } int pri_sr_set_caller(struct pri_sr *sr, char *caller, char *callername, int callerplan, int callerpres) { - sr->caller = caller; - sr->callername = callername; - sr->callerplan = callerplan; - sr->callerpres = callerpres; + q931_party_id_init(&sr->caller); + if (caller) { + sr->caller.number.valid = 1; + sr->caller.number.presentation = callerpres; + sr->caller.number.plan = callerplan; + libpri_copy_string(sr->caller.number.str, caller, sizeof(sr->caller.number.str)); + + if (callername) { + sr->caller.name.valid = 1; + sr->caller.name.presentation = callerpres; + sr->caller.name.char_set = PRI_CHAR_SET_ISO8859_1; + libpri_copy_string(sr->caller.name.str, callername, + sizeof(sr->caller.name.str)); + } + } return 0; } +void pri_sr_set_caller_party(struct pri_sr *sr, const struct pri_party_id *caller) +{ + pri_copy_party_id_to_q931(&sr->caller, caller); +} + int pri_sr_set_redirecting(struct pri_sr *sr, char *num, int plan, int pres, int reason) { - sr->redirectingnum = num; - sr->redirectingplan = plan; - sr->redirectingpres = pres; - sr->redirectingreason = reason; + q931_party_redirecting_init(&sr->redirecting); + if (num && num[0]) { + sr->redirecting.from.number.valid = 1; + sr->redirecting.from.number.presentation = pres; + sr->redirecting.from.number.plan = plan; + libpri_copy_string(sr->redirecting.from.number.str, num, + sizeof(sr->redirecting.from.number.str)); + + sr->redirecting.count = 1; + sr->redirecting.reason = reason; + } return 0; } +void pri_sr_set_redirecting_parties(struct pri_sr *sr, const struct pri_party_redirecting *redirecting) +{ + pri_copy_party_id_to_q931(&sr->redirecting.from, &redirecting->from); + pri_copy_party_id_to_q931(&sr->redirecting.to, &redirecting->to); + pri_copy_party_id_to_q931(&sr->redirecting.orig_called, &redirecting->orig_called); + sr->redirecting.orig_reason = redirecting->orig_reason; + sr->redirecting.reason = redirecting->reason; + if (redirecting->count <= 0) { + if (sr->redirecting.from.number.valid) { + /* + * We are redirecting with an unknown count + * so assume the count is one. + */ + sr->redirecting.count = 1; + } else { + sr->redirecting.count = 0; + } + } else if (redirecting->count < PRI_MAX_REDIRECTS) { + sr->redirecting.count = redirecting->count; + } else { + sr->redirecting.count = PRI_MAX_REDIRECTS; + } +} + void pri_sr_set_reversecharge(struct pri_sr *sr, int requested) { sr->reversecharge = requested; diff --git a/pri_facility.c b/pri_facility.c index 3e2e8ad..58246e0 100644 --- a/pri_facility.c +++ b/pri_facility.c @@ -392,6 +392,490 @@ static int presentation_for_q931(struct pri *ctrl, int presentation) return value; } +/*! + * \internal + * \brief Convert the Q.931 number presentation field to Q.SIG name presentation. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param presentation Q.931 presentation/screening octet. + * \param name_present Non-zero if the name is available. + * + * \return Name presentation enumeration value. + */ +static int qsig_name_presentation_from_q931(struct pri *ctrl, int presentation, int name_present) +{ + int value; + + switch (presentation & PRI_PRES_RESTRICTION) { + case PRI_PRES_ALLOWED: + if (name_present) { + value = 1; /* presentation_allowed */ + } else { + value = 4; /* name_not_available */ + } + break; + default: + pri_message(ctrl, "!! Unsupported Q.931 number presentation value (%d)\n", + presentation); + /* fall through */ + case PRI_PRES_RESTRICTED: + if (name_present) { + value = 2; /* presentation_restricted */ + } else { + value = 3; /* presentation_restricted_null */ + } + break; + case PRI_PRES_UNAVAILABLE: + value = 4; /* name_not_available */ + break; + } + + return value; +} + +/*! + * \internal + * \brief Convert the Q.SIG name presentation to Q.931 presentation field value. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param presentation Q.SIG name presentation value. + * + * \return Q.931 presentation field value. + */ +static int qsig_name_presentation_for_q931(struct pri *ctrl, int presentation) +{ + int value; + + switch (presentation) { + case 1: /* presentation_allowed */ + value = PRI_PRES_ALLOWED; + break; + default: + pri_message(ctrl, + "!! Unsupported Q.SIG name presentation to Q.931 value (%d)\n", + presentation); + /* fall through */ + case 2: /* presentation_restricted */ + case 3: /* presentation_restricted_null */ + value = PRI_PRES_RESTRICTED; + break; + case 0: /* optional_name_not_present */ + case 4: /* name_not_available */ + value = PRI_PRES_UNAVAILABLE; + break; + } + + return value; +} + +/*! + * \internal + * \brief Convert number presentation to Q.SIG diversion subscription notification. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param presentation Number presentation value. + * + * \return Q.SIG diversion subscription notification value. + */ +static int presentation_to_subscription(struct pri *ctrl, int presentation) +{ + /* derive subscription value from presentation value */ + + switch (presentation & PRI_PRES_RESTRICTION) { + case PRI_PRES_ALLOWED: + return QSIG_NOTIFICATION_WITH_DIVERTED_TO_NR; + case PRI_PRES_RESTRICTED: + return QSIG_NOTIFICATION_WITHOUT_DIVERTED_TO_NR; + case PRI_PRES_UNAVAILABLE: /* Number not available due to interworking */ + return QSIG_NOTIFICATION_WITHOUT_DIVERTED_TO_NR; /* ?? QSIG_NO_NOTIFICATION */ + default: + pri_message(ctrl, "!! Unknown Q.SIG presentationIndicator 0x%02x\n", + presentation); + return QSIG_NOTIFICATION_WITHOUT_DIVERTED_TO_NR; + } +} + +/*! + * \internal + * \brief Copy the given rose party number to the q931_party_number + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param q931_number Q.931 party number structure + * \param rose_number ROSE party number structure + * + * \note It is assumed that the q931_number has been initialized before calling. + * + * \return Nothing + */ +static void rose_copy_number_to_q931(struct pri *ctrl, + struct q931_party_number *q931_number, const struct rosePartyNumber *rose_number) +{ + libpri_copy_string(q931_number->str, (char *) rose_number->str, + sizeof(q931_number->str)); + q931_number->plan = numbering_plan_for_q931(ctrl, rose_number->plan) + | typeofnumber_for_q931(ctrl, rose_number->ton); +} + +/*! + * \internal + * \brief Copy the given rose presented screened party number to the q931_party_number + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param q931_number Q.931 party number structure + * \param rose_presented ROSE presented screened party number structure + * + * \return Nothing + */ +static void rose_copy_presented_number_screened_to_q931(struct pri *ctrl, + struct q931_party_number *q931_number, + const struct rosePresentedNumberScreened *rose_presented) +{ + q931_party_number_init(q931_number); + q931_number->valid = 1; + q931_number->presentation = presentation_for_q931(ctrl, rose_presented->presentation); + switch (rose_presented->presentation) { + case 0: /* presentationAllowedNumber */ + case 3: /* presentationRestrictedNumber */ + q931_number->presentation |= + (rose_presented->screened.screening_indicator & PRI_PRES_NUMBER_TYPE); + rose_copy_number_to_q931(ctrl, q931_number, + &rose_presented->screened.number); + break; + default: + q931_number->presentation |= PRI_PRES_USER_NUMBER_UNSCREENED; + break; + } +} + +/*! + * \internal + * \brief Copy the given rose presented unscreened party number to the q931_party_number + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param q931_number Q.931 party number structure + * \param rose_presented ROSE presented unscreened party number structure + * + * \return Nothing + */ +static void rose_copy_presented_number_unscreened_to_q931(struct pri *ctrl, + struct q931_party_number *q931_number, + const struct rosePresentedNumberUnscreened *rose_presented) +{ + q931_party_number_init(q931_number); + q931_number->valid = 1; + q931_number->presentation = presentation_for_q931(ctrl, + rose_presented->presentation) | PRI_PRES_USER_NUMBER_UNSCREENED; + switch (rose_presented->presentation) { + case 0: /* presentationAllowedNumber */ + case 3: /* presentationRestrictedNumber */ + rose_copy_number_to_q931(ctrl, q931_number, &rose_presented->number); + break; + default: + break; + } +} + +/*! + * \internal + * \brief Copy the given rose presented screened party address to the q931_party_number + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param q931_number Q.931 party number structure + * \param rose_presented ROSE presented screened party address structure + * + * \return Nothing + */ +static void rose_copy_presented_address_screened_to_q931(struct pri *ctrl, + struct q931_party_number *q931_number, + const struct rosePresentedAddressScreened *rose_presented) +{ + q931_party_number_init(q931_number); + q931_number->valid = 1; + q931_number->presentation = presentation_for_q931(ctrl, rose_presented->presentation); + switch (rose_presented->presentation) { + case 0: /* presentationAllowedAddress */ + case 3: /* presentationRestrictedAddress */ + q931_number->presentation |= + (rose_presented->screened.screening_indicator & PRI_PRES_NUMBER_TYPE); + rose_copy_number_to_q931(ctrl, q931_number, + &rose_presented->screened.number); + break; + default: + q931_number->presentation |= PRI_PRES_USER_NUMBER_UNSCREENED; + break; + } +} + +/*! + * \internal + * \brief Copy the given rose party name to the q931_party_name + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param qsig_name Q.SIG party name structure + * \param rose_name Q.SIG ROSE party name structure + * + * \return Nothing + */ +static void rose_copy_name_to_q931(struct pri *ctrl, + struct q931_party_name *qsig_name, const struct roseQsigName *rose_name) +{ + //q931_party_name_init(qsig_name); + qsig_name->valid = 1; + qsig_name->presentation = qsig_name_presentation_for_q931(ctrl, + rose_name->presentation); + qsig_name->char_set = rose_name->char_set; + libpri_copy_string(qsig_name->str, (char *) rose_name->data, sizeof(qsig_name->str)); +} + +/*! + * \internal + * \brief Copy the given q931_party_number to the rose party number + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param rose_number ROSE party number structure + * \param q931_number Q.931 party number structure + * + * \return Nothing + */ +static void q931_copy_number_to_rose(struct pri *ctrl, + struct rosePartyNumber *rose_number, const struct q931_party_number *q931_number) +{ + rose_number->plan = numbering_plan_from_q931(ctrl, q931_number->plan); + rose_number->ton = typeofnumber_from_q931(ctrl, q931_number->plan); + /* Truncate the q931_number->str if necessary. */ + libpri_copy_string((char *) rose_number->str, q931_number->str, + sizeof(rose_number->str)); + rose_number->length = strlen((char *) rose_number->str); +} + +/*! + * \internal + * \brief Copy the given q931_party_number to the rose presented screened party number + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param rose_presented ROSE presented screened party number structure + * \param q931_number Q.931 party number structure + * + * \return Nothing + */ +static void q931_copy_presented_number_screened_to_rose(struct pri *ctrl, + struct rosePresentedNumberScreened *rose_presented, + const struct q931_party_number *q931_number) +{ + if (q931_number->valid) { + rose_presented->presentation = + presentation_from_q931(ctrl, q931_number->presentation, q931_number->str[0]); + rose_presented->screened.screening_indicator = + q931_number->presentation & PRI_PRES_NUMBER_TYPE; + q931_copy_number_to_rose(ctrl, &rose_presented->screened.number, q931_number); + } else { + rose_presented->presentation = 2;/* numberNotAvailableDueToInterworking */ + } +} + +/*! + * \internal + * \brief Copy the given q931_party_number to the rose presented unscreened party number + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param rose_presented ROSE presented unscreened party number structure + * \param q931_number Q.931 party number structure + * + * \return Nothing + */ +static void q931_copy_presented_number_unscreened_to_rose(struct pri *ctrl, + struct rosePresentedNumberUnscreened *rose_presented, + const struct q931_party_number *q931_number) +{ + if (q931_number->valid) { + rose_presented->presentation = + presentation_from_q931(ctrl, q931_number->presentation, q931_number->str[0]); + q931_copy_number_to_rose(ctrl, &rose_presented->number, q931_number); + } else { + rose_presented->presentation = 2;/* numberNotAvailableDueToInterworking */ + } +} + +#if 0 /* In case it is needed in the future */ +/*! + * \internal + * \brief Copy the given q931_party_number to the rose presented screened party address + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param rose_presented ROSE presented screened party address structure + * \param q931_number Q.931 party number structure + * + * \return Nothing + */ +static void q931_copy_presented_address_screened_to_rose(struct pri *ctrl, + struct rosePresentedAddressScreened *rose_presented, + const struct q931_party_number *q931_number) +{ + if (q931_number->valid) { + rose_presented->presentation = + presentation_from_q931(ctrl, q931_number->presentation, q931_number->str[0]); + rose_presented->screened.screening_indicator = + q931_number->presentation & PRI_PRES_NUMBER_TYPE; + q931_copy_number_to_rose(ctrl, &rose_presented->screened.number, q931_number); + rose_presented->screened.subaddress.length = 0; + } else { + rose_presented->presentation = 2;/* numberNotAvailableDueToInterworking */ + } +} +#endif /* In case it is needed in the future */ + +/*! + * \internal + * \brief Copy the given q931_party_name to the rose party name + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param rose_name Q.SIG ROSE party name structure + * \param qsig_name Q.SIG party name structure + * + * \return Nothing + */ +static void q931_copy_name_to_rose(struct pri *ctrl, + struct roseQsigName *rose_name, const struct q931_party_name *qsig_name) +{ + if (qsig_name->valid) { + rose_name->presentation = qsig_name_presentation_from_q931(ctrl, + qsig_name->presentation, qsig_name->str[0]); + rose_name->char_set = qsig_name->char_set; + /* Truncate the qsig_name->str if necessary. */ + libpri_copy_string((char *) rose_name->data, qsig_name->str, sizeof(rose_name->data)); + rose_name->length = strlen((char *) rose_name->data); + } else { + rose_name->presentation = 4;/* name_not_available */ + } +} + +/*! + * \internal + * \brief Encode the Q.SIG DivertingLegInformation1 invoke message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode the facility ie contents. + * \param end End of facility ie contents encoding data buffer. + * \param call Call leg from which to encode diversion leg 1. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *enc_qsig_diverting_leg_information1(struct pri *ctrl, + unsigned char *pos, unsigned char *end, q931_call *call) +{ + struct fac_extension_header header; + struct rose_msg_invoke msg; + + memset(&header, 0, sizeof(header)); + header.nfe_present = 1; + header.nfe.source_entity = 0; /* endPINX */ + header.nfe.destination_entity = 0; /* endPINX */ + header.interpretation_present = 1; + header.interpretation = 0; /* discardAnyUnrecognisedInvokePdu */ + pos = facility_encode_header(ctrl, pos, end, &header); + if (!pos) { + return NULL; + } + + memset(&msg, 0, sizeof(msg)); + msg.operation = ROSE_QSIG_DivertingLegInformation1; + msg.invoke_id = get_invokeid(ctrl); + msg.args.qsig.DivertingLegInformation1.diversion_reason = + redirectingreason_from_q931(ctrl, call->redirecting.reason); + + /* subscriptionOption is the redirecting.to.number.presentation */ + msg.args.qsig.DivertingLegInformation1.subscription_option = + presentation_to_subscription(ctrl, call->redirecting.to.number.presentation); + + /* nominatedNr is the redirecting.to.number */ + q931_copy_number_to_rose(ctrl, + &msg.args.qsig.DivertingLegInformation1.nominated_number, + &call->redirecting.to.number); + + pos = rose_encode_invoke(ctrl, pos, end, &msg); + + return pos; +} + +/*! + * \internal + * \brief Encode the ETSI DivertingLegInformation1 invoke message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode the facility ie contents. + * \param end End of facility ie contents encoding data buffer. + * \param call Call leg from which to encode diversion leg 1. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *enc_etsi_diverting_leg_information1(struct pri *ctrl, + unsigned char *pos, unsigned char *end, q931_call *call) +{ + struct rose_msg_invoke msg; + + pos = facility_encode_header(ctrl, pos, end, NULL); + if (!pos) { + return NULL; + } + + memset(&msg, 0, sizeof(msg)); + msg.operation = ROSE_ETSI_DivertingLegInformation1; + msg.invoke_id = get_invokeid(ctrl); + msg.args.etsi.DivertingLegInformation1.diversion_reason = + redirectingreason_from_q931(ctrl, call->redirecting.reason); + + if (call->redirecting.to.number.valid) { + msg.args.etsi.DivertingLegInformation1.subscription_option = 2; + + /* divertedToNumber is the redirecting.to.number */ + msg.args.etsi.DivertingLegInformation1.diverted_to_present = 1; + q931_copy_presented_number_unscreened_to_rose(ctrl, + &msg.args.etsi.DivertingLegInformation1.diverted_to, + &call->redirecting.to.number); + } else { + msg.args.etsi.DivertingLegInformation1.subscription_option = 1; + } + pos = rose_encode_invoke(ctrl, pos, end, &msg); + + return pos; +} + +/*! + * \brief Encode and queue the DivertingLegInformation1 invoke message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param call Call leg from which to encode diversion leg 1. + * + * \retval 0 on success. + * \retval -1 on error. + */ +int rose_diverting_leg_information1_encode(struct pri *ctrl, q931_call *call) +{ + unsigned char buffer[256]; + unsigned char *end; + + switch (ctrl->switchtype) { + case PRI_SWITCH_EUROISDN_E1: + case PRI_SWITCH_EUROISDN_T1: + end = enc_etsi_diverting_leg_information1(ctrl, buffer, buffer + sizeof(buffer), + call); + break; + case PRI_SWITCH_QSIG: + end = enc_qsig_diverting_leg_information1(ctrl, buffer, buffer + sizeof(buffer), + call); + break; + default: + return -1; + } + if (!end) { + return -1; + } + + return pri_call_apdu_queue(call, Q931_FACILITY, buffer, end - buffer, NULL, NULL); +} + /*! * \internal * \brief Encode the Q.SIG DivertingLegInformation2 invoke message. @@ -425,38 +909,49 @@ static unsigned char *enc_qsig_diverting_leg_information2(struct pri *ctrl, msg.operation = ROSE_QSIG_DivertingLegInformation2; msg.invoke_id = get_invokeid(ctrl); - /* diversionCounter always is 1 because other isn't available in the current design */ - msg.args.qsig.DivertingLegInformation2.diversion_counter = 1; + /* diversionCounter is the redirecting.count */ + msg.args.qsig.DivertingLegInformation2.diversion_counter = call->redirecting.count; msg.args.qsig.DivertingLegInformation2.diversion_reason = - redirectingreason_from_q931(ctrl, call->redirectingreason); + redirectingreason_from_q931(ctrl, call->redirecting.reason); - /* divertingNr */ + /* divertingNr is the redirecting.from.number */ msg.args.qsig.DivertingLegInformation2.diverting_present = 1; - msg.args.qsig.DivertingLegInformation2.diverting.presentation = - presentation_from_q931(ctrl, call->redirectingpres, call->redirectingnum[0]); - msg.args.qsig.DivertingLegInformation2.diverting.number.plan = - numbering_plan_from_q931(ctrl, call->redirectingplan); - msg.args.qsig.DivertingLegInformation2.diverting.number.ton = - typeofnumber_from_q931(ctrl, call->redirectingplan); - libpri_copy_string((char *) - msg.args.qsig.DivertingLegInformation2.diverting.number.str, - call->redirectingnum, - sizeof(msg.args.qsig.DivertingLegInformation2.diverting.number.str)); - msg.args.qsig.DivertingLegInformation2.diverting.number.length = - strlen((char *) msg.args.qsig.DivertingLegInformation2.diverting.number.str); + q931_copy_presented_number_unscreened_to_rose(ctrl, + &msg.args.qsig.DivertingLegInformation2.diverting, + &call->redirecting.from.number); - /* redirectingName */ - if (call->redirectingname[0]) { + /* redirectingName is the redirecting.from.name */ + if (call->redirecting.from.name.valid) { msg.args.qsig.DivertingLegInformation2.redirecting_name_present = 1; - msg.args.qsig.DivertingLegInformation2.redirecting_name.presentation = 1; /* presentation_allowed */ - msg.args.qsig.DivertingLegInformation2.redirecting_name.char_set = 1; /* iso8859-1 */ - libpri_copy_string((char *) - msg.args.qsig.DivertingLegInformation2.redirecting_name.data, - call->redirectingname, - sizeof(msg.args.qsig.DivertingLegInformation2.redirecting_name.data)); - msg.args.qsig.DivertingLegInformation2.redirecting_name.length = strlen((char *) - msg.args.qsig.DivertingLegInformation2.redirecting_name.data); + q931_copy_name_to_rose(ctrl, + &msg.args.qsig.DivertingLegInformation2.redirecting_name, + &call->redirecting.from.name); + } + + if (1 < call->redirecting.count) { + /* originalCalledNr is the redirecting.orig_called.number */ + msg.args.qsig.DivertingLegInformation2.original_called_present = 1; + q931_copy_presented_number_unscreened_to_rose(ctrl, + &msg.args.qsig.DivertingLegInformation2.original_called, + &call->redirecting.orig_called.number); + + msg.args.qsig.DivertingLegInformation2.original_diversion_reason_present = 1; + if (call->redirecting.orig_called.number.valid) { + msg.args.qsig.DivertingLegInformation2.original_diversion_reason = + redirectingreason_from_q931(ctrl, call->redirecting.orig_reason); + } else { + msg.args.qsig.DivertingLegInformation2.original_diversion_reason = + QSIG_DIVERT_REASON_UNKNOWN; + } + + /* originalCalledName is the redirecting.orig_called.name */ + if (call->redirecting.orig_called.name.valid) { + msg.args.qsig.DivertingLegInformation2.original_called_name_present = 1; + q931_copy_name_to_rose(ctrl, + &msg.args.qsig.DivertingLegInformation2.original_called_name, + &call->redirecting.orig_called.name); + } } pos = rose_encode_invoke(ctrl, pos, end, &msg); @@ -466,7 +961,58 @@ static unsigned char *enc_qsig_diverting_leg_information2(struct pri *ctrl, /*! * \internal - * \brief Encode and queue the Q.SIG DivertingLegInformation2 invoke message. + * \brief Encode the ETSI DivertingLegInformation2 invoke message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode the facility ie contents. + * \param end End of facility ie contents encoding data buffer. + * \param call Call leg from which to encode diversion leg 2. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *enc_etsi_diverting_leg_information2(struct pri *ctrl, + unsigned char *pos, unsigned char *end, q931_call *call) +{ + struct rose_msg_invoke msg; + + pos = facility_encode_header(ctrl, pos, end, NULL); + if (!pos) { + return NULL; + } + + memset(&msg, 0, sizeof(msg)); + msg.operation = ROSE_ETSI_DivertingLegInformation2; + msg.invoke_id = get_invokeid(ctrl); + + /* diversionCounter is the redirecting.count */ + msg.args.etsi.DivertingLegInformation2.diversion_counter = call->redirecting.count; + + msg.args.etsi.DivertingLegInformation2.diversion_reason = + redirectingreason_from_q931(ctrl, call->redirecting.reason); + + /* divertingNr is the redirecting.from.number */ + msg.args.etsi.DivertingLegInformation2.diverting_present = 1; + q931_copy_presented_number_unscreened_to_rose(ctrl, + &msg.args.etsi.DivertingLegInformation2.diverting, + &call->redirecting.from.number); + + if (1 < call->redirecting.count) { + /* originalCalledNr is the redirecting.orig_called.number */ + msg.args.etsi.DivertingLegInformation2.original_called_present = 1; + q931_copy_presented_number_unscreened_to_rose(ctrl, + &msg.args.etsi.DivertingLegInformation2.original_called, + &call->redirecting.orig_called.number); + } + + pos = rose_encode_invoke(ctrl, pos, end, &msg); + + return pos; +} + +/*! + * \internal + * \brief Encode and queue the DivertingLegInformation2 invoke message. * * \param ctrl D channel controller for diagnostic messages or global options. * \param call Call leg from which to encode diversion leg 2. @@ -479,8 +1025,19 @@ static int rose_diverting_leg_information2_encode(struct pri *ctrl, q931_call *c unsigned char buffer[256]; unsigned char *end; - end = - enc_qsig_diverting_leg_information2(ctrl, buffer, buffer + sizeof(buffer), call); + switch (ctrl->switchtype) { + case PRI_SWITCH_EUROISDN_E1: + case PRI_SWITCH_EUROISDN_T1: + end = enc_etsi_diverting_leg_information2(ctrl, buffer, buffer + sizeof(buffer), + call); + break; + case PRI_SWITCH_QSIG: + end = enc_qsig_diverting_leg_information2(ctrl, buffer, buffer + sizeof(buffer), + call); + break; + default: + return -1; + } if (!end) { return -1; } @@ -488,6 +1045,128 @@ static int rose_diverting_leg_information2_encode(struct pri *ctrl, q931_call *c return pri_call_apdu_queue(call, Q931_SETUP, buffer, end - buffer, NULL, NULL); } +/*! + * \internal + * \brief Encode the Q.SIG DivertingLegInformation3 invoke message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode the facility ie contents. + * \param end End of facility ie contents encoding data buffer. + * \param call Call leg from which to encode diversion leg 3. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *enc_qsig_diverting_leg_information3(struct pri *ctrl, + unsigned char *pos, unsigned char *end, q931_call *call) +{ + struct fac_extension_header header; + struct rose_msg_invoke msg; + + memset(&header, 0, sizeof(header)); + header.nfe_present = 1; + header.nfe.source_entity = 0; /* endPINX */ + header.nfe.destination_entity = 0; /* endPINX */ + header.interpretation_present = 1; + header.interpretation = 0; /* discardAnyUnrecognisedInvokePdu */ + pos = facility_encode_header(ctrl, pos, end, &header); + if (!pos) { + return NULL; + } + + memset(&msg, 0, sizeof(msg)); + msg.operation = ROSE_QSIG_DivertingLegInformation3; + msg.invoke_id = get_invokeid(ctrl); + + /* redirecting.to.number.presentation also indicates if name presentation is allowed */ + if ((call->redirecting.to.number.presentation & PRI_PRES_RESTRICTION) == PRI_PRES_ALLOWED) { + msg.args.qsig.DivertingLegInformation3.presentation_allowed_indicator = 1; /* TRUE */ + + /* redirectionName is the redirecting.to.name */ + if (call->redirecting.to.name.valid) { + msg.args.qsig.DivertingLegInformation3.redirection_name_present = 1; + q931_copy_name_to_rose(ctrl, + &msg.args.qsig.DivertingLegInformation3.redirection_name, + &call->redirecting.to.name); + } + } + + pos = rose_encode_invoke(ctrl, pos, end, &msg); + + return pos; +} + +/*! + * \internal + * \brief Encode the ETSI DivertingLegInformation3 invoke message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode the facility ie contents. + * \param end End of facility ie contents encoding data buffer. + * \param call Call leg from which to encode diversion leg 3. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *enc_etsi_diverting_leg_information3(struct pri *ctrl, + unsigned char *pos, unsigned char *end, q931_call *call) +{ + struct rose_msg_invoke msg; + + pos = facility_encode_header(ctrl, pos, end, NULL); + if (!pos) { + return NULL; + } + + memset(&msg, 0, sizeof(msg)); + msg.operation = ROSE_ETSI_DivertingLegInformation3; + msg.invoke_id = get_invokeid(ctrl); + + if ((call->redirecting.to.number.presentation & PRI_PRES_RESTRICTION) == PRI_PRES_ALLOWED) { + msg.args.etsi.DivertingLegInformation3.presentation_allowed_indicator = 1; /* TRUE */ + } + + pos = rose_encode_invoke(ctrl, pos, end, &msg); + + return pos; +} + +/*! + * \brief Encode and queue the DivertingLegInformation3 invoke message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param call Call leg from which to encode diversion leg 3. + * \param messagetype Q.931 message type to add facility ie to. + * + * \retval 0 on success. + * \retval -1 on error. + */ +int rose_diverting_leg_information3_encode(struct pri *ctrl, q931_call *call, + int messagetype) +{ + unsigned char buffer[256]; + unsigned char *end; + + switch (ctrl->switchtype) { + case PRI_SWITCH_EUROISDN_E1: + case PRI_SWITCH_EUROISDN_T1: + end = enc_etsi_diverting_leg_information3(ctrl, buffer, buffer + sizeof(buffer), + call); + break; + case PRI_SWITCH_QSIG: + end = enc_qsig_diverting_leg_information3(ctrl, buffer, buffer + sizeof(buffer), + call); + break; + default: + return -1; + } + if (!end) { + return -1; + } + + return pri_call_apdu_queue(call, messagetype, buffer, end - buffer, NULL, NULL); +} + /*! * \internal * \brief Encode the rltThirdParty invoke message. @@ -663,13 +1342,13 @@ static unsigned char *enc_ni2_information_following(struct pri *ctrl, unsigned c * \param ctrl D channel controller for diagnostic messages or global options. * \param pos Starting position to encode the facility ie contents. * \param end End of facility ie contents encoding data buffer. - * \param call Call leg from which to encode name. + * \param name Name data which to encode name. * * \retval Start of the next ASN.1 component to encode on success. * \retval NULL on error. */ static unsigned char *enc_qsig_calling_name(struct pri *ctrl, unsigned char *pos, - unsigned char *end, q931_call *call) + unsigned char *end, const struct q931_party_name *name) { struct fac_extension_header header; struct rose_msg_invoke msg; @@ -692,13 +1371,7 @@ static unsigned char *enc_qsig_calling_name(struct pri *ctrl, unsigned char *pos msg.invoke_id = get_invokeid(ctrl); /* CallingName */ - msg.args.qsig.CallingName.name.presentation = 1; /* presentation_allowed */ - msg.args.qsig.CallingName.name.char_set = 1; /* iso8859-1 */ - /* Truncate the callername if necessary. */ - libpri_copy_string((char *) msg.args.qsig.CallingName.name.data, call->callername, - sizeof(msg.args.qsig.CallingName.name.data)); - msg.args.qsig.CallingName.name.length = - strlen((char *) msg.args.qsig.CallingName.name.data); + q931_copy_name_to_rose(ctrl, &msg.args.qsig.CallingName.name, name); pos = rose_encode_invoke(ctrl, pos, end, &msg); @@ -724,7 +1397,7 @@ static int add_callername_facility_ies(struct pri *ctrl, q931_call *call, int cp unsigned char *end; int mymessage; - if (!call->callername[0]) { + if (!call->local_id.name.valid) { return 0; } @@ -744,7 +1417,9 @@ static int add_callername_facility_ies(struct pri *ctrl, q931_call *call, int cp */ } - end = enc_qsig_calling_name(ctrl, buffer, buffer + sizeof(buffer), call); + /* CallingName is the local_id.name */ + end = enc_qsig_calling_name(ctrl, buffer, buffer + sizeof(buffer), + &call->local_id.name); if (!end) { return -1; } @@ -798,11 +1473,14 @@ static unsigned char *enc_qsig_mwi_activate_message(struct pri *ctrl, unsigned c msg.operation = ROSE_QSIG_MWIActivate; msg.invoke_id = get_invokeid(ctrl); + /* The called.number is the served user */ + q931_copy_number_to_rose(ctrl, &msg.args.qsig.MWIActivate.served_user_number, + &req->called.number); + /* + * For now, we will just force the numbering plan to unknown to preserve + * the original behaviour. + */ msg.args.qsig.MWIActivate.served_user_number.plan = 0; /* unknown */ - libpri_copy_string((char *) msg.args.qsig.MWIActivate.served_user_number.str, - req->called, sizeof(msg.args.qsig.MWIActivate.served_user_number.str)); - msg.args.qsig.MWIActivate.served_user_number.length = strlen((char *) - msg.args.qsig.MWIActivate.served_user_number.str); msg.args.qsig.MWIActivate.basic_service = 1; /* speech */ @@ -844,11 +1522,14 @@ static unsigned char *enc_qsig_mwi_deactivate_message(struct pri *ctrl, msg.operation = ROSE_QSIG_MWIDeactivate; msg.invoke_id = get_invokeid(ctrl); + /* The called.number is the served user */ + q931_copy_number_to_rose(ctrl, &msg.args.qsig.MWIDeactivate.served_user_number, + &req->called.number); + /* + * For now, we will just force the numbering plan to unknown to preserve + * the original behaviour. + */ msg.args.qsig.MWIDeactivate.served_user_number.plan = 0; /* unknown */ - libpri_copy_string((char *) msg.args.qsig.MWIDeactivate.served_user_number.str, - req->called, sizeof(msg.args.qsig.MWIDeactivate.served_user_number.str)); - msg.args.qsig.MWIDeactivate.served_user_number.length = strlen((char *) - msg.args.qsig.MWIDeactivate.served_user_number.str); msg.args.qsig.MWIDeactivate.basic_service = 1; /* speech */ @@ -873,7 +1554,7 @@ int mwi_message_send(struct pri *ctrl, q931_call *call, struct pri_sr *req, int unsigned char buffer[255]; unsigned char *end; - if (!req->called || !req->called[0]) { + if (!req->called.number.valid || !req->called.number.str[0]) { return -1; } @@ -1084,9 +1765,14 @@ int qsig_cf_callrerouting(struct pri *ctrl, q931_call *call, const char *dest, unsigned char *end; int res; + /* + * We are deflecting an incoming call back to the network. + * Therefore, the Caller-ID is the remote party. + */ end = - enc_qsig_call_rerouting(ctrl, buffer, buffer + sizeof(buffer), call->callernum, - dest, original ? original : call->callednum, reason); + enc_qsig_call_rerouting(ctrl, buffer, buffer + sizeof(buffer), + call->remote_id.number.str, dest, original ? original : + call->called.number.str, reason); if (!end) { return -1; } @@ -1330,8 +2016,277 @@ static int aoc_aoce_charging_unit_encode(struct pri *ctrl, q931_call *call, /* ===== Call Transfer Supplementary Service (ECMA-178) ===== */ +/*! + * \internal + * \brief Encode the Q.SIG CallTransferComplete invoke message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode the facility ie contents. + * \param end End of facility ie contents encoding data buffer. + * \param call Call leg from which to encode call transfer. + * \param call_status TRUE if call is alerting. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *enc_qsig_call_transfer_complete(struct pri *ctrl, + unsigned char *pos, unsigned char *end, q931_call *call, int call_status) +{ + struct fac_extension_header header; + struct rose_msg_invoke msg; + + memset(&header, 0, sizeof(header)); + header.nfe_present = 1; + header.nfe.source_entity = 0; /* endPINX */ + header.nfe.destination_entity = 0; /* endPINX */ + header.interpretation_present = 1; + header.interpretation = 0; /* discardAnyUnrecognisedInvokePdu */ + pos = facility_encode_header(ctrl, pos, end, &header); + if (!pos) { + return NULL; + } + + memset(&msg, 0, sizeof(msg)); + msg.operation = ROSE_QSIG_CallTransferComplete; + msg.invoke_id = get_invokeid(ctrl); + msg.args.qsig.CallTransferComplete.end_designation = 0; /* primaryEnd */ + + /* redirectionNumber is the local_id.number */ + q931_copy_presented_number_screened_to_rose(ctrl, + &msg.args.qsig.CallTransferComplete.redirection, &call->local_id.number); + + /* redirectionName is the local_id.name */ + if (call->local_id.name.valid) { + msg.args.qsig.CallTransferComplete.redirection_name_present = 1; + q931_copy_name_to_rose(ctrl, + &msg.args.qsig.CallTransferComplete.redirection_name, + &call->local_id.name); + } + + if (call_status) { + msg.args.qsig.CallTransferComplete.call_status = 1; /* alerting */ + } + pos = rose_encode_invoke(ctrl, pos, end, &msg); + + return pos; +} + +/*! + * \internal + * \brief Encode the ETSI EctInform invoke message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode the facility ie contents. + * \param end End of facility ie contents encoding data buffer. + * \param call Call leg from which to encode inform message. + * \param call_status TRUE if call is alerting. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *enc_etsi_ect_inform(struct pri *ctrl, unsigned char *pos, + unsigned char *end, q931_call *call, int call_status) +{ + struct rose_msg_invoke msg; + + pos = facility_encode_header(ctrl, pos, end, NULL); + if (!pos) { + return NULL; + } + + memset(&msg, 0, sizeof(msg)); + msg.operation = ROSE_ETSI_EctInform; + msg.invoke_id = get_invokeid(ctrl); + + if (!call_status) { + msg.args.etsi.EctInform.status = 1;/* active */ + + /* + * EctInform(active) contains the redirectionNumber + * redirectionNumber is the local_id.number + */ + msg.args.etsi.EctInform.redirection_present = 1; + q931_copy_presented_number_unscreened_to_rose(ctrl, + &msg.args.etsi.EctInform.redirection, &call->local_id.number); + } + + pos = rose_encode_invoke(ctrl, pos, end, &msg); + + return pos; +} + +/*! + * \internal + * \brief Encode and queue the CallTransferComplete/EctInform invoke message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param call Call leg from which to encode call transfer. + * \param call_status TRUE if call is alerting. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int rose_call_transfer_complete_encode(struct pri *ctrl, q931_call *call, + int call_status) +{ + unsigned char buffer[256]; + unsigned char *end; + + switch (ctrl->switchtype) { + case PRI_SWITCH_EUROISDN_E1: + case PRI_SWITCH_EUROISDN_T1: + end = + enc_etsi_ect_inform(ctrl, buffer, buffer + sizeof(buffer), call, call_status); + break; + case PRI_SWITCH_QSIG: + end = + enc_qsig_call_transfer_complete(ctrl, buffer, buffer + sizeof(buffer), call, + call_status); + break; + default: + return -1; + } + if (!end) { + return -1; + } + + return pri_call_apdu_queue(call, Q931_FACILITY, buffer, end - buffer, NULL, NULL); +} + /* ===== End Call Transfer Supplementary Service (ECMA-178) ===== */ +/*! + * \internal + * \brief Encode the Q.SIG CalledName invoke message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode the facility ie contents. + * \param end End of facility ie contents encoding data buffer. + * \param name Name data which to encode name. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *enc_qsig_called_name(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const struct q931_party_name *name) +{ + struct fac_extension_header header; + struct rose_msg_invoke msg; + + memset(&header, 0, sizeof(header)); + header.nfe_present = 1; + header.nfe.source_entity = 0; /* endPINX */ + header.nfe.destination_entity = 0; /* endPINX */ + header.interpretation_present = 1; + header.interpretation = 0; /* discardAnyUnrecognisedInvokePdu */ + pos = facility_encode_header(ctrl, pos, end, &header); + if (!pos) { + return NULL; + } + + memset(&msg, 0, sizeof(msg)); + msg.operation = ROSE_QSIG_CalledName; + msg.invoke_id = get_invokeid(ctrl); + + /* CalledName */ + q931_copy_name_to_rose(ctrl, &msg.args.qsig.CalledName.name, name); + + pos = rose_encode_invoke(ctrl, pos, end, &msg); + + return pos; +} + +/*! + * \internal + * \brief Encode and queue the Q.SIG CalledName invoke message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param call Call leg from which to encode name. + * + * \retval 0 on success. + * \retval -1 on error. + */ +int rose_called_name_encode(struct pri *ctrl, q931_call *call, int messagetype) +{ + unsigned char buffer[256]; + unsigned char *end; + + /* CalledName is the local_id.name */ + end = enc_qsig_called_name(ctrl, buffer, buffer + sizeof(buffer), + &call->local_id.name); + if (!end) { + return -1; + } + + return pri_call_apdu_queue(call, messagetype, buffer, end - buffer, NULL, NULL); +} + +/*! + * \internal + * \brief Encode the Q.SIG ConnectedName invoke message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode the facility ie contents. + * \param end End of facility ie contents encoding data buffer. + * \param name Name data which to encode name. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *enc_qsig_connected_name(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const struct q931_party_name *name) +{ + struct fac_extension_header header; + struct rose_msg_invoke msg; + + memset(&header, 0, sizeof(header)); + header.nfe_present = 1; + header.nfe.source_entity = 0; /* endPINX */ + header.nfe.destination_entity = 0; /* endPINX */ + header.interpretation_present = 1; + header.interpretation = 0; /* discardAnyUnrecognisedInvokePdu */ + pos = facility_encode_header(ctrl, pos, end, &header); + if (!pos) { + return NULL; + } + + memset(&msg, 0, sizeof(msg)); + msg.operation = ROSE_QSIG_ConnectedName; + msg.invoke_id = get_invokeid(ctrl); + + /* ConnectedName */ + q931_copy_name_to_rose(ctrl, &msg.args.qsig.ConnectedName.name, name); + + pos = rose_encode_invoke(ctrl, pos, end, &msg); + + return pos; +} + +/*! + * \internal + * \brief Encode and queue the Q.SIG ConnectedName invoke message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param call Call leg from which to encode name. + * + * \retval 0 on success. + * \retval -1 on error. + */ +int rose_connected_name_encode(struct pri *ctrl, q931_call *call, int messagetype) +{ + unsigned char buffer[256]; + unsigned char *end; + + /* ConnectedName is the local_id.name */ + end = enc_qsig_connected_name(ctrl, buffer, buffer + sizeof(buffer), + &call->local_id.name); + if (!end) { + return -1; + } + + return pri_call_apdu_queue(call, messagetype, buffer, end - buffer, NULL, NULL); +} + /*! * \brief Put the APDU on the call queue. * @@ -1403,10 +2358,33 @@ int pri_call_add_standard_apdus(struct pri *ctrl, q931_call *call) } switch (ctrl->switchtype) { + case PRI_SWITCH_EUROISDN_E1: + case PRI_SWITCH_EUROISDN_T1: + if (q931_is_ptmp(ctrl)) { + /* PTMP mode */ + break; + } + /* PTP mode */ + if (call->redirecting.count) { + rose_diverting_leg_information2_encode(ctrl, call); + + /* + * Expect a DivertingLegInformation3 to update the COLR of the + * redirecting-to party we are attempting to call now. + */ + call->redirecting.state = Q931_REDIRECTING_STATE_EXPECTING_RX_DIV_LEG_3; + } + break; case PRI_SWITCH_QSIG: /* For Q.SIG it does network and cpe operations */ - if (call->redirectingnum[0]) { + if (call->redirecting.count) { rose_diverting_leg_information2_encode(ctrl, call); + + /* + * Expect a DivertingLegInformation3 to update the COLR of the + * redirecting-to party we are attempting to call now. + */ + call->redirecting.state = Q931_REDIRECTING_STATE_EXPECTING_RX_DIV_LEG_3; } add_callername_facility_ies(ctrl, call, 1); break; @@ -1425,6 +2403,28 @@ int pri_call_add_standard_apdus(struct pri *ctrl, q931_call *call) return 0; } +/*! + * \brief Send the CallTransferComplete/EctInform invoke message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param call Call leg from which to encode call transfer. + * \param call_status TRUE if call is alerting. + * + * \retval 0 on success. + * \retval -1 on error. + */ +int send_call_transfer_complete(struct pri *ctrl, q931_call *call, int call_status) +{ + if (rose_call_transfer_complete_encode(ctrl, call, call_status) + || q931_facility(ctrl, call)) { + pri_message(ctrl, + "Could not schedule facility message for call transfer completed.\n"); + return -1; + } + + return 0; +} + /*! * \brief Handle the ROSE reject message. * @@ -1540,10 +2540,34 @@ void rose_handle_result(struct pri *ctrl, q931_call *call, q931_ie *ie, */ break; #endif /* Not handled yet */ +#if 0 /* Not handled yet */ + case ROSE_ETSI_ActivationDiversion: + break; + case ROSE_ETSI_DeactivationDiversion: + break; + case ROSE_ETSI_InterrogationDiversion: + break; + case ROSE_ETSI_CallDeflection: + break; + case ROSE_ETSI_CallRerouting: + break; + case ROSE_ETSI_InterrogateServedUserNumbers: + break; +#endif /* Not handled yet */ #if 0 /* Not handled yet */ case ROSE_ETSI_ChargingRequest: break; #endif /* Not handled yet */ +#if 0 /* Not handled yet */ + case ROSE_ETSI_EctExecute: + break; + case ROSE_ETSI_ExplicitEctExecute: + break; + case ROSE_ETSI_EctLinkIdRequest: + break; + case ROSE_ETSI_EctLoopTest: + break; +#endif /* Not handled yet */ #if 0 /* Not handled yet */ case ROSE_QSIG_ChargeRequest: break; @@ -1604,7 +2628,115 @@ void rose_handle_result(struct pri *ctrl, q931_call *call, q931_ie *ie, void rose_handle_invoke(struct pri *ctrl, q931_call *call, q931_ie *ie, const struct fac_extension_header *header, const struct rose_msg_invoke *invoke) { + struct pri_subcommand *subcmd; + struct q931_party_id party_id; + switch (invoke->operation) { +#if 0 /* Not handled yet */ + case ROSE_ETSI_ActivationDiversion: + break; + case ROSE_ETSI_DeactivationDiversion: + break; + case ROSE_ETSI_ActivationStatusNotificationDiv: + break; + case ROSE_ETSI_DeactivationStatusNotificationDiv: + break; + case ROSE_ETSI_InterrogationDiversion: + break; + case ROSE_ETSI_DiversionInformation: + break; + case ROSE_ETSI_CallDeflection: + break; + case ROSE_ETSI_CallRerouting: + break; + case ROSE_ETSI_InterrogateServedUserNumbers: + break; +#endif /* Not handled yet */ + case ROSE_ETSI_DivertingLegInformation1: + /* divertedToNumber is put in redirecting.to.number */ + switch (invoke->args.etsi.DivertingLegInformation1.subscription_option) { + default: + case 0: /* noNotification */ + case 1: /* notificationWithoutDivertedToNr */ + q931_party_number_init(&call->redirecting.to.number); + call->redirecting.to.number.valid = 1; + call->redirecting.to.number.presentation = + PRI_PRES_RESTRICTED | PRI_PRES_USER_NUMBER_UNSCREENED; + break; + case 2: /* notificationWithDivertedToNr */ + if (invoke->args.etsi.DivertingLegInformation1.diverted_to_present) { + rose_copy_presented_number_unscreened_to_q931(ctrl, + &call->redirecting.to.number, + &invoke->args.etsi.DivertingLegInformation1.diverted_to); + } else { + q931_party_number_init(&call->redirecting.to.number); + call->redirecting.to.number.valid = 1; + } + break; + } + + call->redirecting.reason = redirectingreason_for_q931(ctrl, + invoke->args.etsi.DivertingLegInformation1.diversion_reason); + if (call->redirecting.count < PRI_MAX_REDIRECTS) { + ++call->redirecting.count; + } + call->redirecting.state = Q931_REDIRECTING_STATE_EXPECTING_RX_DIV_LEG_3; + break; + case ROSE_ETSI_DivertingLegInformation2: + call->redirecting.state = Q931_REDIRECTING_STATE_PENDING_TX_DIV_LEG_3; + call->redirecting.count = + invoke->args.etsi.DivertingLegInformation2.diversion_counter; + if (!call->redirecting.count) { + /* To be safe, make sure that the count is non-zero. */ + call->redirecting.count = 1; + } + call->redirecting.reason = redirectingreason_for_q931(ctrl, + invoke->args.etsi.DivertingLegInformation2.diversion_reason); + + /* divertingNr is put in redirecting.from.number */ + if (invoke->args.etsi.DivertingLegInformation2.diverting_present) { + rose_copy_presented_number_unscreened_to_q931(ctrl, + &call->redirecting.from.number, + &invoke->args.etsi.DivertingLegInformation2.diverting); + } else { + q931_party_number_init(&call->redirecting.from.number); + call->redirecting.from.number.valid = 1; + } + + call->redirecting.orig_reason = PRI_REDIR_UNKNOWN; + + /* originalCalledNr is put in redirecting.orig_called.number */ + if (invoke->args.etsi.DivertingLegInformation2.original_called_present) { + rose_copy_presented_number_unscreened_to_q931(ctrl, + &call->redirecting.orig_called.number, + &invoke->args.etsi.DivertingLegInformation2.original_called); + } else { + q931_party_number_init(&call->redirecting.orig_called.number); + } + break; + case ROSE_ETSI_DivertingLegInformation3: + if (!invoke->args.etsi.DivertingLegInformation3.presentation_allowed_indicator) { + call->redirecting.to.number.presentation = + PRI_PRES_RESTRICTED | PRI_PRES_USER_NUMBER_UNSCREENED; + } + + switch (call->redirecting.state) { + case Q931_REDIRECTING_STATE_EXPECTING_RX_DIV_LEG_3: + call->redirecting.state = Q931_REDIRECTING_STATE_IDLE; + subcmd = q931_alloc_subcommand(ctrl); + if (!subcmd) { + pri_error(ctrl, "ERROR: Too many facility subcommands\n"); + break; + } + /* Setup redirecting subcommand */ + subcmd->cmd = PRI_SUBCMD_REDIRECTING; + q931_party_redirecting_copy_to_pri(&subcmd->u.redirecting, + &call->redirecting); + break; + default: + break; + } + break; case ROSE_ETSI_ChargingRequest: /* Ignore messsage */ break; @@ -1647,20 +2779,63 @@ void rose_handle_invoke(struct pri *ctrl, q931_call *call, q931_ie *ie, case ROSE_ITU_IdentificationOfCharge: break; #endif /* Not handled yet */ - case ROSE_QSIG_CallingName: - if (invoke->args.qsig.CallingName.name.presentation == 1) { - libpri_copy_string(call->callername, - (char *) invoke->args.qsig.CallingName.name.data, - sizeof(call->callername)); +#if 0 /* Not handled yet */ + case ROSE_ETSI_EctExecute: + break; + case ROSE_ETSI_ExplicitEctExecute: + break; +#endif /* Not handled yet */ + case ROSE_ETSI_RequestSubaddress: + /* Ignore since we are not handling subaddresses yet. */ + break; +#if 0 /* Not handled yet */ + case ROSE_ETSI_SubaddressTransfer: + break; + case ROSE_ETSI_EctLinkIdRequest: + break; +#endif /* Not handled yet */ + case ROSE_ETSI_EctInform: + /* redirectionNumber is put in remote_id.number */ + if (invoke->args.etsi.EctInform.redirection_present) { + rose_copy_presented_number_unscreened_to_q931(ctrl, + &call->remote_id.number, &invoke->args.etsi.EctInform.redirection); + } + if (!invoke->args.etsi.EctInform.status) { + /* The remote party for the transfer has not answered yet. */ + call->incoming_ct_state = INCOMING_CT_STATE_EXPECT_CT_ACTIVE; } else { - call->callername[0] = '\0'; + call->incoming_ct_state = INCOMING_CT_STATE_POST_CONNECTED_LINE; } break; #if 0 /* Not handled yet */ + case ROSE_ETSI_EctLoopTest: + break; +#endif /* Not handled yet */ + case ROSE_QSIG_CallingName: + /* CallingName is put in remote_id.name */ + rose_copy_name_to_q931(ctrl, &call->remote_id.name, + &invoke->args.qsig.CallingName.name); + break; case ROSE_QSIG_CalledName: + /* CalledName is put in remote_id.name */ + rose_copy_name_to_q931(ctrl, &call->remote_id.name, + &invoke->args.qsig.CalledName.name); + + /* Setup connected line subcommand */ + subcmd = q931_alloc_subcommand(ctrl); + if (!subcmd) { + pri_error(ctrl, "ERROR: Too many facility subcommands\n"); + break; + } + subcmd->cmd = PRI_SUBCMD_CONNECTED_LINE; + q931_party_id_copy_to_pri(&subcmd->u.connected_line.id, &call->remote_id); break; case ROSE_QSIG_ConnectedName: + /* ConnectedName is put in remote_id.name */ + rose_copy_name_to_q931(ctrl, &call->remote_id.name, + &invoke->args.qsig.ConnectedName.name); break; +#if 0 /* Not handled yet */ case ROSE_QSIG_BusyName: break; #endif /* Not handled yet */ @@ -1689,65 +2864,63 @@ void rose_handle_invoke(struct pri *ctrl, q931_call *call, q931_ie *ie, break; case ROSE_QSIG_CallTransferSetup: break; - case ROSE_QSIG_CallTransferActive: - break; #endif /* Not handled yet */ -#if 0 /* This was incomplete */ + case ROSE_QSIG_CallTransferActive: + call->incoming_ct_state = INCOMING_CT_STATE_POST_CONNECTED_LINE; + + /* connectedAddress is put in remote_id.number */ + rose_copy_presented_address_screened_to_q931(ctrl, &call->remote_id.number, + &invoke->args.qsig.CallTransferActive.connected); + + /* connectedName is put in remote_id.name */ + if (invoke->args.qsig.CallTransferActive.connected_name_present) { + rose_copy_name_to_q931(ctrl, &call->remote_id.name, + &invoke->args.qsig.CallTransferActive.connected_name); + } + break; case ROSE_QSIG_CallTransferComplete: - switch (invoke->args.qsig.CallTransferComplete.redirection.presentation) { - case 0: /* presentationAllowedNumber */ - case 3: /* presentationRestrictedNumber */ - libpri_copy_string(call->callernum, (char *) - invoke->args.qsig.CallTransferComplete.redirection.screened.number.str, - sizeof(call->callernum)); - break; - default: - call->callernum[0] = '\0'; - break; - } - call->callername[0] = '\0'; + /* redirectionNumber is put in remote_id.number */ + rose_copy_presented_number_screened_to_q931(ctrl, &call->remote_id.number, + &invoke->args.qsig.CallTransferComplete.redirection); + + /* redirectionName is put in remote_id.name */ if (invoke->args.qsig.CallTransferComplete.redirection_name_present) { - switch (invoke->args.qsig.CallTransferComplete.redirection_name.presentation) { - case 1: /* presentation_allowed */ - case 2: /* presentation_restricted */ - libpri_copy_string(call->callername, - (char *) invoke->args.qsig.CallTransferComplete.redirection_name. - data, sizeof(call->callername)); - break; - default: - break; - } + rose_copy_name_to_q931(ctrl, &call->remote_id.name, + &invoke->args.qsig.CallTransferComplete.redirection_name); + } + + if (invoke->args.qsig.CallTransferComplete.call_status == 1) { + /* The remote party for the transfer has not answered yet. */ + call->incoming_ct_state = INCOMING_CT_STATE_EXPECT_CT_ACTIVE; + } else { + call->incoming_ct_state = INCOMING_CT_STATE_POST_CONNECTED_LINE; } break; -#endif /* This was incomplete */ -#if 0 /* This was incomplete */ case ROSE_QSIG_CallTransferUpdate: - switch (invoke->args.qsig.CallTransferUpdate.redirection.presentation) { - case 0: /* presentationAllowedNumber */ - case 3: /* presentationRestrictedNumber */ - libpri_copy_string(call->callernum, (char *) - invoke->args.qsig.CallTransferUpdate.redirection.screened.number.str, - sizeof(call->callernum)); - break; - default: - call->callernum[0] = '\0'; - break; - } - call->callername[0] = '\0'; + party_id = call->remote_id; + + /* redirectionNumber is put in party_id.number */ + rose_copy_presented_number_screened_to_q931(ctrl, &party_id.number, + &invoke->args.qsig.CallTransferUpdate.redirection); + + /* redirectionName is put in party_id.name */ if (invoke->args.qsig.CallTransferUpdate.redirection_name_present) { - switch (invoke->args.qsig.CallTransferUpdate.redirection_name.presentation) { - case 1: /* presentation_allowed */ - case 2: /* presentation_restricted */ - libpri_copy_string(call->callername, - (char *) invoke->args.qsig.CallTransferUpdate.redirection_name.data, - sizeof(call->callername)); + rose_copy_name_to_q931(ctrl, &party_id.name, + &invoke->args.qsig.CallTransferUpdate.redirection_name); + } + + if (q931_party_id_cmp(&party_id, &call->remote_id)) { + /* The remote_id data has changed. */ + call->remote_id = party_id; + switch (call->incoming_ct_state) { + case INCOMING_CT_STATE_IDLE: + call->incoming_ct_state = INCOMING_CT_STATE_POST_CONNECTED_LINE; break; default: break; } } break; -#endif /* This was incomplete */ #if 0 /* Not handled yet */ case ROSE_QSIG_SubaddressTransfer: break; @@ -1766,96 +2939,124 @@ void rose_handle_invoke(struct pri *ctrl, q931_call *call, q931_ie *ie, break; case ROSE_QSIG_CallRerouting: break; - case ROSE_QSIG_DivertingLegInformation1: - break; #endif /* Not handled yet */ + case ROSE_QSIG_DivertingLegInformation1: + /* nominatedNr is put in redirecting.to.number */ + switch (invoke->args.qsig.DivertingLegInformation1.subscription_option) { + default: + case QSIG_NO_NOTIFICATION: + case QSIG_NOTIFICATION_WITHOUT_DIVERTED_TO_NR: + q931_party_number_init(&call->redirecting.to.number); + call->redirecting.to.number.valid = 1; + call->redirecting.to.number.presentation = + PRI_PRES_RESTRICTED | PRI_PRES_USER_NUMBER_UNSCREENED; + break; + case QSIG_NOTIFICATION_WITH_DIVERTED_TO_NR: + q931_party_number_init(&call->redirecting.to.number); + call->redirecting.to.number.valid = 1; + rose_copy_number_to_q931(ctrl, &call->redirecting.to.number, + &invoke->args.qsig.DivertingLegInformation1.nominated_number); + if (call->redirecting.to.number.str[0]) { + call->redirecting.to.number.presentation = + PRI_PRES_ALLOWED | PRI_PRES_USER_NUMBER_UNSCREENED; + } + break; + } + + call->redirecting.reason = redirectingreason_for_q931(ctrl, + invoke->args.qsig.DivertingLegInformation1.diversion_reason); + if (call->redirecting.count < PRI_MAX_REDIRECTS) { + ++call->redirecting.count; + } + call->redirecting.state = Q931_REDIRECTING_STATE_EXPECTING_RX_DIV_LEG_3; + break; case ROSE_QSIG_DivertingLegInformation2: - call->origredirectingreason = QSIG_DIVERT_REASON_UNKNOWN; + call->redirecting.state = Q931_REDIRECTING_STATE_PENDING_TX_DIV_LEG_3; + call->redirecting.count = + invoke->args.qsig.DivertingLegInformation2.diversion_counter; + if (!call->redirecting.count) { + /* To be safe, make sure that the count is non-zero. */ + call->redirecting.count = 1; + } + call->redirecting.reason = redirectingreason_for_q931(ctrl, + invoke->args.qsig.DivertingLegInformation2.diversion_reason); + + /* divertingNr is put in redirecting.from.number */ + if (invoke->args.qsig.DivertingLegInformation2.diverting_present) { + rose_copy_presented_number_unscreened_to_q931(ctrl, + &call->redirecting.from.number, + &invoke->args.qsig.DivertingLegInformation2.diverting); + } else { + q931_party_number_init(&call->redirecting.from.number); + call->redirecting.from.number.valid = 1; + } + + /* redirectingName is put in redirecting.from.name */ + if (invoke->args.qsig.DivertingLegInformation2.redirecting_name_present) { + rose_copy_name_to_q931(ctrl, &call->redirecting.from.name, + &invoke->args.qsig.DivertingLegInformation2.redirecting_name); + } else { + q931_party_name_init(&call->redirecting.from.name); + } + + call->redirecting.orig_reason = PRI_REDIR_UNKNOWN; if (invoke->args.qsig.DivertingLegInformation2.original_diversion_reason_present) { - call->origredirectingreason = redirectingreason_for_q931(ctrl, + call->redirecting.orig_reason = redirectingreason_for_q931(ctrl, invoke->args.qsig.DivertingLegInformation2.original_diversion_reason); } - call->redirectingreason = redirectingreason_for_q931(ctrl, - invoke->args.qsig.DivertingLegInformation2.diversion_reason); - call->redirectingpres = PRI_PRES_UNAVAILABLE; - call->redirectingnum[0] = '\0'; - call->redirectingplan = (PRI_TON_UNKNOWN << 4) | PRI_NPI_E163_E164; - if (invoke->args.qsig.DivertingLegInformation2.diverting_present) { - call->redirectingpres = - presentation_for_q931(ctrl, - invoke->args.qsig.DivertingLegInformation2.diverting.presentation); - switch (invoke->args.qsig.DivertingLegInformation2.diverting.presentation) { - case 0: /* presentationAllowedNumber */ - case 3: /* presentationRestrictedNumber */ - libpri_copy_string(call->redirectingnum, (char *) - invoke->args.qsig.DivertingLegInformation2.diverting.number.str, - sizeof(call->redirectingnum)); - call->redirectingplan = - numbering_plan_for_q931(ctrl, - invoke->args.qsig.DivertingLegInformation2.diverting.number.plan) - | typeofnumber_for_q931(ctrl, - invoke->args.qsig.DivertingLegInformation2.diverting.number.ton); - break; - default: - break; - } - } + + /* originalCalledNr is put in redirecting.orig_called.number */ if (invoke->args.qsig.DivertingLegInformation2.original_called_present) { - call->origcalledpres = - presentation_for_q931(ctrl, - invoke->args.qsig.DivertingLegInformation2.original_called. - presentation); - switch (invoke->args.qsig.DivertingLegInformation2.original_called. - presentation) { - case 0: /* presentationAllowedNumber */ - case 3: /* presentationRestrictedNumber */ - libpri_copy_string(call->origcallednum, (char *) - invoke->args.qsig.DivertingLegInformation2.original_called.number. - str, sizeof(call->origcallednum)); - call->origcalledplan = - numbering_plan_for_q931(ctrl, - invoke->args.qsig.DivertingLegInformation2.original_called. - number.plan) - | typeofnumber_for_q931(ctrl, - invoke->args.qsig.DivertingLegInformation2.original_called. - number.ton); - break; - default: - call->origcallednum[0] = '\0'; - call->origcalledplan = (PRI_TON_UNKNOWN << 4) | PRI_NPI_E163_E164; - break; - } + rose_copy_presented_number_unscreened_to_q931(ctrl, + &call->redirecting.orig_called.number, + &invoke->args.qsig.DivertingLegInformation2.original_called); + } else { + q931_party_number_init(&call->redirecting.orig_called.number); } - call->redirectingname[0] = '\0'; - if (invoke->args.qsig.DivertingLegInformation2.redirecting_name_present) { - switch (invoke->args.qsig.DivertingLegInformation2.redirecting_name. - presentation) { - case 1: /* presentation_allowed */ - libpri_copy_string(call->redirectingname, (char *) - invoke->args.qsig.DivertingLegInformation2.redirecting_name.data, - sizeof(call->redirectingname)); - break; - default: - break; - } - } - call->origcalledname[0] = '\0'; + + /* originalCalledName is put in redirecting.orig_called.name */ if (invoke->args.qsig.DivertingLegInformation2.original_called_name_present) { - switch (invoke->args.qsig.DivertingLegInformation2.original_called_name. - presentation) { - case 1: /* presentation_allowed */ - libpri_copy_string(call->origcalledname, (char *) - invoke->args.qsig.DivertingLegInformation2.original_called_name.data, - sizeof(call->origcalledname)); - break; - default: + rose_copy_name_to_q931(ctrl, &call->redirecting.orig_called.name, + &invoke->args.qsig.DivertingLegInformation2.original_called_name); + } else { + q931_party_name_init(&call->redirecting.orig_called.name); + } + break; + case ROSE_QSIG_DivertingLegInformation3: + if (!invoke->args.qsig.DivertingLegInformation3.presentation_allowed_indicator) { + call->redirecting.to.number.presentation = + PRI_PRES_RESTRICTED | PRI_PRES_USER_NUMBER_UNSCREENED; + } + + /* redirectionName is put in redirecting.to.name */ + if (invoke->args.qsig.DivertingLegInformation3.redirection_name_present) { + rose_copy_name_to_q931(ctrl, &call->redirecting.to.name, + &invoke->args.qsig.DivertingLegInformation3.redirection_name); + if (!invoke->args.qsig.DivertingLegInformation3.presentation_allowed_indicator) { + call->redirecting.to.name.presentation = PRI_PRES_RESTRICTED; + } + } else { + q931_party_name_init(&call->redirecting.to.name); + } + + switch (call->redirecting.state) { + case Q931_REDIRECTING_STATE_EXPECTING_RX_DIV_LEG_3: + call->redirecting.state = Q931_REDIRECTING_STATE_IDLE; + subcmd = q931_alloc_subcommand(ctrl); + if (!subcmd) { + pri_error(ctrl, "ERROR: Too many facility subcommands\n"); break; } + /* Setup redirecting subcommand */ + subcmd->cmd = PRI_SUBCMD_REDIRECTING; + q931_party_redirecting_copy_to_pri(&subcmd->u.redirecting, + &call->redirecting); + break; + default: + break; } break; #if 0 /* Not handled yet */ - case ROSE_QSIG_DivertingLegInformation3: - break; case ROSE_QSIG_CfnrDivertedLegFailed: break; #endif /* Not handled yet */ diff --git a/pri_facility.h b/pri_facility.h index 9364ce3..1decfcf 100644 --- a/pri_facility.h +++ b/pri_facility.h @@ -60,6 +60,11 @@ #define Q932_TON_SUBSCRIBER 0x04 #define Q932_TON_ABBREVIATED 0x06 +/* Q.SIG Subscription Option. Listed in ECMA-174 */ +#define QSIG_NO_NOTIFICATION 0x00 +#define QSIG_NOTIFICATION_WITHOUT_DIVERTED_TO_NR 0x01 +#define QSIG_NOTIFICATION_WITH_DIVERTED_TO_NR 0x02 + /* Queues an MWI apdu on a the given call */ int mwi_message_send(struct pri *pri, q931_call *call, struct pri_sr *req, int activate); @@ -73,6 +78,14 @@ int qsig_cf_callrerouting(struct pri *pri, q931_call *c, const char* dest, const /* starts a QSIG Path Replacement */ int anfpr_initiate_transfer(struct pri *pri, q931_call *c1, q931_call *c2); +int send_call_transfer_complete(struct pri *pri, q931_call *call, int call_status); + +int rose_diverting_leg_information1_encode(struct pri *pri, q931_call *call); +int rose_diverting_leg_information3_encode(struct pri *pri, q931_call *call, int messagetype); + +int rose_connected_name_encode(struct pri *pri, q931_call *call, int messagetype); +int rose_called_name_encode(struct pri *pri, q931_call *call, int messagetype); + /* Use this function to queue a facility-IE born APDU onto a call * call is the call to use, messagetype is any one of the Q931 messages, * apdu is the apdu data, apdu_len is the length of the apdu data */ diff --git a/pri_internal.h b/pri_internal.h index 08d86d6..7fa36ff 100644 --- a/pri_internal.h +++ b/pri_internal.h @@ -104,6 +104,8 @@ struct pri { struct timeval tv; int schedev; pri_event ev; /* Static event thingy */ + /*! Subcommands for static event thingy. */ + struct pri_subcommands subcmds; /* Q.921 Re-transmission queue */ struct q921_frame *txqueue; @@ -133,24 +135,163 @@ struct pri { unsigned char sendfacility; }; +/*! \brief Maximum name length plus null terminator (From ECMA-164) */ +#define PRI_MAX_NAME_LEN (50 + 1) + +/*! \brief Q.SIG name information. */ +struct q931_party_name { + /*! \brief TRUE if name data is valid */ + unsigned char valid; + /*! + * \brief Q.931 presentation-indicator encoded field + * \note Must tollerate the Q.931 screening-indicator field values being present. + */ + unsigned char presentation; + /*! + * \brief Character set the name is using. + * \details + * unknown(0), + * iso8859-1(1), + * enum-value-withdrawn-by-ITU-T(2) + * iso8859-2(3), + * iso8859-3(4), + * iso8859-4(5), + * iso8859-5(6), + * iso8859-7(7), + * iso10646-BmpString(8), + * iso10646-utf-8String(9) + */ + unsigned char char_set; + /*! \brief Name data with null terminator. */ + char str[PRI_MAX_NAME_LEN]; +}; + +/*! \brief Maximum phone number (address) length plus null terminator */ +#define PRI_MAX_NUMBER_LEN (31 + 1) + +struct q931_party_number { + /*! \brief TRUE if number data is valid */ + unsigned char valid; + /*! \brief Q.931 presentation-indicator and screening-indicator encoded fields */ + unsigned char presentation; + /*! \brief Q.931 Type-Of-Number and numbering-plan encoded fields */ + unsigned char plan; + /*! \brief Number data with terminator. */ + char str[PRI_MAX_NUMBER_LEN]; +}; + +/*! \brief Maximum subaddress length plus null terminator */ +#define PRI_MAX_SUBADDRESS_LEN (20 + 1) + +#if defined(POSSIBLE_FUTURE_SUBADDRESS_SUPPORT) +struct q931_party_subaddress { + /*! \brief TRUE if the subaddress information is valid/present */ + unsigned char valid; + /*! + * \brief Subaddress type. + * \details + * nsap(0), + * user_specified(2) + */ + unsigned char type; + /*! + * \brief TRUE if odd number of address signals + * \note The odd/even indicator is used when the type of subaddress is + * user_specified and the coding is BCD. + */ + unsigned char odd_even_indicator; + /*! \brief Length of the subaddress data */ + unsigned char length; + /*! + * \brief Subaddress data with null terminator. + * \note The null terminator is a convenience only since the data could be + * BCD/binary and thus have a null byte as part of the contents. + */ + char data[PRI_MAX_SUBADDRESS_LEN]; +}; +#endif /* defined(POSSIBLE_FUTURE_SUBADDRESS_SUPPORT) */ + +struct q931_party_address { + /*! \brief Subscriber phone number */ + struct q931_party_number number; +#if defined(POSSIBLE_FUTURE_SUBADDRESS_SUPPORT) + /*! \brief Subscriber subaddress */ + struct q931_party_subaddress subaddress; +#endif /* defined(POSSIBLE_FUTURE_SUBADDRESS_SUPPORT) */ +}; + +/*! \brief Information needed to identify an endpoint in a call. */ +struct q931_party_id { + /*! \brief Subscriber name */ + struct q931_party_name name; + /*! \brief Subscriber phone number */ + struct q931_party_number number; +#if defined(POSSIBLE_FUTURE_SUBADDRESS_SUPPORT) + /*! \brief Subscriber subaddress */ + struct q931_party_subaddress subaddress; +#endif /* defined(POSSIBLE_FUTURE_SUBADDRESS_SUPPORT) */ +}; + +enum Q931_REDIRECTING_STATE { + /*! + * \details + * CDO-Idle/CDF-Inv-Idle + */ + Q931_REDIRECTING_STATE_IDLE, + /*! + * \details + * CDF-Inv-Wait - A DivLeg2 has been received and + * we are waiting for valid presentation restriction information to send. + */ + Q931_REDIRECTING_STATE_PENDING_TX_DIV_LEG_3, + /*! + * \details + * CDO-Divert - A DivLeg1 has been received and + * we are waiting for the presentation restriction information to come in. + */ + Q931_REDIRECTING_STATE_EXPECTING_RX_DIV_LEG_3, +}; + +/*! + * \brief Do not increment above this count. + * \details + * It is not our responsibility to enforce the maximum number of redirects. + * However, we cannot allow an increment past this number without breaking things. + * Besides, more than 255 redirects is probably not a good thing. + */ +#define PRI_MAX_REDIRECTS 0xFF + +/*! \brief Redirecting information struct */ +struct q931_party_redirecting { + enum Q931_REDIRECTING_STATE state; + /*! \brief Who is redirecting the call (Sent to the party the call is redirected toward) */ + struct q931_party_id from; + /*! \brief Call is redirecting to a new party (Sent to the caller) */ + struct q931_party_id to; + /*! Originally called party (in cases of multiple redirects) */ + struct q931_party_id orig_called; + /*! + * \brief Number of times the call was redirected + * \note The call is being redirected if the count is non-zero. + */ + unsigned char count; + /*! Original reason for redirect (in cases of multiple redirects) */ + unsigned char orig_reason; + /*! \brief Redirection reasons */ + unsigned char reason; +}; + /*! \brief New call setup parameter structure */ struct pri_sr { int transmode; int channel; int exclusive; int nonisdn; - char *caller; - int callerplan; - char *callername; - int callerpres; - char *called; - int calledplan; + struct q931_party_redirecting redirecting; + struct q931_party_id caller; + struct q931_party_address called; int userl1; int numcomplete; - char *redirectingnum; - int redirectingplan; - int redirectingpres; - int redirectingreason; int justsignalling; const char *useruserinfo; int transferable; @@ -171,8 +312,29 @@ struct apdu_event { struct apdu_event *next; /* Linked list pointer */ }; -/* q931_call datastructure */ +/*! \brief Incoming call transfer states. */ +enum INCOMING_CT_STATE { + /*! + * \details + * Incoming call transfer is not active. + */ + INCOMING_CT_STATE_IDLE, + /*! + * \details + * We have seen an incoming CallTransferComplete(alerting) + * so we are waiting for the expected CallTransferActive + * before updating the connected line about the remote party id. + */ + INCOMING_CT_STATE_EXPECT_CT_ACTIVE, + /*! + * \details + * A call transfer message came in that updated the remote party id + * that we need to post a connected line update. + */ + INCOMING_CT_STATE_POST_CONNECTED_LINE +}; +/* q931_call datastructure */ struct q931_call { struct pri *pri; /* PRI */ int cr; /* Call Reference */ @@ -204,8 +366,7 @@ struct q931_call { int userl2; int userl3; int rateadaption; - - int sentchannel; + int justsignalling; /* for a signalling-only connection */ int progcode; /* Progress coding */ @@ -213,7 +374,7 @@ struct q931_call { int progress; /* Progress indicator */ int progressmask; /* Progress Indicator bitmask */ - int notify; /* Notification */ + int notify; /* Notification indicator. */ int causecode; /* Cause Coding */ int causeloc; /* Cause Location */ @@ -222,49 +383,76 @@ struct q931_call { int peercallstate; /* Call state of peer as reported */ int ourcallstate; /* Our call state */ int sugcallstate; /* Status call state */ - - int callerplan; - int callerplanani; - int callerpres; /* Caller presentation */ - char callerani[256]; /* Caller */ - char callernum[256]; - char callername[256]; - - char keypad_digits[64]; /* Buffer for digits that come in KEYPAD_FACILITY */ int ani2; /* ANI II */ - - int calledplan; + + /*! Buffer for digits that come in KEYPAD_FACILITY */ + char keypad_digits[32 + 1]; + + /*! Current dialed digits to be sent or just received. */ + char overlap_digits[PRI_MAX_NUMBER_LEN]; + + /*! + * \brief Local party ID + * \details + * The Caller-ID and connected-line ID are just roles the local and remote party + * play while a call is being established. Which roll depends upon the direction + * of the call. + * Outgoing party info is to identify the local party to the other end. + * (Caller-ID for originated or connected-line for answered calls.) + * Incoming party info is to identify the remote party to us. + * (Caller-ID for answered or connected-line for originated calls.) + */ + struct q931_party_id local_id; + /*! + * \brief Remote party ID + * \details + * The Caller-ID and connected-line ID are just roles the local and remote party + * play while a call is being established. Which roll depends upon the direction + * of the call. + * Outgoing party info is to identify the local party to the other end. + * (Caller-ID for originated or connected-line for answered calls.) + * Incoming party info is to identify the remote party to us. + * (Caller-ID for answered or connected-line for originated calls.) + */ + struct q931_party_id remote_id; + + /*! + * \brief Staging place for the Q.931 redirection number ie. + * \note + * The number could be the remote_id.number or redirecting.to.number + * depending upon the notification indicator. + */ + struct q931_party_number redirection_number; + + /*! + * \brief Called party address. + * \note The called.number.str is the accumulated overlap dial digits + * and enbloc digits. + * \note The called.number.presentation value is not used. + */ + struct q931_party_address called; int nonisdn; - char callednum[256]; /* Called Number */ int complete; /* no more digits coming */ int newcall; /* if the received message has a new call reference value */ int retranstimer; /* Timer for retransmitting DISC */ int t308_timedout; /* Whether t308 timed out once */ - int redirectingplan; - int redirectingpres; - int redirectingreason; - char redirectingnum[256]; /* Number of redirecting party */ - char redirectingname[256]; /* Name of redirecting party */ + struct q931_party_redirecting redirecting; - /* Filled in cases of multiple diversions */ - int origcalledplan; - int origcalledpres; - int origredirectingreason; /* Original reason for redirect (in cases of multiple redirects) */ - char origcalledname[256]; /* Original name of person being called */ - char origcallednum[256]; /* Orignal number of person being called */ + /*! \brief Incoming call transfer state. */ + enum INCOMING_CT_STATE incoming_ct_state; int useruserprotocoldisc; char useruserinfo[256]; - char callingsubaddr[256]; /* Calling parties sub address */ + char callingsubaddr[PRI_MAX_SUBADDRESS_LEN]; /* Calling party subaddress */ long aoc_units; /* Advice of Charge Units */ struct apdu_event *apdus; /* APDU queue for call */ - int transferable; + int transferable; /* RLT call is transferable */ unsigned int rlt_call_id; /* RLT call id */ /* Bridged call info */ @@ -294,4 +482,29 @@ struct pri *__pri_new_tei(int fd, int node, int switchtype, struct pri *master, void __pri_free_tei(struct pri *p); +void q931_party_name_init(struct q931_party_name *name); +void q931_party_number_init(struct q931_party_number *number); +void q931_party_address_init(struct q931_party_address *address); +void q931_party_id_init(struct q931_party_id *id); +void q931_party_redirecting_init(struct q931_party_redirecting *redirecting); + +int q931_party_name_cmp(const struct q931_party_name *left, const struct q931_party_name *right); +int q931_party_number_cmp(const struct q931_party_number *left, const struct q931_party_number *right); +int q931_party_id_cmp(const struct q931_party_id *left, const struct q931_party_id *right); + +void q931_party_name_copy_to_pri(struct pri_party_name *pri_name, const struct q931_party_name *q931_name); +void q931_party_number_copy_to_pri(struct pri_party_number *pri_number, const struct q931_party_number *q931_number); +void q931_party_id_copy_to_pri(struct pri_party_id *pri_id, const struct q931_party_id *q931_id); +void q931_party_redirecting_copy_to_pri(struct pri_party_redirecting *pri_redirecting, const struct q931_party_redirecting *q931_redirecting); + +void q931_party_id_fixup(const struct pri *ctrl, struct q931_party_id *id); +int q931_party_id_presentation(const struct q931_party_id *id); + +const char *q931_call_state_str(int callstate); + +int q931_is_ptmp(struct pri *ctrl); +struct pri_subcommand *q931_alloc_subcommand(struct pri *ctrl); + +int q931_notify_redirection(struct pri *ctrl, q931_call *call, int notify, const struct q931_party_number *number); + #endif diff --git a/q931.c b/q931.c index 8ab7d98..74651bb 100644 --- a/q931.c +++ b/q931.c @@ -253,6 +253,353 @@ struct ie { FUNC_SEND(*transmit); }; +/*! + * \brief Determine if layer 2 is in PTMP mode. + * + * \param ctrl D channel controller. + * + * \retval TRUE if in PTMP mode. + * \retval FALSE otherwise. + */ +int q931_is_ptmp(struct pri *ctrl) +{ + return ctrl->tei == Q921_TEI_GROUP; +} + +/*! + * \brief Initialize the given struct q931_party_name + * + * \param name Structure to initialize + * + * \return Nothing + */ +void q931_party_name_init(struct q931_party_name *name) +{ + name->valid = 0; + name->presentation = PRI_PRES_UNAVAILABLE; + name->char_set = PRI_CHAR_SET_ISO8859_1; + name->str[0] = '\0'; +} + +/*! + * \brief Initialize the given struct q931_party_number + * + * \param number Structure to initialize + * + * \return Nothing + */ +void q931_party_number_init(struct q931_party_number *number) +{ + number->valid = 0; + number->presentation = PRI_PRES_UNAVAILABLE | PRI_PRES_USER_NUMBER_UNSCREENED; + number->plan = (PRI_TON_UNKNOWN << 4) | PRI_NPI_E163_E164; + number->str[0] = '\0'; +} + +/*! + * \brief Initialize the given struct q931_party_address + * + * \param address Structure to initialize + * + * \return Nothing + */ +void q931_party_address_init(struct q931_party_address *address) +{ + q931_party_number_init(&address->number); +} + +/*! + * \brief Initialize the given struct q931_party_id + * + * \param id Structure to initialize + * + * \return Nothing + */ +void q931_party_id_init(struct q931_party_id *id) +{ + q931_party_name_init(&id->name); + q931_party_number_init(&id->number); +} + +/*! + * \brief Initialize the given struct q931_party_redirecting + * + * \param redirecting Structure to initialize + * + * \return Nothing + */ +void q931_party_redirecting_init(struct q931_party_redirecting *redirecting) +{ + q931_party_id_init(&redirecting->from); + q931_party_id_init(&redirecting->to); + q931_party_id_init(&redirecting->orig_called); + redirecting->state = Q931_REDIRECTING_STATE_IDLE; + redirecting->count = 0; + redirecting->orig_reason = PRI_REDIR_UNKNOWN; + redirecting->reason = PRI_REDIR_UNKNOWN; +} + +/*! + * \brief Compare the left and right party name. + * + * \param left Left parameter party name. + * \param right Right parameter party name. + * + * \retval < 0 when left < right. + * \retval == 0 when left == right. + * \retval > 0 when left > right. + */ +int q931_party_name_cmp(const struct q931_party_name *left, const struct q931_party_name *right) +{ + int cmp; + + if (!left->valid) { + if (!right->valid) { + return 0; + } + return -1; + } + cmp = left->char_set - right->char_set; + if (cmp) { + return cmp; + } + cmp = strcmp(left->str, right->str); + if (cmp) { + return cmp; + } + cmp = left->presentation - right->presentation; + return cmp; +} + +/*! + * \brief Compare the left and right party number. + * + * \param left Left parameter party number. + * \param right Right parameter party number. + * + * \retval < 0 when left < right. + * \retval == 0 when left == right. + * \retval > 0 when left > right. + */ +int q931_party_number_cmp(const struct q931_party_number *left, const struct q931_party_number *right) +{ + int cmp; + + if (!left->valid) { + if (!right->valid) { + return 0; + } + return -1; + } + cmp = left->plan - right->plan; + if (cmp) { + return cmp; + } + cmp = strcmp(left->str, right->str); + if (cmp) { + return cmp; + } + cmp = left->presentation - right->presentation; + return cmp; +} + +/*! + * \brief Compare the left and right party id. + * + * \param left Left parameter party id. + * \param right Right parameter party id. + * + * \retval < 0 when left < right. + * \retval == 0 when left == right. + * \retval > 0 when left > right. + */ +int q931_party_id_cmp(const struct q931_party_id *left, const struct q931_party_id *right) +{ + int cmp; + + cmp = q931_party_number_cmp(&left->number, &right->number); + if (cmp) { + return cmp; + } + cmp = q931_party_name_cmp(&left->name, &right->name); + return cmp; +} + +/*! + * \brief Copy the Q.931 party name to the PRI party name structure. + * + * \param pri_name PRI party name structure + * \param q931_name Q.931 party name structure + * + * \return Nothing + */ +void q931_party_name_copy_to_pri(struct pri_party_name *pri_name, const struct q931_party_name *q931_name) +{ + if (q931_name->valid) { + pri_name->valid = 1; + pri_name->presentation = q931_name->presentation; + pri_name->char_set = q931_name->char_set; + libpri_copy_string(pri_name->str, q931_name->str, sizeof(pri_name->str)); + } else { + pri_name->valid = 0; + pri_name->presentation = PRI_PRES_UNAVAILABLE; + pri_name->char_set = PRI_CHAR_SET_ISO8859_1; + pri_name->str[0] = 0; + } +} + +/*! + * \brief Copy the Q.931 party number to the PRI party number structure. + * + * \param pri_number PRI party number structure + * \param q931_number Q.931 party number structure + * + * \return Nothing + */ +void q931_party_number_copy_to_pri(struct pri_party_number *pri_number, const struct q931_party_number *q931_number) +{ + if (q931_number->valid) { + pri_number->valid = 1; + pri_number->presentation = q931_number->presentation; + pri_number->plan = q931_number->plan; + libpri_copy_string(pri_number->str, q931_number->str, sizeof(pri_number->str)); + } else { + pri_number->valid = 0; + pri_number->presentation = PRI_PRES_UNAVAILABLE | PRI_PRES_USER_NUMBER_UNSCREENED; + pri_number->plan = (PRI_TON_UNKNOWN << 4) | PRI_NPI_E163_E164; + pri_number->str[0] = 0; + } +} + +/*! + * \brief Copy the Q.931 party id to the PRI party id structure. + * + * \param pri_id PRI party id structure + * \param q931_id Q.931 party id structure + * + * \return Nothing + */ +void q931_party_id_copy_to_pri(struct pri_party_id *pri_id, const struct q931_party_id *q931_id) +{ + q931_party_name_copy_to_pri(&pri_id->name, &q931_id->name); + q931_party_number_copy_to_pri(&pri_id->number, &q931_id->number); +} + +/*! + * \brief Copy the Q.931 redirecting data to the PRI redirecting structure. + * + * \param pri_redirecting PRI redirecting structure + * \param q931_redirecting Q.931 redirecting structure + * + * \return Nothing + */ +void q931_party_redirecting_copy_to_pri(struct pri_party_redirecting *pri_redirecting, const struct q931_party_redirecting *q931_redirecting) +{ + q931_party_id_copy_to_pri(&pri_redirecting->from, &q931_redirecting->from); + q931_party_id_copy_to_pri(&pri_redirecting->to, &q931_redirecting->to); + q931_party_id_copy_to_pri(&pri_redirecting->orig_called, + &q931_redirecting->orig_called); + pri_redirecting->count = q931_redirecting->count; + pri_redirecting->orig_reason = q931_redirecting->orig_reason; + pri_redirecting->reason = q931_redirecting->reason; +} + +/*! + * \brief Fixup some values in the q931_party_id that may be objectionable by switches. + * + * \param ctrl D channel controller. + * \param id Party ID to tweak. + * + * \return Nothing + */ +void q931_party_id_fixup(const struct pri *ctrl, struct q931_party_id *id) +{ + switch (ctrl->switchtype) { + case PRI_SWITCH_DMS100: + case PRI_SWITCH_ATT4ESS: + /* Doesn't like certain presentation types */ + if (id->number.valid && !(id->number.presentation & 0x7c)) { + /* i.e., If presentation is allowed it must be a network number */ + id->number.presentation = PRES_ALLOWED_NETWORK_NUMBER; + } + break; + default: + break; + } +} + +/*! + * \brief Determine the overall presentation value for the given party. + * + * \param id Party to determine the overall presentation value. + * + * \return Overall presentation value for the given party. + */ +int q931_party_id_presentation(const struct q931_party_id *id) +{ + int number_priority; + int number_value; + int number_screening; + int name_priority; + int name_value; + + /* Determine name presentation priority. */ + if (!id->name.valid) { + name_value = PRI_PRES_UNAVAILABLE; + name_priority = 3; + } else { + name_value = id->name.presentation & PRI_PRES_RESTRICTION; + switch (name_value) { + case PRI_PRES_RESTRICTED: + name_priority = 0; + break; + case PRI_PRES_ALLOWED: + name_priority = 1; + break; + case PRI_PRES_UNAVAILABLE: + name_priority = 2; + break; + default: + name_value = PRI_PRES_UNAVAILABLE; + name_priority = 3; + break; + } + } + + /* Determine number presentation priority. */ + if (!id->number.valid) { + number_screening = PRI_PRES_USER_NUMBER_UNSCREENED; + number_value = PRI_PRES_UNAVAILABLE; + number_priority = 3; + } else { + number_screening = id->number.presentation & PRI_PRES_NUMBER_TYPE; + number_value = id->number.presentation & PRI_PRES_RESTRICTION; + switch (number_value) { + case PRI_PRES_RESTRICTED: + number_priority = 0; + break; + case PRI_PRES_ALLOWED: + number_priority = 1; + break; + case PRI_PRES_UNAVAILABLE: + number_priority = 2; + break; + default: + number_screening = PRI_PRES_USER_NUMBER_UNSCREENED; + number_value = PRI_PRES_UNAVAILABLE; + number_priority = 3; + break; + } + } + + /* Select the wining presentation value. */ + if (name_priority < number_priority) { + number_value = name_value; + } + + return number_value | number_screening; +} + static char *code2str(int code, struct msgtype *codes, int max) { int x; @@ -1011,6 +1358,72 @@ static void dump_redirecting_number(int full_ie, struct pri *ctrl, q931_ie *ie, pri_message(ctrl, " '%s' ]\n", cnum); } +static void dump_redirection_number(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix) +{ + unsigned char cnum[256]; + int i = 0; + /* To follow Q.931 (4.5.1), we must search for start of octet 4 by + walking through all bytes until one with ext bit (8) set to 1 */ + do { + switch (i) { + case 0: /* Octet 3 */ + pri_message(ctrl, + "%c Redirection Number (len=%2d) [ Ext: %d TON: %s (%d) NPI: %s (%d)", + prefix, len, ie->data[0] >> 7, + ton2str((ie->data[0] >> 4) & 0x07), (ie->data[0] >> 4) & 0x07, + npi2str(ie->data[0] & 0x0f), ie->data[0] & 0x0f); + break; + case 1: /* Octet 3a */ + pri_message(ctrl, "\n%c Ext: %d Presentation: %s (%d)", + prefix, ie->data[1] >> 7, pri_pres2str(ie->data[1] & 0x7f), ie->data[1] & 0x7f); + break; + } + } while (!(ie->data[i++] & 0x80)); + q931_get_number(cnum, sizeof(cnum), ie->data + i, ie->len - i); + pri_message(ctrl, " '%s' ]\n", cnum); +} + +static int receive_connected_number(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len) +{ + int i = 0; + + call->remote_id.number.valid = 1; + call->remote_id.number.presentation = + PRI_PRES_ALLOWED | PRI_PRES_USER_NUMBER_UNSCREENED; + /* To follow Q.931 (4.5.1), we must search for start of octet 4 by + walking through all bytes until one with ext bit (8) set to 1 */ + do { + switch (i) { + case 0: + call->remote_id.number.plan = ie->data[i] & 0x7f; + break; + case 1: + /* Keep only the presentation and screening fields */ + call->remote_id.number.presentation = + ie->data[i] & (PRI_PRES_RESTRICTION | PRI_PRES_NUMBER_TYPE); + break; + } + } while (!(ie->data[i++] & 0x80)); + q931_get_number((unsigned char *) call->remote_id.number.str, sizeof(call->remote_id.number.str), ie->data + i, ie->len - i); + + return 0; +} + +static int transmit_connected_number(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order) +{ + size_t datalen; + + if (!call->local_id.number.valid) { + return 0; + } + + datalen = strlen(call->local_id.number.str); + ie->data[0] = call->local_id.number.plan; + ie->data[1] = 0x80 | call->local_id.number.presentation; + memcpy(ie->data + 2, call->local_id.number.str, datalen); + return datalen + (2 + 2); +} + static void dump_connected_number(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix) { unsigned char cnum[256]; @@ -1038,37 +1451,53 @@ static int receive_redirecting_number(int full_ie, struct pri *ctrl, q931_call * { int i = 0; + call->redirecting.from.number.valid = 1; + call->redirecting.from.number.presentation = + PRI_PRES_ALLOWED | PRI_PRES_USER_NUMBER_UNSCREENED; + call->redirecting.reason = PRI_REDIR_UNKNOWN; /* To follow Q.931 (4.5.1), we must search for start of octet 4 by walking through all bytes until one with ext bit (8) set to 1 */ do { - switch(i) { + switch (i) { case 0: - call->redirectingplan = ie->data[i] & 0x7f; + call->redirecting.from.number.plan = ie->data[i] & 0x7f; break; case 1: - call->redirectingpres = ie->data[i] & 0x7f; + /* Keep only the presentation and screening fields */ + call->redirecting.from.number.presentation = + ie->data[i] & (PRI_PRES_RESTRICTION | PRI_PRES_NUMBER_TYPE); break; case 2: - call->redirectingreason = ie->data[i] & 0x0f; + call->redirecting.reason = ie->data[i] & 0x0f; break; } - } while(!(ie->data[i++] & 0x80)); - q931_get_number((unsigned char *) call->redirectingnum, sizeof(call->redirectingnum), ie->data + i, ie->len - i); + } while (!(ie->data[i++] & 0x80)); + q931_get_number((unsigned char *) call->redirecting.from.number.str, sizeof(call->redirecting.from.number.str), ie->data + i, ie->len - i); return 0; } static int transmit_redirecting_number(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order) { + size_t datalen; + if (order > 1) return 0; - if (call->redirectingnum && *call->redirectingnum) { - ie->data[0] = call->redirectingplan; - ie->data[1] = call->redirectingpres; - ie->data[2] = (call->redirectingreason & 0x0f) | 0x80; - memcpy(ie->data + 3, call->redirectingnum, strlen(call->redirectingnum)); - return strlen(call->redirectingnum) + 3 + 2; + if (!call->redirecting.from.number.valid) { + return 0; } - return 0; + + datalen = strlen(call->redirecting.from.number.str); + ie->data[0] = call->redirecting.from.number.plan; +#if 1 + /* ETSI and Q.952 do not define the screening field */ + ie->data[1] = call->redirecting.from.number.presentation & PRI_PRES_RESTRICTION; +#else + /* Q.931 defines the screening field */ + ie->data[1] = call->redirecting.from.number.presentation; +#endif + ie->data[2] = (call->redirecting.reason & 0x0f) | 0x80; + memcpy(ie->data + 3, call->redirecting.from.number.str, datalen); + return datalen + (3 + 2); } static void dump_redirecting_subaddr(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix) @@ -1081,6 +1510,49 @@ static void dump_redirecting_subaddr(int full_ie, struct pri *ctrl, q931_ie *ie, (ie->data[0] & 0x08) >> 3, cnum); } +static int receive_redirection_number(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len) +{ + int i = 0; + + call->redirection_number.valid = 1; + call->redirection_number.presentation = + PRI_PRES_ALLOWED | PRI_PRES_USER_NUMBER_UNSCREENED; + /* To follow Q.931 (4.5.1), we must search for start of octet 4 by + walking through all bytes until one with ext bit (8) set to 1 */ + do { + switch (i) { + case 0: + call->redirection_number.plan = ie->data[i] & 0x7f; + break; + case 1: + /* Keep only the presentation and screening fields */ + call->redirection_number.presentation = + ie->data[i] & (PRI_PRES_RESTRICTION | PRI_PRES_NUMBER_TYPE); + break; + } + } while (!(ie->data[i++] & 0x80)); + q931_get_number((unsigned char *) call->redirection_number.str, sizeof(call->redirection_number.str), ie->data + i, ie->len - i); + return 0; +} + +static int transmit_redirection_number(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order) +{ + size_t datalen; + + if (order > 1) { + return 0; + } + if (!call->redirection_number.valid) { + return 0; + } + + datalen = strlen(call->redirection_number.str); + ie->data[0] = call->redirection_number.plan; + ie->data[1] = (call->redirection_number.presentation & PRI_PRES_RESTRICTION) | 0x80; + memcpy(ie->data + 2, call->redirection_number.str, datalen); + return datalen + (2 + 2); +} + static int receive_calling_party_subaddr(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len) { /* copy digits to call->callingsubaddr */ @@ -1090,60 +1562,94 @@ static int receive_calling_party_subaddr(int full_ie, struct pri *ctrl, q931_cal static int receive_called_party_number(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len) { - /* copy digits to call->callednum */ - q931_get_number((unsigned char *) call->callednum, sizeof(call->callednum), ie->data + 1, len - 3); - call->calledplan = ie->data[0] & 0x7f; + size_t called_len; + size_t max_len; + char *called_end; + + if (len < 3) { + return -1; + } + + call->called.number.valid = 1; + call->called.number.plan = ie->data[0] & 0x7f; + if (msgtype == Q931_SETUP) { + q931_get_number((unsigned char *) call->called.number.str, + sizeof(call->called.number.str), ie->data + 1, len - 3); + } else if (call->ourcallstate == Q931_CALL_STATE_OVERLAP_RECEIVING) { + /* + * Since we are receiving overlap digits now, we need to append + * them to any previously received digits in call->called.number.str. + */ + called_len = strlen(call->called.number.str); + called_end = call->called.number.str + called_len; + max_len = (sizeof(call->called.number.str) - 1) - called_len; + if (max_len < len - 3) { + called_len = max_len; + } else { + called_len = len - 3; + } + strncat(called_end, (char *) ie->data + 1, called_len); + } + + q931_get_number((unsigned char *) call->overlap_digits, sizeof(call->overlap_digits), + ie->data + 1, len - 3); return 0; } static int transmit_called_party_number(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order) { - ie->data[0] = 0x80 | call->calledplan; - if (*call->callednum) - memcpy(ie->data + 1, call->callednum, strlen(call->callednum)); - return strlen(call->callednum) + 3; + size_t datalen; + + if (!call->called.number.valid) { + return 0; + } + + datalen = strlen(call->overlap_digits); + ie->data[0] = 0x80 | call->called.number.plan; + memcpy(ie->data + 1, call->overlap_digits, datalen); + return datalen + (1 + 2); } static int receive_calling_party_number(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len) { - u_int8_t *data; - size_t length; - - if (ie->data[0] & 0x80) { - data = ie->data + 1; - length = len - 3; - call->callerpres = 0; /* PI presentation allowed SI user-provided, not screened */ - } else { - data = ie->data + 2; - length = len - 4; - call->callerpres = ie->data[1] & 0x7f; - } + int i = 0; - if (call->callerpres == PRES_ALLOWED_NETWORK_NUMBER || - call->callerpres == PRES_PROHIB_NETWORK_NUMBER) { - q931_get_number((u_int8_t *)call->callerani, sizeof(call->callerani), data, length); - call->callerplanani = ie->data[0] & 0x7f; - - if (!*call->callernum) { /*Copy ANI to CallerID if CallerID is not already set */ - libpri_copy_string(call->callernum, call->callerani, sizeof(call->callernum)); - call->callerplan = call->callerplanani; + call->remote_id.number.valid = 1; + call->remote_id.number.presentation = + PRI_PRES_ALLOWED | PRI_PRES_USER_NUMBER_UNSCREENED; + /* To follow Q.931 (4.5.1), we must search for start of octet 4 by + walking through all bytes until one with ext bit (8) set to 1 */ + do { + switch (i) { + case 0: + call->remote_id.number.plan = ie->data[i] & 0x7f; + break; + case 1: + /* Keep only the presentation and screening fields */ + call->remote_id.number.presentation = + ie->data[i] & (PRI_PRES_RESTRICTION | PRI_PRES_NUMBER_TYPE); + break; } - - } else { - q931_get_number((u_int8_t *)call->callernum, sizeof(call->callernum), data, length); - call->callerplan = ie->data[0] & 0x7f; - } + } while (!(ie->data[i++] & 0x80)); + q931_get_number((unsigned char *) call->remote_id.number.str, + sizeof(call->remote_id.number.str), ie->data + i, ie->len - i); return 0; } static int transmit_calling_party_number(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order) { - ie->data[0] = call->callerplan; - ie->data[1] = 0x80 | call->callerpres; - if (*call->callernum) - memcpy(ie->data + 2, call->callernum, strlen(call->callernum)); - return strlen(call->callernum) + 4; + size_t datalen; + + if (!call->local_id.number.valid) { + return 0; + } + + datalen = strlen(call->local_id.number.str); + ie->data[0] = call->local_id.number.plan; + ie->data[1] = 0x80 | call->local_id.number.presentation; + memcpy(ie->data + 2, call->local_id.number.str, datalen); + return datalen + (2 + 2); } static void dump_user_user(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix) @@ -1262,32 +1768,56 @@ static void dump_progress_indicator(int full_ie, struct pri *ctrl, q931_ie *ie, static int receive_display(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len) { unsigned char *data; + + call->remote_id.name.valid = 1; + data = ie->data; if (data[0] & 0x80) { /* Skip over character set */ data++; len--; } - q931_get_number((unsigned char *) call->callername, sizeof(call->callername), data, len - 2); + call->remote_id.name.char_set = PRI_CHAR_SET_ISO8859_1; + + q931_get_number((unsigned char *) call->remote_id.name.str, sizeof(call->remote_id.name.str), data, len - 2); + if (call->remote_id.name.str[0]) { + call->remote_id.name.presentation = PRI_PRES_ALLOWED; + } else { + call->remote_id.name.presentation = PRI_PRES_RESTRICTED; + } return 0; } static int transmit_display(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order) { + size_t datalen; int i; - - if ((ctrl->switchtype == PRI_SWITCH_QSIG) || - ((ctrl->switchtype == PRI_SWITCH_EUROISDN_E1) && (ctrl->localtype == PRI_CPE)) || - !call->callername[0]) - return 0; i = 0; - if(ctrl->switchtype != PRI_SWITCH_EUROISDN_E1) { + + if (!call->local_id.name.valid || !call->local_id.name.str[0]) { + return 0; + } + switch (ctrl->switchtype) { + case PRI_SWITCH_QSIG: + /* Q.SIG supports names */ + return 0; + case PRI_SWITCH_EUROISDN_E1: + case PRI_SWITCH_EUROISDN_T1: + if (ctrl->localtype == PRI_CPE) { + return 0; + } + break; + default: + /* Prefix name with character set indicator. */ ie->data[0] = 0xb1; ++i; + break; } - memcpy(ie->data + i, call->callername, strlen(call->callername)); - return 2 + i + strlen(call->callername); + + datalen = strlen(call->local_id.name.str); + memcpy(ie->data + i, call->local_id.name.str, datalen); + return 2 + i + datalen; } static int receive_progress_indicator(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len) @@ -1497,7 +2027,7 @@ static int receive_call_state(int full_ie, struct pri *ctrl, q931_call *call, in * * \return String equivalent of the given Q.931 call state. */ -static const char *q931_call_state_str(int callstate) +const char *q931_call_state_str(int callstate) { static struct msgtype callstates[] = { { 0, "Null" }, @@ -1592,16 +2122,8 @@ static int transmit_keypad_facility(int full_ie, struct pri *ctrl, q931_call *ca int sublen; sublen = strlen(call->keypad_digits); - - if (sublen > 32) { - sublen = 32; - call->keypad_digits[32] = '\0'; - } - if (sublen) { - libpri_copy_string((char *)ie->data, (char *)call->keypad_digits, sizeof(call->keypad_digits)); - /* Make sure we clear the field */ - call->keypad_digits[0] = '\0'; + libpri_copy_string((char *) ie->data, call->keypad_digits, sizeof(call->keypad_digits)); return sublen + 2; } else return 0; @@ -2068,10 +2590,13 @@ static int receive_generic_digits(int full_ie, struct pri *ctrl, q931_call *call break; #if 0 case 5: /* Callid */ - if (!call->callernum[0]) { - memcpy(call->callernum, number, sizeof(call->callernum)-1); - call->callerpres = 0; - call->callerplan = 0; + if (!call->remote_id.number.valid) { + call->remote_id.number.valid = 1; + call->remote_id.number.presentation = + PRI_PRES_ALLOWED | PRI_PRES_USER_NUMBER_UNSCREENED; + call->remote_id.number.plan = PRI_UNKNOWN; + libpri_copy_string(call->remote_id.number.str, number, + sizeof(call->remote_id.number.str)); } break; #endif @@ -2194,7 +2719,7 @@ static struct ie ies[] = { { 0, Q931_HIGH_LAYER_COMPAT, "High-layer Compatibility" }, { 1, Q931_PACKET_SIZE, "Packet Size" }, { 0, Q931_IE_FACILITY, "Facility" , dump_facility, receive_facility, transmit_facility }, - { 1, Q931_IE_REDIRECTION_NUMBER, "Redirection Number" }, + { 1, Q931_IE_REDIRECTION_NUMBER, "Redirection Number", dump_redirection_number, receive_redirection_number, transmit_redirection_number }, { 1, Q931_IE_REDIRECTION_SUBADDR, "Redirection Subaddress" }, { 1, Q931_IE_FEATURE_ACTIVATE, "Feature Activation" }, { 1, Q931_IE_INFO_REQUEST, "Feature Request" }, @@ -2213,7 +2738,7 @@ static struct ie ies[] = { { 1, Q931_IE_CALL_STATUS, "Call Status" }, { 1, Q931_IE_CHANGE_STATUS, "Change Status", dump_change_status, receive_change_status, transmit_change_status }, { 1, Q931_IE_CONNECTED_ADDR, "Connected Number", dump_connected_number }, - { 1, Q931_IE_CONNECTED_NUM, "Connected Number", dump_connected_number }, + { 1, Q931_IE_CONNECTED_NUM, "Connected Number", dump_connected_number, receive_connected_number, transmit_connected_number }, { 1, Q931_IE_ORIGINAL_CALLED_NUMBER, "Original Called Number", dump_redirecting_number, receive_redirecting_number, transmit_redirecting_number }, { 1, Q931_IE_USER_USER_FACILITY, "User-User Facility" }, { 1, Q931_IE_UPDATE, "Update" }, @@ -2410,6 +2935,27 @@ static q931_call *q931_getcall(struct pri *ctrl, int cr) cur->newcall = 1; cur->ourcallstate = Q931_CALL_STATE_NULL; cur->peercallstate = Q931_CALL_STATE_NULL; + cur->sugcallstate = -1; + cur->ri = -1; + cur->transcapability = -1; + cur->transmoderate = -1; + cur->transmultiple = -1; + cur->userl1 = -1; + cur->userl2 = -1; + cur->userl3 = -1; + cur->rateadaption = -1; + cur->progress = -1; + cur->causecode = -1; + cur->causeloc = -1; + cur->cause = -1; + cur->useruserprotocoldisc = -1; + cur->aoc_units = -1; + cur->changestatus = -1; + q931_party_number_init(&cur->redirection_number); + q931_party_address_init(&cur->called); + q931_party_id_init(&cur->local_id); + q931_party_id_init(&cur->remote_id); + q931_party_redirecting_init(&cur->redirecting); /* PRI is set to whoever called us */ if (ctrl->bri && (ctrl->localtype == PRI_CPE)) { @@ -2788,12 +3334,23 @@ static int q931_status(struct pri *ctrl, q931_call *c, int cause) return send_message(ctrl, cur, Q931_STATUS, status_ies); } -static int information_ies[] = { Q931_IE_KEYPAD_FACILITY, Q931_CALLED_PARTY_NUMBER, -1 }; +static int information_ies[] = { Q931_CALLED_PARTY_NUMBER, -1 }; int q931_information(struct pri *ctrl, q931_call *c, char digit) { - c->callednum[0] = digit; - c->callednum[1] = '\0'; + c->overlap_digits[0] = digit; + c->overlap_digits[1] = '\0'; + + /* + * Since we are doing overlap dialing now, we need to accumulate + * the digits into call->called.number.str. + */ + c->called.number.valid = 1; + if (strlen(c->called.number.str) < sizeof(c->called.number.str) - 1) { + /* There is enough room for the new digit. */ + strcat(c->called.number.str, c->overlap_digits); + } + return send_message(ctrl, c, Q931_INFORMATION, information_ies); } @@ -2821,20 +3378,44 @@ int q931_facility(struct pri*ctrl, q931_call *c) return send_message(ctrl, c, Q931_FACILITY, facility_ies); } -static int notify_ies[] = { Q931_IE_NOTIFY_IND, -1 }; +static int notify_ies[] = { Q931_IE_NOTIFY_IND, Q931_IE_REDIRECTION_NUMBER, -1 }; + +/*! + * \brief Send a NOTIFY message with optional redirection number. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg + * \param notify Notification indicator + * \param number Redirection number to send if not NULL. + * + * \retval 0 on success. + * \retval -1 on error. + */ +int q931_notify_redirection(struct pri *ctrl, q931_call *call, int notify, const struct q931_party_number *number) +{ + if (number) { + call->redirection_number = *number; + } else { + q931_party_number_init(&call->redirection_number); + } + call->notify = notify; + return send_message(ctrl, call, Q931_NOTIFY, notify_ies); +} int q931_notify(struct pri *ctrl, q931_call *c, int channel, int info) { if ((ctrl->switchtype == PRI_SWITCH_EUROISDN_T1) || (ctrl->switchtype != PRI_SWITCH_EUROISDN_E1)) { - if ((info > 0x2) || (info < 0x00)) + if ((info > 0x2) || (info < 0x00)) { return 0; + } } - if (info >= 0) - c->notify = info & 0x7F; - else - c->notify = -1; - return send_message(ctrl, c, Q931_NOTIFY, notify_ies); + if (info >= 0) { + info = info & 0x7F; + } else { + info = -1; + } + return q931_notify_redirection(ctrl, c, info, NULL); } #ifdef ALERTING_NO_PROGRESS @@ -2924,9 +3505,9 @@ int q931_call_proceeding(struct pri *ctrl, q931_call *c, int channel, int info) return send_message(ctrl, c, Q931_CALL_PROCEEDING, call_proceeding_ies); } #ifndef ALERTING_NO_PROGRESS -static int alerting_ies[] = { Q931_PROGRESS_INDICATOR, Q931_IE_USER_USER, -1 }; +static int alerting_ies[] = { Q931_PROGRESS_INDICATOR, Q931_IE_USER_USER, Q931_IE_FACILITY, -1 }; #else -static int alerting_ies[] = { -1 }; +static int alerting_ies[] = { Q931_IE_FACILITY, -1 }; #endif int q931_alerting(struct pri *ctrl, q931_call *c, int channel, int info) @@ -2942,10 +3523,22 @@ int q931_alerting(struct pri *ctrl, q931_call *c, int channel, int info) UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_CALL_RECEIVED); c->peercallstate = Q931_CALL_STATE_CALL_DELIVERED; c->alive = 1; + + switch (ctrl->switchtype) { + case PRI_SWITCH_QSIG: + if (c->local_id.name.valid) { + /* Send calledName with ALERTING */ + rose_called_name_encode(ctrl, c, Q931_ALERTING); + } + break; + default: + break; + } + return send_message(ctrl, c, Q931_ALERTING, alerting_ies); } -static int connect_ies[] = { Q931_CHANNEL_IDENT, Q931_PROGRESS_INDICATOR, -1 }; +static int connect_ies[] = { Q931_CHANNEL_IDENT, Q931_PROGRESS_INDICATOR, Q931_IE_CONNECTED_NUM, Q931_IE_FACILITY, -1 }; int q931_setup_ack(struct pri *ctrl, q931_call *c, int channel, int nonisdn) { @@ -3007,6 +3600,7 @@ static void pri_release_finaltimeout(void *data) c->peercallstate = Q931_CALL_STATE_NULL; ctrl->schedev = 1; ctrl->ev.e = PRI_EVENT_HANGUP_ACK; + ctrl->ev.hangup.subcmds = &ctrl->subcmds; ctrl->ev.hangup.channel = c->channelno; ctrl->ev.hangup.cause = c->cause; ctrl->ev.hangup.cref = c->cr; @@ -3055,6 +3649,29 @@ int q931_connect(struct pri *ctrl, q931_call *c, int channel, int nonisdn) c->retranstimer = 0; if ((c->ourcallstate == Q931_CALL_STATE_CONNECT_REQUEST) && (ctrl->bri || (!ctrl->subchannel))) c->retranstimer = pri_schedule_event(ctrl, ctrl->timers[PRI_TIMER_T313], pri_connect_timeout, c); + + if (c->redirecting.state == Q931_REDIRECTING_STATE_PENDING_TX_DIV_LEG_3) { + c->redirecting.state = Q931_REDIRECTING_STATE_IDLE; + /* Send DivertingLegInformation3 with CONNECT. */ + c->redirecting.to = c->local_id; + if (!c->redirecting.to.number.valid) { + q931_party_number_init(&c->redirecting.to.number); + c->redirecting.to.number.valid = 1; + c->redirecting.to.number.presentation = + PRI_PRES_RESTRICTED | PRI_PRES_USER_NUMBER_UNSCREENED; + } + rose_diverting_leg_information3_encode(ctrl, c, Q931_CONNECT); + } + switch (ctrl->switchtype) { + case PRI_SWITCH_QSIG: + if (c->local_id.name.valid) { + /* Send connectedName with CONNECT */ + rose_connected_name_encode(ctrl, c, Q931_CONNECT); + } + break; + default: + break; + } return send_message(ctrl, c, Q931_CONNECT, connect_ies); } @@ -3164,46 +3781,22 @@ int q931_setup(struct pri *ctrl, q931_call *c, struct pri_sr *req) c->chanflags = FLAG_EXCLUSIVE; else if (c->channelno) c->chanflags = FLAG_PREFERRED; - if (req->caller) { - libpri_copy_string(c->callernum, req->caller, sizeof(c->callernum)); - c->callerplan = req->callerplan; - if (req->callername) - libpri_copy_string(c->callername, req->callername, sizeof(c->callername)); - else - c->callername[0] = '\0'; - if ((ctrl->switchtype == PRI_SWITCH_DMS100) || - (ctrl->switchtype == PRI_SWITCH_ATT4ESS)) { - /* Doesn't like certain presentation types */ - if (!(req->callerpres & 0x7c)) - req->callerpres = PRES_ALLOWED_NETWORK_NUMBER; - } - c->callerpres = req->callerpres; - } else { - c->callernum[0] = '\0'; - c->callername[0] = '\0'; - c->callerplan = PRI_UNKNOWN; - c->callerpres = PRES_NUMBER_NOT_AVAILABLE; + + if (req->caller.number.valid) { + c->local_id = req->caller; + q931_party_id_fixup(ctrl, &c->local_id); } - if (req->redirectingnum) { - libpri_copy_string(c->redirectingnum, req->redirectingnum, sizeof(c->redirectingnum)); - c->redirectingplan = req->redirectingplan; - if ((ctrl->switchtype == PRI_SWITCH_DMS100) || - (ctrl->switchtype == PRI_SWITCH_ATT4ESS)) { - /* Doesn't like certain presentation types */ - if (!(req->redirectingpres & 0x7c)) - req->redirectingpres = PRES_ALLOWED_NETWORK_NUMBER; - } - c->redirectingpres = req->redirectingpres; - c->redirectingreason = req->redirectingreason; - } else { - c->redirectingnum[0] = '\0'; - c->redirectingplan = PRI_UNKNOWN; - c->redirectingpres = PRES_NUMBER_NOT_AVAILABLE; - c->redirectingreason = PRI_REDIR_UNKNOWN; + + if (req->redirecting.from.number.valid) { + c->redirecting = req->redirecting; + q931_party_id_fixup(ctrl, &c->redirecting.from); + q931_party_id_fixup(ctrl, &c->redirecting.to); + q931_party_id_fixup(ctrl, &c->redirecting.orig_called); } - if (req->called) { - libpri_copy_string(c->callednum, req->called, sizeof(c->callednum)); - c->calledplan = req->calledplan; + + if (req->called.number.valid) { + c->called = req->called; + libpri_copy_string(c->overlap_digits, req->called.number.str, sizeof(c->overlap_digits)); } else return -1; @@ -3376,6 +3969,20 @@ int q931_hangup(struct pri *ctrl, q931_call *c, int cause) return 0; } +static void q931_clr_subcommands(struct pri *ctrl) +{ + ctrl->subcmds.counter_subcmd = 0; +} + +struct pri_subcommand *q931_alloc_subcommand(struct pri *ctrl) +{ + if (ctrl->subcmds.counter_subcmd < PRI_MAX_SUBCOMMANDS) { + return &ctrl->subcmds.subcmd[ctrl->subcmds.counter_subcmd++]; + } + + return NULL; +} + static int prepare_to_handle_maintenance_message(struct pri *ctrl, q931_mh *mh, q931_call *c) { if ((!ctrl) || (!mh) || (!c)) { @@ -3418,7 +4025,6 @@ static int prepare_to_handle_q931_message(struct pri *ctrl, q931_mh *mh, q931_ca c->ri = -1; break; case Q931_FACILITY: - c->callername[0] = '\0'; break; case Q931_SETUP: if (ctrl->debug & PRI_DEBUG_Q931_STATE) @@ -3435,24 +4041,12 @@ static int prepare_to_handle_q931_message(struct pri *ctrl, q931_mh *mh, q931_ca c->userl2 = -1; c->userl3 = -1; c->rateadaption = -1; - c->calledplan = -1; - c->callerplan = -1; - c->callerpres = -1; - c->callernum[0] = '\0'; - c->callednum[0] = '\0'; - c->callername[0] = '\0'; - c->callerani[0] = '\0'; - c->callerplanani = -1; - c->redirectingplan = -1; - c->redirectingpres = -1; - c->redirectingreason = -1; - c->origcalledplan = -1; - c->origcalledpres = -1; - c->origredirectingreason = -1; - c->redirectingnum[0] = '\0'; - c->origcallednum[0] = '\0'; - c->redirectingname[0] = '\0'; - c->origcalledname[0] = '\0'; + + q931_party_address_init(&c->called); + q931_party_id_init(&c->local_id); + q931_party_id_init(&c->remote_id); + q931_party_redirecting_init(&c->redirecting); + c->useruserprotocoldisc = -1; c->useruserinfo[0] = '\0'; c->complete = 0; @@ -3503,13 +4097,19 @@ static int prepare_to_handle_q931_message(struct pri *ctrl, q931_mh *mh, q931_ca c->channelno = -1; break; case Q931_INFORMATION: - c->callednum[0] = '\0'; + /* + * Make sure that keypad and overlap digit buffers are empty in + * case they are not in the message. + */ + c->keypad_digits[0] = '\0'; + c->overlap_digits[0] = '\0'; break; case Q931_STATUS_ENQUIRY: break; case Q931_SETUP_ACKNOWLEDGE: break; case Q931_NOTIFY: + q931_party_number_init(&c->redirection_number); break; case Q931_USER_INFORMATION: case Q931_SEGMENT: @@ -3587,6 +4187,7 @@ int q931_receive(struct pri *ctrl, q931_h *h, int len) } else { prepare_to_handle_q931_message(ctrl, mh, c); } + q931_clr_subcommands(ctrl); /* Handle IEs */ memset(mandies, 0, sizeof(mandies)); @@ -3737,10 +4338,51 @@ static int post_handle_maintenance_message(struct pri *ctrl, struct q931_mh *mh, return -1; } +/*! + * \internal + * \brief Fill in the FACILITY event fields. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg + * + * \return Nothing + */ +static void q931_fill_facility_event(struct pri *ctrl, struct q931_call *call) +{ + ctrl->ev.e = PRI_EVENT_FACILITY; + ctrl->ev.facility.subcmds = &ctrl->subcmds; + ctrl->ev.facility.channel = + call->channelno | (call->ds1no << 8) | (call->ds1explicit << 16); + ctrl->ev.facility.cref = call->cr; + ctrl->ev.facility.call = call; + + /* Need to do this for backward compatibility with struct pri_event_facname */ + libpri_copy_string(ctrl->ev.facility.callingname, call->remote_id.name.str, + sizeof(ctrl->ev.facility.callingname)); + libpri_copy_string(ctrl->ev.facility.callingnum, call->remote_id.number.str, + sizeof(ctrl->ev.facility.callingnum)); + ctrl->ev.facility.callingpres = q931_party_id_presentation(&call->remote_id); + ctrl->ev.facility.callingplan = call->remote_id.number.plan; +} + +/*! + * \internal + * \brief Process the decoded information in the Q.931 message. + * + * \param ctrl D channel controller. + * \param mh Q.931 message header. + * \param c Q.931 call leg. + * \param missingmand Number of missing mandatory ie's. + * + * \retval 0 if no error or event. + * \retval Q931_RES_HAVEEVENT if have an event. + * \retval -1 on error. + */ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct q931_call *c, int missingmand) { int res; struct apdu_event *cur = NULL; + struct pri_subcommand *subcmd; switch(mh->msg) { case Q931_RESTART: @@ -3777,38 +4419,99 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct q931_release_complete(ctrl, c, PRI_CAUSE_BEARERCAPABILITY_NOTIMPL); break; } + + if (c->redirecting.from.number.valid && !c->redirecting.count) { + /* + * This is most likely because the redirecting number came in + * with the redirecting ie only and not a DivertingLegInformation2. + */ + c->redirecting.count = 1; + } + if (c->redirecting.state == Q931_REDIRECTING_STATE_PENDING_TX_DIV_LEG_3) { + /* + * Valid for Q.SIG and ETSI PRI/BRI-PTP modes: + * Setup the redirecting.to informtion so we can identify + * if the user wants to manually supply the COLR for this + * redirected to number if further redirects could happen. + * + * All the user needs to do is set the REDIRECTING(to-pres) + * to the COLR and REDIRECTING(to-num) = complete-dialed-number + * (i.e. CALLERID(dnid)) to be safe after determining that the + * incoming call was redirected by checking if the + * REDIRECTING(count) is nonzero. + */ + c->redirecting.to.number = c->called.number; + c->redirecting.to.number.presentation = + PRI_PRES_RESTRICTED | PRI_PRES_USER_NUMBER_UNSCREENED; + } + ctrl->ev.e = PRI_EVENT_RING; + ctrl->ev.ring.subcmds = &ctrl->subcmds; ctrl->ev.ring.channel = c->channelno | (c->ds1no << 8) | (c->ds1explicit << 16); - ctrl->ev.ring.callingpres = c->callerpres; - ctrl->ev.ring.callingplan = c->callerplan; - ctrl->ev.ring.callingplanani = c->callerplanani; - ctrl->ev.ring.callingplanrdnis = c->redirectingplan; - ctrl->ev.ring.callingplanorigcalled = c->origcalledplan; - ctrl->ev.ring.ani2 = c->ani2; - libpri_copy_string(ctrl->ev.ring.callingani, c->callerani, sizeof(ctrl->ev.ring.callingani)); - libpri_copy_string(ctrl->ev.ring.callingnum, c->callernum, sizeof(ctrl->ev.ring.callingnum)); - libpri_copy_string(ctrl->ev.ring.callingname, c->callername, sizeof(ctrl->ev.ring.callingname)); - ctrl->ev.ring.calledplan = c->calledplan; + + /* Calling party information */ + ctrl->ev.ring.callingpres = q931_party_id_presentation(&c->remote_id); + ctrl->ev.ring.callingplan = c->remote_id.number.plan; + if (c->remote_id.number.valid + && (c->remote_id.number.presentation == PRES_ALLOWED_NETWORK_NUMBER + || c->remote_id.number.presentation == PRES_PROHIB_NETWORK_NUMBER)) { + ctrl->ev.ring.callingplanani = c->remote_id.number.plan; + libpri_copy_string(ctrl->ev.ring.callingani, c->remote_id.number.str, sizeof(ctrl->ev.ring.callingani)); + } else { + ctrl->ev.ring.callingplanani = -1; + ctrl->ev.ring.callingani[0] = '\0'; + } + libpri_copy_string(ctrl->ev.ring.callingnum, c->remote_id.number.str, sizeof(ctrl->ev.ring.callingnum)); + libpri_copy_string(ctrl->ev.ring.callingname, c->remote_id.name.str, sizeof(ctrl->ev.ring.callingname)); libpri_copy_string(ctrl->ev.ring.callingsubaddr, c->callingsubaddr, sizeof(ctrl->ev.ring.callingsubaddr)); - libpri_copy_string(ctrl->ev.ring.callednum, c->callednum, sizeof(ctrl->ev.ring.callednum)); - libpri_copy_string(ctrl->ev.ring.origcalledname, c->origcalledname, sizeof(ctrl->ev.ring.origcalledname)); - libpri_copy_string(ctrl->ev.ring.origcallednum, c->origcallednum, sizeof(ctrl->ev.ring.origcallednum)); - libpri_copy_string(ctrl->ev.ring.redirectingnum, c->redirectingnum, sizeof(ctrl->ev.ring.redirectingnum)); - libpri_copy_string(ctrl->ev.ring.redirectingname, c->redirectingname, sizeof(ctrl->ev.ring.redirectingname)); + + ctrl->ev.ring.ani2 = c->ani2; + + /* Called party information */ + ctrl->ev.ring.calledplan = c->called.number.plan; + libpri_copy_string(ctrl->ev.ring.callednum, c->called.number.str, sizeof(ctrl->ev.ring.callednum)); + + /* Original called party information (For backward compatibility) */ + libpri_copy_string(ctrl->ev.ring.origcalledname, c->redirecting.orig_called.name.str, sizeof(ctrl->ev.ring.origcalledname)); + libpri_copy_string(ctrl->ev.ring.origcallednum, c->redirecting.orig_called.number.str, sizeof(ctrl->ev.ring.origcallednum)); + ctrl->ev.ring.callingplanorigcalled = c->redirecting.orig_called.number.plan; + if (c->redirecting.orig_called.number.valid + || c->redirecting.orig_called.name.valid) { + ctrl->ev.ring.origredirectingreason = c->redirecting.orig_reason; + } else { + ctrl->ev.ring.origredirectingreason = -1; + } + + /* Redirecting from party information (For backward compatibility) */ + ctrl->ev.ring.callingplanrdnis = c->redirecting.from.number.plan; + libpri_copy_string(ctrl->ev.ring.redirectingnum, c->redirecting.from.number.str, sizeof(ctrl->ev.ring.redirectingnum)); + libpri_copy_string(ctrl->ev.ring.redirectingname, c->redirecting.from.name.str, sizeof(ctrl->ev.ring.redirectingname)); + + ctrl->ev.ring.redirectingreason = c->redirecting.reason; + libpri_copy_string(ctrl->ev.ring.useruserinfo, c->useruserinfo, sizeof(ctrl->ev.ring.useruserinfo)); c->useruserinfo[0] = '\0'; - ctrl->ev.ring.redirectingreason = c->redirectingreason; - ctrl->ev.ring.origredirectingreason = c->origredirectingreason; + ctrl->ev.ring.flexible = ! (c->chanflags & FLAG_EXCLUSIVE); ctrl->ev.ring.cref = c->cr; ctrl->ev.ring.call = c; ctrl->ev.ring.layer1 = c->userl1; ctrl->ev.ring.complete = c->complete; ctrl->ev.ring.ctype = c->transcapability; - ctrl->ev.ring.redirectingreason = c->redirectingreason; ctrl->ev.ring.progress = c->progress; ctrl->ev.ring.progressmask = c->progressmask; ctrl->ev.ring.reversecharge = c->reversecharge; + + if (c->redirecting.count) { + subcmd = q931_alloc_subcommand(ctrl); + if (subcmd) { + /* Setup redirecting subcommand */ + subcmd->cmd = PRI_SUBCMD_REDIRECTING; + q931_party_redirecting_copy_to_pri(&subcmd->u.redirecting, + &c->redirecting); + } + } + return Q931_RES_HAVEEVENT; case Q931_ALERTING: if (c->newcall) { @@ -3818,11 +4521,13 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_CALL_DELIVERED); c->peercallstate = Q931_CALL_STATE_CALL_RECEIVED; ctrl->ev.e = PRI_EVENT_RINGING; + ctrl->ev.ringing.subcmds = &ctrl->subcmds; ctrl->ev.ringing.channel = c->channelno | (c->ds1no << 8) | (c->ds1explicit << 16); ctrl->ev.ringing.cref = c->cr; ctrl->ev.ringing.call = c; ctrl->ev.ringing.progress = c->progress; ctrl->ev.ringing.progressmask = c->progressmask; + libpri_copy_string(ctrl->ev.ringing.useruserinfo, c->useruserinfo, sizeof(ctrl->ev.ringing.useruserinfo)); c->useruserinfo[0] = '\0'; @@ -3847,7 +4552,9 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct } UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_ACTIVE); c->peercallstate = Q931_CALL_STATE_CONNECT_REQUEST; + ctrl->ev.e = PRI_EVENT_ANSWER; + ctrl->ev.answer.subcmds = &ctrl->subcmds; ctrl->ev.answer.channel = c->channelno | (c->ds1no << 8) | (c->ds1explicit << 16); ctrl->ev.answer.cref = c->cr; ctrl->ev.answer.call = c; @@ -3855,29 +4562,46 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct ctrl->ev.answer.progressmask = c->progressmask; libpri_copy_string(ctrl->ev.answer.useruserinfo, c->useruserinfo, sizeof(ctrl->ev.answer.useruserinfo)); c->useruserinfo[0] = '\0'; + q931_connect_acknowledge(ctrl, c); + if (c->justsignalling) { /* Make sure WE release when we initiatie a signalling only connection */ q931_release(ctrl, c, PRI_CAUSE_NORMAL_CLEARING); break; - } else + } else { + c->incoming_ct_state = INCOMING_CT_STATE_IDLE; + + /* Setup connected line subcommand */ + subcmd = q931_alloc_subcommand(ctrl); + if (subcmd) { + subcmd->cmd = PRI_SUBCMD_CONNECTED_LINE; + q931_party_id_copy_to_pri(&subcmd->u.connected_line.id, &c->remote_id); + } + return Q931_RES_HAVEEVENT; + } case Q931_FACILITY: if (c->newcall) { q931_release_complete(ctrl,c,PRI_CAUSE_INVALID_CALL_REFERENCE); break; } - ctrl->ev.e = PRI_EVENT_FACNAME; - libpri_copy_string(ctrl->ev.facname.callingname, c->callername, sizeof(ctrl->ev.facname.callingname)); - libpri_copy_string(ctrl->ev.facname.callingnum, c->callernum, sizeof(ctrl->ev.facname.callingnum)); - ctrl->ev.facname.channel = c->channelno | (c->ds1no << 8) | (c->ds1explicit << 16); - ctrl->ev.facname.callingpres = c->callerpres; - ctrl->ev.facname.callingplan = c->callerplan; - ctrl->ev.facname.cref = c->cr; - ctrl->ev.facname.call = c; -#if 0 - pri_message(ctrl, "Sending facility event (%s/%s)\n", ctrl->ev.facname.callingname, ctrl->ev.facname.callingnum); -#endif - return Q931_RES_HAVEEVENT; + switch (c->incoming_ct_state) { + case INCOMING_CT_STATE_POST_CONNECTED_LINE: + c->incoming_ct_state = INCOMING_CT_STATE_IDLE; + subcmd = q931_alloc_subcommand(ctrl); + if (subcmd) { + subcmd->cmd = PRI_SUBCMD_CONNECTED_LINE; + q931_party_id_copy_to_pri(&subcmd->u.connected_line.id, &c->remote_id); + } + break; + default: + break; + } + if (ctrl->subcmds.counter_subcmd) { + q931_fill_facility_event(ctrl, c); + return Q931_RES_HAVEEVENT; + } + break; case Q931_PROGRESS: if (missingmand) { q931_status(ctrl, c, PRI_CAUSE_MANDATORY_IE_MISSING); @@ -3888,6 +4612,7 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct ctrl->ev.proceeding.cause = c->cause; /* Fall through */ case Q931_CALL_PROCEEDING: + ctrl->ev.proceeding.subcmds = &ctrl->subcmds; if (c->newcall) { q931_release_complete(ctrl,c,PRI_CAUSE_INVALID_CALL_REFERENCE); break; @@ -3959,6 +4684,7 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct if (!c->sugcallstate) { #endif + ctrl->ev.hangup.subcmds = &ctrl->subcmds; ctrl->ev.hangup.channel = c->channelno | (c->ds1no << 8) | (c->ds1explicit << 16); ctrl->ev.hangup.cause = c->cause; ctrl->ev.hangup.cref = c->cr; @@ -3987,6 +4713,7 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct case Q931_RELEASE_COMPLETE: UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_NULL); c->peercallstate = Q931_CALL_STATE_NULL; + ctrl->ev.hangup.subcmds = &ctrl->subcmds; ctrl->ev.hangup.channel = c->channelno | (c->ds1no << 8) | (c->ds1explicit << 16); ctrl->ev.hangup.cause = c->cause; ctrl->ev.hangup.cref = c->cr; @@ -4022,6 +4749,7 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct } UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_NULL); ctrl->ev.e = PRI_EVENT_HANGUP; + ctrl->ev.hangup.subcmds = &ctrl->subcmds; ctrl->ev.hangup.channel = c->channelno | (c->ds1no << 8) | (c->ds1explicit << 16); ctrl->ev.hangup.cause = c->cause; ctrl->ev.hangup.cref = c->cr; @@ -4056,6 +4784,7 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct /* Return such an event */ ctrl->ev.e = PRI_EVENT_HANGUP_REQ; + ctrl->ev.hangup.subcmds = &ctrl->subcmds; ctrl->ev.hangup.channel = c->channelno | (c->ds1no << 8) | (c->ds1explicit << 16); ctrl->ev.hangup.cause = c->cause; ctrl->ev.hangup.cref = c->cr; @@ -4085,17 +4814,17 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct } if (c->ourcallstate != Q931_CALL_STATE_OVERLAP_RECEIVING) { ctrl->ev.e = PRI_EVENT_KEYPAD_DIGIT; + ctrl->ev.digit.subcmds = &ctrl->subcmds; ctrl->ev.digit.call = c; ctrl->ev.digit.channel = c->channelno | (c->ds1no << 8); libpri_copy_string(ctrl->ev.digit.digits, c->keypad_digits, sizeof(ctrl->ev.digit.digits)); - /* Make sure we clear it out before we return */ - c->keypad_digits[0] = '\0'; return Q931_RES_HAVEEVENT; } ctrl->ev.e = PRI_EVENT_INFO_RECEIVED; + ctrl->ev.ring.subcmds = &ctrl->subcmds; ctrl->ev.ring.call = c; ctrl->ev.ring.channel = c->channelno | (c->ds1no << 8) | (c->ds1explicit << 16); - libpri_copy_string(ctrl->ev.ring.callednum, c->callednum, sizeof(ctrl->ev.ring.callednum)); + libpri_copy_string(ctrl->ev.ring.callednum, c->overlap_digits, sizeof(ctrl->ev.ring.callednum)); libpri_copy_string(ctrl->ev.ring.callingsubaddr, c->callingsubaddr, sizeof(ctrl->ev.ring.callingsubaddr)); ctrl->ev.ring.complete = c->complete; /* this covers IE 33 (Sending Complete) */ return Q931_RES_HAVEEVENT; @@ -4113,6 +4842,7 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_OVERLAP_SENDING); c->peercallstate = Q931_CALL_STATE_OVERLAP_RECEIVING; ctrl->ev.e = PRI_EVENT_SETUP_ACK; + ctrl->ev.setup_ack.subcmds = &ctrl->subcmds; ctrl->ev.setup_ack.channel = c->channelno | (c->ds1no << 8) | (c->ds1explicit << 16); ctrl->ev.setup_ack.call = c; @@ -4127,10 +4857,69 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct return Q931_RES_HAVEEVENT; case Q931_NOTIFY: - ctrl->ev.e = PRI_EVENT_NOTIFY; - ctrl->ev.notify.channel = c->channelno; - ctrl->ev.notify.info = c->notify; - return Q931_RES_HAVEEVENT; + res = 0; + switch (c->notify) { + case PRI_NOTIFY_CALL_DIVERTING: + if (c->redirection_number.valid) { + c->redirecting.to.number = c->redirection_number; + if (c->redirecting.count < PRI_MAX_REDIRECTS) { + ++c->redirecting.count; + } + switch (c->ourcallstate) { + case Q931_CALL_STATE_CALL_DELIVERED: + /* Call is deflecting after we have seen an ALERTING message */ + c->redirecting.reason = PRI_REDIR_FORWARD_ON_NO_REPLY; + break; + default: + /* Call is deflecting for call forwarding unconditional or busy reason. */ + c->redirecting.reason = PRI_REDIR_UNKNOWN; + break; + } + + /* Setup redirecting subcommand */ + subcmd = q931_alloc_subcommand(ctrl); + if (subcmd) { + subcmd->cmd = PRI_SUBCMD_REDIRECTING; + q931_party_redirecting_copy_to_pri(&subcmd->u.redirecting, + &c->redirecting); + } + } + + if (ctrl->subcmds.counter_subcmd) { + q931_fill_facility_event(ctrl, c); + res = Q931_RES_HAVEEVENT; + } + break; + case PRI_NOTIFY_TRANSFER_ALERTING: + case PRI_NOTIFY_TRANSFER_ACTIVE: + if (c->redirection_number.valid + && q931_party_number_cmp(&c->remote_id.number, &c->redirection_number)) { + /* The remote party information changed. */ + c->remote_id.number = c->redirection_number; + + /* Setup connected line subcommand */ + subcmd = q931_alloc_subcommand(ctrl); + if (subcmd) { + subcmd->cmd = PRI_SUBCMD_CONNECTED_LINE; + q931_party_id_copy_to_pri(&subcmd->u.connected_line.id, + &c->remote_id); + } + } + + if (ctrl->subcmds.counter_subcmd) { + q931_fill_facility_event(ctrl, c); + res = Q931_RES_HAVEEVENT; + } + break; + default: + ctrl->ev.e = PRI_EVENT_NOTIFY; + ctrl->ev.notify.subcmds = &ctrl->subcmds; + ctrl->ev.notify.channel = c->channelno; + ctrl->ev.notify.info = c->notify; + res = Q931_RES_HAVEEVENT; + break; + } + return res; case Q931_USER_INFORMATION: case Q931_SEGMENT: case Q931_CONGESTION_CONTROL: @@ -4149,7 +4938,6 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct pri_error(ctrl, "!! Not yet handling post-handle message type %s (%d)\n", msg2str(mh->msg), mh->msg); /* Fall through */ default: - pri_error(ctrl, "!! Don't know how to post-handle message type %s (%d)\n", msg2str(mh->msg), mh->msg); q931_status(ctrl,c, PRI_CAUSE_MESSAGE_TYPE_NONEXIST); if (c->newcall) @@ -4178,6 +4966,7 @@ static int pri_internal_clear(void *data) UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_NULL); c->peercallstate = Q931_CALL_STATE_NULL; + ctrl->ev.hangup.subcmds = &ctrl->subcmds; ctrl->ev.hangup.channel = c->channelno | (c->ds1no << 8) | (c->ds1explicit << 16); ctrl->ev.hangup.cause = c->cause; ctrl->ev.hangup.cref = c->cr; diff --git a/rose.c b/rose.c index 27de736..778f9fd 100644 --- a/rose.c +++ b/rose.c @@ -192,6 +192,14 @@ struct rose_convert_error { * First_Value = (First_Subidentifier * 40) + Second_Subidentifier */ +/*! \brief ETSI Explicit Call Transfer OID prefix. */ +static const struct asn1_oid rose_etsi_ect = { +/* *INDENT-OFF* */ + /* {ccitt(0) identified-organization(4) etsi(0) 369 operations-and-errors(1)} */ + 4, { 4, 0, 369, 1 } +/* *INDENT-ON* */ +}; + /*! \brief ETSI specific invoke/result encode/decode message table */ static const struct rose_convert_msg rose_etsi_msgs[] = { /* *INDENT-OFF* */ @@ -200,6 +208,71 @@ static const struct rose_convert_msg rose_etsi_msgs[] = { * encode_invoke_args, encode_result_args, * decode_invoke_args, decode_result_args */ + /* + * localValue's from Diversion-Operations + * {ccitt identified-organization etsi(0) 207 operations-and-errors(1)} + */ + { + ROSE_ETSI_ActivationDiversion, NULL, 7, + rose_enc_etsi_ActivationDiversion_ARG, NULL, + rose_dec_etsi_ActivationDiversion_ARG, NULL + }, + { + ROSE_ETSI_DeactivationDiversion, NULL, 8, + rose_enc_etsi_DeactivationDiversion_ARG,NULL, + rose_dec_etsi_DeactivationDiversion_ARG,NULL + }, + { + ROSE_ETSI_ActivationStatusNotificationDiv, NULL, 9, + rose_enc_etsi_ActivationStatusNotificationDiv_ARG,NULL, + rose_dec_etsi_ActivationStatusNotificationDiv_ARG,NULL + }, + { + ROSE_ETSI_DeactivationStatusNotificationDiv,NULL, 10, + rose_enc_etsi_DeactivationStatusNotificationDiv_ARG,NULL, + rose_dec_etsi_DeactivationStatusNotificationDiv_ARG,NULL + }, + { + ROSE_ETSI_InterrogationDiversion, NULL, 11, + rose_enc_etsi_InterrogationDiversion_ARG,rose_enc_etsi_InterrogationDiversion_RES, + rose_dec_etsi_InterrogationDiversion_ARG,rose_dec_etsi_InterrogationDiversion_RES + }, + { + ROSE_ETSI_DiversionInformation, NULL, 12, + rose_enc_etsi_DiversionInformation_ARG, NULL, + rose_dec_etsi_DiversionInformation_ARG, NULL + }, + { + ROSE_ETSI_CallDeflection, NULL, 13, + rose_enc_etsi_CallDeflection_ARG, NULL, + rose_dec_etsi_CallDeflection_ARG, NULL + }, + { + ROSE_ETSI_CallRerouting, NULL, 14, + rose_enc_etsi_CallRerouting_ARG, NULL, + rose_dec_etsi_CallRerouting_ARG, NULL + }, + { + ROSE_ETSI_DivertingLegInformation2, NULL, 15, + rose_enc_etsi_DivertingLegInformation2_ARG,NULL, + rose_dec_etsi_DivertingLegInformation2_ARG,NULL + }, + { + ROSE_ETSI_InterrogateServedUserNumbers, NULL, 17, + NULL, rose_enc_etsi_InterrogateServedUserNumbers_RES, + NULL, rose_dec_etsi_InterrogateServedUserNumbers_RES + }, + { + ROSE_ETSI_DivertingLegInformation1, NULL, 18, + rose_enc_etsi_DivertingLegInformation1_ARG,NULL, + rose_dec_etsi_DivertingLegInformation1_ARG,NULL + }, + { + ROSE_ETSI_DivertingLegInformation3, NULL, 19, + rose_enc_etsi_DivertingLegInformation3_ARG,NULL, + rose_dec_etsi_DivertingLegInformation3_ARG,NULL + }, + /* * localValue's from Advice-of-Charge-Operations * {ccitt identified-organization etsi (0) 182 operations-and-errors (1)} @@ -243,6 +316,51 @@ static const struct rose_convert_msg rose_etsi_msgs[] = { rose_enc_etsi_AOCEChargingUnit_ARG, NULL, rose_dec_etsi_AOCEChargingUnit_ARG, NULL }, + + /* + * localValue's from Explicit-Call-Transfer-Operations-and-Errors + * {ccitt identified-organization etsi(0) 369 operations-and-errors(1)} + */ + { + ROSE_ETSI_EctExecute, NULL, 6, + NULL, NULL, + NULL, NULL + }, + + /* + * globalValue's (OIDs) from Explicit-Call-Transfer-Operations-and-Errors + * {ccitt identified-organization etsi(0) 369 operations-and-errors(1)} + */ + { + ROSE_ETSI_ExplicitEctExecute, &rose_etsi_ect, 1, + rose_enc_etsi_ExplicitEctExecute_ARG, NULL, + rose_dec_etsi_ExplicitEctExecute_ARG, NULL + }, + { + ROSE_ETSI_RequestSubaddress, &rose_etsi_ect, 2, + NULL, NULL, + NULL, NULL + }, + { + ROSE_ETSI_SubaddressTransfer, &rose_etsi_ect, 3, + rose_enc_etsi_SubaddressTransfer_ARG, NULL, + rose_dec_etsi_SubaddressTransfer_ARG, NULL + }, + { + ROSE_ETSI_EctLinkIdRequest, &rose_etsi_ect, 4, + NULL, rose_enc_etsi_EctLinkIdRequest_RES, + NULL, rose_dec_etsi_EctLinkIdRequest_RES + }, + { + ROSE_ETSI_EctInform, &rose_etsi_ect, 5, + rose_enc_etsi_EctInform_ARG, NULL, + rose_dec_etsi_EctInform_ARG, NULL + }, + { + ROSE_ETSI_EctLoopTest, &rose_etsi_ect, 6, + rose_enc_etsi_EctLoopTest_ARG, rose_enc_etsi_EctLoopTest_RES, + rose_dec_etsi_EctLoopTest_ARG, rose_dec_etsi_EctLoopTest_RES + }, /* *INDENT-ON* */ }; @@ -295,6 +413,39 @@ static const struct rose_convert_error rose_etsi_errors[] = { NULL, NULL }, + /* + * localValue Errors from Diversion-Operations + * {ccitt identified-organization etsi(0) 207 operations-and-errors(1)} + */ + { + ROSE_ERROR_Div_InvalidDivertedToNr, NULL, 12, + NULL, NULL + }, + { + ROSE_ERROR_Div_SpecialServiceNr, NULL, 14, + NULL, NULL + }, + { + ROSE_ERROR_Div_DiversionToServedUserNr, NULL, 15, + NULL, NULL + }, + { + ROSE_ERROR_Div_IncomingCallAccepted, NULL, 23, + NULL, NULL + }, + { + ROSE_ERROR_Div_NumberOfDiversionsExceeded, NULL, 24, + NULL, NULL + }, + { + ROSE_ERROR_Div_NotActivated, NULL, 46, + NULL, NULL + }, + { + ROSE_ERROR_Div_RequestAlreadyAccepted, NULL, 48, + NULL, NULL + }, + /* * localValue Errors from Advice-of-Charge-Operations * {ccitt identified-organization etsi (0) 182 operations-and-errors (1)} @@ -303,6 +454,15 @@ static const struct rose_convert_error rose_etsi_errors[] = { ROSE_ERROR_AOC_NoChargingInfoAvailable, NULL, 26, NULL, NULL }, + + /* + * globalValue Errors (OIDs) from Explicit-Call-Transfer-Operations-and-Errors + * {ccitt identified-organization etsi(0) 369 operations-and-errors(1)} + */ + { + ROSE_ERROR_ECT_LinkIdNotAssignedByNetwork, &rose_etsi_ect, 21, + NULL, NULL + }, /* *INDENT-ON* */ }; @@ -880,6 +1040,27 @@ const char *rose_operation2str(enum rose_operation operation) { ROSE_None, "ROSE_None" }, { ROSE_Unknown, "ROSE_Unknown" }, + { ROSE_ETSI_ActivationDiversion, "ROSE_ETSI_ActivationDiversion" }, + { ROSE_ETSI_DeactivationDiversion, "ROSE_ETSI_DeactivationDiversion" }, + { ROSE_ETSI_ActivationStatusNotificationDiv,"ROSE_ETSI_ActivationStatusNotificationDiv" }, + { ROSE_ETSI_DeactivationStatusNotificationDiv,"ROSE_ETSI_DeactivationStatusNotificationDiv" }, + { ROSE_ETSI_InterrogationDiversion, "ROSE_ETSI_InterrogationDiversion" }, + { ROSE_ETSI_DiversionInformation, "ROSE_ETSI_DiversionInformation" }, + { ROSE_ETSI_CallDeflection, "ROSE_ETSI_CallDeflection" }, + { ROSE_ETSI_CallRerouting, "ROSE_ETSI_CallRerouting" }, + { ROSE_ETSI_DivertingLegInformation2, "ROSE_ETSI_DivertingLegInformation2" }, + { ROSE_ETSI_InterrogateServedUserNumbers, "ROSE_ETSI_InterrogateServedUserNumbers" }, + { ROSE_ETSI_DivertingLegInformation1, "ROSE_ETSI_DivertingLegInformation1" }, + { ROSE_ETSI_DivertingLegInformation3, "ROSE_ETSI_DivertingLegInformation3" }, + + { ROSE_ETSI_EctExecute, "ROSE_ETSI_EctExecute" }, + { ROSE_ETSI_ExplicitEctExecute, "ROSE_ETSI_ExplicitEctExecute" }, + { ROSE_ETSI_RequestSubaddress, "ROSE_ETSI_RequestSubaddress" }, + { ROSE_ETSI_SubaddressTransfer, "ROSE_ETSI_SubaddressTransfer" }, + { ROSE_ETSI_EctLinkIdRequest, "ROSE_ETSI_EctLinkIdRequest" }, + { ROSE_ETSI_EctInform, "ROSE_ETSI_EctInform" }, + { ROSE_ETSI_EctLoopTest, "ROSE_ETSI_EctLoopTest" }, + { ROSE_ETSI_ChargingRequest, "ROSE_ETSI_ChargingRequest" }, { ROSE_ETSI_AOCSCurrency, "ROSE_ETSI_AOCSCurrency" }, { ROSE_ETSI_AOCSSpecialArr, "ROSE_ETSI_AOCSSpecialArr" }, @@ -971,10 +1152,15 @@ const char *rose_error2str(enum rose_error_code code) { ROSE_ERROR_Div_InvalidDivertedToNr, "Diversion: Invalid Diverted To Number" }, { ROSE_ERROR_Div_SpecialServiceNr, "Diversion: Special Service Number" }, { ROSE_ERROR_Div_DiversionToServedUserNr, "Diversion: Diversion To Served User Number" }, + { ROSE_ERROR_Div_IncomingCallAccepted, "Diversion: Incoming Call Accepted" }, { ROSE_ERROR_Div_NumberOfDiversionsExceeded, "Diversion: Number Of Diversions Exceeded" }, + { ROSE_ERROR_Div_NotActivated, "Diversion: Not Activated" }, + { ROSE_ERROR_Div_RequestAlreadyAccepted, "Diversion: Request Already Accepted" }, { ROSE_ERROR_AOC_NoChargingInfoAvailable, "AOC: No Charging Info Available" }, + { ROSE_ERROR_ECT_LinkIdNotAssignedByNetwork, "ECT: Link ID Not Assigned By Network" }, + /* Q.SIG specific errors */ { ROSE_ERROR_QSIG_Unspecified, "Unspecified" }, diff --git a/rose.h b/rose.h index 8c91165..7b5b5a3 100644 --- a/rose.h +++ b/rose.h @@ -65,6 +65,20 @@ enum rose_operation { ROSE_Unknown, /* *INDENT-OFF* */ + /* ETSI Diversion-Operations */ + ROSE_ETSI_ActivationDiversion, /*!< Invoke/Result */ + ROSE_ETSI_DeactivationDiversion, /*!< Invoke/Result */ + ROSE_ETSI_ActivationStatusNotificationDiv,/*!< Invoke only */ + ROSE_ETSI_DeactivationStatusNotificationDiv,/*!< Invoke only */ + ROSE_ETSI_InterrogationDiversion, /*!< Invoke/Result */ + ROSE_ETSI_DiversionInformation, /*!< Invoke only */ + ROSE_ETSI_CallDeflection, /*!< Invoke/Result */ + ROSE_ETSI_CallRerouting, /*!< Invoke/Result */ + ROSE_ETSI_InterrogateServedUserNumbers, /*!< Invoke/Result */ + ROSE_ETSI_DivertingLegInformation1, /*!< Invoke only */ + ROSE_ETSI_DivertingLegInformation2, /*!< Invoke only */ + ROSE_ETSI_DivertingLegInformation3, /*!< Invoke only */ + /* * ETSI Advice-of-Charge-Operations * @@ -80,6 +94,15 @@ enum rose_operation { ROSE_ETSI_AOCECurrency, /*!< Invoke only */ ROSE_ETSI_AOCEChargingUnit, /*!< Invoke only */ + /* ETSI Explicit-Call-Transfer-Operations-and-Errors */ + ROSE_ETSI_EctExecute, /*!< Invoke/Result */ + ROSE_ETSI_ExplicitEctExecute, /*!< Invoke/Result */ + ROSE_ETSI_RequestSubaddress, /*!< Invoke only */ + ROSE_ETSI_SubaddressTransfer, /*!< Invoke only */ + ROSE_ETSI_EctLinkIdRequest, /*!< Invoke/Result */ + ROSE_ETSI_EctInform, /*!< Invoke only */ + ROSE_ETSI_EctLoopTest, /*!< Invoke/Result */ + /* Q.SIG Name-Operations */ ROSE_QSIG_CallingName, /*!< Invoke only */ ROSE_QSIG_CalledName, /*!< Invoke only */ @@ -164,11 +187,17 @@ enum rose_error_code { ROSE_ERROR_Div_InvalidDivertedToNr, ROSE_ERROR_Div_SpecialServiceNr, ROSE_ERROR_Div_DiversionToServedUserNr, + ROSE_ERROR_Div_IncomingCallAccepted, ROSE_ERROR_Div_NumberOfDiversionsExceeded, + ROSE_ERROR_Div_NotActivated, + ROSE_ERROR_Div_RequestAlreadyAccepted, /* ETSI Advice-of-Charge-Operations */ ROSE_ERROR_AOC_NoChargingInfoAvailable, + /* ETSI Explicit-Call-Transfer-Operations-and-Errors */ + ROSE_ERROR_ECT_LinkIdNotAssignedByNetwork, + /* Q.SIG from various specifications */ ROSE_ERROR_QSIG_Unspecified, @@ -1046,6 +1075,602 @@ struct roseEtsiAOCEChargingUnit_ARG { /* ------------------------------------------------------------------- */ +/* + * ARGUMENT SEQUENCE { + * procedure Procedure, + * basicService BasicService, + * forwardedToAddress Address, + * servedUserNr ServedUserNr + * } + */ +struct roseEtsiActivationDiversion_ARG { + /*! \brief Forwarded to address */ + struct roseAddress forwarded_to; + + /*! \brief Forward all numbers if not present (Number length is zero). */ + struct rosePartyNumber served_user_number; + + /*! \details cfu(0), cfb(1), cfnr(2) */ + u_int8_t procedure; + + /*! + * \details + * allServices(0), + * speech(1), + * unrestrictedDigitalInformation(2), + * audio3k1Hz(3), + * unrestrictedDigitalInformationWithTonesAndAnnouncements(4), + * multirate(5), + * telephony3k1Hz(32), + * teletex(33), + * telefaxGroup4Class1(34), + * videotexSyntaxBased(35), + * videotelephony(36), + * telefaxGroup2-3(37), + * telephony7kHz(38), + * euroFileTransfer(39), + * fileTransferAndAccessManagement(40), + * videoconference(41), + * audioGraphicConference(42) + */ + u_int8_t basic_service; +}; + + +/* + * ARGUMENT SEQUENCE { + * procedure Procedure, + * basicService BasicService, + * servedUserNr ServedUserNr + * } + */ +struct roseEtsiDeactivationDiversion_ARG { + /*! \brief Forward all numbers if not present (Number length is zero). */ + struct rosePartyNumber served_user_number; + + /*! \details cfu(0), cfb(1), cfnr(2) */ + u_int8_t procedure; + + /*! + * \details + * allServices(0), + * speech(1), + * unrestrictedDigitalInformation(2), + * audio3k1Hz(3), + * unrestrictedDigitalInformationWithTonesAndAnnouncements(4), + * multirate(5), + * telephony3k1Hz(32), + * teletex(33), + * telefaxGroup4Class1(34), + * videotexSyntaxBased(35), + * videotelephony(36), + * telefaxGroup2-3(37), + * telephony7kHz(38), + * euroFileTransfer(39), + * fileTransferAndAccessManagement(40), + * videoconference(41), + * audioGraphicConference(42) + */ + u_int8_t basic_service; +}; + + +/* + * ARGUMENT SEQUENCE { + * procedure Procedure, + * basicService BasicService, + * forwardedToAddresss Address, + * servedUserNr ServedUserNr + * } + */ +struct roseEtsiActivationStatusNotificationDiv_ARG { + /*! \brief Forwarded to address */ + struct roseAddress forwarded_to; + + /*! \brief Forward all numbers if not present (Number length is zero). */ + struct rosePartyNumber served_user_number; + + /*! \details cfu(0), cfb(1), cfnr(2) */ + u_int8_t procedure; + + /*! + * \details + * allServices(0), + * speech(1), + * unrestrictedDigitalInformation(2), + * audio3k1Hz(3), + * unrestrictedDigitalInformationWithTonesAndAnnouncements(4), + * multirate(5), + * telephony3k1Hz(32), + * teletex(33), + * telefaxGroup4Class1(34), + * videotexSyntaxBased(35), + * videotelephony(36), + * telefaxGroup2-3(37), + * telephony7kHz(38), + * euroFileTransfer(39), + * fileTransferAndAccessManagement(40), + * videoconference(41), + * audioGraphicConference(42) + */ + u_int8_t basic_service; +}; + +/* + * ARGUMENT SEQUENCE { + * procedure Procedure, + * basicService BasicService, + * servedUserNr ServedUserNr + * } + */ +struct roseEtsiDeactivationStatusNotificationDiv_ARG { + /*! \brief Forward all numbers if not present (Number length is zero). */ + struct rosePartyNumber served_user_number; + + /*! \details cfu(0), cfb(1), cfnr(2) */ + u_int8_t procedure; + + /*! + * \details + * allServices(0), + * speech(1), + * unrestrictedDigitalInformation(2), + * audio3k1Hz(3), + * unrestrictedDigitalInformationWithTonesAndAnnouncements(4), + * multirate(5), + * telephony3k1Hz(32), + * teletex(33), + * telefaxGroup4Class1(34), + * videotexSyntaxBased(35), + * videotelephony(36), + * telefaxGroup2-3(37), + * telephony7kHz(38), + * euroFileTransfer(39), + * fileTransferAndAccessManagement(40), + * videoconference(41), + * audioGraphicConference(42) + */ + u_int8_t basic_service; +}; + + +/* + * ARGUMENT SEQUENCE { + * procedure Procedure, + * basicService BasicService DEFAULT allServices, + * servedUserNr ServedUserNr + * } + */ +struct roseEtsiInterrogationDiversion_ARG { + /*! \brief Forward all numbers if not present (Number length is zero). */ + struct rosePartyNumber served_user_number; + + /*! \details cfu(0), cfb(1), cfnr(2) */ + u_int8_t procedure; + + /*! + * \details + * DEFAULT allServices + * + * \details + * allServices(0), + * speech(1), + * unrestrictedDigitalInformation(2), + * audio3k1Hz(3), + * unrestrictedDigitalInformationWithTonesAndAnnouncements(4), + * multirate(5), + * telephony3k1Hz(32), + * teletex(33), + * telefaxGroup4Class1(34), + * videotexSyntaxBased(35), + * videotelephony(36), + * telefaxGroup2-3(37), + * telephony7kHz(38), + * euroFileTransfer(39), + * fileTransferAndAccessManagement(40), + * videoconference(41), + * audioGraphicConference(42) + */ + u_int8_t basic_service; +}; + +/* + * IntResult ::= SEQUENCE { + * servedUserNr ServedUserNr, + * basicService BasicService, + * procedure Procedure, + * forwardedToAddress Address + * } + */ +struct roseEtsiForwardingRecord { + /*! \brief Forwarded to address */ + struct roseAddress forwarded_to; + + /*! \brief Forward all numbers if not present (Number length is zero). */ + struct rosePartyNumber served_user_number; + + /*! \details cfu(0), cfb(1), cfnr(2) */ + u_int8_t procedure; + + /*! + * \details + * allServices(0), + * speech(1), + * unrestrictedDigitalInformation(2), + * audio3k1Hz(3), + * unrestrictedDigitalInformationWithTonesAndAnnouncements(4), + * multirate(5), + * telephony3k1Hz(32), + * teletex(33), + * telefaxGroup4Class1(34), + * videotexSyntaxBased(35), + * videotelephony(36), + * telefaxGroup2-3(37), + * telephony7kHz(38), + * euroFileTransfer(39), + * fileTransferAndAccessManagement(40), + * videoconference(41), + * audioGraphicConference(42) + */ + u_int8_t basic_service; +}; + +/* + * roseInterrogationDiversion_RES + * IntResultList ::= SET SIZE (0..29) OF IntResult + */ +struct roseEtsiForwardingList { + /*! + * \brief SET SIZE (0..29) OF Forwarding Records + * \note Reduced the size of the array to conserve + * potential stack usage. + */ + struct roseEtsiForwardingRecord list[10]; + + /*! \brief Number of Forwarding records present */ + u_int8_t num_records; +}; + +/* + * ARGUMENT SEQUENCE { + * diversionReason DiversionReason, + * basicService BasicService, + * servedUserSubaddress PartySubaddress OPTIONAL, + * callingAddress [0] EXPLICIT PresentedAddressScreened OPTIONAL, + * originalCalledNr [1] EXPLICIT PresentedNumberUnscreened OPTIONAL, + * lastDivertingNr [2] EXPLICIT PresentedNumberUnscreened OPTIONAL, + * lastDivertingReason [3] EXPLICIT DiversionReason OPTIONAL, + * + * -- The User-user information element, as specified + * -- in ETS 300 102-1 [11], subclause 4.5.29, shall + * -- be embedded in the userInfo parameter. + * userInfo Q931InformationElement OPTIONAL + * } + */ +struct roseEtsiDiversionInformation_ARG { + /*! \brief Served user subaddress (Optional) */ + struct rosePartySubaddress served_user_subaddress; + + /*! \brief Calling address (Optional) */ + struct rosePresentedAddressScreened calling; + + /*! \brief Original called number (Optional) */ + struct rosePresentedNumberUnscreened original_called; + + /*! \brief Last diverting number (Optional) */ + struct rosePresentedNumberUnscreened last_diverting; + + /*! \brief User-User information embedded in Q.931 IE (Optional) */ + struct roseQ931ie q931ie; + /*! \brief q931ie.contents "allocated" after the stucture. */ + unsigned char q931ie_contents[ROSE_Q931_MAX_USER + 1]; + + /*! + * \brief Last diverting reason (Optional) + * + * \details + * unknown(0), + * cfu(1), + * cfb(2), + * cfnr(3), + * cdAlerting(4), + * cdImmediate(5) + */ + u_int8_t last_diverting_reason; + + /*! \brief TRUE if CallingAddress is present */ + u_int8_t calling_present; + + /*! \brief TRUE if OriginalCalled is present */ + u_int8_t original_called_present; + + /*! \brief TRUE if LastDiverting is present */ + u_int8_t last_diverting_present; + + /*! \brief TRUE if LastDivertingReason is present */ + u_int8_t last_diverting_reason_present; + + /*! + * \details + * unknown(0), + * cfu(1), + * cfb(2), + * cfnr(3), + * cdAlerting(4), + * cdImmediate(5) + */ + u_int8_t diversion_reason; + + /*! + * \details + * allServices(0), + * speech(1), + * unrestrictedDigitalInformation(2), + * audio3k1Hz(3), + * unrestrictedDigitalInformationWithTonesAndAnnouncements(4), + * multirate(5), + * telephony3k1Hz(32), + * teletex(33), + * telefaxGroup4Class1(34), + * videotexSyntaxBased(35), + * videotelephony(36), + * telefaxGroup2-3(37), + * telephony7kHz(38), + * euroFileTransfer(39), + * fileTransferAndAccessManagement(40), + * videoconference(41), + * audioGraphicConference(42) + */ + u_int8_t basic_service; +}; + + +/* + * ARGUMENT SEQUENCE { + * deflectionAddress Address, + * presentationAllowedDivertedToUser PresentationAllowedIndicator OPTIONAL + * } + */ +struct roseEtsiCallDeflection_ARG { + /*! \brief Deflection address (Deflected-To address) */ + struct roseAddress deflection; + + /*! \brief TRUE if PresentationAllowedToDivertedToUser is present */ + u_int8_t presentation_allowed_to_diverted_to_user_present; + + /*! \brief TRUE if presentation is allowed (Optional) */ + u_int8_t presentation_allowed_to_diverted_to_user; +}; + + +/* + * ARGUMENT SEQUENCE { + * reroutingReason DiversionReason, + * calledAddress Address, + * reroutingCounter DiversionCounter, + * + * -- The User-user information element (optional), + * -- High layer compatibility information element (optional), + * -- Bearer capability information element + * -- and Low layer compatibility information element (optional) + * -- as specified in ETS 300 102-1 [11] subclause 4.5 shall be + * -- embedded in the q931InfoElement. + * q931InfoElement Q931InformationElement, + * lastReroutingNr [1] EXPLICIT PresentedNumberUnscreened, + * subscriptionOption [2] EXPLICIT SubscriptionOption DEFAULT noNotification, + * callingPartySubaddress [3] EXPLICIT PartySubaddress OPTIONAL + * } + */ +struct roseEtsiCallRerouting_ARG { + struct roseAddress called_address; + + /*! \brief The BC, HLC (optional), LLC (optional), and User-user (optional) information */ + struct roseQ931ie q931ie; + /*! \brief q931ie.contents "allocated" after the stucture. */ + unsigned char q931ie_contents[ROSE_Q931_MAX_BC + ROSE_Q931_MAX_HLC + + ROSE_Q931_MAX_LLC + ROSE_Q931_MAX_USER + 1]; + + /*! \brief Last rerouting number */ + struct rosePresentedNumberUnscreened last_rerouting; + + /*! \brief Calling party subaddress (Optional) */ + struct rosePartySubaddress calling_subaddress; + + /*! + * \details + * unknown(0), + * cfu(1), + * cfb(2), + * cfnr(3), + * cdAlerting(4), + * cdImmediate(5) + */ + u_int8_t rerouting_reason; + + /*! \brief Range 1-5 */ + u_int8_t rerouting_counter; + + /*! + * \details + * DEFAULT noNotification + * + * \details + * noNotification(0), + * notificationWithoutDivertedToNr(1), + * notificationWithDivertedToNr(2) + */ + u_int8_t subscription_option; +}; + + +/* + * roseInterrogateServedUserNumbers_RES + * ServedUserNumberList ::= SET SIZE (0..99) OF PartyNumber + */ +struct roseEtsiServedUserNumberList { + /*! + * \brief SET SIZE (0..99) OF Served user numbers + * \note Reduced the size of the array to conserve + * potential stack usage. + */ + struct rosePartyNumber number[20]; + + /*! \brief Number of Served user numbers present */ + u_int8_t num_records; +}; + + +/* + * ARGUMENT SEQUENCE { + * diversionReason DiversionReason, + * subscriptionOption SubscriptionOption, + * divertedToNumber PresentedNumberUnscreened OPTIONAL + * } + */ +struct roseEtsiDivertingLegInformation1_ARG { + /*! \brief Diverted to number (Optional) */ + struct rosePresentedNumberUnscreened diverted_to; + + /*! \brief TRUE if DivertedTo is present */ + u_int8_t diverted_to_present; + + /*! + * \details + * unknown(0), + * cfu(1), + * cfb(2), + * cfnr(3), + * cdAlerting(4), + * cdImmediate(5) + */ + u_int8_t diversion_reason; + + /*! + * \details + * noNotification(0), + * notificationWithoutDivertedToNr(1), + * notificationWithDivertedToNr(2) + */ + u_int8_t subscription_option; +}; + +/* + * ARGUMENT SEQUENCE { + * diversionCounter DiversionCounter, + * diversionReason DiversionReason, + * divertingNr [1] EXPLICIT PresentedNumberUnscreened OPTIONAL, + * originalCalledNr [2] EXPLICIT PresentedNumberUnscreened OPTIONAL + * } + */ +struct roseEtsiDivertingLegInformation2_ARG { + /*! \brief Diverting number (Optional) */ + struct rosePresentedNumberUnscreened diverting; + + /*! \brief Original called number (Optional) */ + struct rosePresentedNumberUnscreened original_called; + + /*! \brief TRUE if Diverting number is present */ + u_int8_t diverting_present; + + /*! \brief TRUE if OriginalCalled is present */ + u_int8_t original_called_present; + + /*! + * \details + * unknown(0), + * cfu(1), + * cfb(2), + * cfnr(3), + * cdAlerting(4), + * cdImmediate(5) + */ + u_int8_t diversion_reason; + + /*! \brief Range 1-5 */ + u_int8_t diversion_counter; +}; + +/* + * ARGUMENT presentationAllowedIndicator PresentationAllowedIndicator + */ +struct roseEtsiDivertingLegInformation3_ARG { + /*! \brief TRUE if presentation is allowed */ + u_int8_t presentation_allowed_indicator; +}; + + +/* ------------------------------------------------------------------- */ + + +/* + * ARGUMENT LinkId + */ +struct roseEtsiExplicitEctExecute_ARG { + int16_t link_id; +}; + +/* + * ARGUMENT transferredToSubaddress PartySubaddress + */ +struct roseEtsiSubaddressTransfer_ARG { + /*! \brief Transferred to subaddress */ + struct rosePartySubaddress subaddress; +}; + + +/* + * RESULT LinkId + */ +struct roseEtsiEctLinkIdRequest_RES { + int16_t link_id; +}; + + +/* + * ARGUMENT SEQUENCE { + * ENUMERATED { + * alerting (0), + * active (1) + * }, + * redirectionNumber PresentedNumberUnscreened OPTIONAL + * } + */ +struct roseEtsiEctInform_ARG { + /*! \brief Redirection Number (Optional) */ + struct rosePresentedNumberUnscreened redirection; + + /*! \brief TRUE if the Redirection Number is present */ + u_int8_t redirection_present; + + /*! \details alerting(0), active(1) */ + u_int8_t status; +}; + + +/* + * ARGUMENT CallTransferIdentity + */ +struct roseEtsiEctLoopTest_ARG { + int8_t call_transfer_id; +}; + +/* + * RESULT LoopResult + */ +struct roseEtsiEctLoopTest_RES { + /*! + * \details + * insufficientInformation(0), + * noLoopExists(1), + * simultaneousTransfer(2) + */ + u_int8_t loop_result; +}; + + +/* ------------------------------------------------------------------- */ + + /* * Name ::= CHOICE { * -- iso8859-1 is implied in namePresentationAllowedSimple. @@ -2638,12 +3263,40 @@ union rose_msg_invoke_etsi_args { struct roseEtsiAOCDChargingUnit_ARG AOCDChargingUnit; struct roseEtsiAOCECurrency_ARG AOCECurrency; struct roseEtsiAOCEChargingUnit_ARG AOCEChargingUnit; + + /* ETSI Call Diversion */ + struct roseEtsiActivationDiversion_ARG ActivationDiversion; + struct roseEtsiDeactivationDiversion_ARG DeactivationDiversion; + struct roseEtsiActivationStatusNotificationDiv_ARG ActivationStatusNotificationDiv; + struct roseEtsiDeactivationStatusNotificationDiv_ARG + DeactivationStatusNotificationDiv; + struct roseEtsiInterrogationDiversion_ARG InterrogationDiversion; + struct roseEtsiDiversionInformation_ARG DiversionInformation; + struct roseEtsiCallDeflection_ARG CallDeflection; + struct roseEtsiCallRerouting_ARG CallRerouting; + struct roseEtsiDivertingLegInformation1_ARG DivertingLegInformation1; + struct roseEtsiDivertingLegInformation2_ARG DivertingLegInformation2; + struct roseEtsiDivertingLegInformation3_ARG DivertingLegInformation3; + + /* ETSI Explicit Call Transfer (ECT) */ + struct roseEtsiExplicitEctExecute_ARG ExplicitEctExecute; + struct roseEtsiSubaddressTransfer_ARG SubaddressTransfer; + struct roseEtsiEctInform_ARG EctInform; + struct roseEtsiEctLoopTest_ARG EctLoopTest; }; /*! \brief Facility ie result etsi messages with arguments. */ union rose_msg_result_etsi_args { /* ETSI Advice Of Charge (AOC) */ struct roseEtsiChargingRequest_RES ChargingRequest; + + /* ETSI Call Diversion */ + struct roseEtsiForwardingList InterrogationDiversion; + struct roseEtsiServedUserNumberList InterrogateServedUserNumbers; + + /* ETSI Explicit Call Transfer (ECT) */ + struct roseEtsiEctLinkIdRequest_RES EctLinkIdRequest; + struct roseEtsiEctLoopTest_RES EctLoopTest; }; /*! \brief Facility ie invoke qsig messages with arguments. */ diff --git a/rose_etsi_diversion.c b/rose_etsi_diversion.c new file mode 100644 index 0000000..ddb571c --- /dev/null +++ b/rose_etsi_diversion.c @@ -0,0 +1,1623 @@ +/* + * libpri: An implementation of Primary Rate ISDN + * + * Copyright (C) 2009 Digium, Inc. + * + * Richard Mudgett + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2 as published by the + * Free Software Foundation. See the LICENSE file included with + * this program for more details. + * + * In addition, when this program is distributed with Asterisk in + * any form that would qualify as a 'combined work' or as a + * 'derivative work' (but not mere aggregation), you can redistribute + * and/or modify the combination under the terms of the license + * provided with that copy of Asterisk, instead of the license + * terms granted here. + */ + +/*! + * \file + * \brief ROSE Call diversion operations + * + * Diversion Supplementary Services ETS 300 207-1 Table 3 + * + * \author Richard Mudgett + */ + + +#include "compat.h" +#include "libpri.h" +#include "pri_internal.h" +#include "rose.h" +#include "rose_internal.h" +#include "asn1.h" + + +/* ------------------------------------------------------------------- */ + +/*! + * \internal + * \brief Encode the ServedUserNr type. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * \param served_user_number Served user number information to encode. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *rose_enc_etsi_ServedUserNumber(struct pri *ctrl, + unsigned char *pos, unsigned char *end, + const struct rosePartyNumber *served_user_number) +{ + if (served_user_number->length) { + /* Forward this number */ + pos = rose_enc_PartyNumber(ctrl, pos, end, served_user_number); + } else { + /* Forward all numbers */ + pos = asn1_enc_null(pos, end, ASN1_TYPE_NULL); + } + + return pos; +} + +/*! + * \internal + * \brief Encode the IntResult type. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * \param tag Component tag to identify the encoded component. + * The tag should be ASN1_TAG_SEQUENCE unless the caller implicitly + * tags it otherwise. + * \param int_result Forwarding record information to encode. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *rose_enc_etsi_IntResult(struct pri *ctrl, unsigned char *pos, + unsigned char *end, unsigned tag, const struct roseEtsiForwardingRecord *int_result) +{ + unsigned char *seq_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, tag); + + ASN1_CALL(pos, rose_enc_etsi_ServedUserNumber(ctrl, pos, end, + &int_result->served_user_number)); + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, + int_result->basic_service)); + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, int_result->procedure)); + ASN1_CALL(pos, rose_enc_Address(ctrl, pos, end, ASN1_TAG_SEQUENCE, + &int_result->forwarded_to)); + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \internal + * \brief Encode the IntResultList type. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * \param tag Component tag to identify the encoded component. + * The tag should be ASN1_TAG_SET unless the caller implicitly + * tags it otherwise. + * \param int_result_list Forwarding record list information to encode. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *rose_enc_etsi_IntResultList(struct pri *ctrl, unsigned char *pos, + unsigned char *end, unsigned tag, + const struct roseEtsiForwardingList *int_result_list) +{ + unsigned index; + unsigned char *set_len; + + ASN1_CONSTRUCTED_BEGIN(set_len, pos, end, tag); + + for (index = 0; index < int_result_list->num_records; ++index) { + ASN1_CALL(pos, rose_enc_etsi_IntResult(ctrl, pos, end, ASN1_TAG_SEQUENCE, + &int_result_list->list[index])); + } + + ASN1_CONSTRUCTED_END(set_len, pos, end); + + return pos; +} + +/*! + * \internal + * \brief Encode the ServedUserNumberList type. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * \param tag Component tag to identify the encoded component. + * The tag should be ASN1_TAG_SET unless the caller implicitly + * tags it otherwise. + * \param served_user_number_list Served user record list information to encode. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *rose_enc_etsi_ServedUserNumberList(struct pri *ctrl, + unsigned char *pos, unsigned char *end, unsigned tag, + const struct roseEtsiServedUserNumberList *served_user_number_list) +{ + unsigned index; + unsigned char *set_len; + + ASN1_CONSTRUCTED_BEGIN(set_len, pos, end, tag); + + for (index = 0; index < served_user_number_list->num_records; ++index) { + ASN1_CALL(pos, rose_enc_PartyNumber(ctrl, pos, end, + &served_user_number_list->number[index])); + } + + ASN1_CONSTRUCTED_END(set_len, pos, end); + + return pos; +} + +/*! + * \brief Encode the ActivationDiversion invoke facility ie arguments. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * \param args Arguments to encode in the buffer. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +unsigned char *rose_enc_etsi_ActivationDiversion_ARG(struct pri *ctrl, + unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) +{ + const struct roseEtsiActivationDiversion_ARG *activation_diversion; + unsigned char *seq_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); + + activation_diversion = &args->etsi.ActivationDiversion; + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, + activation_diversion->procedure)); + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, + activation_diversion->basic_service)); + ASN1_CALL(pos, rose_enc_Address(ctrl, pos, end, ASN1_TAG_SEQUENCE, + &activation_diversion->forwarded_to)); + ASN1_CALL(pos, rose_enc_etsi_ServedUserNumber(ctrl, pos, end, + &activation_diversion->served_user_number)); + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \brief Encode the DeactivationDiversion invoke facility ie arguments. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * \param args Arguments to encode in the buffer. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +unsigned char *rose_enc_etsi_DeactivationDiversion_ARG(struct pri *ctrl, + unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) +{ + const struct roseEtsiDeactivationDiversion_ARG *deactivation_diversion; + unsigned char *seq_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); + + deactivation_diversion = &args->etsi.DeactivationDiversion; + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, + deactivation_diversion->procedure)); + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, + deactivation_diversion->basic_service)); + ASN1_CALL(pos, rose_enc_etsi_ServedUserNumber(ctrl, pos, end, + &deactivation_diversion->served_user_number)); + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \brief Encode the ActivationStatusNotificationDiv invoke facility ie arguments. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * \param args Arguments to encode in the buffer. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +unsigned char *rose_enc_etsi_ActivationStatusNotificationDiv_ARG(struct pri *ctrl, + unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) +{ + const struct roseEtsiActivationStatusNotificationDiv_ARG + *activation_status_notification_div; + unsigned char *seq_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); + + activation_status_notification_div = &args->etsi.ActivationStatusNotificationDiv; + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, + activation_status_notification_div->procedure)); + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, + activation_status_notification_div->basic_service)); + ASN1_CALL(pos, rose_enc_Address(ctrl, pos, end, ASN1_TAG_SEQUENCE, + &activation_status_notification_div->forwarded_to)); + ASN1_CALL(pos, rose_enc_etsi_ServedUserNumber(ctrl, pos, end, + &activation_status_notification_div->served_user_number)); + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \brief Encode the DeactivationStatusNotificationDiv invoke facility ie arguments. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * \param args Arguments to encode in the buffer. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +unsigned char *rose_enc_etsi_DeactivationStatusNotificationDiv_ARG(struct pri *ctrl, + unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) +{ + const struct roseEtsiDeactivationStatusNotificationDiv_ARG + *deactivation_status_notification_div; + unsigned char *seq_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); + + deactivation_status_notification_div = &args->etsi.DeactivationStatusNotificationDiv; + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, + deactivation_status_notification_div->procedure)); + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, + deactivation_status_notification_div->basic_service)); + ASN1_CALL(pos, rose_enc_etsi_ServedUserNumber(ctrl, pos, end, + &deactivation_status_notification_div->served_user_number)); + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \brief Encode the InterrogationDiversion invoke facility ie arguments. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * \param args Arguments to encode in the buffer. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +unsigned char *rose_enc_etsi_InterrogationDiversion_ARG(struct pri *ctrl, + unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) +{ + const struct roseEtsiInterrogationDiversion_ARG *interrogation_diversion; + unsigned char *seq_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); + + interrogation_diversion = &args->etsi.InterrogationDiversion; + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, + interrogation_diversion->procedure)); + if (interrogation_diversion->basic_service) { + /* Not the DEFAULT value */ + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, + interrogation_diversion->basic_service)); + } + ASN1_CALL(pos, rose_enc_etsi_ServedUserNumber(ctrl, pos, end, + &interrogation_diversion->served_user_number)); + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \brief Encode the InterrogationDiversion result facility ie arguments. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * \param args Arguments to encode in the buffer. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +unsigned char *rose_enc_etsi_InterrogationDiversion_RES(struct pri *ctrl, + unsigned char *pos, unsigned char *end, const union rose_msg_result_args *args) +{ + return rose_enc_etsi_IntResultList(ctrl, pos, end, ASN1_TAG_SET, + &args->etsi.InterrogationDiversion); +} + +/*! + * \brief Encode the DiversionInformation invoke facility ie arguments. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * \param args Arguments to encode in the buffer. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +unsigned char *rose_enc_etsi_DiversionInformation_ARG(struct pri *ctrl, + unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) +{ + const struct roseEtsiDiversionInformation_ARG *diversion_information; + unsigned char *seq_len; + unsigned char *explicit_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); + + diversion_information = &args->etsi.DiversionInformation; + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, + diversion_information->diversion_reason)); + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, + diversion_information->basic_service)); + if (diversion_information->served_user_subaddress.length) { + ASN1_CALL(pos, rose_enc_PartySubaddress(ctrl, pos, end, + &diversion_information->served_user_subaddress)); + } + if (diversion_information->calling_present) { + /* EXPLICIT tag */ + ASN1_CONSTRUCTED_BEGIN(explicit_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 0); + ASN1_CALL(pos, rose_enc_PresentedAddressScreened(ctrl, pos, end, + &diversion_information->calling)); + ASN1_CONSTRUCTED_END(explicit_len, pos, end); + } + if (diversion_information->original_called_present) { + /* EXPLICIT tag */ + ASN1_CONSTRUCTED_BEGIN(explicit_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 1); + ASN1_CALL(pos, rose_enc_PresentedNumberUnscreened(ctrl, pos, end, + &diversion_information->original_called)); + ASN1_CONSTRUCTED_END(explicit_len, pos, end); + } + if (diversion_information->last_diverting_present) { + /* EXPLICIT tag */ + ASN1_CONSTRUCTED_BEGIN(explicit_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 2); + ASN1_CALL(pos, rose_enc_PresentedNumberUnscreened(ctrl, pos, end, + &diversion_information->last_diverting)); + ASN1_CONSTRUCTED_END(explicit_len, pos, end); + } + if (diversion_information->last_diverting_reason_present) { + /* EXPLICIT tag */ + ASN1_CONSTRUCTED_BEGIN(explicit_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 3); + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, + diversion_information->last_diverting_reason)); + ASN1_CONSTRUCTED_END(explicit_len, pos, end); + } + if (diversion_information->q931ie.length) { + ASN1_CALL(pos, rose_enc_Q931ie(ctrl, pos, end, ASN1_CLASS_APPLICATION | 0, + &diversion_information->q931ie)); + } + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \brief Encode the CallDeflection invoke facility ie arguments. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * \param args Arguments to encode in the buffer. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +unsigned char *rose_enc_etsi_CallDeflection_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args) +{ + const struct roseEtsiCallDeflection_ARG *call_deflection; + unsigned char *seq_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); + + call_deflection = &args->etsi.CallDeflection; + ASN1_CALL(pos, rose_enc_Address(ctrl, pos, end, ASN1_TAG_SEQUENCE, + &call_deflection->deflection)); + if (call_deflection->presentation_allowed_to_diverted_to_user_present) { + ASN1_CALL(pos, asn1_enc_boolean(pos, end, ASN1_TYPE_BOOLEAN, + call_deflection->presentation_allowed_to_diverted_to_user)); + } + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \brief Encode the CallRerouting invoke facility ie arguments. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * \param args Arguments to encode in the buffer. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +unsigned char *rose_enc_etsi_CallRerouting_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args) +{ + const struct roseEtsiCallRerouting_ARG *call_rerouting; + unsigned char *seq_len; + unsigned char *explicit_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); + + call_rerouting = &args->etsi.CallRerouting; + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, + call_rerouting->rerouting_reason)); + ASN1_CALL(pos, rose_enc_Address(ctrl, pos, end, ASN1_TAG_SEQUENCE, + &call_rerouting->called_address)); + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_INTEGER, + call_rerouting->rerouting_counter)); + ASN1_CALL(pos, rose_enc_Q931ie(ctrl, pos, end, ASN1_CLASS_APPLICATION | 0, + &call_rerouting->q931ie)); + + /* EXPLICIT tag */ + ASN1_CONSTRUCTED_BEGIN(explicit_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 1); + ASN1_CALL(pos, rose_enc_PresentedNumberUnscreened(ctrl, pos, end, + &call_rerouting->last_rerouting)); + ASN1_CONSTRUCTED_END(explicit_len, pos, end); + + if (call_rerouting->subscription_option) { + /* Not the DEFAULT value */ + /* EXPLICIT tag */ + ASN1_CONSTRUCTED_BEGIN(explicit_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 2); + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, + call_rerouting->subscription_option)); + ASN1_CONSTRUCTED_END(explicit_len, pos, end); + } + + if (call_rerouting->calling_subaddress.length) { + /* EXPLICIT tag */ + ASN1_CONSTRUCTED_BEGIN(explicit_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 3); + ASN1_CALL(pos, rose_enc_PartySubaddress(ctrl, pos, end, + &call_rerouting->calling_subaddress)); + ASN1_CONSTRUCTED_END(explicit_len, pos, end); + } + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \brief Encode the InterrogateServedUserNumbers result facility ie arguments. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * \param args Arguments to encode in the buffer. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +unsigned char *rose_enc_etsi_InterrogateServedUserNumbers_RES(struct pri *ctrl, + unsigned char *pos, unsigned char *end, const union rose_msg_result_args *args) +{ + return rose_enc_etsi_ServedUserNumberList(ctrl, pos, end, ASN1_TAG_SET, + &args->etsi.InterrogateServedUserNumbers); +} + +/*! + * \brief Encode the DivertingLegInformation1 invoke facility ie arguments. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * \param args Arguments to encode in the buffer. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +unsigned char *rose_enc_etsi_DivertingLegInformation1_ARG(struct pri *ctrl, + unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) +{ + const struct roseEtsiDivertingLegInformation1_ARG *diverting_leg_information_1; + unsigned char *seq_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); + + diverting_leg_information_1 = &args->etsi.DivertingLegInformation1; + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, + diverting_leg_information_1->diversion_reason)); + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, + diverting_leg_information_1->subscription_option)); + if (diverting_leg_information_1->diverted_to_present) { + ASN1_CALL(pos, rose_enc_PresentedNumberUnscreened(ctrl, pos, end, + &diverting_leg_information_1->diverted_to)); + } + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \brief Encode the DivertingLegInformation2 invoke facility ie arguments. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * \param args Arguments to encode in the buffer. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +unsigned char *rose_enc_etsi_DivertingLegInformation2_ARG(struct pri *ctrl, + unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) +{ + const struct roseEtsiDivertingLegInformation2_ARG *diverting_leg_information_2; + unsigned char *seq_len; + unsigned char *explicit_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); + + diverting_leg_information_2 = &args->etsi.DivertingLegInformation2; + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_INTEGER, + diverting_leg_information_2->diversion_counter)); + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, + diverting_leg_information_2->diversion_reason)); + + if (diverting_leg_information_2->diverting_present) { + /* EXPLICIT tag */ + ASN1_CONSTRUCTED_BEGIN(explicit_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 1); + ASN1_CALL(pos, rose_enc_PresentedNumberUnscreened(ctrl, pos, end, + &diverting_leg_information_2->diverting)); + ASN1_CONSTRUCTED_END(explicit_len, pos, end); + } + + if (diverting_leg_information_2->original_called_present) { + /* EXPLICIT tag */ + ASN1_CONSTRUCTED_BEGIN(explicit_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 2); + ASN1_CALL(pos, rose_enc_PresentedNumberUnscreened(ctrl, pos, end, + &diverting_leg_information_2->original_called)); + ASN1_CONSTRUCTED_END(explicit_len, pos, end); + } + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \brief Encode the DivertingLegInformation3 invoke facility ie arguments. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * \param args Arguments to encode in the buffer. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +unsigned char *rose_enc_etsi_DivertingLegInformation3_ARG(struct pri *ctrl, + unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) +{ + return asn1_enc_boolean(pos, end, ASN1_TYPE_BOOLEAN, + args->etsi.DivertingLegInformation3.presentation_allowed_indicator); +} + +/*! + * \internal + * \brief Decode the ServedUserNr argument parameters. + * + * \param ctrl D channel controller for any diagnostic messages. + * \param name Field name + * \param tag Component tag that identified this production. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param served_user_number Parameter storage to fill. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +static const unsigned char *rose_dec_etsi_ServedUserNumber(struct pri *ctrl, + const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, + struct rosePartyNumber *served_user_number) +{ + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " %s ServedUserNumber\n", name); + } + if (tag == ASN1_TYPE_NULL) { + served_user_number->length = 0; + pos = asn1_dec_null(ctrl, "allNumbers", tag, pos, end); + } else { + /* Must be a PartyNumber (Which is itself a CHOICE) */ + pos = + rose_dec_PartyNumber(ctrl, "individualNumber", tag, pos, end, + served_user_number); + } + return pos; +} + +/*! + * \internal + * \brief Decode the IntResult argument parameters. + * + * \param ctrl D channel controller for any diagnostic messages. + * \param name Field name + * \param tag Component tag that identified this production. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param int_result Parameter storage to fill. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +static const unsigned char *rose_dec_etsi_IntResult(struct pri *ctrl, const char *name, + unsigned tag, const unsigned char *pos, const unsigned char *end, + struct roseEtsiForwardingRecord *int_result) +{ + int32_t value; + int length; + int seq_offset; + const unsigned char *seq_end; + + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " %s IntResult %s\n", name, asn1_tag2str(tag)); + } + ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); + ASN1_END_SETUP(seq_end, seq_offset, length, pos, end); + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CALL(pos, rose_dec_etsi_ServedUserNumber(ctrl, "servedUserNr", tag, pos, + seq_end, &int_result->served_user_number)); + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "basicService", tag, pos, seq_end, &value)); + int_result->basic_service = value; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "procedure", tag, pos, seq_end, &value)); + int_result->procedure = value; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + ASN1_CALL(pos, rose_dec_Address(ctrl, "forwardedToAddress", tag, pos, seq_end, + &int_result->forwarded_to)); + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \internal + * \brief Decode the IntResultList argument parameters. + * + * \param ctrl D channel controller for any diagnostic messages. + * \param name Field name + * \param tag Component tag that identified this production. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param int_result_list Parameter storage to fill. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +static const unsigned char *rose_dec_etsi_IntResultList(struct pri *ctrl, + const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, + struct roseEtsiForwardingList *int_result_list) +{ + int length; + int set_offset; + const unsigned char *set_end; + + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " %s IntResultList %s\n", name, asn1_tag2str(tag)); + } + ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); + ASN1_END_SETUP(set_end, set_offset, length, pos, end); + + int_result_list->num_records = 0; + while (pos < set_end && *pos != ASN1_INDEF_TERM) { + if (int_result_list->num_records < ARRAY_LEN(int_result_list->list)) { + ASN1_CALL(pos, asn1_dec_tag(pos, set_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + ASN1_CALL(pos, rose_dec_etsi_IntResult(ctrl, "listEntry", tag, pos, set_end, + &int_result_list->list[int_result_list->num_records])); + ++int_result_list->num_records; + } else { + /* Too many records */ + return NULL; + } + } + + ASN1_END_FIXUP(ctrl, pos, set_offset, set_end, end); + + return pos; +} + +/*! + * \internal + * \brief Decode the ServedUserNumberList argument parameters. + * + * \param ctrl D channel controller for any diagnostic messages. + * \param name Field name + * \param tag Component tag that identified this production. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param served_user_number_list Parameter storage to fill. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +static const unsigned char *rose_dec_etsi_ServedUserNumberList(struct pri *ctrl, + const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, + struct roseEtsiServedUserNumberList *served_user_number_list) +{ + int length; + int set_offset; + const unsigned char *set_end; + + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " %s ServedUserNumberList %s\n", name, asn1_tag2str(tag)); + } + ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); + ASN1_END_SETUP(set_end, set_offset, length, pos, end); + + served_user_number_list->num_records = 0; + while (pos < set_end && *pos != ASN1_INDEF_TERM) { + if (served_user_number_list->num_records < + ARRAY_LEN(served_user_number_list->number)) { + ASN1_CALL(pos, asn1_dec_tag(pos, set_end, &tag)); + ASN1_CALL(pos, rose_dec_PartyNumber(ctrl, "listEntry", tag, pos, set_end, + &served_user_number_list->number[served_user_number_list->num_records])); + ++served_user_number_list->num_records; + } else { + /* Too many records */ + return NULL; + } + } + + ASN1_END_FIXUP(ctrl, pos, set_offset, set_end, end); + + return pos; +} + +/*! + * \brief Decode the ActivationDiversion invoke argument parameters. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param tag Component tag that identified this structure. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param args Arguments to fill in from the decoded buffer. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +const unsigned char *rose_dec_etsi_ActivationDiversion_ARG(struct pri *ctrl, + unsigned tag, const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args) +{ + struct roseEtsiActivationDiversion_ARG *activation_diversion; + int length; + int seq_offset; + const unsigned char *seq_end; + int32_t value; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " ActivationDiversion %s\n", asn1_tag2str(tag)); + } + ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); + ASN1_END_SETUP(seq_end, seq_offset, length, pos, end); + + activation_diversion = &args->etsi.ActivationDiversion; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "procedure", tag, pos, seq_end, &value)); + activation_diversion->procedure = value; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "basicService", tag, pos, seq_end, &value)); + activation_diversion->basic_service = value; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + ASN1_CALL(pos, rose_dec_Address(ctrl, "forwardedToAddress", tag, pos, seq_end, + &activation_diversion->forwarded_to)); + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CALL(pos, rose_dec_etsi_ServedUserNumber(ctrl, "servedUserNr", tag, pos, + seq_end, &activation_diversion->served_user_number)); + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \brief Decode the DeactivationDiversion invoke argument parameters. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param tag Component tag that identified this structure. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param args Arguments to fill in from the decoded buffer. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +const unsigned char *rose_dec_etsi_DeactivationDiversion_ARG(struct pri *ctrl, + unsigned tag, const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args) +{ + struct roseEtsiDeactivationDiversion_ARG *deactivation_diversion; + int length; + int seq_offset; + const unsigned char *seq_end; + int32_t value; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " DeactivationDiversion %s\n", asn1_tag2str(tag)); + } + ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); + ASN1_END_SETUP(seq_end, seq_offset, length, pos, end); + + deactivation_diversion = &args->etsi.DeactivationDiversion; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "procedure", tag, pos, seq_end, &value)); + deactivation_diversion->procedure = value; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "basicService", tag, pos, seq_end, &value)); + deactivation_diversion->basic_service = value; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CALL(pos, rose_dec_etsi_ServedUserNumber(ctrl, "servedUserNr", tag, pos, + seq_end, &deactivation_diversion->served_user_number)); + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \brief Decode the ActivationStatusNotificationDiv invoke argument parameters. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param tag Component tag that identified this structure. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param args Arguments to fill in from the decoded buffer. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +const unsigned char *rose_dec_etsi_ActivationStatusNotificationDiv_ARG(struct pri *ctrl, + unsigned tag, const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args) +{ + struct roseEtsiActivationStatusNotificationDiv_ARG + *activation_status_notification_div; + int length; + int seq_offset; + const unsigned char *seq_end; + int32_t value; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " ActivationStatusNotificationDiv %s\n", asn1_tag2str(tag)); + } + ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); + ASN1_END_SETUP(seq_end, seq_offset, length, pos, end); + + activation_status_notification_div = &args->etsi.ActivationStatusNotificationDiv; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "procedure", tag, pos, seq_end, &value)); + activation_status_notification_div->procedure = value; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "basicService", tag, pos, seq_end, &value)); + activation_status_notification_div->basic_service = value; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + ASN1_CALL(pos, rose_dec_Address(ctrl, "forwardedToAddress", tag, pos, seq_end, + &activation_status_notification_div->forwarded_to)); + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CALL(pos, rose_dec_etsi_ServedUserNumber(ctrl, "servedUserNr", tag, pos, + seq_end, &activation_status_notification_div->served_user_number)); + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \brief Decode the DeactivationStatusNotificationDiv invoke argument parameters. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param tag Component tag that identified this structure. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param args Arguments to fill in from the decoded buffer. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +const unsigned char *rose_dec_etsi_DeactivationStatusNotificationDiv_ARG(struct pri + *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args) +{ + struct roseEtsiDeactivationStatusNotificationDiv_ARG + *deactivation_status_notification_div; + int length; + int seq_offset; + const unsigned char *seq_end; + int32_t value; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " DeactivationStatusNotificationDiv %s\n", asn1_tag2str(tag)); + } + ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); + ASN1_END_SETUP(seq_end, seq_offset, length, pos, end); + + deactivation_status_notification_div = &args->etsi.DeactivationStatusNotificationDiv; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "procedure", tag, pos, seq_end, &value)); + deactivation_status_notification_div->procedure = value; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "basicService", tag, pos, seq_end, &value)); + deactivation_status_notification_div->basic_service = value; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CALL(pos, rose_dec_etsi_ServedUserNumber(ctrl, "forwardedToAddress", tag, pos, + seq_end, &deactivation_status_notification_div->served_user_number)); + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \brief Decode the InterrogationDiversion invoke argument parameters. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param tag Component tag that identified this structure. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param args Arguments to fill in from the decoded buffer. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +const unsigned char *rose_dec_etsi_InterrogationDiversion_ARG(struct pri *ctrl, + unsigned tag, const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args) +{ + struct roseEtsiInterrogationDiversion_ARG *interrogation_diversion; + int length; + int seq_offset; + const unsigned char *seq_end; + int32_t value; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " InterrogationDiversion %s\n", asn1_tag2str(tag)); + } + ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); + ASN1_END_SETUP(seq_end, seq_offset, length, pos, end); + + interrogation_diversion = &args->etsi.InterrogationDiversion; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "procedure", tag, pos, seq_end, &value)); + interrogation_diversion->procedure = value; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + if (tag == ASN1_TYPE_ENUMERATED) { + ASN1_CALL(pos, asn1_dec_int(ctrl, "basicService", tag, pos, seq_end, &value)); + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + } else { + value = 0; /* DEFAULT BasicService value (allServices) */ + } + interrogation_diversion->basic_service = value; + + ASN1_CALL(pos, rose_dec_etsi_ServedUserNumber(ctrl, "servedUserNr", tag, pos, + seq_end, &interrogation_diversion->served_user_number)); + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \brief Decode the InterrogationDiversion result parameters. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param tag Component tag that identified this structure. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param args Arguments to fill in from the decoded buffer. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +const unsigned char *rose_dec_etsi_InterrogationDiversion_RES(struct pri *ctrl, + unsigned tag, const unsigned char *pos, const unsigned char *end, + union rose_msg_result_args *args) +{ + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SET); + return rose_dec_etsi_IntResultList(ctrl, "diversionList", tag, pos, end, + &args->etsi.InterrogationDiversion); +} + +/*! + * \brief Decode the DiversionInformation invoke argument parameters. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param tag Component tag that identified this structure. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param args Arguments to fill in from the decoded buffer. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +const unsigned char *rose_dec_etsi_DiversionInformation_ARG(struct pri *ctrl, + unsigned tag, const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args) +{ + struct roseEtsiDiversionInformation_ARG *diversion_information; + int length; + int seq_offset; + int explicit_offset; + const unsigned char *seq_end; + const unsigned char *explicit_end; + const unsigned char *save_pos; + int32_t value; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " DiversionInformation %s\n", asn1_tag2str(tag)); + } + ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); + ASN1_END_SETUP(seq_end, seq_offset, length, pos, end); + + diversion_information = &args->etsi.DiversionInformation; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "diversionReason", tag, pos, seq_end, &value)); + diversion_information->diversion_reason = value; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "basicService", tag, pos, seq_end, &value)); + diversion_information->basic_service = value; + + /* + * A sequence specifies an ordered list of component types. + * However, for simplicity we are not checking the order of + * the remaining optional components. + */ + diversion_information->served_user_subaddress.length = 0; + diversion_information->calling_present = 0; + diversion_information->original_called_present = 0; + diversion_information->last_diverting_present = 0; + diversion_information->last_diverting_reason_present = 0; + diversion_information->q931ie.length = 0; + while (pos < seq_end && *pos != ASN1_INDEF_TERM) { + save_pos = pos; + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + switch (tag) { + case ASN1_TAG_SEQUENCE: + case ASN1_TYPE_OCTET_STRING: + case ASN1_TYPE_OCTET_STRING | ASN1_PC_CONSTRUCTED: + ASN1_CALL(pos, rose_dec_PartySubaddress(ctrl, "servedUserSubaddress", tag, + pos, seq_end, &diversion_information->served_user_subaddress)); + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 0: + /* Remove EXPLICIT tag */ + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " Explicit %s\n", asn1_tag2str(tag)); + } + ASN1_CALL(pos, asn1_dec_length(pos, seq_end, &length)); + ASN1_END_SETUP(explicit_end, explicit_offset, length, pos, seq_end); + + ASN1_CALL(pos, asn1_dec_tag(pos, explicit_end, &tag)); + ASN1_CALL(pos, rose_dec_PresentedAddressScreened(ctrl, "callingAddress", tag, + pos, explicit_end, &diversion_information->calling)); + diversion_information->calling_present = 1; + + ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, seq_end); + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 1: + /* Remove EXPLICIT tag */ + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " Explicit %s\n", asn1_tag2str(tag)); + } + ASN1_CALL(pos, asn1_dec_length(pos, seq_end, &length)); + ASN1_END_SETUP(explicit_end, explicit_offset, length, pos, seq_end); + + ASN1_CALL(pos, asn1_dec_tag(pos, explicit_end, &tag)); + ASN1_CALL(pos, rose_dec_PresentedNumberUnscreened(ctrl, "originalCalledNr", + tag, pos, explicit_end, &diversion_information->original_called)); + diversion_information->original_called_present = 1; + + ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, seq_end); + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 2: + /* Remove EXPLICIT tag */ + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " Explicit %s\n", asn1_tag2str(tag)); + } + ASN1_CALL(pos, asn1_dec_length(pos, seq_end, &length)); + ASN1_END_SETUP(explicit_end, explicit_offset, length, pos, seq_end); + + ASN1_CALL(pos, asn1_dec_tag(pos, explicit_end, &tag)); + ASN1_CALL(pos, rose_dec_PresentedNumberUnscreened(ctrl, "lastDivertingNr", + tag, pos, explicit_end, &diversion_information->last_diverting)); + diversion_information->last_diverting_present = 1; + + ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, seq_end); + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 3: + /* Remove EXPLICIT tag */ + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " Explicit %s\n", asn1_tag2str(tag)); + } + ASN1_CALL(pos, asn1_dec_length(pos, seq_end, &length)); + ASN1_END_SETUP(explicit_end, explicit_offset, length, pos, seq_end); + + ASN1_CALL(pos, asn1_dec_tag(pos, explicit_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "lastDivertingReason", tag, pos, + explicit_end, &value)); + diversion_information->last_diverting_reason = value; + diversion_information->last_diverting_reason_present = 1; + + ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, seq_end); + break; + case ASN1_CLASS_APPLICATION | 0: + case ASN1_CLASS_APPLICATION | ASN1_PC_CONSTRUCTED | 0: + ASN1_CALL(pos, rose_dec_Q931ie(ctrl, "userInfo", tag, pos, seq_end, + &diversion_information->q931ie, + sizeof(diversion_information->q931ie_contents))); + break; + default: + pos = save_pos; + goto cancel_options; + } + } +cancel_options:; + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \brief Decode the CallDeflection invoke argument parameters. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param tag Component tag that identified this structure. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param args Arguments to fill in from the decoded buffer. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +const unsigned char *rose_dec_etsi_CallDeflection_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) +{ + struct roseEtsiCallDeflection_ARG *call_deflection; + int length; + int seq_offset; + const unsigned char *seq_end; + int32_t value; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " CallDeflection %s\n", asn1_tag2str(tag)); + } + ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); + ASN1_END_SETUP(seq_end, seq_offset, length, pos, end); + + call_deflection = &args->etsi.CallDeflection; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + ASN1_CALL(pos, rose_dec_Address(ctrl, "deflectionAddress", tag, pos, seq_end, + &call_deflection->deflection)); + + if (pos < seq_end && *pos != ASN1_INDEF_TERM) { + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_BOOLEAN); + ASN1_CALL(pos, asn1_dec_boolean(ctrl, "presentationAllowedDivertedToUser", tag, + pos, seq_end, &value)); + call_deflection->presentation_allowed_to_diverted_to_user = value; + call_deflection->presentation_allowed_to_diverted_to_user_present = 1; + } else { + call_deflection->presentation_allowed_to_diverted_to_user_present = 0; + } + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \brief Decode the CallRerouting invoke argument parameters. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param tag Component tag that identified this structure. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param args Arguments to fill in from the decoded buffer. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +const unsigned char *rose_dec_etsi_CallRerouting_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) +{ + struct roseEtsiCallRerouting_ARG *call_rerouting; + int length; + int seq_offset; + int explicit_offset; + const unsigned char *seq_end; + const unsigned char *explicit_end; + const unsigned char *save_pos; + int32_t value; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " CallRerouting %s\n", asn1_tag2str(tag)); + } + ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); + ASN1_END_SETUP(seq_end, seq_offset, length, pos, end); + + call_rerouting = &args->etsi.CallRerouting; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "reroutingReason", tag, pos, seq_end, &value)); + call_rerouting->rerouting_reason = value; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + ASN1_CALL(pos, rose_dec_Address(ctrl, "calledAddress", tag, pos, seq_end, + &call_rerouting->called_address)); + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_INTEGER); + ASN1_CALL(pos, asn1_dec_int(ctrl, "reroutingCounter", tag, pos, seq_end, &value)); + call_rerouting->rerouting_counter = value; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag & ~ASN1_PC_MASK, ASN1_CLASS_APPLICATION | 0); + ASN1_CALL(pos, rose_dec_Q931ie(ctrl, "q931ie", tag, pos, seq_end, + &call_rerouting->q931ie, sizeof(call_rerouting->q931ie_contents))); + + /* Remove EXPLICIT tag */ + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, + ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 1); + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " Explicit %s\n", asn1_tag2str(tag)); + } + ASN1_CALL(pos, asn1_dec_length(pos, seq_end, &length)); + ASN1_END_SETUP(explicit_end, explicit_offset, length, pos, seq_end); + + ASN1_CALL(pos, asn1_dec_tag(pos, explicit_end, &tag)); + ASN1_CALL(pos, rose_dec_PresentedNumberUnscreened(ctrl, "lastReroutingNr", tag, pos, + explicit_end, &call_rerouting->last_rerouting)); + + ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, seq_end); + + /* + * A sequence specifies an ordered list of component types. + * However, for simplicity we are not checking the order of + * the remaining optional components. + */ + call_rerouting->subscription_option = 0; /* DEFAULT value noNotification */ + call_rerouting->calling_subaddress.length = 0; + while (pos < seq_end && *pos != ASN1_INDEF_TERM) { + save_pos = pos; + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + switch (tag) { + case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 2: + /* Remove EXPLICIT tag */ + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " Explicit %s\n", asn1_tag2str(tag)); + } + ASN1_CALL(pos, asn1_dec_length(pos, seq_end, &length)); + ASN1_END_SETUP(explicit_end, explicit_offset, length, pos, seq_end); + + ASN1_CALL(pos, asn1_dec_tag(pos, explicit_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "subscriptionOption", tag, pos, + explicit_end, &value)); + call_rerouting->subscription_option = value; + + ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, seq_end); + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 3: + /* Remove EXPLICIT tag */ + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " Explicit %s\n", asn1_tag2str(tag)); + } + ASN1_CALL(pos, asn1_dec_length(pos, seq_end, &length)); + ASN1_END_SETUP(explicit_end, explicit_offset, length, pos, seq_end); + + ASN1_CALL(pos, asn1_dec_tag(pos, explicit_end, &tag)); + ASN1_CALL(pos, rose_dec_PartySubaddress(ctrl, "callingPartySubaddress", tag, + pos, explicit_end, &call_rerouting->calling_subaddress)); + + ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, seq_end); + break; + default: + pos = save_pos; + goto cancel_options; + } + } +cancel_options:; + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \brief Decode the InterrogateServedUserNumbers result parameters. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param tag Component tag that identified this structure. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param args Arguments to fill in from the decoded buffer. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +const unsigned char *rose_dec_etsi_InterrogateServedUserNumbers_RES(struct pri *ctrl, + unsigned tag, const unsigned char *pos, const unsigned char *end, + union rose_msg_result_args *args) +{ + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SET); + return rose_dec_etsi_ServedUserNumberList(ctrl, "interrogateServedUserNumbers", tag, + pos, end, &args->etsi.InterrogateServedUserNumbers); +} + +/*! + * \brief Decode the DivertingLegInformation1 invoke argument parameters. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param tag Component tag that identified this structure. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param args Arguments to fill in from the decoded buffer. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +const unsigned char *rose_dec_etsi_DivertingLegInformation1_ARG(struct pri *ctrl, + unsigned tag, const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args) +{ + struct roseEtsiDivertingLegInformation1_ARG *diverting_leg_informtion_1; + int length; + int seq_offset; + const unsigned char *seq_end; + int32_t value; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " DivertingLegInformation1 %s\n", asn1_tag2str(tag)); + } + ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); + ASN1_END_SETUP(seq_end, seq_offset, length, pos, end); + + diverting_leg_informtion_1 = &args->etsi.DivertingLegInformation1; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "diversionReason", tag, pos, seq_end, &value)); + diverting_leg_informtion_1->diversion_reason = value; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "subscriptionOption", tag, pos, seq_end, &value)); + diverting_leg_informtion_1->subscription_option = value; + + if (pos < seq_end && *pos != ASN1_INDEF_TERM) { + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CALL(pos, rose_dec_PresentedNumberUnscreened(ctrl, "divertedToNumber", tag, + pos, seq_end, &diverting_leg_informtion_1->diverted_to)); + diverting_leg_informtion_1->diverted_to_present = 1; + } else { + diverting_leg_informtion_1->diverted_to_present = 0; + } + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \brief Decode the DivertingLegInformation2 invoke argument parameters. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param tag Component tag that identified this structure. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param args Arguments to fill in from the decoded buffer. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +const unsigned char *rose_dec_etsi_DivertingLegInformation2_ARG(struct pri *ctrl, + unsigned tag, const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args) +{ + struct roseEtsiDivertingLegInformation2_ARG *diverting_leg_information_2; + int length; + int seq_offset; + int explicit_offset; + const unsigned char *seq_end; + const unsigned char *explicit_end; + const unsigned char *save_pos; + int32_t value; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " DivertingLegInformation2 %s\n", asn1_tag2str(tag)); + } + ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); + ASN1_END_SETUP(seq_end, seq_offset, length, pos, end); + + diverting_leg_information_2 = &args->etsi.DivertingLegInformation2; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_INTEGER); + ASN1_CALL(pos, asn1_dec_int(ctrl, "diversionCounter", tag, pos, seq_end, &value)); + diverting_leg_information_2->diversion_counter = value; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "diversionReason", tag, pos, seq_end, &value)); + diverting_leg_information_2->diversion_reason = value; + + /* + * A sequence specifies an ordered list of component types. + * However, for simplicity we are not checking the order of + * the remaining optional components. + */ + diverting_leg_information_2->diverting_present = 0; + diverting_leg_information_2->original_called_present = 0; + while (pos < seq_end && *pos != ASN1_INDEF_TERM) { + save_pos = pos; + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + switch (tag) { + case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 1: + /* Remove EXPLICIT tag */ + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " Explicit %s\n", asn1_tag2str(tag)); + } + ASN1_CALL(pos, asn1_dec_length(pos, seq_end, &length)); + ASN1_END_SETUP(explicit_end, explicit_offset, length, pos, seq_end); + + ASN1_CALL(pos, asn1_dec_tag(pos, explicit_end, &tag)); + ASN1_CALL(pos, rose_dec_PresentedNumberUnscreened(ctrl, "divertingNr", tag, + pos, explicit_end, &diverting_leg_information_2->diverting)); + diverting_leg_information_2->diverting_present = 1; + + ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, seq_end); + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 2: + /* Remove EXPLICIT tag */ + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " Explicit %s\n", asn1_tag2str(tag)); + } + ASN1_CALL(pos, asn1_dec_length(pos, seq_end, &length)); + ASN1_END_SETUP(explicit_end, explicit_offset, length, pos, seq_end); + + ASN1_CALL(pos, asn1_dec_tag(pos, explicit_end, &tag)); + ASN1_CALL(pos, rose_dec_PresentedNumberUnscreened(ctrl, "originalCalledNr", + tag, pos, explicit_end, &diverting_leg_information_2->original_called)); + diverting_leg_information_2->original_called_present = 1; + + ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, seq_end); + break; + default: + pos = save_pos; + goto cancel_options; + } + } +cancel_options:; + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \brief Decode the DivertingLegInformation3 invoke argument parameters. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param tag Component tag that identified this structure. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param args Arguments to fill in from the decoded buffer. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +const unsigned char *rose_dec_etsi_DivertingLegInformation3_ARG(struct pri *ctrl, + unsigned tag, const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args) +{ + int32_t value; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_BOOLEAN); + ASN1_CALL(pos, asn1_dec_boolean(ctrl, "presentationAllowedIndicator", tag, pos, end, + &value)); + args->etsi.DivertingLegInformation3.presentation_allowed_indicator = value; + + return pos; +} + +/* ------------------------------------------------------------------- */ +/* end rose_etsi_diversion.c */ diff --git a/rose_etsi_ect.c b/rose_etsi_ect.c new file mode 100644 index 0000000..53326ad --- /dev/null +++ b/rose_etsi_ect.c @@ -0,0 +1,332 @@ +/* + * libpri: An implementation of Primary Rate ISDN + * + * Copyright (C) 2009 Digium, Inc. + * + * Richard Mudgett + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2 as published by the + * Free Software Foundation. See the LICENSE file included with + * this program for more details. + * + * In addition, when this program is distributed with Asterisk in + * any form that would qualify as a 'combined work' or as a + * 'derivative work' (but not mere aggregation), you can redistribute + * and/or modify the combination under the terms of the license + * provided with that copy of Asterisk, instead of the license + * terms granted here. + */ + +/*! + * \file + * \brief ROSE Explicit Call Transfer operations. + * + * Explicit Call Transfer (ECT) Supplementary Services ETS 300 369-1 + * + * \author Richard Mudgett + */ + + +#include "compat.h" +#include "libpri.h" +#include "pri_internal.h" +#include "rose.h" +#include "rose_internal.h" +#include "asn1.h" + + +/* ------------------------------------------------------------------- */ + +/*! + * \brief Encode the ExplicitEctExecute invoke facility ie arguments. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * \param args Arguments to encode in the buffer. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +unsigned char *rose_enc_etsi_ExplicitEctExecute_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args) +{ + return asn1_enc_int(pos, end, ASN1_TYPE_INTEGER, + args->etsi.ExplicitEctExecute.link_id); +} + +/*! + * \brief Encode the SubaddressTransfer invoke facility ie arguments. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * \param args Arguments to encode in the buffer. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +unsigned char *rose_enc_etsi_SubaddressTransfer_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args) +{ + return rose_enc_PartySubaddress(ctrl, pos, end, + &args->etsi.SubaddressTransfer.subaddress); +} + +/*! + * \brief Encode the EctLinkIdRequest result facility ie arguments. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * \param args Arguments to encode in the buffer. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +unsigned char *rose_enc_etsi_EctLinkIdRequest_RES(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_result_args *args) +{ + return asn1_enc_int(pos, end, ASN1_TYPE_INTEGER, + args->etsi.EctLinkIdRequest.link_id); +} + +/*! + * \brief Encode the EctInform invoke facility ie arguments. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * \param args Arguments to encode in the buffer. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +unsigned char *rose_enc_etsi_EctInform_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args) +{ + const struct roseEtsiEctInform_ARG *ect_inform; + unsigned char *seq_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); + + ect_inform = &args->etsi.EctInform; + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, ect_inform->status)); + if (ect_inform->redirection_present) { + ASN1_CALL(pos, rose_enc_PresentedNumberUnscreened(ctrl, pos, end, + &ect_inform->redirection)); + } + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \brief Encode the EctLoopTest invoke facility ie arguments. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * \param args Arguments to encode in the buffer. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +unsigned char *rose_enc_etsi_EctLoopTest_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args) +{ + return asn1_enc_int(pos, end, ASN1_TYPE_INTEGER, + args->etsi.EctLoopTest.call_transfer_id); +} + +/*! + * \brief Encode the EctLoopTest result facility ie arguments. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * \param args Arguments to encode in the buffer. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +unsigned char *rose_enc_etsi_EctLoopTest_RES(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_result_args *args) +{ + return asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, + args->etsi.EctLoopTest.loop_result); +} + +/*! + * \brief Decode the ExplicitEctExecute invoke argument parameters. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param tag Component tag that identified this structure. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param args Arguments to fill in from the decoded buffer. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +const unsigned char *rose_dec_etsi_ExplicitEctExecute_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) +{ + int32_t value; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_INTEGER); + ASN1_CALL(pos, asn1_dec_int(ctrl, "linkId", tag, pos, end, &value)); + args->etsi.ExplicitEctExecute.link_id = value; + + return pos; +} + +/*! + * \brief Decode the SubaddressTransfer invoke argument parameters. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param tag Component tag that identified this structure. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param args Arguments to fill in from the decoded buffer. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +const unsigned char *rose_dec_etsi_SubaddressTransfer_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) +{ + return rose_dec_PartySubaddress(ctrl, "transferredToSubaddress", tag, pos, end, + &args->etsi.SubaddressTransfer.subaddress); +} + +/*! + * \brief Decode the EctLinkIdRequest result argument parameters. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param tag Component tag that identified this structure. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param args Arguments to fill in from the decoded buffer. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +const unsigned char *rose_dec_etsi_EctLinkIdRequest_RES(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_result_args *args) +{ + int32_t value; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_INTEGER); + ASN1_CALL(pos, asn1_dec_int(ctrl, "linkId", tag, pos, end, &value)); + args->etsi.EctLinkIdRequest.link_id = value; + + return pos; +} + +/*! + * \brief Decode the EctInform invoke argument parameters. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param tag Component tag that identified this structure. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param args Arguments to fill in from the decoded buffer. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +const unsigned char *rose_dec_etsi_EctInform_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) +{ + struct roseEtsiEctInform_ARG *ect_inform; + int length; + int seq_offset; + const unsigned char *seq_end; + int32_t value; + + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " EctInform %s\n", asn1_tag2str(tag)); + } + ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); + ASN1_END_SETUP(seq_end, seq_offset, length, pos, end); + + ect_inform = &args->etsi.EctInform; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "callStatus", tag, pos, seq_end, &value)); + ect_inform->status = value; + + if (pos < seq_end && *pos != ASN1_INDEF_TERM) { + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CALL(pos, rose_dec_PresentedNumberUnscreened(ctrl, "redirectionNumber", tag, + pos, seq_end, &ect_inform->redirection)); + ect_inform->redirection_present = 1; + } else { + ect_inform->redirection_present = 0; + } + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \brief Decode the EctLoopTest invoke argument parameters. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param tag Component tag that identified this structure. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param args Arguments to fill in from the decoded buffer. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +const unsigned char *rose_dec_etsi_EctLoopTest_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) +{ + int32_t value; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_INTEGER); + ASN1_CALL(pos, asn1_dec_int(ctrl, "callTransferId", tag, pos, end, &value)); + args->etsi.EctLoopTest.call_transfer_id = value; + + return pos; +} + +/*! + * \brief Decode the EctLoopTest result argument parameters. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param tag Component tag that identified this structure. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param args Arguments to fill in from the decoded buffer. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +const unsigned char *rose_dec_etsi_EctLoopTest_RES(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_result_args *args) +{ + int32_t value; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "loopResult", tag, pos, end, &value)); + args->etsi.EctLoopTest.loop_result = value; + + return pos; +} + +/* ------------------------------------------------------------------- */ +/* end rose_etsi_ect.c */ diff --git a/rose_internal.h b/rose_internal.h index 52af967..83ec353 100644 --- a/rose_internal.h +++ b/rose_internal.h @@ -137,6 +137,107 @@ const unsigned char *rose_dec_etsi_AOCEChargingUnit_ARG(struct pri *ctrl, unsign const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args); +/* ETSI Call Diversion */ +unsigned char *rose_enc_etsi_ActivationDiversion_ARG(struct pri *ctrl, + unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_etsi_DeactivationDiversion_ARG(struct pri *ctrl, + unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_etsi_ActivationStatusNotificationDiv_ARG(struct pri *ctrl, + unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_etsi_DeactivationStatusNotificationDiv_ARG(struct pri *ctrl, + unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_etsi_InterrogationDiversion_ARG(struct pri *ctrl, + unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_etsi_InterrogationDiversion_RES(struct pri *ctrl, + unsigned char *pos, unsigned char *end, const union rose_msg_result_args *args); +unsigned char *rose_enc_etsi_DiversionInformation_ARG(struct pri *ctrl, + unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_etsi_CallDeflection_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_etsi_CallRerouting_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_etsi_InterrogateServedUserNumbers_RES(struct pri *ctrl, + unsigned char *pos, unsigned char *end, const union rose_msg_result_args *args); +unsigned char *rose_enc_etsi_DivertingLegInformation1_ARG(struct pri *ctrl, + unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_etsi_DivertingLegInformation2_ARG(struct pri *ctrl, + unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_etsi_DivertingLegInformation3_ARG(struct pri *ctrl, + unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); + +const unsigned char *rose_dec_etsi_ActivationDiversion_ARG(struct pri *ctrl, + unsigned tag, const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_etsi_DeactivationDiversion_ARG(struct pri *ctrl, + unsigned tag, const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_etsi_ActivationStatusNotificationDiv_ARG(struct pri *ctrl, + unsigned tag, const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_etsi_DeactivationStatusNotificationDiv_ARG(struct pri + *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_etsi_InterrogationDiversion_ARG(struct pri *ctrl, + unsigned tag, const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_etsi_InterrogationDiversion_RES(struct pri *ctrl, + unsigned tag, const unsigned char *pos, const unsigned char *end, + union rose_msg_result_args *args); +const unsigned char *rose_dec_etsi_DiversionInformation_ARG(struct pri *ctrl, + unsigned tag, const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_etsi_CallDeflection_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_etsi_CallRerouting_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_etsi_InterrogateServedUserNumbers_RES(struct pri *ctrl, + unsigned tag, const unsigned char *pos, const unsigned char *end, + union rose_msg_result_args *args); +const unsigned char *rose_dec_etsi_DivertingLegInformation1_ARG(struct pri *ctrl, + unsigned tag, const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_etsi_DivertingLegInformation2_ARG(struct pri *ctrl, + unsigned tag, const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_etsi_DivertingLegInformation3_ARG(struct pri *ctrl, + unsigned tag, const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); + +/* ETSI Explicit Call Transfer (ECT) */ +unsigned char *rose_enc_etsi_ExplicitEctExecute_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_etsi_SubaddressTransfer_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_etsi_EctLinkIdRequest_RES(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_result_args *args); +unsigned char *rose_enc_etsi_EctInform_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_etsi_EctLoopTest_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_etsi_EctLoopTest_RES(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_result_args *args); + +const unsigned char *rose_dec_etsi_ExplicitEctExecute_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_etsi_SubaddressTransfer_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_etsi_EctLinkIdRequest_RES(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_result_args *args); +const unsigned char *rose_dec_etsi_EctInform_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_etsi_EctLoopTest_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_etsi_EctLoopTest_RES(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_result_args *args); + /* Q.SIG Name-Operations */ unsigned char *rose_enc_qsig_Name(struct pri *ctrl, unsigned char *pos, unsigned char *end, const struct roseQsigName *name); diff --git a/rosetest.c b/rosetest.c index 3a66408..fa8d065 100644 --- a/rosetest.c +++ b/rosetest.c @@ -80,6 +80,18 @@ static const struct fac_extension_header fac_headers[] = { static const struct rose_message rose_etsi_msgs[] = { /* *INDENT-OFF* */ + /* Error messages */ + { + .type = ROSE_COMP_TYPE_ERROR, + .component.error.invoke_id = 82, + .component.error.code = ROSE_ERROR_Div_SpecialServiceNr, + }, + { + .type = ROSE_COMP_TYPE_ERROR, + .component.error.invoke_id = 8, + .component.error.code = ROSE_ERROR_ECT_LinkIdNotAssignedByNetwork, + }, + /* Reject messages */ { .type = ROSE_COMP_TYPE_REJECT, @@ -513,6 +525,432 @@ static const struct rose_message rose_etsi_msgs[] = { .component.invoke.args.etsi.AOCEChargingUnit.charging_unit.charging_association.type = 0, .component.invoke.args.etsi.AOCEChargingUnit.charging_unit.charging_association.id = -37, }, + + /* Call diversion */ + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_ActivationDiversion, + .component.invoke.invoke_id = 67, + .component.invoke.linked_id_present = 1, + .component.invoke.linked_id = 27, + .component.invoke.args.etsi.ActivationDiversion.procedure = 2, + .component.invoke.args.etsi.ActivationDiversion.basic_service = 3, + .component.invoke.args.etsi.ActivationDiversion.forwarded_to.number.plan = 4, + .component.invoke.args.etsi.ActivationDiversion.forwarded_to.number.length = 4, + .component.invoke.args.etsi.ActivationDiversion.forwarded_to.number.str = "1803", + .component.invoke.args.etsi.ActivationDiversion.served_user_number.plan = 4, + .component.invoke.args.etsi.ActivationDiversion.served_user_number.length = 4, + .component.invoke.args.etsi.ActivationDiversion.served_user_number.str = "5398", + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_ActivationDiversion, + .component.invoke.invoke_id = 68, + .component.invoke.args.etsi.ActivationDiversion.procedure = 1, + .component.invoke.args.etsi.ActivationDiversion.basic_service = 5, + .component.invoke.args.etsi.ActivationDiversion.forwarded_to.number.plan = 4, + .component.invoke.args.etsi.ActivationDiversion.forwarded_to.number.length = 4, + .component.invoke.args.etsi.ActivationDiversion.forwarded_to.number.str = "1803", + }, + { + .type = ROSE_COMP_TYPE_RESULT, + .component.result.operation = ROSE_ETSI_ActivationDiversion, + .component.result.invoke_id = 69, + }, + + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_DeactivationDiversion, + .component.invoke.invoke_id = 70, + .component.invoke.args.etsi.DeactivationDiversion.procedure = 1, + .component.invoke.args.etsi.DeactivationDiversion.basic_service = 5, + }, + { + .type = ROSE_COMP_TYPE_RESULT, + .component.result.operation = ROSE_ETSI_DeactivationDiversion, + .component.result.invoke_id = 71, + }, + + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_ActivationStatusNotificationDiv, + .component.invoke.invoke_id = 72, + .component.invoke.args.etsi.ActivationStatusNotificationDiv.procedure = 1, + .component.invoke.args.etsi.ActivationStatusNotificationDiv.basic_service = 5, + .component.invoke.args.etsi.ActivationStatusNotificationDiv.forwarded_to.number.plan = 4, + .component.invoke.args.etsi.ActivationStatusNotificationDiv.forwarded_to.number.length = 4, + .component.invoke.args.etsi.ActivationStatusNotificationDiv.forwarded_to.number.str = "1803", + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_DeactivationStatusNotificationDiv, + .component.invoke.invoke_id = 73, + .component.invoke.args.etsi.DeactivationStatusNotificationDiv.procedure = 1, + .component.invoke.args.etsi.DeactivationStatusNotificationDiv.basic_service = 5, + }, + + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_InterrogationDiversion, + .component.invoke.invoke_id = 74, + .component.invoke.args.etsi.InterrogationDiversion.procedure = 1, + .component.invoke.args.etsi.InterrogationDiversion.basic_service = 5, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_InterrogationDiversion, + .component.invoke.invoke_id = 75, + .component.invoke.args.etsi.InterrogationDiversion.procedure = 1, + }, + { + .type = ROSE_COMP_TYPE_RESULT, + .component.result.operation = ROSE_ETSI_InterrogationDiversion, + .component.result.invoke_id = 76, + .component.result.args.etsi.InterrogationDiversion.num_records = 2, + .component.result.args.etsi.InterrogationDiversion.list[0].procedure = 2, + .component.result.args.etsi.InterrogationDiversion.list[0].basic_service = 5, + .component.result.args.etsi.InterrogationDiversion.list[0].forwarded_to.number.plan = 4, + .component.result.args.etsi.InterrogationDiversion.list[0].forwarded_to.number.length = 4, + .component.result.args.etsi.InterrogationDiversion.list[0].forwarded_to.number.str = "1803", + .component.result.args.etsi.InterrogationDiversion.list[1].procedure = 1, + .component.result.args.etsi.InterrogationDiversion.list[1].basic_service = 3, + .component.result.args.etsi.InterrogationDiversion.list[1].forwarded_to.number.plan = 4, + .component.result.args.etsi.InterrogationDiversion.list[1].forwarded_to.number.length = 4, + .component.result.args.etsi.InterrogationDiversion.list[1].forwarded_to.number.str = "1903", + .component.result.args.etsi.InterrogationDiversion.list[1].served_user_number.plan = 4, + .component.result.args.etsi.InterrogationDiversion.list[1].served_user_number.length = 4, + .component.result.args.etsi.InterrogationDiversion.list[1].served_user_number.str = "5398", + }, + + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_DiversionInformation, + .component.invoke.invoke_id = 77, + .component.invoke.args.etsi.DiversionInformation.diversion_reason = 3, + .component.invoke.args.etsi.DiversionInformation.basic_service = 5, + .component.invoke.args.etsi.DiversionInformation.served_user_subaddress.type = 1, + .component.invoke.args.etsi.DiversionInformation.served_user_subaddress.length = 4, + .component.invoke.args.etsi.DiversionInformation.served_user_subaddress.u.nsap = "6492", + .component.invoke.args.etsi.DiversionInformation.calling_present = 1, + .component.invoke.args.etsi.DiversionInformation.calling.presentation = 0, + .component.invoke.args.etsi.DiversionInformation.calling.screened.screening_indicator = 3, + .component.invoke.args.etsi.DiversionInformation.calling.screened.number.plan = 4, + .component.invoke.args.etsi.DiversionInformation.calling.screened.number.length = 4, + .component.invoke.args.etsi.DiversionInformation.calling.screened.number.str = "1803", + .component.invoke.args.etsi.DiversionInformation.original_called_present = 1, + .component.invoke.args.etsi.DiversionInformation.original_called.presentation = 1, + .component.invoke.args.etsi.DiversionInformation.last_diverting_present = 1, + .component.invoke.args.etsi.DiversionInformation.last_diverting.presentation = 2, + .component.invoke.args.etsi.DiversionInformation.last_diverting_reason_present = 1, + .component.invoke.args.etsi.DiversionInformation.last_diverting_reason = 3, + .component.invoke.args.etsi.DiversionInformation.q931ie.length = 5, + .component.invoke.args.etsi.DiversionInformation.q931ie_contents = "79828", + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_DiversionInformation, + .component.invoke.invoke_id = 78, + .component.invoke.args.etsi.DiversionInformation.diversion_reason = 3, + .component.invoke.args.etsi.DiversionInformation.basic_service = 5, + .component.invoke.args.etsi.DiversionInformation.calling_present = 1, + .component.invoke.args.etsi.DiversionInformation.calling.presentation = 1, + .component.invoke.args.etsi.DiversionInformation.original_called_present = 1, + .component.invoke.args.etsi.DiversionInformation.original_called.presentation = 2, + .component.invoke.args.etsi.DiversionInformation.last_diverting_present = 1, + .component.invoke.args.etsi.DiversionInformation.last_diverting.presentation = 1, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_DiversionInformation, + .component.invoke.invoke_id = 79, + .component.invoke.args.etsi.DiversionInformation.diversion_reason = 2, + .component.invoke.args.etsi.DiversionInformation.basic_service = 3, + .component.invoke.args.etsi.DiversionInformation.calling_present = 1, + .component.invoke.args.etsi.DiversionInformation.calling.presentation = 2, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_DiversionInformation, + .component.invoke.invoke_id = 80, + .component.invoke.args.etsi.DiversionInformation.diversion_reason = 3, + .component.invoke.args.etsi.DiversionInformation.basic_service = 5, + .component.invoke.args.etsi.DiversionInformation.calling_present = 1, + .component.invoke.args.etsi.DiversionInformation.calling.presentation = 3, + .component.invoke.args.etsi.DiversionInformation.calling.screened.screening_indicator = 2, + .component.invoke.args.etsi.DiversionInformation.calling.screened.number.plan = 4, + .component.invoke.args.etsi.DiversionInformation.calling.screened.number.length = 4, + .component.invoke.args.etsi.DiversionInformation.calling.screened.number.str = "1803", + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_DiversionInformation, + .component.invoke.invoke_id = 81, + .component.invoke.args.etsi.DiversionInformation.diversion_reason = 2, + .component.invoke.args.etsi.DiversionInformation.basic_service = 4, + .component.invoke.args.etsi.DiversionInformation.q931ie.length = 5, + .component.invoke.args.etsi.DiversionInformation.q931ie_contents = "79828", + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_DiversionInformation, + .component.invoke.invoke_id = 82, + .component.invoke.args.etsi.DiversionInformation.diversion_reason = 2, + .component.invoke.args.etsi.DiversionInformation.basic_service = 4, + }, + + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_CallDeflection, + .component.invoke.invoke_id = 83, + .component.invoke.args.etsi.CallDeflection.deflection.number.plan = 4, + .component.invoke.args.etsi.CallDeflection.deflection.number.length = 4, + .component.invoke.args.etsi.CallDeflection.deflection.number.str = "1803", + .component.invoke.args.etsi.CallDeflection.presentation_allowed_to_diverted_to_user_present = 1, + .component.invoke.args.etsi.CallDeflection.presentation_allowed_to_diverted_to_user = 1, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_CallDeflection, + .component.invoke.invoke_id = 84, + .component.invoke.args.etsi.CallDeflection.deflection.number.plan = 4, + .component.invoke.args.etsi.CallDeflection.deflection.number.length = 4, + .component.invoke.args.etsi.CallDeflection.deflection.number.str = "1803", + .component.invoke.args.etsi.CallDeflection.presentation_allowed_to_diverted_to_user_present = 1, + .component.invoke.args.etsi.CallDeflection.presentation_allowed_to_diverted_to_user = 0, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_CallDeflection, + .component.invoke.invoke_id = 85, + .component.invoke.args.etsi.CallDeflection.deflection.number.plan = 4, + .component.invoke.args.etsi.CallDeflection.deflection.number.length = 4, + .component.invoke.args.etsi.CallDeflection.deflection.number.str = "1803", + }, + { + .type = ROSE_COMP_TYPE_RESULT, + .component.result.operation = ROSE_ETSI_CallDeflection, + .component.result.invoke_id = 86, + }, + + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_CallRerouting, + .component.invoke.invoke_id = 87, + .component.invoke.args.etsi.CallRerouting.rerouting_reason = 3, + .component.invoke.args.etsi.CallRerouting.rerouting_counter = 2, + .component.invoke.args.etsi.CallRerouting.called_address.number.plan = 4, + .component.invoke.args.etsi.CallRerouting.called_address.number.length = 4, + .component.invoke.args.etsi.CallRerouting.called_address.number.str = "1803", + .component.invoke.args.etsi.CallRerouting.q931ie.length = 129, + .component.invoke.args.etsi.CallRerouting.q931ie_contents = + "YEHAW." + " The quick brown fox jumped over the lazy dog test." + " Now is the time for all good men to come to the aid of their country.", + .component.invoke.args.etsi.CallRerouting.last_rerouting.presentation = 1, + .component.invoke.args.etsi.CallRerouting.subscription_option = 2, + .component.invoke.args.etsi.CallRerouting.calling_subaddress.type = 1, + .component.invoke.args.etsi.CallRerouting.calling_subaddress.length = 4, + .component.invoke.args.etsi.CallRerouting.calling_subaddress.u.nsap = "6492", + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_CallRerouting, + .component.invoke.invoke_id = 88, + .component.invoke.args.etsi.CallRerouting.rerouting_reason = 3, + .component.invoke.args.etsi.CallRerouting.rerouting_counter = 2, + .component.invoke.args.etsi.CallRerouting.called_address.number.plan = 4, + .component.invoke.args.etsi.CallRerouting.called_address.number.length = 4, + .component.invoke.args.etsi.CallRerouting.called_address.number.str = "1803", + .component.invoke.args.etsi.CallRerouting.q931ie.length = 2, + .component.invoke.args.etsi.CallRerouting.q931ie_contents = "RT", + .component.invoke.args.etsi.CallRerouting.last_rerouting.presentation = 1, + .component.invoke.args.etsi.CallRerouting.subscription_option = 2, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_CallRerouting, + .component.invoke.invoke_id = 89, + .component.invoke.args.etsi.CallRerouting.rerouting_reason = 3, + .component.invoke.args.etsi.CallRerouting.rerouting_counter = 2, + .component.invoke.args.etsi.CallRerouting.called_address.number.plan = 4, + .component.invoke.args.etsi.CallRerouting.called_address.number.length = 4, + .component.invoke.args.etsi.CallRerouting.called_address.number.str = "1803", + .component.invoke.args.etsi.CallRerouting.q931ie.length = 2, + .component.invoke.args.etsi.CallRerouting.q931ie_contents = "RT", + .component.invoke.args.etsi.CallRerouting.last_rerouting.presentation = 2, + }, + { + .type = ROSE_COMP_TYPE_RESULT, + .component.result.operation = ROSE_ETSI_CallRerouting, + .component.result.invoke_id = 90, + }, + + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_InterrogateServedUserNumbers, + .component.invoke.invoke_id = 91, + }, + { + .type = ROSE_COMP_TYPE_RESULT, + .component.result.operation = ROSE_ETSI_InterrogateServedUserNumbers, + .component.result.invoke_id = 92, + .component.result.args.etsi.InterrogateServedUserNumbers.num_records = 2, + .component.result.args.etsi.InterrogateServedUserNumbers.number[0].plan = 4, + .component.result.args.etsi.InterrogateServedUserNumbers.number[0].length = 4, + .component.result.args.etsi.InterrogateServedUserNumbers.number[0].str = "1803", + .component.result.args.etsi.InterrogateServedUserNumbers.number[1].plan = 4, + .component.result.args.etsi.InterrogateServedUserNumbers.number[1].length = 4, + .component.result.args.etsi.InterrogateServedUserNumbers.number[1].str = "5786", + }, + + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_DivertingLegInformation1, + .component.invoke.invoke_id = 93, + .component.invoke.args.etsi.DivertingLegInformation1.diversion_reason = 4, + .component.invoke.args.etsi.DivertingLegInformation1.subscription_option = 1, + .component.invoke.args.etsi.DivertingLegInformation1.diverted_to_present = 1, + .component.invoke.args.etsi.DivertingLegInformation1.diverted_to.presentation = 2, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_DivertingLegInformation1, + .component.invoke.invoke_id = 94, + .component.invoke.args.etsi.DivertingLegInformation1.diversion_reason = 4, + .component.invoke.args.etsi.DivertingLegInformation1.subscription_option = 1, + }, + + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_DivertingLegInformation2, + .component.invoke.invoke_id = 95, + .component.invoke.args.etsi.DivertingLegInformation2.diversion_counter = 3, + .component.invoke.args.etsi.DivertingLegInformation2.diversion_reason = 2, + .component.invoke.args.etsi.DivertingLegInformation2.diverting_present = 1, + .component.invoke.args.etsi.DivertingLegInformation2.diverting.presentation = 2, + .component.invoke.args.etsi.DivertingLegInformation2.original_called_present = 1, + .component.invoke.args.etsi.DivertingLegInformation2.original_called.presentation = 1, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_DivertingLegInformation2, + .component.invoke.invoke_id = 96, + .component.invoke.args.etsi.DivertingLegInformation2.diversion_counter = 3, + .component.invoke.args.etsi.DivertingLegInformation2.diversion_reason = 2, + .component.invoke.args.etsi.DivertingLegInformation2.original_called_present = 1, + .component.invoke.args.etsi.DivertingLegInformation2.original_called.presentation = 1, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_DivertingLegInformation2, + .component.invoke.invoke_id = 97, + .component.invoke.args.etsi.DivertingLegInformation2.diversion_counter = 1, + .component.invoke.args.etsi.DivertingLegInformation2.diversion_reason = 2, + }, + + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_DivertingLegInformation3, + .component.invoke.invoke_id = 98, + .component.invoke.args.etsi.DivertingLegInformation3.presentation_allowed_indicator = 1, + }, + + /* Explicit Call Transfer (ECT) */ + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_EctExecute, + .component.invoke.invoke_id = 54, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_ExplicitEctExecute, + .component.invoke.invoke_id = 55, + .component.invoke.args.etsi.ExplicitEctExecute.link_id = 23, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_RequestSubaddress, + .component.invoke.invoke_id = 56, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_SubaddressTransfer, + .component.invoke.invoke_id = 57, + .component.invoke.args.etsi.SubaddressTransfer.subaddress.type = 1, + .component.invoke.args.etsi.SubaddressTransfer.subaddress.length = 4, + .component.invoke.args.etsi.SubaddressTransfer.subaddress.u.nsap = "6492", + }, + + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_EctLinkIdRequest, + .component.invoke.invoke_id = 58, + }, + { + .type = ROSE_COMP_TYPE_RESULT, + .component.result.operation = ROSE_ETSI_EctLinkIdRequest, + .component.result.invoke_id = 59, + .component.result.args.etsi.EctLinkIdRequest.link_id = 76, + }, + + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_EctInform, + .component.invoke.invoke_id = 60, + .component.invoke.args.etsi.EctInform.status = 1, + .component.invoke.args.etsi.EctInform.redirection_present = 1, + .component.invoke.args.etsi.EctInform.redirection.presentation = 0, + .component.invoke.args.etsi.EctInform.redirection.number.plan = 8, + .component.invoke.args.etsi.EctInform.redirection.number.length = 4, + .component.invoke.args.etsi.EctInform.redirection.number.str = "6229", + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_EctInform, + .component.invoke.invoke_id = 61, + .component.invoke.args.etsi.EctInform.status = 1, + .component.invoke.args.etsi.EctInform.redirection_present = 1, + .component.invoke.args.etsi.EctInform.redirection.presentation = 1, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_EctInform, + .component.invoke.invoke_id = 62, + .component.invoke.args.etsi.EctInform.status = 1, + .component.invoke.args.etsi.EctInform.redirection_present = 1, + .component.invoke.args.etsi.EctInform.redirection.presentation = 2, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_EctInform, + .component.invoke.invoke_id = 63, + .component.invoke.args.etsi.EctInform.status = 1, + .component.invoke.args.etsi.EctInform.redirection_present = 1, + .component.invoke.args.etsi.EctInform.redirection.presentation = 3, + .component.invoke.args.etsi.EctInform.redirection.number.plan = 8, + .component.invoke.args.etsi.EctInform.redirection.number.length = 4, + .component.invoke.args.etsi.EctInform.redirection.number.str = "3340", + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_EctInform, + .component.invoke.invoke_id = 64, + .component.invoke.args.etsi.EctInform.status = 1, + .component.invoke.args.etsi.EctInform.redirection_present = 0, + }, + + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_EctLoopTest, + .component.invoke.invoke_id = 65, + .component.invoke.args.etsi.EctLoopTest.call_transfer_id = 7, + }, + { + .type = ROSE_COMP_TYPE_RESULT, + .component.result.operation = ROSE_ETSI_EctLoopTest, + .component.result.invoke_id = 66, + .component.result.args.etsi.EctLoopTest.loop_result = 2, + }, /* *INDENT-ON* */ }; @@ -2013,6 +2451,16 @@ int main(int argc, char *argv[]) (unsigned) sizeof(struct roseQsigMWIInterrogateRes)); pri_message(&dummy_ctrl, "\n"); + pri_message(&dummy_ctrl, "sizeof(struct roseEtsiForwardingList) = %u\n", + (unsigned) sizeof(struct roseEtsiForwardingList)); + pri_message(&dummy_ctrl, "sizeof(struct roseEtsiServedUserNumberList) = %u\n", + (unsigned) sizeof(struct roseEtsiServedUserNumberList)); + + pri_message(&dummy_ctrl, "\n"); + pri_message(&dummy_ctrl, "sizeof(struct roseEtsiCallRerouting_ARG) = %u\n", + (unsigned) sizeof(struct roseEtsiCallRerouting_ARG)); + pri_message(&dummy_ctrl, "sizeof(struct roseEtsiDiversionInformation_ARG) = %u\n", + (unsigned) sizeof(struct roseEtsiDiversionInformation_ARG)); pri_message(&dummy_ctrl, "sizeof(struct roseEtsiAOCSCurrencyInfoList) = %u\n", (unsigned) sizeof(struct roseEtsiAOCSCurrencyInfoList));