diff options
-rw-r--r-- | doc/smdi.txt | 148 | ||||
-rw-r--r-- | res/res_smdi.c | 90 |
2 files changed, 204 insertions, 34 deletions
diff --git a/doc/smdi.txt b/doc/smdi.txt index a4aa6bbd6..2181bc401 100644 --- a/doc/smdi.txt +++ b/doc/smdi.txt @@ -1,25 +1,137 @@ -Asterisk SMDI (Simple Message Desk Interface) integration ---------------------------------------------------------- +=============================================================================== +=============================================================================== +=== Asterisk SMDI (Simple Message Desk Interface) integration ================= +=============================================================================== +=============================================================================== -SMDI integration is configured in smdi.conf, zaptel.conf, and voicemail.conf. -Various characteristics of the SMDI interfaces to be used (serial ports) are -defined in smdi.conf. SMDI integration for callerid and MWI are defined in -zaptel.conf and voicemail.conf respectively. SMDI only works with Zaptel -interfaces configured for FXS signalling. +=============================================================================== +===== 1) Accessing SMDI information in the dialplan. ========================== +=============================================================================== -When SMDI is enabled and a call comes into Asterisk, the forwarding station -number is used as the destination for the call and any callerid information -present is used. This way you can configure your extensions.conf as follows to -behave as a message desk. +There are two dialplan functions that can be used to access the details of +incoming SMDI messages. -[default] +*CLI> core show function SMDI_MSG_RETRIEVE -exten => _XXXXXXX,1,VoiceMail(${EXTEN}|${SMDI_VM_TYPE}) -exten => _XXXXXXX,n,Hangup + -= Info about function 'SMDI_MSG_RETRIEVE' =- -exten => s,1,VoiceMailMain(${CALLERID(num)}) -exten => s,n,Hangup +[Syntax] +SMDI_MSG_RETRIEVE(<smdi port>,<search key>[,timeout[,options]]) -The ${SMDI_VM_TYPE} variable will be set to u, b, or nothing depending on the -contents of the type of SMDI message received. +[Synopsis] +Retrieve an SMDI message. +[Description] + This function is used to retrieve an incoming SMDI message. It returns +an ID which can be used with the SMDI_MSG() function to access details of +the message. Note that this is a destructive function in the sense that +once an SMDI message is retrieved using this function, it is no longer in +the global SMDI message queue, and can not be accessed by any other Asterisk +channels. The timeout for this function is optional, and the default is +3 seconds. When providing a timeout, it should be in milliseconds. + The default search is done on the forwarding station ID. However, if +you set one of the search key options in the options field, you can change +this behavior. + Options: + t - Instead of searching on the forwarding station, search on the message + desk terminal. + n - Instead of searching on the forwarding station, search on the message + desk number. + + +*CLI> core show function SMDI_MSG + + -= Info about function 'SMDI_MSG' =- + +[Syntax] +SMDI_MSG(<message_id>,<component>) + +[Synopsis] +Retrieve details about an SMDI message. + +[Description] + This function is used to access details of an SMDI message that was +pulled from the incoming SMDI message queue using the SMDI_MSG_RETRIEVE() +function. + Valid message components are: + station - The forwarding station + callerid - The callerID of the calling party that was forwarded + type - The call type. The value here is the exact character + that came in on the SMDI link. Typically, example values + are: D - Direct Calls, A - Forward All Calls, + B - Forward Busy Calls, N - Forward No Answer Calls + + +Here is an example of how to use these functions: + +; Retrieve the SMDI message that is associated with the number that +; was called in Asterisk. +exten => _0XXX,1,Set(SMDI_MSG_ID=${SMDI_MSG_RETRIEVE(/dev/tty0,${EXTEN})}) + +; Ensure that the message was retrieved. +exten => _0XXX,n,GotoIf($["x${SMDI_MSG_ID}" != "x"]?processcall:hangup) +exten => _0XXX,n(hangup),NoOp(No SMDI message retrieved for ${EXTEN}) + +; Grab the details out of the SMDI message. +exten => _0XXX,n(processcall),NoOp(Message found for ${EXTEN}) +exten => _0XXX,n,Set(SMDI_EXTEN=${SMDI_MSG(${SMDI_MSG_ID},station)}) +exten => _0XXX,n,Set(SMDI_CID=${SMDI_MSG(${SMDI_MSG_ID},callerid)}) + +; Map SMDI message types to the right voicemail option. If it is "B", use the +; busy option. Otherwise, use the unavailable option. +exten => _0XXX,n,GotoIf($["${SMDI_MSG(${SMDI_MSG_ID},type)}" == "B"]?usebusy:useunavail) + +exten => _0XXX,n(usebusy),Set(SMDI_VM_TYPE=b) +exten => _0XXX,n,Goto(continue) + +exten => _0XXX,n,(useunavil),Set(SMDI_VM_TYPE=u) + +exten => _0XXX,n(continue),NoOp( Process the rest of the call ... ) + + +=============================================================================== +===== 2) Ensuring complete MWI information over SMDI ========================== +=============================================================================== + +Another change has been made to ensure that MWI state is properly propagated +over the SMDI link. This replaces the use of externnotify=smdi for +voicemail.conf. The issue is that we have to poll mailboxes occasionally for +changes that were made using an IMAP client. So, this ability was added to +res_smdi. To configure this, there is a new section in smdi.conf. It looks +like this: + +[mailboxes] +; This section configures parameters related to MWI handling for the SMDI link. + ; +; This option configures the polling interval used to check to see if the +; mailboxes have any new messages. This option is specified in seconds. +; The default value is 10 seconds. +; +;pollinginterval=10 +; +; Before specifying mailboxes, you must specify an SMDI interface. All mailbox +; definitions that follow will correspond to that SMDI interface. If you +; specify another interface, then all definitions following that will correspond +; to the new interface. +; +; Every other entry in this section of the configuration file is interpreted as +; a mapping between the mailbox ID on the SMDI link, and the local Asterisk +; mailbox name. In many cases, they are the same thing, but they still must be +; listed here so that this module knows which mailboxes it needs to pay +; attention to. +; +; Syntax: +; <SMDI mailbox ID>=<Asterisk Mailbox Name>[@Asterisk Voicemail Context] +; +; If no Asterisk voicemail context is specified, "default" will be assumed. +; +; +;smdiport=/dev/ttyS0 +;2565551234=1234@vmcontext1 +;2565555678=5678@vmcontext2 +;smdiport=/dev/ttyS1 +;2565559999=9999 + +=============================================================================== +=============================================================================== +=============================================================================== diff --git a/res/res_smdi.c b/res/res_smdi.c index b6a0b4593..83a5e1f7c 100644 --- a/res/res_smdi.c +++ b/res/res_smdi.c @@ -369,8 +369,13 @@ static void *smdi_msg_pop(struct ast_smdi_interface *iface, enum smdi_message_ty return msg; } +enum { + OPT_SEARCH_TERMINAL = (1 << 0), + OPT_SEARCH_NUMBER = (1 << 1), +}; + static void *smdi_msg_find(struct ast_smdi_interface *iface, - enum smdi_message_type type, const char *station) + enum smdi_message_type type, const char *search_key, struct ast_flags options) { void *msg = NULL; @@ -378,10 +383,35 @@ static void *smdi_msg_find(struct ast_smdi_interface *iface, switch (type) { case SMDI_MD: - msg = ASTOBJ_CONTAINER_FIND(&iface->md_q, station); + if (ast_test_flag(&options, OPT_SEARCH_TERMINAL)) { + struct ast_smdi_md_message *md_msg = NULL; + + /* Searching by the message desk terminal */ + + ASTOBJ_CONTAINER_TRAVERSE(&iface->md_q, !md_msg, do { + if (!strcasecmp(iterator->mesg_desk_term, search_key)) + md_msg = ASTOBJ_REF(iterator); + } while (0); ); + + msg = md_msg; + } else if (ast_test_flag(&options, OPT_SEARCH_NUMBER)) { + struct ast_smdi_md_message *md_msg = NULL; + + /* Searching by the message desk number */ + + ASTOBJ_CONTAINER_TRAVERSE(&iface->md_q, !md_msg, do { + if (!strcasecmp(iterator->mesg_desk_num, search_key)) + md_msg = ASTOBJ_REF(iterator); + } while (0); ); + + msg = md_msg; + } else { + /* Searching by the forwarding station */ + msg = ASTOBJ_CONTAINER_FIND(&iface->md_q, search_key); + } break; case SMDI_MWI: - msg = ASTOBJ_CONTAINER_FIND(&iface->mwi_q, station); + msg = ASTOBJ_CONTAINER_FIND(&iface->mwi_q, search_key); break; } @@ -389,7 +419,7 @@ static void *smdi_msg_find(struct ast_smdi_interface *iface, } static void *smdi_message_wait(struct ast_smdi_interface *iface, int timeout, - enum smdi_message_type type, const char *station) + enum smdi_message_type type, const char *search_key, struct ast_flags options) { struct timeval start; long diff = 0; @@ -415,7 +445,7 @@ static void *smdi_message_wait(struct ast_smdi_interface *iface, int timeout, lock_msg_q(iface, type); - if ((msg = smdi_msg_find(iface, type, station))) { + if ((msg = smdi_msg_find(iface, type, search_key, options))) { unlock_msg_q(iface, type); return msg; } @@ -429,7 +459,7 @@ static void *smdi_message_wait(struct ast_smdi_interface *iface, int timeout, ast_cond_timedwait(cond, lock, &ts); - if ((msg = smdi_msg_find(iface, type, station))) { + if ((msg = smdi_msg_find(iface, type, search_key, options))) { unlock_msg_q(iface, type); return msg; } @@ -450,7 +480,8 @@ struct ast_smdi_md_message *ast_smdi_md_message_pop(struct ast_smdi_interface *i struct ast_smdi_md_message *ast_smdi_md_message_wait(struct ast_smdi_interface *iface, int timeout) { - return smdi_message_wait(iface, timeout, SMDI_MD, NULL); + struct ast_flags options = { 0 }; + return smdi_message_wait(iface, timeout, SMDI_MD, NULL, options); } struct ast_smdi_mwi_message *ast_smdi_mwi_message_pop(struct ast_smdi_interface *iface) @@ -460,13 +491,15 @@ struct ast_smdi_mwi_message *ast_smdi_mwi_message_pop(struct ast_smdi_interface struct ast_smdi_mwi_message *ast_smdi_mwi_message_wait(struct ast_smdi_interface *iface, int timeout) { - return smdi_message_wait(iface, timeout, SMDI_MWI, NULL); + struct ast_flags options = { 0 }; + return smdi_message_wait(iface, timeout, SMDI_MWI, NULL, options); } struct ast_smdi_mwi_message *ast_smdi_mwi_message_wait_station(struct ast_smdi_interface *iface, int timeout, const char *station) { - return smdi_message_wait(iface, timeout, SMDI_MWI, station); + struct ast_flags options = { 0 }; + return smdi_message_wait(iface, timeout, SMDI_MWI, station, options); } struct ast_smdi_interface *ast_smdi_interface_find(const char *iface_name) @@ -1059,14 +1092,21 @@ static int smdi_msg_id; /*! In milliseconds */ #define SMDI_RETRIEVE_TIMEOUT_DEFAULT 3000 +AST_APP_OPTIONS(smdi_msg_ret_options, BEGIN_OPTIONS + AST_APP_OPTION('t', OPT_SEARCH_TERMINAL), + AST_APP_OPTION('n', OPT_SEARCH_NUMBER), +END_OPTIONS ); + static int smdi_msg_retrieve_read(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len) { struct ast_module_user *u; AST_DECLARE_APP_ARGS(args, AST_APP_ARG(port); - AST_APP_ARG(station); + AST_APP_ARG(search_key); AST_APP_ARG(timeout); + AST_APP_ARG(options); ); + struct ast_flags options = { 0 }; unsigned int timeout = SMDI_RETRIEVE_TIMEOUT_DEFAULT; int res = -1; char *parse = NULL; @@ -1092,7 +1132,7 @@ static int smdi_msg_retrieve_read(struct ast_channel *chan, char *cmd, char *dat parse = ast_strdupa(data); AST_STANDARD_APP_ARGS(args, parse); - if (ast_strlen_zero(args.port) || ast_strlen_zero(args.station)) { + if (ast_strlen_zero(args.port) || ast_strlen_zero(args.search_key)) { ast_log(LOG_ERROR, "Invalid arguments provided to SMDI_MSG_RETRIEVE\n"); goto return_error; } @@ -1102,6 +1142,10 @@ static int smdi_msg_retrieve_read(struct ast_channel *chan, char *cmd, char *dat goto return_error; } + if (!ast_strlen_zero(args.options)) { + ast_app_parse_options(smdi_msg_ret_options, &options, NULL, args.options); + } + if (!ast_strlen_zero(args.timeout)) { if (sscanf(args.timeout, "%u", &timeout) != 1) { ast_log(LOG_ERROR, "'%s' is not a valid timeout\n", args.timeout); @@ -1109,9 +1153,9 @@ static int smdi_msg_retrieve_read(struct ast_channel *chan, char *cmd, char *dat } } - if (!(md_msg = smdi_message_wait(iface, timeout, SMDI_MD, args.station))) { - ast_log(LOG_WARNING, "No SMDI message retrieved for station '%s' after " - "waiting %u ms.\n", args.station, timeout); + if (!(md_msg = smdi_message_wait(iface, timeout, SMDI_MD, args.search_key, options))) { + ast_log(LOG_WARNING, "No SMDI message retrieved for search key '%s' after " + "waiting %u ms.\n", args.search_key, timeout); goto return_error; } @@ -1200,7 +1244,11 @@ static int smdi_msg_read(struct ast_channel *chan, char *cmd, char *data, char * smd = datastore->data; - if (!strcasecmp(args.component, "station")) { + if (!strcasecmp(args.component, "number")) { + ast_copy_string(buf, smd->md_msg->mesg_desk_num, len); + } else if (!strcasecmp(args.component, "terminal")) { + ast_copy_string(buf, smd->md_msg->mesg_desk_term, len); + } else if (!strcasecmp(args.component, "station")) { ast_copy_string(buf, smd->md_msg->fwd_st, len); } else if (!strcasecmp(args.component, "callerid")) { ast_copy_string(buf, smd->md_msg->calling_st, len); @@ -1223,7 +1271,7 @@ return_error: static struct ast_custom_function smdi_msg_retrieve_function = { .name = "SMDI_MSG_RETRIEVE", .synopsis = "Retrieve an SMDI message.", - .syntax = "SMDI_MSG_RETRIEVE(<smdi port>,<station>[,timeout])", + .syntax = "SMDI_MSG_RETRIEVE(<smdi port>,<search key>[,timeout[,options]])", .desc = " This function is used to retrieve an incoming SMDI message. It returns\n" "an ID which can be used with the SMDI_MSG() function to access details of\n" @@ -1232,6 +1280,14 @@ static struct ast_custom_function smdi_msg_retrieve_function = { "the global SMDI message queue, and can not be accessed by any other Asterisk\n" "channels. The timeout for this function is optional, and the default is\n" "3 seconds. When providing a timeout, it should be in milliseconds.\n" + " The default search is done on the forwarding station ID. However, if\n" + "you set one of the search key options in the options field, you can change\n" + "this behavior.\n" + " Options:\n" + " t - Instead of searching on the forwarding station, search on the message\n" + " desk terminal.\n" + " n - Instead of searching on the forwarding station, search on the message\n" + " desk number.\n" "", .read = smdi_msg_retrieve_read, }; @@ -1245,6 +1301,8 @@ static struct ast_custom_function smdi_msg_function = { "pulled from the incoming SMDI message queue using the SMDI_MSG_RETRIEVE()\n" "function.\n" " Valid message components are:\n" + " number - The message desk number\n" + " terminal - The message desk terminal\n" " station - The forwarding station\n" " callerid - The callerID of the calling party that was forwarded\n" " type - The call type. The value here is the exact character\n" |